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 |