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

Side by Side Diff: gclient_scm.py

Issue 19359002: Allow gclient clone in non-empty directories (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: bug fixes Created 7 years, 5 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/gclient_scm_test.py » ('j') | tests/gclient_scm_test.py » ('J')
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
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 309 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 files = [] if file_list is not None else None 333 files = [] if file_list is not None else None
333 334
334 printed_path = False 335 printed_path = False
335 verbose = [] 336 verbose = []
336 if options.verbose: 337 if options.verbose:
337 print('\n_____ %s%s' % (self.relpath, rev_str)) 338 print('\n_____ %s%s' % (self.relpath, rev_str))
338 verbose = ['--verbose'] 339 verbose = ['--verbose']
339 printed_path = True 340 printed_path = True
340 341
341 url = self._CreateOrUpdateCache(url, options) 342 url = self._CreateOrUpdateCache(url, options)
342 343 if (not os.path.exists(self.checkout_path) or
343 if revision.startswith('refs/'): 344 (os.path.isdir(self.checkout_path) and
344 rev_type = "branch" 345 not os.path.exists(os.path.join(self.checkout_path, '.git')))):
345 elif revision.startswith('origin/'):
346 # For compatability with old naming, translate 'origin' to 'refs/heads'
347 revision = revision.replace('origin/', 'refs/heads/')
348 rev_type = "branch"
349 else:
350 # hash is also a tag, only make a distinction at checkout
351 rev_type = "hash"
352
353 if not os.path.exists(self.checkout_path) or (
354 os.path.isdir(self.checkout_path) and
355 not os.listdir(self.checkout_path)):
356 gclient_utils.safe_makedirs(os.path.dirname(self.checkout_path))
357 self._Clone(revision, url, options) 346 self._Clone(revision, url, options)
358 self.UpdateSubmoduleConfig() 347 self.UpdateSubmoduleConfig()
359 if file_list is not None: 348 if file_list is not None:
360 files = self._Capture(['ls-files']).splitlines() 349 files = self._Capture(['ls-files']).splitlines()
361 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) 350 file_list.extend([os.path.join(self.checkout_path, f) for f in files])
362 if not verbose: 351 if not verbose:
363 # Make the output a little prettier. It's nice to have some whitespace 352 # Make the output a little prettier. It's nice to have some whitespace
364 # between projects when cloning. 353 # between projects when cloning.
365 print('') 354 print('')
366 return 355 return
(...skipping 26 matching lines...) Expand all
393 ['git', 'config', 'remote.origin.gclient-auto-fix-url'], 382 ['git', 'config', 'remote.origin.gclient-auto-fix-url'],
394 cwd=self.checkout_path).strip() != 'False'): 383 cwd=self.checkout_path).strip() != 'False'):
395 print('_____ switching %s to a new upstream' % self.relpath) 384 print('_____ switching %s to a new upstream' % self.relpath)
396 # Make sure it's clean 385 # Make sure it's clean
397 self._CheckClean(rev_str) 386 self._CheckClean(rev_str)
398 # Switch over to the new upstream 387 # Switch over to the new upstream
399 self._Run(['remote', 'set-url', 'origin', url], options) 388 self._Run(['remote', 'set-url', 'origin', url], options)
400 self._FetchAndReset(revision, file_list, options) 389 self._FetchAndReset(revision, file_list, options)
401 return 390 return
402 391
403 if not self._IsValidGitRepo():
404 # .git directory is hosed for some reason, set it back up.
405 print('_____ %s/.git is corrupted, rebuilding' % self.relpath)
406 self._Run(['init'], options)
407 self._Run(['remote', 'set-url', 'origin', url], options)
408
409 if not self._HasHead(): 392 if not self._HasHead():
410 # Previous checkout was aborted before branches could be created in repo, 393 # Previous checkout was aborted before branches could be created in repo,
411 # so we need to reconstruct them here. 394 # so we need to reconstruct them here.
395 self._Run(['init'], options)
396 self._Run(['remote', 'set-url', 'origin', url], options)
412 self._Run(['-c', 'core.deltaBaseCacheLimit=2g', 'pull', 'origin', 397 self._Run(['-c', 'core.deltaBaseCacheLimit=2g', 'pull', 'origin',
413 'master'], options) 398 'master'], options)
414 self._FetchAndReset(revision, file_list, options) 399 self._FetchAndReset(revision, file_list, options)
415 400
416 cur_branch = self._GetCurrentBranch() 401 cur_branch = self._GetCurrentBranch()
417 402
418 # Cases: 403 # Cases:
419 # 0) HEAD is detached. Probably from our initial clone. 404 # 0) HEAD is detached. Probably from our initial clone.
420 # - make sure HEAD is contained by a named ref, then update. 405 # - make sure HEAD is contained by a named ref, then update.
421 # Cases 1-4. HEAD is a branch. 406 # Cases 1-4. HEAD is a branch.
422 # 1) current branch is not tracking a remote branch (could be git-svn) 407 # 1) current branch is not tracking a remote branch (could be git-svn)
423 # - try to rebase onto the new hash or branch 408 # - try to rebase onto the new hash or branch
424 # 2) current branch is tracking a remote branch with local committed 409 # 2) current branch is tracking a remote branch with local committed
425 # changes, but the DEPS file switched to point to a hash 410 # changes, but the DEPS file switched to point to a hash
426 # - rebase those changes on top of the hash 411 # - rebase those changes on top of the hash
427 # 3) current branch is tracking a remote branch w/or w/out changes, 412 # 3) current branch is tracking a remote branch w/or w/out changes,
428 # no switch 413 # no switch
429 # - see if we can FF, if not, prompt the user for rebase, merge, or stop 414 # - see if we can FF, if not, prompt the user for rebase, merge, or stop
430 # 4) current branch is tracking a remote branch, switches to a different 415 # 4) current branch is tracking a remote branch, switches to a different
431 # remote branch 416 # remote branch
432 # - exit 417 # - exit
433 418
434 # GetUpstreamBranch returns something like 'refs/remotes/origin/master' for 419 # GetUpstreamBranch returns something like 'refs/remotes/origin/master' for
435 # a tracking branch 420 # a tracking branch
436 # or 'master' if not a tracking branch (it's based on a specific rev/hash) 421 # or 'master' if not a tracking branch (it's based on a specific rev/hash)
437 # or it returns None if it couldn't find an upstream 422 # or it returns None if it couldn't find an upstream
423 if revision.startswith('refs/'):
424 rev_type = "branch"
425 elif revision.startswith('origin/'):
426 # For compatability with old naming, translate 'origin' to 'refs/heads'
427 revision = revision.replace('origin/', 'refs/heads/')
428 rev_type = "branch"
429 else:
430 # hash is also a tag, only make a distinction at checkout
431 rev_type = "hash"
432
438 if cur_branch is None: 433 if cur_branch is None:
439 upstream_branch = None 434 upstream_branch = None
440 current_type = "detached" 435 current_type = "detached"
441 logging.debug("Detached HEAD") 436 logging.debug("Detached HEAD")
442 else: 437 else:
443 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) 438 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path)
444 if not upstream_branch or not upstream_branch.startswith('refs/remotes'): 439 if not upstream_branch or not upstream_branch.startswith('refs/remotes'):
445 current_type = "hash" 440 current_type = "hash"
446 logging.debug("Current branch is not tracking an upstream (remote)" 441 logging.debug("Current branch is not tracking an upstream (remote)"
447 " branch.") 442 " branch.")
(...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after
781 revision is a branch head. If it is a tag or a specific commit, then we 776 revision is a branch head. If it is a tag or a specific commit, then we
782 leave HEAD detached as it makes future updates simpler -- in this case the 777 leave HEAD detached as it makes future updates simpler -- in this case the
783 user should first create a new branch or switch to an existing branch before 778 user should first create a new branch or switch to an existing branch before
784 making changes in the repo.""" 779 making changes in the repo."""
785 if not options.verbose: 780 if not options.verbose:
786 # git clone doesn't seem to insert a newline properly before printing 781 # git clone doesn't seem to insert a newline properly before printing
787 # to stdout 782 # to stdout
788 print('') 783 print('')
789 template_path = os.path.join( 784 template_path = os.path.join(
790 os.path.dirname(THIS_FILE_PATH), 'git-templates') 785 os.path.dirname(THIS_FILE_PATH), 'git-templates')
791 clone_cmd = ['-c', 'core.deltaBaseCacheLimit=2g', 'clone', '--progress', 786 clone_cmd = ['-c', 'core.deltaBaseCacheLimit=2g', 'clone', '--no-checkout',
792 '--template=%s' % template_path] 787 '--progress', '--template=%s' % template_path]
793 if self.cache_dir: 788 if self.cache_dir:
794 clone_cmd.append('--shared') 789 clone_cmd.append('--shared')
795 if revision.startswith('refs/heads/'):
796 clone_cmd.extend(['-b', revision.replace('refs/heads/', '')])
797 detach_head = False
798 else:
799 detach_head = True
800 if options.verbose: 790 if options.verbose:
801 clone_cmd.append('--verbose') 791 clone_cmd.append('--verbose')
802 clone_cmd.extend([url, self.checkout_path]) 792 clone_cmd.append(url)
803
804 # If the parent directory does not exist, Git clone on Windows will not 793 # If the parent directory does not exist, Git clone on Windows will not
805 # create it, so we need to do it manually. 794 # create it, so we need to do it manually.
806 parent_dir = os.path.dirname(self.checkout_path) 795 parent_dir = os.path.dirname(self.checkout_path)
807 if not os.path.exists(parent_dir): 796 if not os.path.exists(self.checkout_path):
M-A Ruel 2013/07/19 20:42:40 While at it, replace with isdir()
Isaac (away) 2013/07/19 21:50:28 Done.
808 gclient_utils.safe_makedirs(parent_dir) 797 gclient_utils.safe_makedirs(self.checkout_path)
809 798 tmp_dir = tempfile.mkdtemp(
810 for _ in range(3): 799 prefix='_gclient_%s_' % os.path.basename(self.checkout_path),
811 try: 800 dir=parent_dir)
812 self._Run(clone_cmd, options, cwd=self._root_dir, git_filter=True) 801 try:
813 break 802 clone_cmd.append(tmp_dir)
814 except subprocess2.CalledProcessError, e: 803 for _ in range(3):
815 # Too bad we don't have access to the actual output yet. 804 try:
816 # We should check for "transfer closed with NNN bytes remaining to 805 self._Run(clone_cmd, options, cwd=None, git_filter=True)
817 # read". In the meantime, just make sure .git exists. 806 break
818 if (e.returncode == 128 and 807 except subprocess2.CalledProcessError, e:
819 os.path.exists(os.path.join(self.checkout_path, '.git'))): 808 # Too bad we don't have access to the actual output yet.
820 print(str(e)) 809 # We should check for "transfer closed with NNN bytes remaining to
821 print('Retrying...') 810 # read". In the meantime, just make sure .git exists.
822 continue 811 if (e.returncode == 128 and
823 raise e 812 os.path.exists(os.path.join(tmp_dir, '.git'))):
824 813 print(str(e))
825 # Update the "branch-heads" remote-tracking branches, since we might need it 814 print('Retrying...')
826 # to checkout a specific revision below. 815 continue
827 self._UpdateBranchHeads(options, fetch=True) 816 raise e
828 817 os.rename(os.path.join(tmp_dir, '.git'),
829 if detach_head: 818 os.path.join(self.checkout_path, '.git'))
819 finally:
820 if os.listdir(tmp_dir):
821 print('\n_____ removing non-empty tmp dir %s' % tmp_dir)
822 gclient_utils.rmtree(tmp_dir)
823 if revision.startswith('refs/heads/'):
824 self._Run(['checkout', revision.replace('refs/heads/', '')], options)
825 else:
830 # Squelch git's very verbose detached HEAD warning and use our own 826 # Squelch git's very verbose detached HEAD warning and use our own
831 self._Capture(['checkout', '--quiet', '%s' % revision]) 827 self._Run(['checkout', '--quiet', revision], options)
832 print( 828 print(
833 ('Checked out %s to a detached HEAD. Before making any commits\n' 829 ('Checked out %s to a detached HEAD. Before making any commits\n'
834 'in this repo, you should use \'git checkout <branch>\' to switch to\n' 830 'in this repo, you should use \'git checkout <branch>\' to switch to\n'
835 'an existing branch or use \'git checkout origin -b <branch>\' to\n' 831 'an existing branch or use \'git checkout origin -b <branch>\' to\n'
836 'create a new branch for your work.') % revision) 832 'create a new branch for your work.') % revision)
837 833
838 def _AttemptRebase(self, upstream, files, options, newbase=None, 834 def _AttemptRebase(self, upstream, files, options, newbase=None,
839 branch=None, printed_path=False): 835 branch=None, printed_path=False):
840 """Attempt to rebase onto either upstream or, if specified, newbase.""" 836 """Attempt to rebase onto either upstream or, if specified, newbase."""
841 if files is not None: 837 if files is not None:
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
899 "manually.\ncd %s && git " % 895 "manually.\ncd %s && git " %
900 self.checkout_path 896 self.checkout_path
901 + "%s" % ' '.join(rebase_cmd)) 897 + "%s" % ' '.join(rebase_cmd))
902 898
903 print(rebase_output.strip()) 899 print(rebase_output.strip())
904 if not options.verbose: 900 if not options.verbose:
905 # Make the output a little prettier. It's nice to have some 901 # Make the output a little prettier. It's nice to have some
906 # whitespace between projects when syncing. 902 # whitespace between projects when syncing.
907 print('') 903 print('')
908 904
909 def _IsValidGitRepo(self):
910 """Returns if the directory is a valid git repository.
911
912 Checks if git status works.
913 """
914 try:
915 self._Capture(['status'])
916 return True
917 except subprocess2.CalledProcessError:
918 return False
919
920 def _HasHead(self): 905 def _HasHead(self):
921 """Returns True if any commit is checked out. 906 """Returns True if any commit is checked out.
922 907
923 This is done by checking if rev-parse HEAD works in the current repository. 908 This is done by checking if rev-parse HEAD works in the current repository.
924 """ 909 """
925 try: 910 try:
926 self._GetCurrentBranch() 911 self._GetCurrentBranch()
927 return True 912 return True
928 except subprocess2.CalledProcessError: 913 except subprocess2.CalledProcessError:
929 return False 914 return False
(...skipping 505 matching lines...) Expand 10 before | Expand all | Expand 10 after
1435 new_command.append('--force') 1420 new_command.append('--force')
1436 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: 1421 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]:
1437 new_command.extend(('--accept', 'theirs-conflict')) 1422 new_command.extend(('--accept', 'theirs-conflict'))
1438 elif options.manually_grab_svn_rev: 1423 elif options.manually_grab_svn_rev:
1439 new_command.append('--force') 1424 new_command.append('--force')
1440 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: 1425 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]:
1441 new_command.extend(('--accept', 'postpone')) 1426 new_command.extend(('--accept', 'postpone'))
1442 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: 1427 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]:
1443 new_command.extend(('--accept', 'postpone')) 1428 new_command.extend(('--accept', 'postpone'))
1444 return new_command 1429 return new_command
OLDNEW
« no previous file with comments | « no previous file | tests/gclient_scm_test.py » ('j') | tests/gclient_scm_test.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698