Merge instead of rebasing upstream changes in `gclient sync` when --merge is given.
Merge ALL the things!
BUG=none
Review URL: https://codereview.chromium.org/61823002
git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@246575 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/gclient_scm.py b/gclient_scm.py
index f7f0b88..e7c7aa5 100644
--- a/gclient_scm.py
+++ b/gclient_scm.py
@@ -78,18 +78,6 @@
return re.sub("[a|b]/" + self._current_file, self._replacement_file, line)
-def ask_for_data(prompt, options):
- if options.jobs > 1:
- raise gclient_utils.Error("Background task requires input. Rerun "
- "gclient with --jobs=1 so that\n"
- "interaction is possible.")
- try:
- return raw_input(prompt)
- except KeyboardInterrupt:
- # Hide the exception.
- sys.exit(1)
-
-
### SCM abstraction layer
# Factory Method for SCM wrapper creation
@@ -473,7 +461,8 @@
if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None:
# Our git-svn branch (upstream_branch) is our upstream
self._AttemptRebase(upstream_branch, files, options,
- newbase=revision, printed_path=printed_path)
+ newbase=revision, printed_path=printed_path,
+ merge=options.merge)
printed_path = True
else:
# Can't find a merge-base since we don't know our upstream. That makes
@@ -483,12 +472,13 @@
if options.revision or deps_revision:
upstream_branch = revision
self._AttemptRebase(upstream_branch, files, options,
- printed_path=printed_path)
+ printed_path=printed_path, merge=options.merge)
printed_path = True
elif rev_type == 'hash':
# case 2
self._AttemptRebase(upstream_branch, files, options,
- newbase=revision, printed_path=printed_path)
+ newbase=revision, printed_path=printed_path,
+ merge=options.merge)
printed_path = True
elif revision.replace('heads', 'remotes/' + self.remote) != upstream_branch:
# case 4
@@ -509,25 +499,30 @@
print('Trying fast-forward merge to branch : %s' % upstream_branch)
try:
merge_args = ['merge']
- if not options.merge:
+ if options.merge:
+ merge_args.append('--ff')
+ else:
merge_args.append('--ff-only')
merge_args.append(upstream_branch)
merge_output = scm.GIT.Capture(merge_args, cwd=self.checkout_path)
except subprocess2.CalledProcessError as e:
if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr):
+ files = []
if not printed_path:
print('\n_____ %s%s' % (self.relpath, rev_str))
printed_path = True
while True:
try:
- action = ask_for_data(
- 'Cannot fast-forward merge, attempt to rebase? '
- '(y)es / (q)uit / (s)kip : ', options)
+ action = self._AskForData(
+ 'Cannot %s, attempt to rebase? '
+ '(y)es / (q)uit / (s)kip : ' %
+ ('merge' if options.merge else 'fast-forward merge'),
+ options)
except ValueError:
raise gclient_utils.Error('Invalid Character')
if re.match(r'yes|y', action, re.I):
self._AttemptRebase(upstream_branch, files, options,
- printed_path=printed_path)
+ printed_path=printed_path, merge=False)
printed_path = True
break
elif re.match(r'quit|q', action, re.I):
@@ -883,20 +878,40 @@
'an existing branch or use \'git checkout %s -b <branch>\' to\n'
'create a new branch for your work.') % (revision, self.remote))
+ @staticmethod
+ def _AskForData(prompt, options):
+ if options.jobs > 1:
+ raise gclient_utils.Error("Background task requires input. Rerun "
+ "gclient with --jobs=1 so that\n"
+ "interaction is possible.")
+ try:
+ return raw_input(prompt)
+ except KeyboardInterrupt:
+ # Hide the exception.
+ sys.exit(1)
+
+
def _AttemptRebase(self, upstream, files, options, newbase=None,
- branch=None, printed_path=False):
+ branch=None, printed_path=False, merge=False):
"""Attempt to rebase onto either upstream or, if specified, newbase."""
if files is not None:
files.extend(self._Capture(['diff', upstream, '--name-only']).split())
revision = upstream
if newbase:
revision = newbase
+ action = 'merge' if merge else 'rebase'
if not printed_path:
- print('\n_____ %s : Attempting rebase onto %s...' % (
- self.relpath, revision))
+ print('\n_____ %s : Attempting %s onto %s...' % (
+ self.relpath, action, revision))
printed_path = True
else:
- print('Attempting rebase onto %s...' % revision)
+ print('Attempting %s onto %s...' % (action, revision))
+
+ if merge:
+ merge_output = self._Capture(['merge', revision])
+ if options.verbose:
+ print(merge_output)
+ return
# Build the rebase command here using the args
# git rebase [options] [--onto <newbase>] <upstream> [<branch>]
@@ -916,7 +931,7 @@
re.match(r'cannot rebase: your index contains uncommitted changes',
e.stderr)):
while True:
- rebase_action = ask_for_data(
+ rebase_action = self._AskForData(
'Cannot rebase because of unstaged changes.\n'
'\'git reset --hard HEAD\' ?\n'
'WARNING: destroys any uncommitted work in your current branch!'
diff --git a/tests/gclient_scm_test.py b/tests/gclient_scm_test.py
index 5607f85..741727e 100755
--- a/tests/gclient_scm_test.py
+++ b/tests/gclient_scm_test.py
@@ -16,7 +16,6 @@
import sys
import tempfile
import unittest
-import __builtin__
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
@@ -752,6 +751,20 @@
M 100644 :4 a
M 100644 :5 b
+blob
+mark :7
+data 5
+Mooh
+
+commit refs/heads/feature
+mark :8
+author Bob <bob@example.com> 1390311986 -0000
+committer Bob <bob@example.com> 1390311986 -0000
+data 6
+Add C
+from :3
+M 100644 :7 c
+
reset refs/heads/master
from :3
"""
@@ -785,6 +798,12 @@
stderr=STDOUT, cwd=path).communicate()
return True
+ def _GetAskForDataCallback(self, expected_prompt, return_value):
+ def AskForData(prompt, options):
+ self.assertEquals(prompt, expected_prompt)
+ return return_value
+ return AskForData
+
def setUp(self):
TestCaseUtils.setUp(self)
unittest.TestCase.setUp(self)
@@ -967,6 +986,51 @@
'a7142dc9f0009350b96a11f372b6ea658592aa95')
sys.stdout.close()
+ def testUpdateMerge(self):
+ if not self.enabled:
+ return
+ options = self.Options()
+ options.merge = True
+ scm = gclient_scm.CreateSCM(url=self.url, root_dir=self.root_dir,
+ relpath=self.relpath)
+ scm._Run(['checkout', '-q', 'feature'], options)
+ rev = scm.revinfo(options, (), None)
+ file_list = []
+ scm.update(options, (), file_list)
+ self.assertEquals(file_list, [join(self.base_path, x)
+ for x in ['a', 'b', 'c']])
+ # The actual commit that is created is unstable, so we verify its tree and
+ # parents instead.
+ self.assertEquals(scm._Capture(['rev-parse', 'HEAD:']),
+ 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
+ self.assertEquals(scm._Capture(['rev-parse', 'HEAD^1']), rev)
+ self.assertEquals(scm._Capture(['rev-parse', 'HEAD^2']),
+ scm._Capture(['rev-parse', 'origin/master']))
+ sys.stdout.close()
+
+ def testUpdateRebase(self):
+ if not self.enabled:
+ return
+ options = self.Options()
+ scm = gclient_scm.CreateSCM(url=self.url, root_dir=self.root_dir,
+ relpath=self.relpath)
+ scm._Run(['checkout', '-q', 'feature'], options)
+ file_list = []
+ # Fake a 'y' key press.
+ scm._AskForData = self._GetAskForDataCallback(
+ 'Cannot fast-forward merge, attempt to rebase? '
+ '(y)es / (q)uit / (s)kip : ', 'y')
+ scm.update(options, (), file_list)
+ self.assertEquals(file_list, [join(self.base_path, x)
+ for x in ['a', 'b', 'c']])
+ # The actual commit that is created is unstable, so we verify its tree and
+ # parent instead.
+ self.assertEquals(scm._Capture(['rev-parse', 'HEAD:']),
+ 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
+ self.assertEquals(scm._Capture(['rev-parse', 'HEAD^']),
+ scm._Capture(['rev-parse', 'origin/master']))
+ sys.stdout.close()
+
def testUpdateReset(self):
if not self.enabled:
return
@@ -1039,7 +1103,9 @@
file_path = join(self.base_path, 'b')
open(file_path, 'w').writelines('conflict\n')
scm._Run(['commit', '-am', 'test'], options)
- __builtin__.raw_input = lambda x: 'y'
+ scm._AskForData = self._GetAskForDataCallback(
+ 'Cannot fast-forward merge, attempt to rebase? '
+ '(y)es / (q)uit / (s)kip : ', 'y')
exception = ('Conflict while rebasing this branch.\n'
'Fix the conflict and run gclient again.\n'
'See \'man git-rebase\' for details.\n')