Skip to content

Commit

Permalink
magic cherry-pick supports GitHub commit URLs and "user@sha" notation
Browse files Browse the repository at this point in the history
Creates new remotes if needed and fetches objects before cherry-picking.

    $ git cherry-pick http://github.com/mislav/REPO/commit/SHA
    > git remote add -f mislav git://github.com/mislav/REPO.git
    > git cherry-pick SHA
  • Loading branch information
mislav authored and defunkt committed Apr 29, 2010
1 parent 7ac3b02 commit 3c6af16
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 5 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,20 @@ superpowers:
> git remote add xoebus ...
> git fetch --multiple mislav xoebus

### git cherry-pick

$ git cherry-pick http://github.com/mislav/REPO/commit/SHA
> git remote add -f mislav git://github.com/mislav/REPO.git
> git cherry-pick SHA

$ git cherry-pick mislav@SHA
> git remote add -f mislav git://github.com/mislav/CURRENT_REPO.git
> git cherry-pick SHA

$ git cherry-pick mislav@SHA
> git fetch mislav
> git cherry-pick SHA

### git fork

$ git fork
Expand Down
42 changes: 42 additions & 0 deletions lib/hub/commands.rb
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,48 @@ def fetch(args)
end
end

# $ git cherry-pick http://github.com/mislav/hub/commit/a319d88#comments
# > git remote add -f mislav git://github.com/mislav/hub.git
# > git cherry-pick a319d88
#
# $ git cherry-pick mislav@a319d88
# > git remote add -f mislav git://github.com/mislav/hub.git
# > git cherry-pick a319d88
#
# $ git cherry-pick mislav@SHA
# > git fetch mislav
# > git cherry-pick SHA
def cherry_pick(args)
unless args.include?('-m') or args.include?('--mainline')
case ref = args.words.last
when %r{^(https?:)//github.com/(.+?)/(.+?)/commit/([a-f1-9]{7,40})}
scheme, user, repo, sha = $1, $2, $3, $4
args[args.index(ref)] = sha
when /^(\w+)@([a-f1-9]{7,40})$/
scheme, user, repo, sha = nil, $1, nil, $2
args[args.index(ref)] = sha
else
user = nil
end

if user
# cherry-pick comes after the fetch
args.after args.to_exec.join(' ')

if user == repo_owner
# fetch from origin if the repo belongs to the user
args.replace ['fetch', default_remote]
elsif remotes.include?(user)
args.replace ['fetch', user]
else
secure = scheme == 'https:'
remote_url = github_url(:user => user, :repo => repo, :private => secure)
args.replace ['remote', 'add', '-f', user, remote_url]
end
end
end
end

# $ hub init -g
# > git init
# > git remote add origin git@github.com:USER/REPO.git
Expand Down
6 changes: 3 additions & 3 deletions lib/hub/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ def initialize(*args)
# Hack to emulate git-style
@args.unshift 'help' if @args.grep(/^[^-]|version/).empty?

if Commands.respond_to?(@args[0])
Commands.send(@args[0], @args)
end
# git commands can have dashes
cmd = @args[0].sub(/(\w)-/, '\1_')
Commands.send(cmd, @args) if Commands.respond_to?(cmd)
end

# Shortcut
Expand Down
28 changes: 28 additions & 0 deletions man/hub.1
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
\fBgit fetch\fR \fIUSER\-1\fR,[\fIUSER\-2\fR,...]
.
.br
\fBgit cherry\-pick\fR \fIGITHUB\-REF\fR
.
.br
\fBgit push\fR \fIREMOTE\-1\fR,\fIREMOTE\-2\fR,...,\fIREMOTE\-N\fR \fIREF\fR
.
.br
Expand Down Expand Up @@ -76,6 +79,13 @@ Adds missing remote(s) with \fBgit remote add\fR prior to fetching. New
remotes are only added if they correspond to valid forks on GitHub.
.
.TP
\fBgit cherry\-pick\fR \fIGITHUB\-REF\fR
Cherry\-pick a commit from a fork using either full URL to the commit
or GitHub\-flavored Markdown notation, which is \fBuser@sha\fR. If the remote
doesn't yet exist, it will be added. A \fBgit fetch <user>\fR is issued
prior to the cherry\-pick attempt.
.
.TP
\fBgit push\fR \fIREMOTE\-1\fR,\fIREMOTE\-2\fR,...,\fIREMOTE\-N\fR \fIREF\fR
Push \fIREF\fR to each of \fIREMOTE\-1\fR through \fIREMOTE\-N\fR by executing
multiple \fBgit push\fR commands.
Expand Down Expand Up @@ -206,6 +216,24 @@ $ git fetch mislav,xoebus
.
.fi
.
.SS "git cherry\-pick"
.
.nf

$ git cherry\-pick http://github.com/mislav/REPO/commit/SHA
> git remote add \-f mislav git://github.com/mislav/REPO.git
> git cherry\-pick SHA

$ git cherry\-pick mislav@SHA
> git remote add \-f mislav git://github.com/mislav/CURRENT_REPO.git
> git cherry\-pick SHA

$ git cherry\-pick mislav@SHA
> git fetch mislav
> git cherry\-pick SHA
.
.fi
.
.SS "git fork"
.
.nf
Expand Down
20 changes: 20 additions & 0 deletions man/hub.1.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions man/hub.1.ronn
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ hub(1) -- git + hub = github
`git clone` [`-p`] <OPTIONS> [<USER>/]<REPOSITORY> <DIRECTORY>
`git remote add` [`-p`] <OPTIONS> <USER>[/<REPOSITORY>]
`git fetch` <USER-1>,[<USER-2>,...]
`git cherry-pick` <GITHUB-REF>
`git push` <REMOTE-1>,<REMOTE-2>,...,<REMOTE-N> <REF>
`git browse` [`-p`] [`-u`] [[<USER>`/`]<REPOSITORY>] [SUBPAGE]
`git compare` [`-p`] [`-u`] [<USER>] [<START>...]<END>
Expand Down Expand Up @@ -49,6 +50,12 @@ alias command displays information on configuring your environment:
Adds missing remote(s) with `git remote add` prior to fetching. New
remotes are only added if they correspond to valid forks on GitHub.

* `git cherry-pick` <GITHUB-REF>:
Cherry-pick a commit from a fork using either full URL to the commit
or GitHub-flavored Markdown notation, which is `user@sha`. If the remote
doesn't yet exist, it will be added. A `git fetch <user>` is issued
prior to the cherry-pick attempt.

* `git push` <REMOTE-1>,<REMOTE-2>,...,<REMOTE-N> <REF>:
Push <REF> to each of <REMOTE-1> through <REMOTE-N> by executing
multiple `git push` commands.
Expand Down Expand Up @@ -138,6 +145,20 @@ cloning:
> git remote add xoebus ...
> git fetch --multiple mislav xoebus

### git cherry-pick

$ git cherry-pick http://github.com/mislav/REPO/commit/SHA
> git remote add -f mislav git://github.com/mislav/REPO.git
> git cherry-pick SHA

$ git cherry-pick mislav@SHA
> git remote add -f mislav git://github.com/mislav/CURRENT_REPO.git
> git cherry-pick SHA

$ git cherry-pick mislav@SHA
> git fetch mislav
> git cherry-pick SHA

### git fork

$ git fork
Expand Down
6 changes: 4 additions & 2 deletions test/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,13 @@ def assert_alias_command(shell, command)

# Asserts that `haystack` includes `needle`.
def assert_includes(needle, haystack)
assert haystack.include?(needle)
assert haystack.include?(needle),
"expected #{needle.inspect} in #{haystack.inspect}"
end

# Asserts that `haystack` does not include `needle`.
def assert_not_includes(needle, haystack)
assert !haystack.include?(needle)
assert !haystack.include?(needle),
"didn't expect #{needle.inspect} in #{haystack.inspect}"
end
end
59 changes: 59 additions & 0 deletions test/hub_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,65 @@ def test_fetch_multiple_new_remotes_with_filtering
assert_equal expected, h.after
end

def test_cherry_pick
h = Hub("cherry-pick a319d88")
assert_equal "git cherry-pick a319d88", h.command
assert !h.args.after?
end

def test_cherry_pick_url
url = 'http://github.com/mislav/hub/commit/a319d88#comments'
h = Hub("cherry-pick #{url}")
assert_equal "git fetch mislav", h.command
assert_equal "git cherry-pick a319d88", h.after
end

def test_cherry_pick_url_with_remote_add
url = 'http://github.com/xoebus/hub/commit/a319d88'
h = Hub("cherry-pick #{url}")
assert_equal "git remote add -f xoebus git://github.com/xoebus/hub.git", h.command
assert_equal "git cherry-pick a319d88", h.after
end

def test_cherry_pick_private_url_with_remote_add
url = 'https://github.com/xoebus/hub/commit/a319d88'
h = Hub("cherry-pick #{url}")
assert_equal "git remote add -f xoebus git@github.com:xoebus/hub.git", h.command
assert_equal "git cherry-pick a319d88", h.after
end

def test_cherry_pick_origin_url
url = 'https://github.com/defunkt/hub/commit/a319d88'
h = Hub("cherry-pick #{url}")
assert_equal "git fetch origin", h.command
assert_equal "git cherry-pick a319d88", h.after
end

def test_cherry_pick_github_user_notation
h = Hub("cherry-pick mislav@a319d88")
assert_equal "git fetch mislav", h.command
assert_equal "git cherry-pick a319d88", h.after
end

def test_cherry_pick_github_user_repo_notation
# not supported
h = Hub("cherry-pick mislav/hubbub@a319d88")
assert_equal "git cherry-pick mislav/hubbub@a319d88", h.command
assert !h.args.after?
end

def test_cherry_pick_github_notation_too_short
h = Hub("cherry-pick mislav@a319")
assert_equal "git cherry-pick mislav@a319", h.command
assert !h.args.after?
end

def test_cherry_pick_github_notation_with_remote_add
h = Hub("cherry-pick xoebus@a319d88")
assert_equal "git remote add -f xoebus git://github.com/xoebus/hub.git", h.command
assert_equal "git cherry-pick a319d88", h.after
end

def test_init
h = Hub("init -g")
assert_equal "git init", h.command
Expand Down

0 comments on commit 3c6af16

Please sign in to comment.