| 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(): | |
| 418 # Previous checkout was aborted before branches could be created in repo, | |
| 419 # so we need to reconstruct them here. | |
| 420 self._Run(['-c', 'core.deltaBaseCacheLimit=2g', 'pull', 'origin', | |
| 421 'master'], options) | |
| 422 self._FetchAndReset(revision, file_list, options) | |
| 423 | |
| 424 cur_branch = self._GetCurrentBranch() | 411 cur_branch = self._GetCurrentBranch() |
| 425 | 412 |
| 426 # Cases: | 413 # Cases: |
| 427 # 0) HEAD is detached. Probably from our initial clone. | 414 # 0) HEAD is detached. Probably from our initial clone. |
| 428 # - make sure HEAD is contained by a named ref, then update. | 415 # - make sure HEAD is contained by a named ref, then update. |
| 429 # Cases 1-4. HEAD is a branch. | 416 # Cases 1-4. HEAD is a branch. |
| 430 # 1) current branch is not tracking a remote branch (could be git-svn) | 417 # 1) current branch is not tracking a remote branch (could be git-svn) |
| 431 # - try to rebase onto the new hash or branch | 418 # - try to rebase onto the new hash or branch |
| 432 # 2) current branch is tracking a remote branch with local committed | 419 # 2) current branch is tracking a remote branch with local committed |
| 433 # changes, but the DEPS file switched to point to a hash | 420 # changes, but the DEPS file switched to point to a hash |
| (...skipping 418 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 | 839 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 | 840 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 | 841 user should first create a new branch or switch to an existing branch before |
| 855 making changes in the repo.""" | 842 making changes in the repo.""" |
| 856 if not options.verbose: | 843 if not options.verbose: |
| 857 # git clone doesn't seem to insert a newline properly before printing | 844 # git clone doesn't seem to insert a newline properly before printing |
| 858 # to stdout | 845 # to stdout |
| 859 print('') | 846 print('') |
| 860 template_path = os.path.join( | 847 template_path = os.path.join( |
| 861 os.path.dirname(THIS_FILE_PATH), 'git-templates') | 848 os.path.dirname(THIS_FILE_PATH), 'git-templates') |
| 862 clone_cmd = ['-c', 'core.deltaBaseCacheLimit=2g', 'clone', '--progress', | 849 clone_cmd = ['-c', 'core.deltaBaseCacheLimit=2g', 'clone', '--no-checkout', |
| 863 '--template=%s' % template_path] | 850 '--progress', '--template=%s' % template_path] |
| 864 if self.cache_dir: | 851 if self.cache_dir: |
| 865 clone_cmd.append('--shared') | 852 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: | 853 if options.verbose: |
| 872 clone_cmd.append('--verbose') | 854 clone_cmd.append('--verbose') |
| 873 clone_cmd.extend([url, self.checkout_path]) | 855 clone_cmd.append(url) |
| 874 | |
| 875 # If the parent directory does not exist, Git clone on Windows will not | 856 # If the parent directory does not exist, Git clone on Windows will not |
| 876 # create it, so we need to do it manually. | 857 # create it, so we need to do it manually. |
| 877 parent_dir = os.path.dirname(self.checkout_path) | 858 parent_dir = os.path.dirname(self.checkout_path) |
| 878 if not os.path.exists(parent_dir): | 859 gclient_utils.safe_makedirs(parent_dir) |
| 879 gclient_utils.safe_makedirs(parent_dir) | 860 tmp_dir = tempfile.mkdtemp( |
| 880 | 861 prefix='_gclient_%s_' % os.path.basename(self.checkout_path), |
| 881 for _ in range(3): | 862 dir=parent_dir) |
| 882 try: | 863 try: |
| 883 self._Run(clone_cmd, options, cwd=self._root_dir, git_filter=True) | 864 clone_cmd.append(tmp_dir) |
| 884 break | 865 for i in xrange(3): |
| 885 except subprocess2.CalledProcessError, e: | 866 try: |
| 886 # Too bad we don't have access to the actual output yet. | 867 self._Run(clone_cmd, options, cwd=self._root_dir, git_filter=True) |
| 887 # We should check for "transfer closed with NNN bytes remaining to | 868 break |
| 888 # read". In the meantime, just make sure .git exists. | 869 except subprocess2.CalledProcessError as e: |
| 889 if (e.returncode == 128 and | 870 gclient_utils.rmtree(os.path.join(tmp_dir, '.git')) |
| 890 os.path.exists(os.path.join(self.checkout_path, '.git'))): | 871 if e.returncode != 128 or i == 2: |
| 872 raise |
| 891 print(str(e)) | 873 print(str(e)) |
| 892 print('Retrying...') | 874 print('Retrying...') |
| 893 continue | 875 gclient_utils.safe_makedirs(self.checkout_path) |
| 894 raise e | 876 os.rename(os.path.join(tmp_dir, '.git'), |
| 895 | 877 os.path.join(self.checkout_path, '.git')) |
| 896 # Update the "branch-heads" remote-tracking branches, since we might need it | 878 finally: |
| 897 # to checkout a specific revision below. | 879 if os.listdir(tmp_dir): |
| 898 self._UpdateBranchHeads(options, fetch=True) | 880 print('\n_____ removing non-empty tmp dir %s' % tmp_dir) |
| 899 | 881 gclient_utils.rmtree(tmp_dir) |
| 900 if detach_head: | 882 if revision.startswith('refs/heads/'): |
| 883 self._Run( |
| 884 ['checkout', '--quiet', revision.replace('refs/heads/', '')], options) |
| 885 else: |
| 901 # Squelch git's very verbose detached HEAD warning and use our own | 886 # Squelch git's very verbose detached HEAD warning and use our own |
| 902 self._Capture(['checkout', '--quiet', '%s' % revision]) | 887 self._Run(['checkout', '--quiet', revision], options) |
| 903 print( | 888 print( |
| 904 ('Checked out %s to a detached HEAD. Before making any commits\n' | 889 ('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' | 890 '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' | 891 'an existing branch or use \'git checkout origin -b <branch>\' to\n' |
| 907 'create a new branch for your work.') % revision) | 892 'create a new branch for your work.') % revision) |
| 908 | 893 |
| 909 def _AttemptRebase(self, upstream, files, options, newbase=None, | 894 def _AttemptRebase(self, upstream, files, options, newbase=None, |
| 910 branch=None, printed_path=False): | 895 branch=None, printed_path=False): |
| 911 """Attempt to rebase onto either upstream or, if specified, newbase.""" | 896 """Attempt to rebase onto either upstream or, if specified, newbase.""" |
| 912 if files is not None: | 897 if files is not None: |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 970 "manually.\ncd %s && git " % | 955 "manually.\ncd %s && git " % |
| 971 self.checkout_path | 956 self.checkout_path |
| 972 + "%s" % ' '.join(rebase_cmd)) | 957 + "%s" % ' '.join(rebase_cmd)) |
| 973 | 958 |
| 974 print(rebase_output.strip()) | 959 print(rebase_output.strip()) |
| 975 if not options.verbose: | 960 if not options.verbose: |
| 976 # Make the output a little prettier. It's nice to have some | 961 # Make the output a little prettier. It's nice to have some |
| 977 # whitespace between projects when syncing. | 962 # whitespace between projects when syncing. |
| 978 print('') | 963 print('') |
| 979 | 964 |
| 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): | |
| 992 """Returns True if any commit is checked out. | |
| 993 | |
| 994 This is done by checking if rev-parse HEAD works in the current repository. | |
| 995 """ | |
| 996 try: | |
| 997 self._GetCurrentBranch() | |
| 998 return True | |
| 999 except subprocess2.CalledProcessError: | |
| 1000 return False | |
| 1001 | |
| 1002 @staticmethod | 965 @staticmethod |
| 1003 def _CheckMinVersion(min_version): | 966 def _CheckMinVersion(min_version): |
| 1004 (ok, current_version) = scm.GIT.AssertVersion(min_version) | 967 (ok, current_version) = scm.GIT.AssertVersion(min_version) |
| 1005 if not ok: | 968 if not ok: |
| 1006 raise gclient_utils.Error('git version %s < minimum required %s' % | 969 raise gclient_utils.Error('git version %s < minimum required %s' % |
| 1007 (current_version, min_version)) | 970 (current_version, min_version)) |
| 1008 | 971 |
| 1009 def _IsRebasing(self): | 972 def _IsRebasing(self): |
| 1010 # Check for any of REBASE-i/REBASE-m/REBASE/AM. Unfortunately git doesn't | 973 # Check for any of REBASE-i/REBASE-m/REBASE/AM. Unfortunately git doesn't |
| 1011 # have a plumbing command to determine whether a rebase is in progress, so | 974 # have a plumbing command to determine whether a rebase is in progress, so |
| (...skipping 494 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1506 new_command.append('--force') | 1469 new_command.append('--force') |
| 1507 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1470 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
| 1508 new_command.extend(('--accept', 'theirs-conflict')) | 1471 new_command.extend(('--accept', 'theirs-conflict')) |
| 1509 elif options.manually_grab_svn_rev: | 1472 elif options.manually_grab_svn_rev: |
| 1510 new_command.append('--force') | 1473 new_command.append('--force') |
| 1511 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1474 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
| 1512 new_command.extend(('--accept', 'postpone')) | 1475 new_command.extend(('--accept', 'postpone')) |
| 1513 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1476 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
| 1514 new_command.extend(('--accept', 'postpone')) | 1477 new_command.extend(('--accept', 'postpone')) |
| 1515 return new_command | 1478 return new_command |
| OLD | NEW |