From 30c46d64d7bb4fe63eec8d7b146e1d3494e38097 Mon Sep 17 00:00:00 2001 From: "bauerb@chromium.org" Date: Thu, 23 Jan 2014 12:11:56 +0000 Subject: [PATCH] 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 --- gclient_scm.py | 65 ++++++++++++++++++++++-------------- tests/gclient_scm_test.py | 70 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 108 insertions(+), 27 deletions(-) diff --git a/gclient_scm.py b/gclient_scm.py index f7f0b885c..e7c7aa56b 100644 --- a/gclient_scm.py +++ b/gclient_scm.py @@ -78,18 +78,6 @@ class GitDiffFilterer(DiffFiltererWrapper): 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 @@ class GitWrapper(SCMWrapper): 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 @@ class GitWrapper(SCMWrapper): 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 @@ class GitWrapper(SCMWrapper): 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 @@ class GitWrapper(SCMWrapper): 'an existing branch or use \'git checkout %s -b \' 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 ] [] @@ -916,7 +931,7 @@ class GitWrapper(SCMWrapper): 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 5607f85dd..741727e34 100755 --- a/tests/gclient_scm_test.py +++ b/tests/gclient_scm_test.py @@ -16,7 +16,6 @@ import os 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 @@ from :3 M 100644 :4 a M 100644 :5 b +blob +mark :7 +data 5 +Mooh + +commit refs/heads/feature +mark :8 +author Bob 1390311986 -0000 +committer Bob 1390311986 -0000 +data 6 +Add C +from :3 +M 100644 :7 c + reset refs/heads/master from :3 """ @@ -785,6 +798,12 @@ from :3 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 @@ class ManagedGitWrapperTestCase(BaseGitWrapperTestCase): '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 @@ class ManagedGitWrapperTestCase(BaseGitWrapperTestCase): 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')