Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(70)

Side by Side Diff: gclient_scm.py

Issue 61823002: Merge instead of rebasing upstream changes in `gclient sync` when --merge is given. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: lint fixes Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tests/gclient_scm_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Gclient-specific SCM-specific operations.""" 5 """Gclient-specific SCM-specific operations."""
6 6
7 import collections 7 import collections
8 import logging 8 import logging
9 import os 9 import os
10 import posixpath 10 import posixpath
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
71 index_string = "diff --git " 71 index_string = "diff --git "
72 72
73 def SetCurrentFile(self, current_file): 73 def SetCurrentFile(self, current_file):
74 # Get filename by parsing "a/<filename> b/<filename>" 74 # Get filename by parsing "a/<filename> b/<filename>"
75 self._current_file = current_file[:(len(current_file)/2)][2:] 75 self._current_file = current_file[:(len(current_file)/2)][2:]
76 76
77 def _Replace(self, line): 77 def _Replace(self, line):
78 return re.sub("[a|b]/" + self._current_file, self._replacement_file, line) 78 return re.sub("[a|b]/" + self._current_file, self._replacement_file, line)
79 79
80 80
81 def ask_for_data(prompt, options):
82 if options.jobs > 1:
83 raise gclient_utils.Error("Background task requires input. Rerun "
84 "gclient with --jobs=1 so that\n"
85 "interaction is possible.")
86 try:
87 return raw_input(prompt)
88 except KeyboardInterrupt:
89 # Hide the exception.
90 sys.exit(1)
91
92
93 ### SCM abstraction layer 81 ### SCM abstraction layer
94 82
95 # Factory Method for SCM wrapper creation 83 # Factory Method for SCM wrapper creation
96 84
97 def GetScmName(url): 85 def GetScmName(url):
98 if url: 86 if url:
99 url, _ = gclient_utils.SplitUrlRevision(url) 87 url, _ = gclient_utils.SplitUrlRevision(url)
100 if (url.startswith('git://') or url.startswith('ssh://') or 88 if (url.startswith('git://') or url.startswith('ssh://') or
101 url.startswith('git+http://') or url.startswith('git+https://') or 89 url.startswith('git+http://') or url.startswith('git+https://') or
102 url.endswith('.git')): 90 url.endswith('.git')):
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after
466 self._CheckClean(rev_str) 454 self._CheckClean(rev_str)
467 self._CheckDetachedHead(rev_str, options) 455 self._CheckDetachedHead(rev_str, options)
468 self._Capture(['checkout', '--quiet', '%s' % revision]) 456 self._Capture(['checkout', '--quiet', '%s' % revision])
469 if not printed_path: 457 if not printed_path:
470 print('\n_____ %s%s' % (self.relpath, rev_str)) 458 print('\n_____ %s%s' % (self.relpath, rev_str))
471 elif current_type == 'hash': 459 elif current_type == 'hash':
472 # case 1 460 # case 1
473 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: 461 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None:
474 # Our git-svn branch (upstream_branch) is our upstream 462 # Our git-svn branch (upstream_branch) is our upstream
475 self._AttemptRebase(upstream_branch, files, options, 463 self._AttemptRebase(upstream_branch, files, options,
476 newbase=revision, printed_path=printed_path) 464 newbase=revision, printed_path=printed_path,
465 merge=options.merge)
477 printed_path = True 466 printed_path = True
478 else: 467 else:
479 # Can't find a merge-base since we don't know our upstream. That makes 468 # Can't find a merge-base since we don't know our upstream. That makes
480 # this command VERY likely to produce a rebase failure. For now we 469 # this command VERY likely to produce a rebase failure. For now we
481 # assume origin is our upstream since that's what the old behavior was. 470 # assume origin is our upstream since that's what the old behavior was.
482 upstream_branch = self.remote 471 upstream_branch = self.remote
483 if options.revision or deps_revision: 472 if options.revision or deps_revision:
484 upstream_branch = revision 473 upstream_branch = revision
485 self._AttemptRebase(upstream_branch, files, options, 474 self._AttemptRebase(upstream_branch, files, options,
486 printed_path=printed_path) 475 printed_path=printed_path, merge=options.merge)
487 printed_path = True 476 printed_path = True
488 elif rev_type == 'hash': 477 elif rev_type == 'hash':
489 # case 2 478 # case 2
490 self._AttemptRebase(upstream_branch, files, options, 479 self._AttemptRebase(upstream_branch, files, options,
491 newbase=revision, printed_path=printed_path) 480 newbase=revision, printed_path=printed_path,
481 merge=options.merge)
492 printed_path = True 482 printed_path = True
493 elif revision.replace('heads', 'remotes/' + self.remote) != upstream_branch: 483 elif revision.replace('heads', 'remotes/' + self.remote) != upstream_branch:
494 # case 4 484 # case 4
495 new_base = revision.replace('heads', 'remotes/' + self.remote) 485 new_base = revision.replace('heads', 'remotes/' + self.remote)
496 if not printed_path: 486 if not printed_path:
497 print('\n_____ %s%s' % (self.relpath, rev_str)) 487 print('\n_____ %s%s' % (self.relpath, rev_str))
498 switch_error = ("Switching upstream branch from %s to %s\n" 488 switch_error = ("Switching upstream branch from %s to %s\n"
499 % (upstream_branch, new_base) + 489 % (upstream_branch, new_base) +
500 "Please merge or rebase manually:\n" + 490 "Please merge or rebase manually:\n" +
501 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + 491 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) +
502 "OR git checkout -b <some new branch> %s" % new_base) 492 "OR git checkout -b <some new branch> %s" % new_base)
503 raise gclient_utils.Error(switch_error) 493 raise gclient_utils.Error(switch_error)
504 else: 494 else:
505 # case 3 - the default case 495 # case 3 - the default case
506 if files is not None: 496 if files is not None:
507 files = self._Capture(['diff', upstream_branch, '--name-only']).split() 497 files = self._Capture(['diff', upstream_branch, '--name-only']).split()
508 if verbose: 498 if verbose:
509 print('Trying fast-forward merge to branch : %s' % upstream_branch) 499 print('Trying fast-forward merge to branch : %s' % upstream_branch)
510 try: 500 try:
511 merge_args = ['merge'] 501 merge_args = ['merge']
512 if not options.merge: 502 if options.merge:
503 merge_args.append('--ff')
504 else:
513 merge_args.append('--ff-only') 505 merge_args.append('--ff-only')
514 merge_args.append(upstream_branch) 506 merge_args.append(upstream_branch)
515 merge_output = scm.GIT.Capture(merge_args, cwd=self.checkout_path) 507 merge_output = scm.GIT.Capture(merge_args, cwd=self.checkout_path)
516 except subprocess2.CalledProcessError as e: 508 except subprocess2.CalledProcessError as e:
517 if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr): 509 if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr):
510 files = []
518 if not printed_path: 511 if not printed_path:
519 print('\n_____ %s%s' % (self.relpath, rev_str)) 512 print('\n_____ %s%s' % (self.relpath, rev_str))
520 printed_path = True 513 printed_path = True
521 while True: 514 while True:
522 try: 515 try:
523 action = ask_for_data( 516 action = self._AskForData(
524 'Cannot fast-forward merge, attempt to rebase? ' 517 'Cannot %s, attempt to rebase? '
525 '(y)es / (q)uit / (s)kip : ', options) 518 '(y)es / (q)uit / (s)kip : ' %
519 ('merge' if options.merge else 'fast-forward merge'),
520 options)
526 except ValueError: 521 except ValueError:
527 raise gclient_utils.Error('Invalid Character') 522 raise gclient_utils.Error('Invalid Character')
528 if re.match(r'yes|y', action, re.I): 523 if re.match(r'yes|y', action, re.I):
529 self._AttemptRebase(upstream_branch, files, options, 524 self._AttemptRebase(upstream_branch, files, options,
530 printed_path=printed_path) 525 printed_path=printed_path, merge=False)
531 printed_path = True 526 printed_path = True
532 break 527 break
533 elif re.match(r'quit|q', action, re.I): 528 elif re.match(r'quit|q', action, re.I):
534 raise gclient_utils.Error("Can't fast-forward, please merge or " 529 raise gclient_utils.Error("Can't fast-forward, please merge or "
535 "rebase manually.\n" 530 "rebase manually.\n"
536 "cd %s && git " % self.checkout_path 531 "cd %s && git " % self.checkout_path
537 + "rebase %s" % upstream_branch) 532 + "rebase %s" % upstream_branch)
538 elif re.match(r'skip|s', action, re.I): 533 elif re.match(r'skip|s', action, re.I):
539 print('Skipping %s' % self.relpath) 534 print('Skipping %s' % self.relpath)
540 return 535 return
(...skipping 335 matching lines...) Expand 10 before | Expand all | Expand 10 after
876 ['checkout', '--quiet', revision.replace('refs/heads/', '')], options) 871 ['checkout', '--quiet', revision.replace('refs/heads/', '')], options)
877 else: 872 else:
878 # Squelch git's very verbose detached HEAD warning and use our own 873 # Squelch git's very verbose detached HEAD warning and use our own
879 self._Run(['checkout', '--quiet', revision], options) 874 self._Run(['checkout', '--quiet', revision], options)
880 print( 875 print(
881 ('Checked out %s to a detached HEAD. Before making any commits\n' 876 ('Checked out %s to a detached HEAD. Before making any commits\n'
882 'in this repo, you should use \'git checkout <branch>\' to switch to\n' 877 'in this repo, you should use \'git checkout <branch>\' to switch to\n'
883 'an existing branch or use \'git checkout %s -b <branch>\' to\n' 878 'an existing branch or use \'git checkout %s -b <branch>\' to\n'
884 'create a new branch for your work.') % (revision, self.remote)) 879 'create a new branch for your work.') % (revision, self.remote))
885 880
881 @staticmethod
882 def _AskForData(prompt, options):
883 if options.jobs > 1:
884 raise gclient_utils.Error("Background task requires input. Rerun "
885 "gclient with --jobs=1 so that\n"
886 "interaction is possible.")
887 try:
888 return raw_input(prompt)
889 except KeyboardInterrupt:
890 # Hide the exception.
891 sys.exit(1)
892
893
886 def _AttemptRebase(self, upstream, files, options, newbase=None, 894 def _AttemptRebase(self, upstream, files, options, newbase=None,
887 branch=None, printed_path=False): 895 branch=None, printed_path=False, merge=False):
888 """Attempt to rebase onto either upstream or, if specified, newbase.""" 896 """Attempt to rebase onto either upstream or, if specified, newbase."""
889 if files is not None: 897 if files is not None:
890 files.extend(self._Capture(['diff', upstream, '--name-only']).split()) 898 files.extend(self._Capture(['diff', upstream, '--name-only']).split())
891 revision = upstream 899 revision = upstream
892 if newbase: 900 if newbase:
893 revision = newbase 901 revision = newbase
902 action = 'merge' if merge else 'rebase'
894 if not printed_path: 903 if not printed_path:
895 print('\n_____ %s : Attempting rebase onto %s...' % ( 904 print('\n_____ %s : Attempting %s onto %s...' % (
896 self.relpath, revision)) 905 self.relpath, action, revision))
897 printed_path = True 906 printed_path = True
898 else: 907 else:
899 print('Attempting rebase onto %s...' % revision) 908 print('Attempting %s onto %s...' % (action, revision))
909
910 if merge:
911 merge_output = self._Capture(['merge', revision])
912 if options.verbose:
913 print(merge_output)
914 return
900 915
901 # Build the rebase command here using the args 916 # Build the rebase command here using the args
902 # git rebase [options] [--onto <newbase>] <upstream> [<branch>] 917 # git rebase [options] [--onto <newbase>] <upstream> [<branch>]
903 rebase_cmd = ['rebase'] 918 rebase_cmd = ['rebase']
904 if options.verbose: 919 if options.verbose:
905 rebase_cmd.append('--verbose') 920 rebase_cmd.append('--verbose')
906 if newbase: 921 if newbase:
907 rebase_cmd.extend(['--onto', newbase]) 922 rebase_cmd.extend(['--onto', newbase])
908 rebase_cmd.append(upstream) 923 rebase_cmd.append(upstream)
909 if branch: 924 if branch:
910 rebase_cmd.append(branch) 925 rebase_cmd.append(branch)
911 926
912 try: 927 try:
913 rebase_output = scm.GIT.Capture(rebase_cmd, cwd=self.checkout_path) 928 rebase_output = scm.GIT.Capture(rebase_cmd, cwd=self.checkout_path)
914 except subprocess2.CalledProcessError, e: 929 except subprocess2.CalledProcessError, e:
915 if (re.match(r'cannot rebase: you have unstaged changes', e.stderr) or 930 if (re.match(r'cannot rebase: you have unstaged changes', e.stderr) or
916 re.match(r'cannot rebase: your index contains uncommitted changes', 931 re.match(r'cannot rebase: your index contains uncommitted changes',
917 e.stderr)): 932 e.stderr)):
918 while True: 933 while True:
919 rebase_action = ask_for_data( 934 rebase_action = self._AskForData(
920 'Cannot rebase because of unstaged changes.\n' 935 'Cannot rebase because of unstaged changes.\n'
921 '\'git reset --hard HEAD\' ?\n' 936 '\'git reset --hard HEAD\' ?\n'
922 'WARNING: destroys any uncommitted work in your current branch!' 937 'WARNING: destroys any uncommitted work in your current branch!'
923 ' (y)es / (q)uit / (s)how : ', options) 938 ' (y)es / (q)uit / (s)how : ', options)
924 if re.match(r'yes|y', rebase_action, re.I): 939 if re.match(r'yes|y', rebase_action, re.I):
925 self._Run(['reset', '--hard', 'HEAD'], options) 940 self._Run(['reset', '--hard', 'HEAD'], options)
926 # Should this be recursive? 941 # Should this be recursive?
927 rebase_output = scm.GIT.Capture(rebase_cmd, cwd=self.checkout_path) 942 rebase_output = scm.GIT.Capture(rebase_cmd, cwd=self.checkout_path)
928 break 943 break
929 elif re.match(r'quit|q', rebase_action, re.I): 944 elif re.match(r'quit|q', rebase_action, re.I):
(...skipping 594 matching lines...) Expand 10 before | Expand all | Expand 10 after
1524 new_command.append('--force') 1539 new_command.append('--force')
1525 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: 1540 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]:
1526 new_command.extend(('--accept', 'theirs-conflict')) 1541 new_command.extend(('--accept', 'theirs-conflict'))
1527 elif options.manually_grab_svn_rev: 1542 elif options.manually_grab_svn_rev:
1528 new_command.append('--force') 1543 new_command.append('--force')
1529 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: 1544 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]:
1530 new_command.extend(('--accept', 'postpone')) 1545 new_command.extend(('--accept', 'postpone'))
1531 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: 1546 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]:
1532 new_command.extend(('--accept', 'postpone')) 1547 new_command.extend(('--accept', 'postpone'))
1533 return new_command 1548 return new_command
OLDNEW
« no previous file with comments | « no previous file | tests/gclient_scm_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698