Make it possible to cherry-pick a change
Option -d already makes it possible to get a change into a separate branch, but for large projects and trivial changes, it makes sense to also be able to just cherry-pick the change to the currently active local branch. Also add modes that invokes "git cherry-pick -n" or "git cherry-pick -x". Program exits with exit code 69 whenever "git cherry-pick" fails. This feature was subsequently amended by Marcin Cieślak <saper@saper.info>. Change-Id: I0a30052fb2baf357ed81e3f1ef4618bf786cb7ee
This commit is contained in:
83
git-review
83
git-review
@@ -664,7 +664,7 @@ class ResetHardFailed(CommandFailed):
|
||||
EXIT_CODE = 66
|
||||
|
||||
|
||||
def download_review(review, masterbranch, remote):
|
||||
def fetch_review(review, masterbranch, remote):
|
||||
|
||||
(hostname, username, port, project_name) = \
|
||||
parse_git_show(remote, "Push")
|
||||
@@ -701,7 +701,6 @@ def download_review(review, masterbranch, remote):
|
||||
raise PatchSetInformationNotFound(review)
|
||||
|
||||
try:
|
||||
revision = review_info['currentPatchSet']['revision']
|
||||
refspec = review_info['currentPatchSet']['ref']
|
||||
except KeyError:
|
||||
raise PatchSetNotFound(review)
|
||||
@@ -718,14 +717,20 @@ def download_review(review, masterbranch, remote):
|
||||
author = 'unknown'
|
||||
branch_name = "review/%s/%s" % (author, topic)
|
||||
|
||||
print("Downloading %s from gerrit into %s" % (refspec, branch_name))
|
||||
print("Downloading %s from gerrit" % refspec)
|
||||
run_command_exc(PatchSetGitFetchFailed,
|
||||
"git", "fetch", remote, refspec)
|
||||
return branch_name
|
||||
|
||||
|
||||
def checkout_review(branch_name):
|
||||
"""Checkout a newly fetched (FETCH_HEAD) change
|
||||
into a branch"""
|
||||
|
||||
try:
|
||||
output = run_command_exc(CheckoutNewBranchFailed,
|
||||
"git", "checkout", "-b",
|
||||
branch_name, "FETCH_HEAD")
|
||||
run_command_exc(CheckoutNewBranchFailed,
|
||||
"git", "checkout", "-b",
|
||||
branch_name, "FETCH_HEAD")
|
||||
|
||||
except CheckoutNewBranchFailed as e:
|
||||
if re.search("already exists\.?", e.output):
|
||||
@@ -737,7 +742,20 @@ def download_review(review, masterbranch, remote):
|
||||
else:
|
||||
raise
|
||||
|
||||
print("Switched to branch '%s'" % branch_name)
|
||||
print("Switched to branch \"%s\"" % branch_name)
|
||||
|
||||
|
||||
class PatchSetGitCherrypickFailed(CommandFailed):
|
||||
"There was a problem applying changeset contents to the current branch."
|
||||
EXIT_CODE = 69
|
||||
|
||||
|
||||
def cherrypick_review(option=None):
|
||||
cmd = ["git", "cherry-pick"]
|
||||
if option:
|
||||
cmd.append(option)
|
||||
cmd.append("FETCH_HEAD")
|
||||
print(run_command_exc(PatchSetGitCherrypickFailed, *cmd))
|
||||
|
||||
|
||||
class CheckoutBackExistingBranchFailed(CommandFailed):
|
||||
@@ -787,6 +805,14 @@ def main():
|
||||
usage = "git review [OPTIONS] ... [BRANCH]"
|
||||
|
||||
import argparse
|
||||
|
||||
class DownloadFlag(argparse.Action):
|
||||
"""Additional option parsing: store value in 'dest', but
|
||||
at the same time set one of the flag options to True"""
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
setattr(namespace, self.dest, values)
|
||||
setattr(namespace, self.const, True)
|
||||
|
||||
parser = argparse.ArgumentParser(usage=usage, description=COPYRIGHT)
|
||||
|
||||
parser.add_argument("-t", "--topic", dest="topic",
|
||||
@@ -811,9 +837,35 @@ def main():
|
||||
parser.add_argument("-F", "--force-rebase", dest="force_rebase",
|
||||
action="store_true",
|
||||
help="Force rebase even when not needed.")
|
||||
parser.add_argument("-d", "--download", dest="download",
|
||||
help="Download the contents of an existing gerrit "
|
||||
|
||||
fetch = parser.add_mutually_exclusive_group()
|
||||
fetch.set_defaults(download=False, cherrypickcommit=False,
|
||||
cherrypickindicate=False, cherrypickonly=False)
|
||||
fetch.add_argument("-d", "--download", dest="changeidentifier",
|
||||
action=DownloadFlag, metavar="CHANGE",
|
||||
const="download",
|
||||
help="Download the contents of an existing gerrit "
|
||||
"review into a branch")
|
||||
fetch.add_argument("-x", "--cherrypick", dest="changeidentifier",
|
||||
action=DownloadFlag, metavar="CHANGE",
|
||||
const="cherrypickcommit",
|
||||
help="Apply the contents of an existing gerrit "
|
||||
"review onto the current branch and commit "
|
||||
"(cherry pick; not recommended in most "
|
||||
"situations)")
|
||||
fetch.add_argument("-X", "--cherrypickindicate", dest="changeidentifier",
|
||||
action=DownloadFlag, metavar="CHANGE",
|
||||
const="cherrypickindicate",
|
||||
help="Apply the contents of an existing gerrit "
|
||||
"review onto the current branch and commit, "
|
||||
"indicating its origin")
|
||||
fetch.add_argument("-N", "--cherrypickonly", dest="changeidentifier",
|
||||
action=DownloadFlag, metavar="CHANGE",
|
||||
const="cherrypickonly",
|
||||
help="Apply the contents of an existing gerrit "
|
||||
"review to the working directory and prepare "
|
||||
"for commit")
|
||||
|
||||
parser.add_argument("-u", "--update", dest="update", action="store_true",
|
||||
help="Force updates from remote locations")
|
||||
parser.add_argument("-s", "--setup", dest="setup", action="store_true",
|
||||
@@ -864,8 +916,17 @@ def main():
|
||||
check_remote(branch, remote,
|
||||
config['hostname'], config['port'], config['project'])
|
||||
|
||||
if options.download is not None:
|
||||
download_review(options.download, branch, remote)
|
||||
if options.changeidentifier:
|
||||
local_branch = fetch_review(options.changeidentifier, branch, remote)
|
||||
if options.download:
|
||||
checkout_review(local_branch)
|
||||
else:
|
||||
if options.cherrypickcommit:
|
||||
cherrypick_review()
|
||||
elif options.cherrypickonly:
|
||||
cherrypick_review("-n")
|
||||
if options.cherrypickindicate:
|
||||
cherrypick_review("-x")
|
||||
return
|
||||
elif options.list:
|
||||
list_reviews(remote)
|
||||
|
||||
64
git-review.1
64
git-review.1
@@ -13,6 +13,21 @@
|
||||
.Op Ar branch
|
||||
.Nm
|
||||
.Op Fl r Ar remote
|
||||
.Op Fl uv
|
||||
.Fl x Ar change
|
||||
.Op Ar branch
|
||||
.Nm
|
||||
.Op Fl r Ar remote
|
||||
.Op Fl uv
|
||||
.Fl N Ar change
|
||||
.Op Ar branch
|
||||
.Nm
|
||||
.Op Fl r Ar remote
|
||||
.Op Fl uv
|
||||
.Fl X Ar change
|
||||
.Op Ar branch
|
||||
.Nm
|
||||
.Op Fl r Ar remote
|
||||
.Op Fl fnuv
|
||||
.Fl s
|
||||
.Op Ar branch
|
||||
@@ -39,6 +54,39 @@ Download
|
||||
from Gerrit
|
||||
into a local branch. The branch will be named after the patch author and the name of a topic.
|
||||
If the local branch already exists, it will attempt to update with the latest patchset for this change.
|
||||
.It Fl x Ar change , Fl -cherrypick= Ns Ar change
|
||||
Apply
|
||||
.Ar change
|
||||
from Gerrit and commit into the current local branch ("cherry pick").
|
||||
No additional branch is created.
|
||||
.Pp
|
||||
This makes it possible to review a change without creating a local branch for
|
||||
it. On the other hand, be aware: if you are not careful, this can easily result
|
||||
in additional patch sets for dependent changes. Also, if the current branch is
|
||||
different enough, the change may not apply at all or produce merge conflicts
|
||||
that need to be resolved by hand.
|
||||
.It Fl N Ar change , Fl -cherrypickonly= Ns Ar change
|
||||
Apply
|
||||
.Ar change
|
||||
from Gerrit
|
||||
into the current working directory, add it to the staging area ("git index"), but do not commit it.
|
||||
.Pp
|
||||
This makes it possible to review a change without creating a local commit for
|
||||
it. Useful if you want to merge several commits into one that will be submitted for review.
|
||||
.Pp
|
||||
If the current branch is different enough, the change may not apply at all
|
||||
or produce merge conflicts that need to be resolved by hand.
|
||||
.It Fl X Ar change , Fl -cherrypickindicate= Ns Ar change
|
||||
Apply
|
||||
.Ar change
|
||||
from Gerrit and commit into the current local branch ("cherry pick"),
|
||||
indicating which commit this change was cherry-picked from.
|
||||
.Pp
|
||||
This makes it possible to re-review a change for a different branch without
|
||||
creating a local branch for it.
|
||||
.Pp
|
||||
If the current branch is different enough, the change may not apply at all
|
||||
or produce merge conflicts that need to be resolved by hand.
|
||||
.It Fl f , Fl -finish
|
||||
Close down the local branch and switch back to the target branch on
|
||||
successful submission.
|
||||
@@ -72,7 +120,7 @@ When submitting a change for review, you will usually want it to be based on the
|
||||
Print the version number and exit.
|
||||
.El
|
||||
.Sh FILES
|
||||
To use
|
||||
To use
|
||||
.Nm
|
||||
with your project, it is recommended that you create
|
||||
a file at the root of the repository named
|
||||
@@ -86,16 +134,16 @@ project=\fIproject name\fP
|
||||
defaultbranch=\fIbranch to work on\fP
|
||||
.Ed
|
||||
.Pp
|
||||
It is also possible to specify optional default name for
|
||||
It is also possible to specify optional default name for
|
||||
the Git remote using the
|
||||
.Cm defaultremote
|
||||
configuration parameter.
|
||||
configuration parameter.
|
||||
.Pp
|
||||
Setting
|
||||
.Cm defaultrebase
|
||||
to zero will make
|
||||
.Nm
|
||||
not to rebase changes by default (same as the
|
||||
not to rebase changes by default (same as the
|
||||
.Fl R
|
||||
command line option)
|
||||
.Bd -literal -offset indent
|
||||
@@ -141,6 +189,12 @@ Cannot switch to some other branch when trying to finish
|
||||
the current branch.
|
||||
.It 68
|
||||
Cannot delete current branch.
|
||||
.It 69
|
||||
Requested patchset cannot be fully applied to the current branch. This exit
|
||||
status will be returned when there are merge conflicts with the current branch.
|
||||
Possible reasons include an attempt to apply patchset from the different branch
|
||||
or code. This exit status will also be returned if the patchset is already
|
||||
applied to the current branch.
|
||||
.El
|
||||
.Pp
|
||||
Exit status larger than 31 indicates problem with
|
||||
@@ -176,7 +230,7 @@ $ git branch
|
||||
* master
|
||||
.Ed
|
||||
.Pp
|
||||
An example
|
||||
An example
|
||||
.Pa .gitreview
|
||||
configuration file for a project
|
||||
.Pa department/project
|
||||
|
||||
Reference in New Issue
Block a user