提交 98a5a280 编写于 作者: C Chris Wanstrath

Merge remote branch 'mislav/tracking'

......@@ -36,12 +36,16 @@ Install
Assuming `~/bin/` is in your `$PATH`, you're ready to roll:
$ hub version
git version 1.6.4.2
hub version 0.3.2
git version 1.7.0.4
hub version 1.1.0
### Homebrew
brew install hub
$ brew install hub
$ which hub
/usr/local/bin/hub
$ hub version
...
### RubyGems
......@@ -49,18 +53,20 @@ Though not recommended, `hub` can also be installed as a RubyGem:
$ gem install git-hub
(Yes, the gem name is `git-hub`.)
Yes, the gem name is "git-hub".
(It's not recommended because of the RubyGems startup time. See [this
gist][speed] for information.)
(It's not recommended for casual use because of the RubyGems startup
time. See [this gist][speed] for information.)
### Standalone via RubyGems
Yes, the gem name is still `git-hub`:
Yes, the gem name is still "git-hub":
$ gem install git-hub
$ hub hub standalone > ~/bin/hub && chmod 755 ~/bin/hub
$ gem uninstall git-hub
This installs a standalone version which doesn't require RubyGems to
run.
### Source
......@@ -153,6 +159,9 @@ login" below for details.
$ git browse
> open http://github.com/CURRENT_REPO
$ git browse -- issues
> open http://github.com/CURRENT_REPO/issues
$ git browse schacon/ticgit
> open http://github.com/schacon/ticgit
......@@ -162,6 +171,9 @@ login" below for details.
$ git browse resque
> open http://github.com/YOUR_USER/resque
$ git browse resque network
> open http://github.com/YOUR_USER/resque/network
$ git browse -p resque
> open https://github.com:YOUR_USER/resque
......@@ -221,13 +233,13 @@ Configuration
-------------
If you prefer `http://` clones to `git://` clones, you can set the
`hub.http-clone` option using `git-config`.
`hub.http-clone` option to true using `git-config`.
For example:
$ git clone defunkt/repl
< git clone >
$ git config --global --add hub.http-clone yes
$ git config --global --bool hub.http-clone true
$ git clone defunkt/repl
< http clone >
......
......@@ -5,7 +5,8 @@ require 'rake/testtask'
#
def command?(command)
system("type #{command} > /dev/null")
`type -t #{command}`
$?.success?
end
task :load_hub do
......
require 'hub/version'
require 'hub/args'
require 'hub/context'
require 'hub/commands'
require 'hub/runner'
module Hub
# See context.rb
module Context; end
# The Commands module houses the git commands that hub
# lovingly wraps. If a method exists here, it is expected to have a
# corresponding git command which either gets run before or after
......@@ -31,25 +34,11 @@ module Hub
instance_methods.each { |m| undef_method(m) unless m =~ /(^__|send|to\?$)/ }
extend self
# Templates and useful information.
USER = `git config --global github.user`.chomp
TOKEN = `git config --global github.token`.chomp
ORIGIN = `git config remote.origin.url`.chomp
HTTP_CLONE = `git config --global hub.http-clone`.chomp == 'yes'
PUBLIC = (HTTP_CLONE ? 'http' : 'git') + '://github.com/%s/%s.git'
PRIVATE = 'git@github.com:%s/%s.git'
LGHCONF = "http://github.com/guides/local-github-config"
API_REPO = 'http://github.com/api/v2/yaml/repos/show/%s/%s'
API_FORK = 'http://github.com/api/v2/yaml/repos/fork/%s/%s'
# Set the repo name based on the current origin or, as a fallback,
# the cwd.
if ORIGIN =~ %r{\bgithub\.com[:/](.+)/(.+).git$}
OWNER, REPO = $1, $2
else
REPO = File.basename(Dir.pwd)
OWNER = ''
end
# Provides `github_url` and various inspection methods
extend Context
API_REPO = 'http://github.com/api/v2/yaml/repos/show/%s/%s'
API_FORK = 'http://github.com/api/v2/yaml/repos/fork/%s/%s'
# $ hub clone rtomayko/tilt
# > git clone git://github.com/rtomayko/tilt.
......@@ -76,15 +65,10 @@ module Hub
if arg =~ %r{.+?://|.+?@} || File.directory?(arg)
# Bail out early for URLs and local paths.
break
elsif arg.scan('/').size == 1 && !arg.include?(':')
elsif arg.scan('/').size <= 1 && !arg.include?(':')
# $ hub clone rtomayko/tilt
url = ssh ? PRIVATE : PUBLIC
args[args.index(arg)] = url % arg.split('/')
break
elsif arg !~ /:|\//
# $ hub clone tilt
url = ssh ? PRIVATE : PUBLIC
args[args.index(arg)] = url % [ github_user, arg ]
args[args.index(arg)] = github_url(:repo => arg, :private => ssh)
break
end
end
......@@ -103,7 +87,7 @@ module Hub
args.delete_at index
branch = args.index('-b') || args.index('--branch')
if branch
if branch
args.delete_at branch
branch_name = args.delete_at branch
end
......@@ -128,15 +112,14 @@ module Hub
return if args[1] != 'add' or args.last =~ %r{.+?://|.+?@|^[./]}
ssh = args.delete('-p')
url = ssh ? PRIVATE : PUBLIC
# user/repo
args.last =~ /\b(.+?)(?:\/(.+))?$/
user, repo = $1, $2 || REPO
user, repo = $1, $2
if args.words[2] == 'origin' && args.words[3].nil?
# Origin special case.
user = github_user
# Origin special case triggers default user/repo
user = repo = nil
elsif args.words[-2] == args.words[1]
# rtomayko/tilt => rtomayko
# Make sure you dance around flags.
......@@ -149,7 +132,7 @@ module Hub
args.replace args[0...-1]
end
args << url % [ user, repo ]
args << github_url(:user => user, :repo => repo, :private => ssh)
end
# $ hub init -g
......@@ -159,7 +142,7 @@ module Hub
if args.delete('-g')
# Can't do anything if we don't have a USER set.
url = PRIVATE % [ github_user, REPO ]
url = github_url(:private => true)
args.after "git remote add origin #{url}"
end
end
......@@ -171,9 +154,9 @@ module Hub
require 'net/http'
# can't do anything without token and original owner name
if github_user && github_token && !OWNER.empty?
if github_user && github_token && repo_owner
if own_repo_exists?
puts "#{github_user}/#{REPO} already exists on GitHub"
puts "#{github_user}/#{repo_name} already exists on GitHub"
else
fork_repo
end
......@@ -181,7 +164,7 @@ module Hub
if args.include?('--no-remote')
exit
else
url = PRIVATE % [ github_user, REPO ]
url = github_url(:private => true)
args.replace %W"remote add -f #{github_user} #{url}"
args.after { puts "new remote: #{github_user}" }
end
......@@ -210,6 +193,9 @@ module Hub
# $ hub browse
# > open http://github.com/CURRENT_REPO
#
# $ hub browse -- issues
# > open http://github.com/CURRENT_REPO/issues
#
# $ hub browse pjhyett/github-services
# > open http://github.com/pjhyett/github-services
#
......@@ -219,58 +205,75 @@ module Hub
# $ hub browse github-services
# > open http://github.com/YOUR_LOGIN/github-services
#
# $ hub browse github-services wiki
# > open http://wiki.github.com/YOUR_LOGIN/github-services
#
# $ hub browse -p github-fi
# > open https://github.com/YOUR_LOGIN/github-fi
def browse(args)
args.shift
protocol = args.delete('-p') ? 'https' : 'http'
dest = args.pop
browse_command(args) do
user = repo = nil
dest = args.shift
dest = nil if dest == '--'
if dest
if dest.include? '/'
if dest
# $ hub browse pjhyett/github-services
user, repo = dest.split('/')
else
# $ hub browse github-services
user, repo = github_user, dest
repo = dest
elsif repo_user
# $ hub browse
user = repo_user
else
warn "Usage: hub browse [<USER>/]<REPOSITORY>"
exit(1)
end
elsif !OWNER.empty?
# $ hub browse
user, repo = OWNER, REPO
else
warn "Usage: hub browse [<USER>/]<REPOSITORY>"
exit(1)
end
browser args, "#{protocol}://github.com/#{user}/#{repo}"
params = { :user => user, :repo => repo }
# $ hub browse -- wiki
case subpage = args.shift
when 'wiki'
params[:web] = 'wiki'
when 'commits'
branch = (!dest && tracked_branch) || 'master'
params[:web] = "/commits/#{branch}"
when 'tree', NilClass
branch = !dest && tracked_branch
params[:web] = "/tree/#{branch}" if branch and branch != 'master'
else
params[:web] = "/#{subpage}"
end
params
end
end
# $ hub compare 1.0...fix
# > open http://github.com/CURRENT_REPO/compare/1.0...fix
# $ hub compare refactor
# > open http://github.com/CURRENT_REPO/comapre/refactor
# > open http://github.com/CURRENT_REPO/compare/refactor
# $ hub compare myfork feature
# > open http://github.com/myfork/REPO/compare/feature
# $ hub compare -p myfork topsecret
# > open https://github.com/myfork/REPO/compare/topsecret
# $ hub compare -u 1.0...2.0
# (Prints the URL for the compare view)
# prints "http://github.com/CURRENT_REPO/compare/1.0...2.0"
def compare(args)
args.shift
url_only = args.delete('-u')
range = args.pop
user = args.pop || OWNER
if range && !OWNER.empty?
url = "http://github.com/#{user}/#{REPO}/compare/#{range}"
else
warn "Usage: hub compare [<START>...]<END>"
exit(1)
end
if url_only
puts url
exit
else
browser args, url
browse_command(args) do
if args.empty?
if branch = tracked_branch and branch != 'master'
range, user = branch, repo_user
else
warn "Usage: hub compare [USER] [<START>...]<END>"
exit(1)
end
else
range = args.pop
user = args.pop || repo_user
end
{ :user => user, :web => "/compare/#{range}" }
end
end
......@@ -415,56 +418,45 @@ help
#
# Returns a Boolean.
def command?(name)
system "type #{name} > /dev/null 2>&1"
`type -t #{command}`
$?.success?
end
# Launches a given url in the user's browser, checking $BROWSER
# first then falling back to a few common launchers. Prints an
# error if it can't find anything appropriate.
# Detects commands to launch the user's browser, checking $BROWSER
# first then falling back to a few common launchers. Aborts with
# an error if it can't find anything appropriate.
#
# args - The args for this command. We modify them in place.
# url - The String URL to open in a browser.
#
# Returns nothing.
def browser(args, url)
# Returns a launch command.
def browser_launcher
if ENV['BROWSER']
cmd = ENV['BROWSER']
ENV['BROWSER']
elsif RUBY_PLATFORM.include?('darwin')
cmd = "open"
"open"
elsif command?("xdg-open")
cmd = "xdg-open"
"xdg-open"
elsif command?("cygstart")
cmd = "cygstart"
"cygstart"
else
abort "Please set $BROWSER to a web launcher to use this command."
end
args.executable = cmd
args.push url
end
# Either returns the GitHub user as set by git-config(1) or aborts
# with an error message.
def github_user
if USER.empty?
abort "** No GitHub user set. See #{LGHCONF}"
else
USER
end
end
# Handles common functionality of browser commands like `browse`
# and `compare`. Yields a block that returns params for `github_url`.
def browse_command(args)
url_only = args.delete('-u')
secure = args.delete('-p')
params = yield
def github_token
if TOKEN.empty?
abort "** No GitHub token set. See #{LGHCONF}"
else
TOKEN
end
args.executable = url_only ? 'echo' : browser_launcher
args.push github_url({:web => true, :private => secure}.update(params))
end
# Returns the terminal-formatted manpage, ready to be printed to
# the screen.
def hub_manpage
return "** Can't find groff(1)" unless groff?
return "** Can't find groff(1)" unless command?('groff')
require 'open3'
out = nil
......@@ -476,12 +468,6 @@ help
out
end
# Returns true if groff is installed and in our path, false if
# not.
def groff?
system("which groff")
end
# The groff command complete with crazy arguments we need to run
# in order to turn our raw roff (manpage markup) into something
# readable on the terminal.
......@@ -550,7 +536,7 @@ help
#
# Returns a Boolean.
def own_repo_exists?
url = API_REPO % [USER, REPO]
url = API_REPO % [github_user, repo_name]
Net::HTTPSuccess === Net::HTTP.get_response(URI(url))
end
......@@ -558,8 +544,8 @@ help
#
# Returns nothing.
def fork_repo
url = API_FORK % [OWNER, REPO]
Net::HTTP.post_form(URI(url), 'login' => USER, 'token' => TOKEN)
url = API_FORK % [repo_owner, repo_name]
Net::HTTP.post_form(URI(url), 'login' => github_user, 'token' => github_token)
end
end
end
module Hub
# Provides methods for inspecting the environment, such as GitHub user/token
# settings, repository info, and similar.
module Context
# Caches output when shelling out to git
GIT_CONFIG = Hash.new do |cache, cmd|
result = %x{git #{cmd}}.chomp
cache[cmd] = $?.success? && !result.empty? ? result : nil
end
# Parses URLs for git remotes and stores info
REMOTES = Hash.new do |cache, remote|
url = GIT_CONFIG["config remote.#{remote}.url"]
if url and url.to_s =~ %r{\bgithub\.com[:/](.+)/(.+).git$}
cache[remote] = { :user => $1, :repo => $2 }
else
cache[remote] = { }
end
end
LGHCONF = "http://github.com/guides/local-github-config"
private
def repo_owner
REMOTES[default_remote][:user]
end
def repo_user
REMOTES[current_remote][:user]
end
def repo_name
REMOTES[default_remote][:repo] || File.basename(Dir.pwd)
end
# Either returns the GitHub user as set by git-config(1) or aborts
# with an error message.
def github_user(fatal = true)
GIT_CONFIG['config github.user'] or
fatal ? abort("** No GitHub user set. See #{LGHCONF}") : nil
end
def github_token(fatal = true)
GIT_CONFIG['config github.token'] or
fatal ? abort("** No GitHub token set. See #{LGHCONF}") : nil
end
def current_branch
GIT_CONFIG['symbolic-ref -q HEAD']
end
def tracked_branch
branch = current_branch && tracked_for(current_branch)
normalize_branch(branch) if branch
end
def current_remote
(current_branch && remote_for(current_branch)) || default_remote
end
def default_remote
'origin'
end
def normalize_branch(branch)
branch.sub('refs/heads/', '')
end
def remote_for(branch)
GIT_CONFIG['config branch.%s.remote' % normalize_branch(branch)]
end
def tracked_for(branch)
GIT_CONFIG['config branch.%s.merge' % normalize_branch(branch)]
end
def http_clone?
GIT_CONFIG['config --bool hub.http-clone'] == 'true'
end
def github_url(options = {})
repo = options[:repo]
user, repo = repo.split('/') if repo and repo.index('/')
user ||= options[:user] || github_user
repo ||= repo_name
secure = options[:private]
if options[:web] == 'wiki'
scheme = secure ? 'https:' : 'http:'
'%s//wiki.github.com/%s/%s/' % [scheme, user, repo]
elsif options[:web]
scheme = secure ? 'https:' : 'http:'
path = options[:web] == true ? '' : options[:web].to_s
'%s//github.com/%s/%s%s' % [scheme, user, repo, path]
else
if secure
'git@github.com:%s/%s.git'
elsif http_clone?
'http://github.com/%s/%s.git'
else
'git://github.com/%s/%s.git'
end % [user, repo]
end
end
end
end
......@@ -7,7 +7,10 @@
\fBhub\fR \-\- git + hub = github
.
.SH "SYNOPSIS"
\fBhub\fR \fICOMMAND\fR \fIOPTIONS\fR \fBhub alias\fR [\fB\-s\fR] \fISHELL\fR
\fBhub\fR \fICOMMAND\fR \fIOPTIONS\fR
.
.br
\fBhub alias\fR [\fB\-s\fR] \fISHELL\fR
.
.P
\fBgit init \-g\fR \fIOPTIONS\fR
......@@ -22,14 +25,17 @@
\fBgit push\fR \fIREMOTE\-1\fR,\fIREMOTE\-2\fR,...,\fIREMOTE\-N\fR \fIREF\fR
.
.br
\fBgit fork\fR
\fBgit browse\fR [\fB\-p\fR] [\fB\-u\fR] [[\fIUSER\fR\fB/\fR]\fIREPOSITORY\fR] [SUBPAGE]
.
.br
\fBgit browse\fR [\fB\-p\fR] [\fIUSER\fR/]\fIREPOSITORY\fR
\fBgit compare\fR [\fB\-p\fR] [\fB\-u\fR] [\fIUSER\fR] [\fISTART\fR...]\fIEND\fR
.
.br
\fBgit submodule add\fR [\fB\-p\fR] \fIOPTIONS\fR [\fIUSER\fR/]\fIREPOSITORY\fR \fIDIRECTORY\fR
.
.br
\fBgit fork\fR [\fB\-\-no\-remote\fR]
.
.SH "DESCRIPTION"
\fBhub\fR enhances various \fBgit\fR commands with GitHub remote expansion. The
alias command displays information on configuring your environment:
......@@ -37,7 +43,10 @@ alias command displays information on configuring your environment:
.TP
\fBhub alias\fR [\fB\-s\fR] \fISHELL\fR
Writes shell aliasing code for \fISHELL\fR (\fBbash\fR, \fBsh\fR, \fBzsh\fR, \fBcsh\fR) to standard output. With the \fB\-s\fR option, the output of
this command can be evaluated directly within the shell: \fBeval $(hub alias \-s bash)\fR
this command can be evaluated directly within the shell:
.
.br
\fBeval $(hub alias \-s bash)\fR
.
.TP
\fBgit init\fR \fB\-g\fR \fIOPTIONS\fR
......@@ -64,23 +73,19 @@ Push \fIREF\fR to each of \fIREMOTE\-1\fR through \fIREMOTE\-N\fR by executing
multiple \fBgit push\fR commands.
.
.TP
\fBgit fork\fR
Forks the original repo on GitHub and adds the new remote under your
username. It requires your GitHub login and token to be present. See
CONFIGURATION below.
.
.TP
\fBgit browse\fR [\fB\-p\fR] [[\fIUSER\fR\fB/\fR]\fIREPOSITORY\fR]
\fBgit browse\fR [\fB\-p\fR] [\fB\-u\fR] [[\fIUSER\fR\fB/\fR]\fIREPOSITORY\fR] [SUBPAGE]
Open repository's GitHub page in the system's default web browser
using \fBopen(1)\fR or the \fBBROWSER\fR env variable. Use \fB\-p\fR to open a
page with https. If the repository isn't specified, \fBbrowse\fR opens
the page of the repository found in the current directory.
the page of the repository found in the current directory. If SUBPAGE
is specified, the browser will open on the specified subpage: one of
"wiki", "commits", "issues" or other (the default is "tree").
.
.TP
\fBgit compare\fR [\fB\-u\fR] [\fIUSER\fR] [\fISTART\fR...]\fIEND\fR
\fBgit compare\fR [\fB\-p\fR] [\fB\-u\fR] [\fIUSER\fR] [\fISTART\fR...]\fIEND\fR
Open a GitHub compare view page in the system's default web browser. \fISTART\fR to \fIEND\fR are branch names, tag names, or commit SHA1s specifying
the range of history to compare. If \fISTART\fR is omitted,
the repository's default branch is assumed.
the range of history to compare. If \fISTART\fR is omitted, GitHub will
compare against the base branch (the default is "master").
.
.TP
\fBgit submodule add\fR [\fB\-p\fR] \fIOPTIONS\fR [\fIUSER\fR/]\fIREPOSITORY\fR \fIDIRECTORY\fR
......@@ -90,9 +95,9 @@ your GitHub login. With \fB\-p\fR, use private remote
.
.TP
\fBgit fork\fR [\fB\-\-no\-remote\fR]
Forks the original project (as specified in "origin" remote) on GitHub
and adds a new remote named \fIUSER\fR referencing the newly created repo.
Requires \fBgithub.token\fR to be set (see CONFIGURATION).
Forks the original project (referenced by "origin" remote) on GitHub and
adds a new remote for it under your username. Requires \fBgithub.token\fR to
be set (see CONFIGURATION).
.
.TP
\fBgit help\fR
......@@ -137,7 +142,7 @@ cloning:
.
.nf
$ git config \-\-global \-\-add hub.http\-clone yes
$ git config \-\-global \-\-bool hub.http\-clone true
.
.fi
.
......@@ -213,6 +218,12 @@ $ git push origin,staging,qa bert_timeout
.
.nf
$ git browse
> open http://github.com/CURRENT_REPO
$ git browse \-\- issues
> open http://github.com/CURRENT_REPO/issues
$ git browse schacon/ticgit
> open http://github.com/schacon/ticgit
......@@ -222,8 +233,11 @@ $ git browse \-p schacon/ticgit
$ git browse resque
> open http://github.com/YOUR_USER/resque
$ git browse resque network
> open http://github.com/YOUR_USER/resque/network
$ git browse \-p resque
> open https://github.com:YOUR_USER/resque
> open https://github.com/YOUR_USER/resque
.
.fi
.
......
......@@ -67,16 +67,17 @@
<h2>SYNOPSIS</h2>
<p><code>hub</code> <var>COMMAND</var> <var>OPTIONS</var>
<p><code>hub</code> <var>COMMAND</var> <var>OPTIONS</var><br />
<code>hub alias</code> [<code>-s</code>] <var>SHELL</var></p>
<p><code>git init -g</code> <var>OPTIONS</var><br />
<code>git clone</code> [<code>-p</code>] <var>OPTIONS</var> [<var>USER</var>/]<var>REPOSITORY</var> <var>DIRECTORY</var><br />
<code>git remote add</code> [<code>-p</code>] <var>OPTIONS</var> <var>USER</var>[/<var>REPOSITORY</var>]<br />
<code>git push</code> <var>REMOTE-1</var>,<var>REMOTE-2</var>,...,<var>REMOTE-N</var> <var>REF</var><br />
<code>git fork</code><br />
<code>git browse</code> [<code>-p</code>] [<var>USER</var>/]<var>REPOSITORY</var><br />
<code>git submodule add</code> [<code>-p</code>] <var>OPTIONS</var> [<var>USER</var>/]<var>REPOSITORY</var> <var>DIRECTORY</var></p>
<code>git browse</code> [<code>-p</code>] [<code>-u</code>] [[<var>USER</var><code>/</code>]<var>REPOSITORY</var>] [SUBPAGE]<br />
<code>git compare</code> [<code>-p</code>] [<code>-u</code>] [<var>USER</var>] [<var>START</var>...]<var>END</var><br />
<code>git submodule add</code> [<code>-p</code>] <var>OPTIONS</var> [<var>USER</var>/]<var>REPOSITORY</var> <var>DIRECTORY</var><br />
<code>git fork</code> [<code>--no-remote</code>]</p>
<h2>DESCRIPTION</h2>
......@@ -86,7 +87,7 @@ alias command displays information on configuring your environment:</p>
<dl>
<dt><code>hub alias</code> [<code>-s</code>] <var>SHELL</var></dt><dd><p>Writes shell aliasing code for <var>SHELL</var> (<code>bash</code>, <code>sh</code>, <code>zsh</code>,
<code>csh</code>) to standard output. With the <code>-s</code> option, the output of
this command can be evaluated directly within the shell:
this command can be evaluated directly within the shell:<br />
<code>eval $(hub alias -s bash)</code></p></dd>
<dt><code>git init</code> <code>-g</code> <var>OPTIONS</var></dt><dd><p>Create a git repository as with git-init(1) and add remote <code>origin</code> at
"git@github.com:<var>USER</var>/<var>REPOSITORY</var>.git"; <var>USER</var> is your GitHub username and
......@@ -102,24 +103,23 @@ current working directory is used. With <code>-p</code>, use private remote
then uses your GitHub login.</p></dd>
<dt><code>git push</code> <var>REMOTE-1</var>,<var>REMOTE-2</var>,...,<var>REMOTE-N</var> <var>REF</var></dt><dd><p>Push <var>REF</var> to each of <var>REMOTE-1</var> through <var>REMOTE-N</var> by executing
multiple <code>git push</code> commands.</p></dd>
<dt><code>git fork</code></dt><dd><p>Forks the original repo on GitHub and adds the new remote under your
username. It requires your GitHub login and token to be present. See
CONFIGURATION below.</p></dd>
<dt><code>git browse</code> [<code>-p</code>] [[<var>USER</var><code>/</code>]<var>REPOSITORY</var>]</dt><dd><p>Open repository's GitHub page in the system's default web browser
<dt><code>git browse</code> [<code>-p</code>] [<code>-u</code>] [[<var>USER</var><code>/</code>]<var>REPOSITORY</var>] [SUBPAGE]</dt><dd><p>Open repository's GitHub page in the system's default web browser
using <code>open(1)</code> or the <code>BROWSER</code> env variable. Use <code>-p</code> to open a
page with https. If the repository isn't specified, <code>browse</code> opens
the page of the repository found in the current directory.</p></dd>
<dt><code>git compare</code> [<code>-u</code>] [<var>USER</var>] [<var>START</var>...]<var>END</var></dt><dd><p>Open a GitHub compare view page in the system's default web browser.
the page of the repository found in the current directory. If SUBPAGE
is specified, the browser will open on the specified subpage: one of
"wiki", "commits", "issues" or other (the default is "tree").</p></dd>
<dt><code>git compare</code> [<code>-p</code>] [<code>-u</code>] [<var>USER</var>] [<var>START</var>...]<var>END</var></dt><dd><p>Open a GitHub compare view page in the system's default web browser.
<var>START</var> to <var>END</var> are branch names, tag names, or commit SHA1s specifying
the range of history to compare. If <var>START</var> is omitted,
the repository's default branch is assumed.</p></dd>
the range of history to compare. If <var>START</var> is omitted, GitHub will
compare against the base branch (the default is "master").</p></dd>
<dt><code>git submodule add</code> [<code>-p</code>] <var>OPTIONS</var> [<var>USER</var>/]<var>REPOSITORY</var> <var>DIRECTORY</var></dt><dd><p>Submodule repository "git://github.com/<var>USER</var>/<var>REPOSITORY</var>.git" into
<var>DIRECTORY</var> as with git-submodule(1). When <var>USER</var>/ is omitted, assumes
your GitHub login. With <code>-p</code>, use private remote
"git@github.com:<var>USER</var>/<var>REPOSITORY</var>.git".</p></dd>
<dt><code>git fork</code> [<code>--no-remote</code>]</dt><dd><p>Forks the original project (as specified in "origin" remote) on GitHub
and adds a new remote named <var>USER</var> referencing the newly created repo.
Requires <code>github.token</code> to be set (see CONFIGURATION).</p></dd>
<dt><code>git fork</code> [<code>--no-remote</code>]</dt><dd><p>Forks the original project (referenced by "origin" remote) on GitHub and
adds a new remote for it under your username. Requires <code>github.token</code> to
be set (see CONFIGURATION).</p></dd>
<dt><code>git help</code></dt><dd><p>Display enhanced git-help(1).</p></dd>
</dl>
......@@ -143,7 +143,7 @@ information.</p>
<p>You can also tell <code>hub</code> to use <code>http://</code> rather than <code>git://</code> when
cloning:</p>
<pre><code>$ git config --global --add hub.http-clone yes
<pre><code>$ git config --global --bool hub.http-clone true
</code></pre>
<h2>EXAMPLES</h2>
......@@ -199,7 +199,13 @@ $ git remote add origin
<h3>git browse</h3>
<pre><code>$ git browse schacon/ticgit
<pre><code>$ git browse
&gt; open http://github.com/CURRENT_REPO
$ git browse -- issues
&gt; open http://github.com/CURRENT_REPO/issues
$ git browse schacon/ticgit
&gt; open http://github.com/schacon/ticgit
$ git browse -p schacon/ticgit
......@@ -208,8 +214,11 @@ $ git browse -p schacon/ticgit
$ git browse resque
&gt; open http://github.com/YOUR_USER/resque
$ git browse resque network
&gt; open http://github.com/YOUR_USER/resque/network
$ git browse -p resque
&gt; open https://github.com:YOUR_USER/resque
&gt; open https://github.com/YOUR_USER/resque
</code></pre>
<h3>git compare</h3>
......
......@@ -3,16 +3,17 @@ hub(1) -- git + hub = github
## SYNOPSIS
`hub` <COMMAND> <OPTIONS>
`hub` <COMMAND> <OPTIONS>
`hub alias` [`-s`] <SHELL>
`git init -g` <OPTIONS>
`git clone` [`-p`] <OPTIONS> [<USER>/]<REPOSITORY> <DIRECTORY>
`git remote add` [`-p`] <OPTIONS> <USER>[/<REPOSITORY>]
`git push` <REMOTE-1>,<REMOTE-2>,...,<REMOTE-N> <REF>
`git fork`
`git browse` [`-p`] [<USER>/]<REPOSITORY>
`git browse` [`-p`] [`-u`] [[<USER>`/`]<REPOSITORY>] [SUBPAGE]
`git compare` [`-p`] [`-u`] [<USER>] [<START>...]<END>
`git submodule add` [`-p`] <OPTIONS> [<USER>/]<REPOSITORY> <DIRECTORY>
`git fork` [`--no-remote`]
## DESCRIPTION
......@@ -22,7 +23,7 @@ alias command displays information on configuring your environment:
* `hub alias` [`-s`] <SHELL>:
Writes shell aliasing code for <SHELL> (`bash`, `sh`, `zsh`,
`csh`) to standard output. With the `-s` option, the output of
this command can be evaluated directly within the shell:
this command can be evaluated directly within the shell:
`eval $(hub alias -s bash)`
* `git init` `-g` <OPTIONS>:
......@@ -47,22 +48,19 @@ alias command displays information on configuring your environment:
Push <REF> to each of <REMOTE-1> through <REMOTE-N> by executing
multiple `git push` commands.
* `git fork`:
Forks the original repo on GitHub and adds the new remote under your
username. It requires your GitHub login and token to be present. See
CONFIGURATION below.
* `git browse` [`-p`] [[<USER>`/`]<REPOSITORY>]:
* `git browse` [`-p`] [`-u`] [[<USER>`/`]<REPOSITORY>] [SUBPAGE]:
Open repository's GitHub page in the system's default web browser
using `open(1)` or the `BROWSER` env variable. Use `-p` to open a
page with https. If the repository isn't specified, `browse` opens
the page of the repository found in the current directory.
the page of the repository found in the current directory. If SUBPAGE
is specified, the browser will open on the specified subpage: one of
"wiki", "commits", "issues" or other (the default is "tree").
* `git compare` [`-u`] [<USER>] [<START>...]<END>:
* `git compare` [`-p`] [`-u`] [<USER>] [<START>...]<END>:
Open a GitHub compare view page in the system's default web browser.
<START> to <END> are branch names, tag names, or commit SHA1s specifying
the range of history to compare. If <START> is omitted,
the repository's default branch is assumed.
the range of history to compare. If <START> is omitted, GitHub will
compare against the base branch (the default is "master").
* `git submodule add` [`-p`] <OPTIONS> [<USER>/]<REPOSITORY> <DIRECTORY>:
Submodule repository "git://github.com/<USER>/<REPOSITORY>.git" into
......@@ -71,9 +69,9 @@ alias command displays information on configuring your environment:
"git@github.com:<USER>/<REPOSITORY>.git".
* `git fork` [`--no-remote`]:
Forks the original project (as specified in "origin" remote) on GitHub
and adds a new remote named <USER> referencing the newly created repo.
Requires `github.token` to be set (see CONFIGURATION).
Forks the original project (referenced by "origin" remote) on GitHub and
adds a new remote for it under your username. Requires `github.token` to
be set (see CONFIGURATION).
* `git help`:
Display enhanced git-help(1).
......@@ -95,7 +93,7 @@ information.
You can also tell `hub` to use `http://` rather than `git://` when
cloning:
$ git config --global --add hub.http-clone yes
$ git config --global --bool hub.http-clone true
## EXAMPLES
......@@ -145,6 +143,12 @@ cloning:
### git browse
$ git browse
> open http://github.com/CURRENT_REPO
$ git browse -- issues
> open http://github.com/CURRENT_REPO/issues
$ git browse schacon/ticgit
> open http://github.com/schacon/ticgit
......@@ -154,8 +158,11 @@ cloning:
$ git browse resque
> open http://github.com/YOUR_USER/resque
$ git browse resque network
> open http://github.com/YOUR_USER/resque/network
$ git browse -p resque
> open https://github.com:YOUR_USER/resque
> open https://github.com/YOUR_USER/resque
### git compare
......
#!/bin/sh
if [[ $1 == "--version" ]]; then
echo "git version 1.7.0.4"
else
echo "ERROR: git was called, but wasn't supposed to:" git $*
exit 1
fi
#!/bin/sh
echo "ERROR: open was called, but wasn't supposed to:" open $*
exit 1
\ No newline at end of file
......@@ -12,6 +12,10 @@ require 'hub/standalone'
# We're looking for `open` in the tests.
ENV['BROWSER'] = 'open'
# Setup path with fake executables in case a test hits them
fakebin_dir = File.expand_path('../fakebin', __FILE__)
ENV['PATH'] = "#{fakebin_dir}:#{ENV['PATH']}"
class Test::Unit::TestCase
# Shortcut for creating a `Hub` instance. Pass it what you would
# normally pass `hub` on the command line, e.g.
......
......@@ -5,11 +5,32 @@ require 'webmock/test_unit'
class HubTest < Test::Unit::TestCase
include WebMock
COMMANDS = []
Hub::Commands.class_eval do
remove_method :command?
define_method :command? do |name|
COMMANDS.include?(name)
end
end
def setup
Hub::Commands::REPO.replace("hub")
Hub::Commands::USER.replace("tpw")
Hub::Commands::TOKEN.replace("abc123")
Hub::Commands::OWNER.replace("defunkt")
COMMANDS.replace %w[open groff]
@git = Hub::Context::GIT_CONFIG.replace(Hash.new { |h, k|
raise ArgumentError, "`git #{k}` not stubbed"
}).update(
'symbolic-ref -q HEAD' => 'refs/heads/master',
'config github.user' => 'tpw',
'config github.token' => 'abc123',
'config remote.origin.url' => 'git://github.com/defunkt/hub.git',
'config remote.mislav.url' => 'git://github.com/mislav/hub.git',
'config branch.master.remote' => 'origin',
'config branch.master.merge' => 'refs/heads/master',
'config branch.feature.remote' => 'mislav',
'config branch.feature.merge' => 'refs/heads/experimental',
'config --bool hub.http-clone' => 'false'
)
end
def test_private_clone
......@@ -44,7 +65,7 @@ class HubTest < Test::Unit::TestCase
def test_your_private_clone_fails_without_config
out = hub("clone -p mustache") do
Hub::Commands::USER.replace("")
stub_github_user(nil)
end
assert_equal "** No GitHub user set. See http://github.com/guides/local-github-config\n", out
......@@ -52,7 +73,7 @@ class HubTest < Test::Unit::TestCase
def test_your_public_clone_fails_without_config
out = hub("clone mustache") do
Hub::Commands::USER.replace("")
stub_github_user(nil)
end
assert_equal "** No GitHub user set. See http://github.com/guides/local-github-config\n", out
......@@ -204,7 +225,7 @@ class HubTest < Test::Unit::TestCase
def test_init_no_login
out = hub("init -g") do
Hub::Commands::USER.replace("")
stub_github_user(nil)
end
assert_equal "** No GitHub user set. See http://github.com/guides/local-github-config\n", out
......@@ -255,7 +276,7 @@ class HubTest < Test::Unit::TestCase
def test_version
out = hub('--version')
assert_includes "git version", out
assert_includes "git version 1.7.0.4", out
assert_includes "hub version #{Hub::Version}", out
end
......@@ -281,13 +302,8 @@ config
end
def test_help_hub_no_groff
help_manpage = hub("help hub") do
Hub::Commands.class_eval do
remove_method :groff?
def groff?; false end
end
end
assert_equal "** Can't find groff(1)\n", help_manpage
stub_available_commands()
assert_equal "** Can't find groff(1)\n", hub("help hub")
end
def test_hub_standalone
......@@ -298,42 +314,194 @@ config
def test_hub_compare
assert_command "compare refactor",
"open http://github.com/defunkt/hub/compare/refactor"
end
def test_hub_compare_nothing
expected = "Usage: hub compare [USER] [<START>...]<END>\n"
assert_equal expected, hub("compare")
end
def test_hub_compare_tracking_nothing
stub_tracking_nothing
expected = "Usage: hub compare [USER] [<START>...]<END>\n"
assert_equal expected, hub("compare")
end
def test_hub_compare_tracking_branch
stub_branch('refs/heads/feature')
assert_command "compare",
"open http://github.com/mislav/hub/compare/experimental"
end
def test_hub_compare_range
assert_command "compare 1.0...fix",
"open http://github.com/defunkt/hub/compare/1.0...fix"
end
def test_hub_compare_fork
assert_command "compare myfork feature",
"open http://github.com/myfork/hub/compare/feature"
end
def test_hub_open
def test_hub_compare_private
assert_command "compare -p myfork topsecret",
"open https://github.com/myfork/hub/compare/topsecret"
end
def test_hub_compare_url
assert_command "compare -u 1.0...1.1",
"echo http://github.com/defunkt/hub/compare/1.0...1.1"
end
def test_hub_browse
assert_command "browse mojombo/bert", "open http://github.com/mojombo/bert"
end
def test_hub_browse_tracking_nothing
stub_tracking_nothing
assert_command "browse mojombo/bert", "open http://github.com/mojombo/bert"
end
def test_hub_open_private
def test_hub_browse_url
assert_command "browse -u mojombo/bert", "echo http://github.com/mojombo/bert"
end
def test_hub_browse_private
assert_command "browse -p bmizerany/sinatra",
"open https://github.com/bmizerany/sinatra"
end
def test_hub_open_self
def test_hub_browse_self
assert_command "browse resque", "open http://github.com/tpw/resque"
end
def test_hub_browse_subpage
assert_command "browse resque commits",
"open http://github.com/tpw/resque/commits/master"
assert_command "browse resque issues",
"open http://github.com/tpw/resque/issues"
assert_command "browse resque wiki",
"open http://wiki.github.com/tpw/resque/"
end
def test_hub_browse_on_branch
stub_branch('refs/heads/feature')
assert_command "browse resque", "open http://github.com/tpw/resque"
assert_command "browse resque commits",
"open http://github.com/tpw/resque/commits/master"
assert_command "browse",
"open http://github.com/mislav/hub/tree/experimental"
assert_command "browse -- tree",
"open http://github.com/mislav/hub/tree/experimental"
assert_command "browse -- commits",
"open http://github.com/mislav/hub/commits/experimental"
end
def test_hub_open_self_private
def test_hub_browse_self_private
assert_command "browse -p github", "open https://github.com/tpw/github"
end
def test_hub_open_current
def test_hub_browse_current
assert_command "browse", "open http://github.com/defunkt/hub"
assert_command "browse --", "open http://github.com/defunkt/hub"
end
def test_hub_browse_current_subpage
assert_command "browse -- network",
"open http://github.com/defunkt/hub/network"
assert_command "browse -- anything/everything",
"open http://github.com/defunkt/hub/anything/everything"
end
def test_hub_open_current_private
def test_hub_browse_current_private
assert_command "browse -p", "open https://github.com/defunkt/hub"
end
def test_hub_open_no_repo
Hub::Commands::OWNER.replace("")
input = "browse"
assert_equal "Usage: hub browse [<USER>/]<REPOSITORY>\n", hub(input)
def test_hub_browse_no_repo
stub_repo_url(nil)
assert_equal "Usage: hub browse [<USER>/]<REPOSITORY>\n", hub("browse")
end
def test_custom_browser
with_browser_env("custom") do
assert_browser("custom")
end
end
def test_linux_browser
stub_available_commands "open", "xdg-open", "cygstart"
with_browser_env(nil) do
with_ruby_platform("i686-linux") do
assert_browser("xdg-open")
end
end
end
def test_cygwin_browser
stub_available_commands "open", "cygstart"
with_browser_env(nil) do
with_ruby_platform("i686-linux") do
assert_browser("cygstart")
end
end
end
def test_no_browser
stub_available_commands()
expected = "Please set $BROWSER to a web launcher to use this command.\n"
with_browser_env(nil) do
with_ruby_platform("i686-linux") do
assert_equal expected, hub("browse")
end
end
end
protected
def stub_github_user(name)
@git['config github.user'] = name
end
def stub_repo_url(value)
@git['config remote.origin.url'] = value
Hub::Context::REMOTES.clear
end
def stub_branch(value)
@git['symbolic-ref -q HEAD'] = value
end
def stub_tracking_nothing
@git['config branch.master.remote'] = nil
@git['config branch.master.merge'] = nil
end
def stub_available_commands(*names)
COMMANDS.replace names
end
def with_browser_env(value)
browser, ENV['BROWSER'] = ENV['BROWSER'], value
yield
ensure
ENV['BROWSER'] = browser
end
def assert_browser(browser)
assert_command "browse", "#{browser} http://github.com/defunkt/hub"
end
def with_ruby_platform(value)
platform = RUBY_PLATFORM
Object.send(:remove_const, :RUBY_PLATFORM)
Object.const_set(:RUBY_PLATFORM, value)
yield
ensure
Object.send(:remove_const, :RUBY_PLATFORM)
Object.const_set(:RUBY_PLATFORM, platform)
end
end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册