Commit 30c46d64 authored by bauerb@chromium.org's avatar bauerb@chromium.org

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
parent 9d2165cd
...@@ -78,18 +78,6 @@ class GitDiffFilterer(DiffFiltererWrapper): ...@@ -78,18 +78,6 @@ class GitDiffFilterer(DiffFiltererWrapper):
return re.sub("[a|b]/" + self._current_file, self._replacement_file, line) 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 ### SCM abstraction layer
# Factory Method for SCM wrapper creation # Factory Method for SCM wrapper creation
...@@ -473,7 +461,8 @@ class GitWrapper(SCMWrapper): ...@@ -473,7 +461,8 @@ class GitWrapper(SCMWrapper):
if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None:
# Our git-svn branch (upstream_branch) is our upstream # Our git-svn branch (upstream_branch) is our upstream
self._AttemptRebase(upstream_branch, files, options, self._AttemptRebase(upstream_branch, files, options,
newbase=revision, printed_path=printed_path) newbase=revision, printed_path=printed_path,
merge=options.merge)
printed_path = True printed_path = True
else: else:
# Can't find a merge-base since we don't know our upstream. That makes # Can't find a merge-base since we don't know our upstream. That makes
...@@ -483,12 +472,13 @@ class GitWrapper(SCMWrapper): ...@@ -483,12 +472,13 @@ class GitWrapper(SCMWrapper):
if options.revision or deps_revision: if options.revision or deps_revision:
upstream_branch = revision upstream_branch = revision
self._AttemptRebase(upstream_branch, files, options, self._AttemptRebase(upstream_branch, files, options,
printed_path=printed_path) printed_path=printed_path, merge=options.merge)
printed_path = True printed_path = True
elif rev_type == 'hash': elif rev_type == 'hash':
# case 2 # case 2
self._AttemptRebase(upstream_branch, files, options, self._AttemptRebase(upstream_branch, files, options,
newbase=revision, printed_path=printed_path) newbase=revision, printed_path=printed_path,
merge=options.merge)
printed_path = True printed_path = True
elif revision.replace('heads', 'remotes/' + self.remote) != upstream_branch: elif revision.replace('heads', 'remotes/' + self.remote) != upstream_branch:
# case 4 # case 4
...@@ -509,25 +499,30 @@ class GitWrapper(SCMWrapper): ...@@ -509,25 +499,30 @@ class GitWrapper(SCMWrapper):
print('Trying fast-forward merge to branch : %s' % upstream_branch) print('Trying fast-forward merge to branch : %s' % upstream_branch)
try: try:
merge_args = ['merge'] merge_args = ['merge']
if not options.merge: if options.merge:
merge_args.append('--ff')
else:
merge_args.append('--ff-only') merge_args.append('--ff-only')
merge_args.append(upstream_branch) merge_args.append(upstream_branch)
merge_output = scm.GIT.Capture(merge_args, cwd=self.checkout_path) merge_output = scm.GIT.Capture(merge_args, cwd=self.checkout_path)
except subprocess2.CalledProcessError as e: except subprocess2.CalledProcessError as e:
if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr): if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr):
files = []
if not printed_path: if not printed_path:
print('\n_____ %s%s' % (self.relpath, rev_str)) print('\n_____ %s%s' % (self.relpath, rev_str))
printed_path = True printed_path = True
while True: while True:
try: try:
action = ask_for_data( action = self._AskForData(
'Cannot fast-forward merge, attempt to rebase? ' 'Cannot %s, attempt to rebase? '
'(y)es / (q)uit / (s)kip : ', options) '(y)es / (q)uit / (s)kip : ' %
('merge' if options.merge else 'fast-forward merge'),
options)
except ValueError: except ValueError:
raise gclient_utils.Error('Invalid Character') raise gclient_utils.Error('Invalid Character')
if re.match(r'yes|y', action, re.I): if re.match(r'yes|y', action, re.I):
self._AttemptRebase(upstream_branch, files, options, self._AttemptRebase(upstream_branch, files, options,
printed_path=printed_path) printed_path=printed_path, merge=False)
printed_path = True printed_path = True
break break
elif re.match(r'quit|q', action, re.I): elif re.match(r'quit|q', action, re.I):
...@@ -883,20 +878,40 @@ class GitWrapper(SCMWrapper): ...@@ -883,20 +878,40 @@ class GitWrapper(SCMWrapper):
'an existing branch or use \'git checkout %s -b <branch>\' to\n' 'an existing branch or use \'git checkout %s -b <branch>\' to\n'
'create a new branch for your work.') % (revision, self.remote)) '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, 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.""" """Attempt to rebase onto either upstream or, if specified, newbase."""
if files is not None: if files is not None:
files.extend(self._Capture(['diff', upstream, '--name-only']).split()) files.extend(self._Capture(['diff', upstream, '--name-only']).split())
revision = upstream revision = upstream
if newbase: if newbase:
revision = newbase revision = newbase
action = 'merge' if merge else 'rebase'
if not printed_path: if not printed_path:
print('\n_____ %s : Attempting rebase onto %s...' % ( print('\n_____ %s : Attempting %s onto %s...' % (
self.relpath, revision)) self.relpath, action, revision))
printed_path = True printed_path = True
else: 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 # Build the rebase command here using the args
# git rebase [options] [--onto <newbase>] <upstream> [<branch>] # git rebase [options] [--onto <newbase>] <upstream> [<branch>]
...@@ -916,7 +931,7 @@ class GitWrapper(SCMWrapper): ...@@ -916,7 +931,7 @@ class GitWrapper(SCMWrapper):
re.match(r'cannot rebase: your index contains uncommitted changes', re.match(r'cannot rebase: your index contains uncommitted changes',
e.stderr)): e.stderr)):
while True: while True:
rebase_action = ask_for_data( rebase_action = self._AskForData(
'Cannot rebase because of unstaged changes.\n' 'Cannot rebase because of unstaged changes.\n'
'\'git reset --hard HEAD\' ?\n' '\'git reset --hard HEAD\' ?\n'
'WARNING: destroys any uncommitted work in your current branch!' 'WARNING: destroys any uncommitted work in your current branch!'
......
...@@ -16,7 +16,6 @@ import os ...@@ -16,7 +16,6 @@ import os
import sys import sys
import tempfile import tempfile
import unittest import unittest
import __builtin__
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
...@@ -752,6 +751,20 @@ from :3 ...@@ -752,6 +751,20 @@ from :3
M 100644 :4 a M 100644 :4 a
M 100644 :5 b 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 reset refs/heads/master
from :3 from :3
""" """
...@@ -785,6 +798,12 @@ from :3 ...@@ -785,6 +798,12 @@ from :3
stderr=STDOUT, cwd=path).communicate() stderr=STDOUT, cwd=path).communicate()
return True 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): def setUp(self):
TestCaseUtils.setUp(self) TestCaseUtils.setUp(self)
unittest.TestCase.setUp(self) unittest.TestCase.setUp(self)
...@@ -967,6 +986,51 @@ class ManagedGitWrapperTestCase(BaseGitWrapperTestCase): ...@@ -967,6 +986,51 @@ class ManagedGitWrapperTestCase(BaseGitWrapperTestCase):
'a7142dc9f0009350b96a11f372b6ea658592aa95') 'a7142dc9f0009350b96a11f372b6ea658592aa95')
sys.stdout.close() 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): def testUpdateReset(self):
if not self.enabled: if not self.enabled:
return return
...@@ -1039,7 +1103,9 @@ class ManagedGitWrapperTestCase(BaseGitWrapperTestCase): ...@@ -1039,7 +1103,9 @@ class ManagedGitWrapperTestCase(BaseGitWrapperTestCase):
file_path = join(self.base_path, 'b') file_path = join(self.base_path, 'b')
open(file_path, 'w').writelines('conflict\n') open(file_path, 'w').writelines('conflict\n')
scm._Run(['commit', '-am', 'test'], options) 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' exception = ('Conflict while rebasing this branch.\n'
'Fix the conflict and run gclient again.\n' 'Fix the conflict and run gclient again.\n'
'See \'man git-rebase\' for details.\n') 'See \'man git-rebase\' for details.\n')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment