Improve checkout performance for the common unmodified case

Most projects will have their branch heads matching in all branches,
so switching between them should be just a matter of updating the
work tree's HEAD symref.  This can be done in pure Python, saving
quite a bit of time over forking 'git checkout'.

Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce 2009-04-18 15:04:41 -07:00
parent 0f0dfa3930
commit 89e717d948
3 changed files with 54 additions and 21 deletions

View File

@ -779,9 +779,8 @@ class Project(object):
all = self.bare_ref.all all = self.bare_ref.all
if (R_HEADS + name) in all: if (R_HEADS + name) in all:
cmd = ['checkout', name, '--']
return GitCommand(self, return GitCommand(self,
cmd, ['checkout', name, '--'],
capture_stdout = True, capture_stdout = True,
capture_stderr = True).Wait() == 0 capture_stderr = True).Wait() == 0
@ -815,9 +814,8 @@ class Project(object):
branch.Save() branch.Save()
return True return True
cmd = ['checkout', '-b', branch.name, rev]
if GitCommand(self, if GitCommand(self,
cmd, ['checkout', '-b', branch.name, rev],
capture_stdout = True, capture_stdout = True,
capture_stderr = True).Wait() == 0: capture_stderr = True).Wait() == 0:
branch.Save() branch.Save()
@ -827,16 +825,39 @@ class Project(object):
def CheckoutBranch(self, name): def CheckoutBranch(self, name):
"""Checkout a local topic branch. """Checkout a local topic branch.
""" """
rev = R_HEADS + name
head = self.work_git.GetHead()
if head == rev:
# Already on the branch
#
return True
# Be sure the branch exists all = self.bare_ref.all
try: try:
tip_rev = self.bare_git.rev_parse(R_HEADS + name) revid = all[rev]
except GitError: except KeyError:
return False; # Branch does not exist in this project
#
return False
# Do the checkout if head.startswith(R_HEADS):
cmd = ['checkout', name, '--'] try:
return GitCommand(self, cmd).Wait() == 0 head = all[head]
except KeyError:
head = None
if head == revid:
# Same revision; just update HEAD to point to the new
# target branch, but otherwise take no other action.
#
_lwrite(os.path.join(self.worktree, '.git', HEAD),
'ref: %s%s\n' % (R_HEADS, name))
return True
return GitCommand(self,
['checkout', name, '--'],
capture_stdout = True,
capture_stderr = True).Wait() == 0
def AbandonBranch(self, name): def AbandonBranch(self, name):
"""Destroy a local topic branch. """Destroy a local topic branch.

View File

@ -15,6 +15,7 @@
import sys import sys
from command import Command from command import Command
from progress import Progress
class Checkout(Command): class Checkout(Command):
common = True common = True
@ -35,13 +36,23 @@ The command is equivalent to:
if not args: if not args:
self.Usage() self.Usage()
retValue = 0; nb = args[0]
err = []
all = self.GetProjects(args[1:])
branch = args[0] pm = Progress('Checkout %s' % nb, len(all))
for project in self.GetProjects(args[1:]): for project in all:
if not project.CheckoutBranch(branch): pm.update()
retValue = 1; if not project.CheckoutBranch(nb):
print >>sys.stderr, "error: checking out branch '%s' in %s failed" % (branch, project.name) err.append(project)
pm.end()
if (retValue != 0): if err:
sys.exit(retValue); if len(err) == len(all):
print >>sys.stderr, 'error: no project has branch %s' % nb
else:
for p in err:
print >>sys.stderr,\
"error: %s/: cannot checkout %s" \
% (p.relpath, nb)
sys.exit(1)

View File

@ -49,7 +49,8 @@ revision specified in the manifest.
pm.end() pm.end()
if err: if err:
err.sort()
for p in err: for p in err:
print >>sys.stderr, "error: cannot start in %s" % p.relpath print >>sys.stderr,\
"error: %s/: cannot start %s" \
% (p.relpath, nb)
sys.exit(1) sys.exit(1)