| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |