|
|
|
@ -834,6 +834,43 @@ def GetCurrentBranch():
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _ParsedIssueNumberArgument(object):
|
|
|
|
|
def __init__(self, issue=None, patchset=None, hostname=None):
|
|
|
|
|
self.issue = issue
|
|
|
|
|
self.patchset = patchset
|
|
|
|
|
self.hostname = hostname
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def valid(self):
|
|
|
|
|
return self.issue is not None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _RietveldParsedIssueNumberArgument(_ParsedIssueNumberArgument):
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
self.patch_url = kwargs.pop('patch_url', None)
|
|
|
|
|
super(_RietveldParsedIssueNumberArgument, self).__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ParseIssueNumberArgument(arg):
|
|
|
|
|
"""Parses the issue argument and returns _ParsedIssueNumberArgument."""
|
|
|
|
|
fail_result = _ParsedIssueNumberArgument()
|
|
|
|
|
|
|
|
|
|
if arg.isdigit():
|
|
|
|
|
return _ParsedIssueNumberArgument(issue=int(arg))
|
|
|
|
|
if not arg.startswith('http'):
|
|
|
|
|
return fail_result
|
|
|
|
|
url = gclient_utils.UpgradeToHttps(arg)
|
|
|
|
|
try:
|
|
|
|
|
parsed_url = urlparse.urlparse(url)
|
|
|
|
|
except ValueError:
|
|
|
|
|
return fail_result
|
|
|
|
|
for cls in _CODEREVIEW_IMPLEMENTATIONS.itervalues():
|
|
|
|
|
tmp = cls.ParseIssueURL(parsed_url)
|
|
|
|
|
if tmp is not None:
|
|
|
|
|
return tmp
|
|
|
|
|
return fail_result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Changelist(object):
|
|
|
|
|
"""Changelist works with one changelist in local branch.
|
|
|
|
|
|
|
|
|
@ -1257,6 +1294,20 @@ class Changelist(object):
|
|
|
|
|
('%s\nMaybe your depot_tools is out of date?\n'
|
|
|
|
|
'If all fails, contact maruel@') % e)
|
|
|
|
|
|
|
|
|
|
def CMDPatchIssue(self, issue_arg, reject, nocommit, directory):
|
|
|
|
|
"""Fetches and applies the issue patch from codereview to local branch."""
|
|
|
|
|
if issue_arg.isdigit():
|
|
|
|
|
parsed_issue_arg = _RietveldParsedIssueNumberArgument(int(issue_arg))
|
|
|
|
|
else:
|
|
|
|
|
# Assume url.
|
|
|
|
|
parsed_issue_arg = self._codereview_impl.ParseIssueURL(
|
|
|
|
|
urlparse.urlparse(issue_arg))
|
|
|
|
|
if not parsed_issue_arg or not parsed_issue_arg.valid:
|
|
|
|
|
DieWithError('Failed to parse issue argument "%s". '
|
|
|
|
|
'Must be an issue number or a valid URL.' % issue_arg)
|
|
|
|
|
return self._codereview_impl.CMDPatchWithParsedIssue(
|
|
|
|
|
parsed_issue_arg, reject, nocommit, directory)
|
|
|
|
|
|
|
|
|
|
# Forward methods to codereview specific implementation.
|
|
|
|
|
|
|
|
|
|
def CloseIssue(self):
|
|
|
|
@ -1346,6 +1397,26 @@ class _ChangelistCodereviewBase(object):
|
|
|
|
|
"""Returns the most recent patchset number from the codereview site."""
|
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
|
|
def CMDPatchWithParsedIssue(self, parsed_issue_arg, reject, nocommit,
|
|
|
|
|
directory):
|
|
|
|
|
"""Fetches and applies the issue.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
parsed_issue_arg: instance of _ParsedIssueNumberArgument.
|
|
|
|
|
reject: if True, reject the failed patch instead of switching to 3-way
|
|
|
|
|
merge. Rietveld only.
|
|
|
|
|
nocommit: do not commit the patch, thus leave the tree dirty. Rietveld
|
|
|
|
|
only.
|
|
|
|
|
directory: switch to directory before applying the patch. Rietveld only.
|
|
|
|
|
"""
|
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def ParseIssueURL(parsed_url):
|
|
|
|
|
"""Parses url and returns instance of _ParsedIssueNumberArgument or None if
|
|
|
|
|
failed."""
|
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _RietveldChangelistImpl(_ChangelistCodereviewBase):
|
|
|
|
|
def __init__(self, changelist, auth_config=None, rietveld_server=None):
|
|
|
|
@ -1518,6 +1589,94 @@ class _RietveldChangelistImpl(_ChangelistCodereviewBase):
|
|
|
|
|
def GetRieveldObjForPresubmit(self):
|
|
|
|
|
return self.RpcServer()
|
|
|
|
|
|
|
|
|
|
def CMDPatchWithParsedIssue(self, parsed_issue_arg, reject, nocommit,
|
|
|
|
|
directory):
|
|
|
|
|
# TODO(maruel): Use apply_issue.py
|
|
|
|
|
|
|
|
|
|
# PatchIssue should never be called with a dirty tree. It is up to the
|
|
|
|
|
# caller to check this, but just in case we assert here since the
|
|
|
|
|
# consequences of the caller not checking this could be dire.
|
|
|
|
|
assert(not git_common.is_dirty_git_tree('apply'))
|
|
|
|
|
assert(parsed_issue_arg.valid)
|
|
|
|
|
self._changelist.issue = parsed_issue_arg.issue
|
|
|
|
|
if parsed_issue_arg.hostname:
|
|
|
|
|
self._rietveld_server = 'https://%s' % parsed_issue_arg.hostname
|
|
|
|
|
|
|
|
|
|
if parsed_issue_arg.patch_url:
|
|
|
|
|
assert parsed_issue_arg.patchset
|
|
|
|
|
patchset = parsed_issue_arg.patchset
|
|
|
|
|
patch_data = urllib2.urlopen(parsed_issue_arg.patch_url).read()
|
|
|
|
|
else:
|
|
|
|
|
patchset = parsed_issue_arg.patchset or self.GetMostRecentPatchset()
|
|
|
|
|
patch_data = self.GetPatchSetDiff(self.GetIssue(), patchset)
|
|
|
|
|
|
|
|
|
|
# Switch up to the top-level directory, if necessary, in preparation for
|
|
|
|
|
# applying the patch.
|
|
|
|
|
top = settings.GetRelativeRoot()
|
|
|
|
|
if top:
|
|
|
|
|
os.chdir(top)
|
|
|
|
|
|
|
|
|
|
# Git patches have a/ at the beginning of source paths. We strip that out
|
|
|
|
|
# with a sed script rather than the -p flag to patch so we can feed either
|
|
|
|
|
# Git or svn-style patches into the same apply command.
|
|
|
|
|
# re.sub() should be used but flags=re.MULTILINE is only in python 2.7.
|
|
|
|
|
try:
|
|
|
|
|
patch_data = subprocess2.check_output(
|
|
|
|
|
['sed', '-e', 's|^--- a/|--- |; s|^+++ b/|+++ |'], stdin=patch_data)
|
|
|
|
|
except subprocess2.CalledProcessError:
|
|
|
|
|
DieWithError('Git patch mungling failed.')
|
|
|
|
|
logging.info(patch_data)
|
|
|
|
|
|
|
|
|
|
# We use "git apply" to apply the patch instead of "patch" so that we can
|
|
|
|
|
# pick up file adds.
|
|
|
|
|
# The --index flag means: also insert into the index (so we catch adds).
|
|
|
|
|
cmd = ['git', 'apply', '--index', '-p0']
|
|
|
|
|
if directory:
|
|
|
|
|
cmd.extend(('--directory', directory))
|
|
|
|
|
if reject:
|
|
|
|
|
cmd.append('--reject')
|
|
|
|
|
elif IsGitVersionAtLeast('1.7.12'):
|
|
|
|
|
cmd.append('--3way')
|
|
|
|
|
try:
|
|
|
|
|
subprocess2.check_call(cmd, env=GetNoGitPagerEnv(),
|
|
|
|
|
stdin=patch_data, stdout=subprocess2.VOID)
|
|
|
|
|
except subprocess2.CalledProcessError:
|
|
|
|
|
print 'Failed to apply the patch'
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
# If we had an issue, commit the current state and register the issue.
|
|
|
|
|
if not nocommit:
|
|
|
|
|
RunGit(['commit', '-m', (self.GetDescription() + '\n\n' +
|
|
|
|
|
'patch from issue %(i)s at patchset '
|
|
|
|
|
'%(p)s (http://crrev.com/%(i)s#ps%(p)s)'
|
|
|
|
|
% {'i': self.GetIssue(), 'p': patchset})])
|
|
|
|
|
self.SetIssue(self.GetIssue())
|
|
|
|
|
self.SetPatchset(patchset)
|
|
|
|
|
print "Committed patch locally."
|
|
|
|
|
else:
|
|
|
|
|
print "Patch applied to index."
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def ParseIssueURL(parsed_url):
|
|
|
|
|
if not parsed_url.scheme or not parsed_url.scheme.startswith('http'):
|
|
|
|
|
return None
|
|
|
|
|
# Typical url: https://domain/<issue_number>[/[other]]
|
|
|
|
|
match = re.match('/(\d+)(/.*)?$', parsed_url.path)
|
|
|
|
|
if match:
|
|
|
|
|
return _RietveldParsedIssueNumberArgument(
|
|
|
|
|
issue=int(match.group(1)),
|
|
|
|
|
hostname=parsed_url.netloc)
|
|
|
|
|
# Rietveld patch: https://domain/download/issue<number>_<patchset>.diff
|
|
|
|
|
match = re.match(r'/download/issue(\d+)_(\d+).diff$', parsed_url.path)
|
|
|
|
|
if match:
|
|
|
|
|
return _RietveldParsedIssueNumberArgument(
|
|
|
|
|
issue=int(match.group(1)),
|
|
|
|
|
patchset=int(match.group(2)),
|
|
|
|
|
hostname=parsed_url.netloc,
|
|
|
|
|
patch_url=gclient_utils.UpgradeToHttps(parsed_url.geturl()))
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
|
|
|
def __init__(self, changelist, auth_config=None):
|
|
|
|
@ -1696,6 +1855,63 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
|
|
|
print('Issue %s has been submitted.' % self.GetIssueURL())
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
def CMDPatchWithParsedIssue(self, parsed_issue_arg, reject, nocommit,
|
|
|
|
|
directory):
|
|
|
|
|
assert not reject
|
|
|
|
|
assert not nocommit
|
|
|
|
|
assert not directory
|
|
|
|
|
assert parsed_issue_arg.valid
|
|
|
|
|
|
|
|
|
|
self._changelist.issue = parsed_issue_arg.issue
|
|
|
|
|
|
|
|
|
|
if parsed_issue_arg.hostname:
|
|
|
|
|
self._gerrit_host = parsed_issue_arg.hostname
|
|
|
|
|
self._gerrit_server = 'https://%s' % self._gerrit_host
|
|
|
|
|
|
|
|
|
|
detail = self._GetChangeDetail(['ALL_REVISIONS'])
|
|
|
|
|
|
|
|
|
|
if not parsed_issue_arg.patchset:
|
|
|
|
|
# Use current revision by default.
|
|
|
|
|
revision_info = detail['revisions'][detail['current_revision']]
|
|
|
|
|
patchset = int(revision_info['_number'])
|
|
|
|
|
else:
|
|
|
|
|
patchset = parsed_issue_arg.patchset
|
|
|
|
|
for revision_info in detail['revisions'].itervalues():
|
|
|
|
|
if int(revision_info['_number']) == parsed_issue_arg.patchset:
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
DieWithError('Couldn\'t find patchset %i in issue %i' %
|
|
|
|
|
(parsed_issue_arg.patchset, self.GetIssue()))
|
|
|
|
|
|
|
|
|
|
fetch_info = revision_info['fetch']['http']
|
|
|
|
|
RunGit(['fetch', fetch_info['url'], fetch_info['ref']])
|
|
|
|
|
RunGit(['cherry-pick', 'FETCH_HEAD'])
|
|
|
|
|
self.SetIssue(self.GetIssue())
|
|
|
|
|
self.SetPatchset(patchset)
|
|
|
|
|
print('Committed patch for issue %i pathset %i locally' %
|
|
|
|
|
(self.GetIssue(), self.GetPatchset()))
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def ParseIssueURL(parsed_url):
|
|
|
|
|
if not parsed_url.scheme or not parsed_url.scheme.startswith('http'):
|
|
|
|
|
return None
|
|
|
|
|
# Gerrit's new UI is https://domain/c/<issue_number>[/[patchset]]
|
|
|
|
|
# But current GWT UI is https://domain/#/c/<issue_number>[/[patchset]]
|
|
|
|
|
# Short urls like https://domain/<issue_number> can be used, but don't allow
|
|
|
|
|
# specifying the patchset (you'd 404), but we allow that here.
|
|
|
|
|
if parsed_url.path == '/':
|
|
|
|
|
part = parsed_url.fragment
|
|
|
|
|
else:
|
|
|
|
|
part = parsed_url.path
|
|
|
|
|
match = re.match('(/c)?/(\d+)(/(\d+)?/?)?$', part)
|
|
|
|
|
if match:
|
|
|
|
|
return _ParsedIssueNumberArgument(
|
|
|
|
|
issue=int(match.group(2)),
|
|
|
|
|
patchset=int(match.group(4)) if match.group(4) else None,
|
|
|
|
|
hostname=parsed_url.netloc)
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_CODEREVIEW_IMPLEMENTATIONS = {
|
|
|
|
|
'rietveld': _RietveldChangelistImpl,
|
|
|
|
@ -3593,15 +3809,6 @@ def CMDland(parser, args):
|
|
|
|
|
return SendUpstream(parser, args, 'land')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ParseIssueNum(arg):
|
|
|
|
|
"""Parses the issue number from args if present otherwise returns None."""
|
|
|
|
|
if re.match(r'\d+', arg):
|
|
|
|
|
return arg
|
|
|
|
|
if arg.startswith('http'):
|
|
|
|
|
return re.sub(r'.*/(\d+)/?', r'\1', arg)
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@subcommand.usage('<patch url or issue id or issue url>')
|
|
|
|
|
def CMDpatch(parser, args):
|
|
|
|
|
"""Patches in a code review."""
|
|
|
|
@ -3611,64 +3818,69 @@ def CMDpatch(parser, args):
|
|
|
|
|
help='with -b, clobber any existing branch')
|
|
|
|
|
parser.add_option('-d', '--directory', action='store', metavar='DIR',
|
|
|
|
|
help='Change to the directory DIR immediately, '
|
|
|
|
|
'before doing anything else.')
|
|
|
|
|
'before doing anything else. Rietveld only.')
|
|
|
|
|
parser.add_option('--reject', action='store_true',
|
|
|
|
|
help='failed patches spew .rej files rather than '
|
|
|
|
|
'attempting a 3-way merge')
|
|
|
|
|
'attempting a 3-way merge. Rietveld only.')
|
|
|
|
|
parser.add_option('-n', '--no-commit', action='store_true', dest='nocommit',
|
|
|
|
|
help="don't commit after patch applies")
|
|
|
|
|
|
|
|
|
|
group = optparse.OptionGroup(parser,
|
|
|
|
|
"""Options for continuing work on the current issue uploaded
|
|
|
|
|
from a different clone (e.g. different machine). Must be used independently from
|
|
|
|
|
the other options. No issue number should be specified, and the branch must have
|
|
|
|
|
an issue number associated with it""")
|
|
|
|
|
group.add_option('--reapply', action='store_true',
|
|
|
|
|
dest='reapply',
|
|
|
|
|
help="""Reset the branch and reapply the issue.
|
|
|
|
|
CAUTION: This will undo any local changes in this branch""")
|
|
|
|
|
help='don\'t commit after patch applies. Rietveld only.')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
group = optparse.OptionGroup(
|
|
|
|
|
parser,
|
|
|
|
|
'Options for continuing work on the current issue uploaded from a '
|
|
|
|
|
'different clone (e.g. different machine). Must be used independently '
|
|
|
|
|
'from the other options. No issue number should be specified, and the '
|
|
|
|
|
'branch must have an issue number associated with it')
|
|
|
|
|
group.add_option('--reapply', action='store_true', dest='reapply',
|
|
|
|
|
help='Reset the branch and reapply the issue.\n'
|
|
|
|
|
'CAUTION: This will undo any local changes in this '
|
|
|
|
|
'branch')
|
|
|
|
|
|
|
|
|
|
group.add_option('--pull', action='store_true', dest='pull',
|
|
|
|
|
help="Performs a pull before reapplying.")
|
|
|
|
|
help='Performs a pull before reapplying.')
|
|
|
|
|
parser.add_option_group(group)
|
|
|
|
|
|
|
|
|
|
auth.add_auth_options(parser)
|
|
|
|
|
(options, args) = parser.parse_args(args)
|
|
|
|
|
auth_config = auth.extract_auth_config_from_options(options)
|
|
|
|
|
|
|
|
|
|
cl = Changelist(auth_config=auth_config)
|
|
|
|
|
|
|
|
|
|
issue_arg = None
|
|
|
|
|
if options.reapply :
|
|
|
|
|
if len(args) > 0:
|
|
|
|
|
parser.error("--reapply implies no additional arguments.")
|
|
|
|
|
parser.error('--reapply implies no additional arguments.')
|
|
|
|
|
|
|
|
|
|
cl = Changelist()
|
|
|
|
|
issue_arg = cl.GetIssue()
|
|
|
|
|
upstream = cl.GetUpstreamBranch()
|
|
|
|
|
if upstream == None:
|
|
|
|
|
parser.error("No upstream branch specified. Cannot reset branch")
|
|
|
|
|
parser.error('No upstream branch specified. Cannot reset branch')
|
|
|
|
|
|
|
|
|
|
RunGit(['reset', '--hard', upstream])
|
|
|
|
|
if options.pull:
|
|
|
|
|
RunGit(['pull'])
|
|
|
|
|
else:
|
|
|
|
|
if len(args) != 1:
|
|
|
|
|
parser.error("Must specify issue number")
|
|
|
|
|
|
|
|
|
|
issue_arg = ParseIssueNum(args[0])
|
|
|
|
|
parser.error('Must specify issue number or url')
|
|
|
|
|
issue_arg = args[0]
|
|
|
|
|
|
|
|
|
|
# The patch URL works because ParseIssueNum won't do any substitution
|
|
|
|
|
# as the re.sub pattern fails to match and just returns it.
|
|
|
|
|
if issue_arg == None:
|
|
|
|
|
if not issue_arg:
|
|
|
|
|
parser.print_help()
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
if cl.IsGerrit():
|
|
|
|
|
if options.reject:
|
|
|
|
|
parser.error('--reject is not supported with Gerrit codereview.')
|
|
|
|
|
if options.nocommit:
|
|
|
|
|
parser.error('--nocommit is not supported with Gerrit codereview.')
|
|
|
|
|
if options.directory:
|
|
|
|
|
parser.error('--directory is not supported with Gerrit codereview.')
|
|
|
|
|
|
|
|
|
|
# We don't want uncommitted changes mixed up with the patch.
|
|
|
|
|
if git_common.is_dirty_git_tree('patch'):
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
# TODO(maruel): Use apply_issue.py
|
|
|
|
|
# TODO(ukai): use gerrit-cherry-pick for gerrit repository?
|
|
|
|
|
|
|
|
|
|
if options.newbranch:
|
|
|
|
|
if options.reapply:
|
|
|
|
|
parser.error("--reapply excludes any option other than --pull")
|
|
|
|
@ -3678,84 +3890,8 @@ CAUTION: This will undo any local changes in this branch""")
|
|
|
|
|
RunGit(['checkout', '-b', options.newbranch,
|
|
|
|
|
Changelist().GetUpstreamBranch()])
|
|
|
|
|
|
|
|
|
|
return PatchIssue(issue_arg, options.reject, options.nocommit,
|
|
|
|
|
options.directory, auth_config)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def PatchIssue(issue_arg, reject, nocommit, directory, auth_config):
|
|
|
|
|
# PatchIssue should never be called with a dirty tree. It is up to the
|
|
|
|
|
# caller to check this, but just in case we assert here since the
|
|
|
|
|
# consequences of the caller not checking this could be dire.
|
|
|
|
|
assert(not git_common.is_dirty_git_tree('apply'))
|
|
|
|
|
|
|
|
|
|
# TODO(tandrii): implement for Gerrit.
|
|
|
|
|
if type(issue_arg) is int or issue_arg.isdigit():
|
|
|
|
|
# Input is an issue id. Figure out the URL.
|
|
|
|
|
issue = int(issue_arg)
|
|
|
|
|
cl = Changelist(issue=issue, codereview='rietveld', auth_config=auth_config)
|
|
|
|
|
patchset = cl.GetMostRecentPatchset()
|
|
|
|
|
patch_data = cl._codereview_impl.GetPatchSetDiff(issue, patchset)
|
|
|
|
|
else:
|
|
|
|
|
# Assume it's a URL to the patch. Default to https.
|
|
|
|
|
issue_url = gclient_utils.UpgradeToHttps(issue_arg)
|
|
|
|
|
match = re.match(r'(.*?)/download/issue(\d+)_(\d+).diff', issue_url)
|
|
|
|
|
if not match:
|
|
|
|
|
DieWithError('Must pass an issue ID or full URL for '
|
|
|
|
|
'\'Download raw patch set\'')
|
|
|
|
|
issue = int(match.group(2))
|
|
|
|
|
cl = Changelist(issue=issue, codereview='rietveld',
|
|
|
|
|
rietveld_server=match.group(1), auth_config=auth_config)
|
|
|
|
|
patchset = int(match.group(3))
|
|
|
|
|
patch_data = urllib2.urlopen(issue_arg).read()
|
|
|
|
|
|
|
|
|
|
# Switch up to the top-level directory, if necessary, in preparation for
|
|
|
|
|
# applying the patch.
|
|
|
|
|
top = settings.GetRelativeRoot()
|
|
|
|
|
if top:
|
|
|
|
|
os.chdir(top)
|
|
|
|
|
|
|
|
|
|
# Git patches have a/ at the beginning of source paths. We strip that out
|
|
|
|
|
# with a sed script rather than the -p flag to patch so we can feed either
|
|
|
|
|
# Git or svn-style patches into the same apply command.
|
|
|
|
|
# re.sub() should be used but flags=re.MULTILINE is only in python 2.7.
|
|
|
|
|
try:
|
|
|
|
|
patch_data = subprocess2.check_output(
|
|
|
|
|
['sed', '-e', 's|^--- a/|--- |; s|^+++ b/|+++ |'], stdin=patch_data)
|
|
|
|
|
except subprocess2.CalledProcessError:
|
|
|
|
|
DieWithError('Git patch mungling failed.')
|
|
|
|
|
logging.info(patch_data)
|
|
|
|
|
|
|
|
|
|
# We use "git apply" to apply the patch instead of "patch" so that we can
|
|
|
|
|
# pick up file adds.
|
|
|
|
|
# The --index flag means: also insert into the index (so we catch adds).
|
|
|
|
|
cmd = ['git', 'apply', '--index', '-p0']
|
|
|
|
|
if directory:
|
|
|
|
|
cmd.extend(('--directory', directory))
|
|
|
|
|
if reject:
|
|
|
|
|
cmd.append('--reject')
|
|
|
|
|
elif IsGitVersionAtLeast('1.7.12'):
|
|
|
|
|
cmd.append('--3way')
|
|
|
|
|
try:
|
|
|
|
|
subprocess2.check_call(cmd, env=GetNoGitPagerEnv(),
|
|
|
|
|
stdin=patch_data, stdout=subprocess2.VOID)
|
|
|
|
|
except subprocess2.CalledProcessError:
|
|
|
|
|
print 'Failed to apply the patch'
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
# If we had an issue, commit the current state and register the issue.
|
|
|
|
|
if not nocommit:
|
|
|
|
|
RunGit(['commit', '-m', (cl.GetDescription() + '\n\n' +
|
|
|
|
|
'patch from issue %(i)s at patchset '
|
|
|
|
|
'%(p)s (http://crrev.com/%(i)s#ps%(p)s)'
|
|
|
|
|
% {'i': issue, 'p': patchset})])
|
|
|
|
|
cl = Changelist(codereview='rietveld', auth_config=auth_config,
|
|
|
|
|
rietveld_server=cl.GetCodereviewServer())
|
|
|
|
|
cl.SetIssue(issue)
|
|
|
|
|
cl.SetPatchset(patchset)
|
|
|
|
|
print "Committed patch locally."
|
|
|
|
|
else:
|
|
|
|
|
print "Patch applied to index."
|
|
|
|
|
return 0
|
|
|
|
|
return cl.CMDPatchIssue(issue_arg, options.reject, options.nocommit,
|
|
|
|
|
options.directory)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def CMDrebase(parser, args):
|
|
|
|
@ -4157,7 +4293,7 @@ def CMDdiff(parser, args):
|
|
|
|
|
parser.error('Unrecognized args: %s' % ' '.join(args))
|
|
|
|
|
|
|
|
|
|
# Uncommitted (staged and unstaged) changes will be destroyed by
|
|
|
|
|
# "git reset --hard" if there are merging conflicts in PatchIssue().
|
|
|
|
|
# "git reset --hard" if there are merging conflicts in CMDPatchIssue().
|
|
|
|
|
# Staged changes would be committed along with the patch from last
|
|
|
|
|
# upload, hence counted toward the "last upload" side in the final
|
|
|
|
|
# diff output, and this is not what we want.
|
|
|
|
@ -4175,8 +4311,7 @@ def CMDdiff(parser, args):
|
|
|
|
|
# Create a new branch based on the merge-base
|
|
|
|
|
RunGit(['checkout', '-q', '-b', TMP_BRANCH, base_branch])
|
|
|
|
|
try:
|
|
|
|
|
# Patch in the latest changes from rietveld.
|
|
|
|
|
rtn = PatchIssue(issue, False, False, None, auth_config)
|
|
|
|
|
rtn = cl.CMDPatchIssue(issue, reject=False, nocommit=False, directory=None)
|
|
|
|
|
if rtn != 0:
|
|
|
|
|
RunGit(['reset', '--hard'])
|
|
|
|
|
return rtn
|
|
|
|
@ -4384,16 +4519,18 @@ def CMDformat(parser, args):
|
|
|
|
|
@subcommand.usage('<codereview url or issue id>')
|
|
|
|
|
def CMDcheckout(parser, args):
|
|
|
|
|
"""Checks out a branch associated with a given Rietveld issue."""
|
|
|
|
|
# TODO(tandrii): consider adding this for Gerrit?
|
|
|
|
|
_, args = parser.parse_args(args)
|
|
|
|
|
|
|
|
|
|
if len(args) != 1:
|
|
|
|
|
parser.print_help()
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
target_issue = ParseIssueNum(args[0])
|
|
|
|
|
if target_issue == None:
|
|
|
|
|
issue_arg = ParseIssueNumberArgument(args[0])
|
|
|
|
|
if issue_arg.valid:
|
|
|
|
|
parser.print_help()
|
|
|
|
|
return 1
|
|
|
|
|
target_issue = issue_arg.issue
|
|
|
|
|
|
|
|
|
|
key_and_issues = [x.split() for x in RunGit(
|
|
|
|
|
['config', '--local', '--get-regexp', r'branch\..*\.rietveldissue'])
|
|
|
|
|