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 |
11 import re | 11 import re |
12 import sys | 12 import sys |
13 import tempfile | |
13 import threading | 14 import threading |
14 import time | 15 import time |
15 | 16 |
16 import gclient_utils | 17 import gclient_utils |
17 import scm | 18 import scm |
18 import subprocess2 | 19 import subprocess2 |
19 | 20 |
20 | 21 |
21 THIS_FILE_PATH = os.path.abspath(__file__) | 22 THIS_FILE_PATH = os.path.abspath(__file__) |
22 | 23 |
(...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
343 if revision.startswith('refs/'): | 344 if revision.startswith('refs/'): |
344 rev_type = "branch" | 345 rev_type = "branch" |
345 elif revision.startswith('origin/'): | 346 elif revision.startswith('origin/'): |
346 # For compatability with old naming, translate 'origin' to 'refs/heads' | 347 # For compatability with old naming, translate 'origin' to 'refs/heads' |
347 revision = revision.replace('origin/', 'refs/heads/') | 348 revision = revision.replace('origin/', 'refs/heads/') |
348 rev_type = "branch" | 349 rev_type = "branch" |
349 else: | 350 else: |
350 # hash is also a tag, only make a distinction at checkout | 351 # hash is also a tag, only make a distinction at checkout |
351 rev_type = "hash" | 352 rev_type = "hash" |
352 | 353 |
353 if not os.path.exists(self.checkout_path) or ( | 354 if (not os.path.exists(self.checkout_path) or |
354 os.path.isdir(self.checkout_path) and | 355 (os.path.isdir(self.checkout_path) and |
355 not os.listdir(self.checkout_path)): | 356 not os.path.exists(os.path.join(self.checkout_path, '.git')))): |
356 gclient_utils.safe_makedirs(os.path.dirname(self.checkout_path)) | |
357 self._Clone(revision, url, options) | 357 self._Clone(revision, url, options) |
358 self.UpdateSubmoduleConfig() | 358 self.UpdateSubmoduleConfig() |
359 if file_list is not None: | 359 if file_list is not None: |
360 files = self._Capture(['ls-files']).splitlines() | 360 files = self._Capture(['ls-files']).splitlines() |
361 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 361 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
362 if not verbose: | 362 if not verbose: |
363 # Make the output a little prettier. It's nice to have some whitespace | 363 # Make the output a little prettier. It's nice to have some whitespace |
364 # between projects when cloning. | 364 # between projects when cloning. |
365 print('') | 365 print('') |
366 return | 366 return |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
401 self._FetchAndReset(revision, file_list, options) | 401 self._FetchAndReset(revision, file_list, options) |
402 return_early = True | 402 return_early = True |
403 | 403 |
404 # Need to do this in the normal path as well as in the post-remote-switch | 404 # Need to do this in the normal path as well as in the post-remote-switch |
405 # path. | 405 # path. |
406 self._PossiblySwitchCache(url, options) | 406 self._PossiblySwitchCache(url, options) |
407 | 407 |
408 if return_early: | 408 if return_early: |
409 return | 409 return |
410 | 410 |
411 if not self._IsValidGitRepo(): | |
412 # .git directory is hosed for some reason, set it back up. | |
413 print('_____ %s/.git is corrupted, rebuilding' % self.relpath) | |
414 self._Run(['init'], options) | |
415 self._Run(['remote', 'set-url', 'origin', url], options) | |
416 | |
417 if not self._HasHead(): | 411 if not self._HasHead(): |
418 # Previous checkout was aborted before branches could be created in repo, | 412 # Previous checkout was aborted before branches could be created in repo, |
419 # so we need to reconstruct them here. | 413 # so we need to reconstruct them here. |
414 self._Run(['init'], options) | |
415 self._Run(['remote', 'set-url', 'origin', url], options) | |
420 self._Run(['-c', 'core.deltaBaseCacheLimit=2g', 'pull', 'origin', | 416 self._Run(['-c', 'core.deltaBaseCacheLimit=2g', 'pull', 'origin', |
421 'master'], options) | 417 'master'], options) |
422 self._FetchAndReset(revision, file_list, options) | 418 self._FetchAndReset(revision, file_list, options) |
iannucci
2013/07/19 23:59:07
This whole clause is superfluous if we're moving .
Isaac (away)
2013/07/20 00:38:27
I'd prefer to modify the 'if' below in a later CL.
| |
423 | 419 |
424 cur_branch = self._GetCurrentBranch() | 420 cur_branch = self._GetCurrentBranch() |
425 | 421 |
426 # Cases: | 422 # Cases: |
427 # 0) HEAD is detached. Probably from our initial clone. | 423 # 0) HEAD is detached. Probably from our initial clone. |
428 # - make sure HEAD is contained by a named ref, then update. | 424 # - make sure HEAD is contained by a named ref, then update. |
429 # Cases 1-4. HEAD is a branch. | 425 # Cases 1-4. HEAD is a branch. |
430 # 1) current branch is not tracking a remote branch (could be git-svn) | 426 # 1) current branch is not tracking a remote branch (could be git-svn) |
431 # - try to rebase onto the new hash or branch | 427 # - try to rebase onto the new hash or branch |
432 # 2) current branch is tracking a remote branch with local committed | 428 # 2) current branch is tracking a remote branch with local committed |
(...skipping 419 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
852 revision is a branch head. If it is a tag or a specific commit, then we | 848 revision is a branch head. If it is a tag or a specific commit, then we |
853 leave HEAD detached as it makes future updates simpler -- in this case the | 849 leave HEAD detached as it makes future updates simpler -- in this case the |
854 user should first create a new branch or switch to an existing branch before | 850 user should first create a new branch or switch to an existing branch before |
855 making changes in the repo.""" | 851 making changes in the repo.""" |
856 if not options.verbose: | 852 if not options.verbose: |
857 # git clone doesn't seem to insert a newline properly before printing | 853 # git clone doesn't seem to insert a newline properly before printing |
858 # to stdout | 854 # to stdout |
859 print('') | 855 print('') |
860 template_path = os.path.join( | 856 template_path = os.path.join( |
861 os.path.dirname(THIS_FILE_PATH), 'git-templates') | 857 os.path.dirname(THIS_FILE_PATH), 'git-templates') |
862 clone_cmd = ['-c', 'core.deltaBaseCacheLimit=2g', 'clone', '--progress', | 858 clone_cmd = ['-c', 'core.deltaBaseCacheLimit=2g', 'clone', '--no-checkout', |
863 '--template=%s' % template_path] | 859 '--progress', '--template=%s' % template_path] |
864 if self.cache_dir: | 860 if self.cache_dir: |
865 clone_cmd.append('--shared') | 861 clone_cmd.append('--shared') |
866 if revision.startswith('refs/heads/'): | |
867 clone_cmd.extend(['-b', revision.replace('refs/heads/', '')]) | |
868 detach_head = False | |
869 else: | |
870 detach_head = True | |
871 if options.verbose: | 862 if options.verbose: |
iannucci
2013/07/19 23:59:07
I think you'll need to rebase. There's some cache_
Isaac (away)
2013/07/20 00:38:27
I'm not sure what you're referring to, this is reb
| |
872 clone_cmd.append('--verbose') | 863 clone_cmd.append('--verbose') |
873 clone_cmd.extend([url, self.checkout_path]) | 864 clone_cmd.append(url) |
874 | |
875 # If the parent directory does not exist, Git clone on Windows will not | 865 # If the parent directory does not exist, Git clone on Windows will not |
876 # create it, so we need to do it manually. | 866 # create it, so we need to do it manually. |
877 parent_dir = os.path.dirname(self.checkout_path) | 867 parent_dir = os.path.dirname(self.checkout_path) |
878 if not os.path.exists(parent_dir): | 868 gclient_utils.safe_makedirs(self.checkout_path) |
879 gclient_utils.safe_makedirs(parent_dir) | 869 tmp_dir = tempfile.mkdtemp( |
880 | 870 prefix='_gclient_%s_' % os.path.basename(self.checkout_path), |
881 for _ in range(3): | 871 dir=parent_dir) |
882 try: | 872 try: |
883 self._Run(clone_cmd, options, cwd=self._root_dir, git_filter=True) | 873 clone_cmd.append(tmp_dir) |
884 break | 874 for _ in range(3): |
885 except subprocess2.CalledProcessError, e: | 875 try: |
886 # Too bad we don't have access to the actual output yet. | 876 self._Run(clone_cmd, options, cwd=self._root_dir, git_filter=True) |
887 # We should check for "transfer closed with NNN bytes remaining to | 877 break |
888 # read". In the meantime, just make sure .git exists. | 878 except subprocess2.CalledProcessError, e: |
889 if (e.returncode == 128 and | 879 # Too bad we don't have access to the actual output yet. |
890 os.path.exists(os.path.join(self.checkout_path, '.git'))): | 880 # We should check for "transfer closed with NNN bytes remaining to |
891 print(str(e)) | 881 # read". In the meantime, just make sure .git exists. |
892 print('Retrying...') | 882 if (e.returncode == 128 and |
893 continue | 883 os.path.exists(os.path.join(tmp_dir, '.git'))): |
iannucci
2013/07/19 23:59:07
If we fail this way we should completely nuke the
Isaac (away)
2013/07/20 00:38:27
Could you elaborate on what you're suggesting?
| |
894 raise e | 884 print(str(e)) |
895 | 885 print('Retrying...') |
896 # Update the "branch-heads" remote-tracking branches, since we might need it | 886 continue |
897 # to checkout a specific revision below. | 887 raise e |
898 self._UpdateBranchHeads(options, fetch=True) | 888 os.rename(os.path.join(tmp_dir, '.git'), |
899 | 889 os.path.join(self.checkout_path, '.git')) |
900 if detach_head: | 890 finally: |
891 if os.listdir(tmp_dir): | |
892 print('\n_____ removing non-empty tmp dir %s' % tmp_dir) | |
893 gclient_utils.rmtree(tmp_dir) | |
894 if revision.startswith('refs/heads/'): | |
iannucci
2013/07/19 23:59:07
(aside: This substitution feels like we shouldn't
Isaac (away)
2013/07/20 00:38:27
I don't know what's going on here but I'd prefer t
| |
895 self._Capture( | |
896 ['checkout', '-quiet', revision.replace('refs/heads/', '')]) | |
897 else: | |
901 # Squelch git's very verbose detached HEAD warning and use our own | 898 # Squelch git's very verbose detached HEAD warning and use our own |
902 self._Capture(['checkout', '--quiet', '%s' % revision]) | 899 self._Capture(['checkout', '--quiet', revision]) |
903 print( | 900 print( |
904 ('Checked out %s to a detached HEAD. Before making any commits\n' | 901 ('Checked out %s to a detached HEAD. Before making any commits\n' |
905 'in this repo, you should use \'git checkout <branch>\' to switch to\n' | 902 'in this repo, you should use \'git checkout <branch>\' to switch to\n' |
906 'an existing branch or use \'git checkout origin -b <branch>\' to\n' | 903 'an existing branch or use \'git checkout origin -b <branch>\' to\n' |
907 'create a new branch for your work.') % revision) | 904 'create a new branch for your work.') % revision) |
908 | 905 |
909 def _AttemptRebase(self, upstream, files, options, newbase=None, | 906 def _AttemptRebase(self, upstream, files, options, newbase=None, |
910 branch=None, printed_path=False): | 907 branch=None, printed_path=False): |
911 """Attempt to rebase onto either upstream or, if specified, newbase.""" | 908 """Attempt to rebase onto either upstream or, if specified, newbase.""" |
912 if files is not None: | 909 if files is not None: |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
970 "manually.\ncd %s && git " % | 967 "manually.\ncd %s && git " % |
971 self.checkout_path | 968 self.checkout_path |
972 + "%s" % ' '.join(rebase_cmd)) | 969 + "%s" % ' '.join(rebase_cmd)) |
973 | 970 |
974 print(rebase_output.strip()) | 971 print(rebase_output.strip()) |
975 if not options.verbose: | 972 if not options.verbose: |
976 # Make the output a little prettier. It's nice to have some | 973 # Make the output a little prettier. It's nice to have some |
977 # whitespace between projects when syncing. | 974 # whitespace between projects when syncing. |
978 print('') | 975 print('') |
979 | 976 |
980 def _IsValidGitRepo(self): | |
981 """Returns if the directory is a valid git repository. | |
982 | |
983 Checks if git status works. | |
984 """ | |
985 try: | |
986 self._Capture(['status']) | |
987 return True | |
988 except subprocess2.CalledProcessError: | |
989 return False | |
990 | |
991 def _HasHead(self): | 977 def _HasHead(self): |
992 """Returns True if any commit is checked out. | 978 """Returns True if any commit is checked out. |
993 | 979 |
994 This is done by checking if rev-parse HEAD works in the current repository. | 980 This is done by checking if rev-parse HEAD works in the current repository. |
995 """ | 981 """ |
996 try: | 982 try: |
997 self._GetCurrentBranch() | 983 self._GetCurrentBranch() |
998 return True | 984 return True |
999 except subprocess2.CalledProcessError: | 985 except subprocess2.CalledProcessError: |
1000 return False | 986 return False |
(...skipping 505 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1506 new_command.append('--force') | 1492 new_command.append('--force') |
1507 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1493 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
1508 new_command.extend(('--accept', 'theirs-conflict')) | 1494 new_command.extend(('--accept', 'theirs-conflict')) |
1509 elif options.manually_grab_svn_rev: | 1495 elif options.manually_grab_svn_rev: |
1510 new_command.append('--force') | 1496 new_command.append('--force') |
1511 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1497 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
1512 new_command.extend(('--accept', 'postpone')) | 1498 new_command.extend(('--accept', 'postpone')) |
1513 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1499 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
1514 new_command.extend(('--accept', 'postpone')) | 1500 new_command.extend(('--accept', 'postpone')) |
1515 return new_command | 1501 return new_command |
OLD | NEW |