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>
diff --git a/project.py b/project.py
index 10a9b25..029a80f 100644
--- a/project.py
+++ b/project.py
@@ -779,9 +779,8 @@
all = self.bare_ref.all
if (R_HEADS + name) in all:
- cmd = ['checkout', name, '--']
return GitCommand(self,
- cmd,
+ ['checkout', name, '--'],
capture_stdout = True,
capture_stderr = True).Wait() == 0
@@ -815,9 +814,8 @@
branch.Save()
return True
- cmd = ['checkout', '-b', branch.name, rev]
if GitCommand(self,
- cmd,
+ ['checkout', '-b', branch.name, rev],
capture_stdout = True,
capture_stderr = True).Wait() == 0:
branch.Save()
@@ -827,16 +825,39 @@
def CheckoutBranch(self, name):
"""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:
- tip_rev = self.bare_git.rev_parse(R_HEADS + name)
- except GitError:
- return False;
+ revid = all[rev]
+ except KeyError:
+ # Branch does not exist in this project
+ #
+ return False
- # Do the checkout
- cmd = ['checkout', name, '--']
- return GitCommand(self, cmd).Wait() == 0
+ if head.startswith(R_HEADS):
+ try:
+ 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):
"""Destroy a local topic branch.
diff --git a/subcmds/checkout.py b/subcmds/checkout.py
index 07644c9..4198acd 100644
--- a/subcmds/checkout.py
+++ b/subcmds/checkout.py
@@ -15,6 +15,7 @@
import sys
from command import Command
+from progress import Progress
class Checkout(Command):
common = True
@@ -35,13 +36,23 @@
if not args:
self.Usage()
- retValue = 0;
+ nb = args[0]
+ err = []
+ all = self.GetProjects(args[1:])
- branch = args[0]
- for project in self.GetProjects(args[1:]):
- if not project.CheckoutBranch(branch):
- retValue = 1;
- print >>sys.stderr, "error: checking out branch '%s' in %s failed" % (branch, project.name)
+ pm = Progress('Checkout %s' % nb, len(all))
+ for project in all:
+ pm.update()
+ if not project.CheckoutBranch(nb):
+ err.append(project)
+ pm.end()
- if (retValue != 0):
- sys.exit(retValue);
+ if err:
+ 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)
diff --git a/subcmds/start.py b/subcmds/start.py
index 49bb0e1..8c74625 100644
--- a/subcmds/start.py
+++ b/subcmds/start.py
@@ -49,7 +49,8 @@
pm.end()
if err:
- err.sort()
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)