Speed up 'repo start' by removing some forks

Its quite common for most projects to be matching the current
manifest revision, as most developers only modify one or two projects
at any one time.  We can speed up `repo start foo` (that impacts
the entire client) by performing most of the branch creation and
switch operations in pure Python, and thus avoid 4 forks per project.

Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce 2009-04-18 14:45:51 -07:00
parent db45da1208
commit accc56d82b
2 changed files with 103 additions and 25 deletions

View File

@ -57,6 +57,7 @@ class GitConfig(object):
self.file = file self.file = file
self.defaults = defaults self.defaults = defaults
self._cache_dict = None self._cache_dict = None
self._section_dict = None
self._remotes = {} self._remotes = {}
self._branches = {} self._branches = {}
self._pickle = os.path.join( self._pickle = os.path.join(
@ -168,6 +169,33 @@ class GitConfig(object):
self._branches[b.name] = b self._branches[b.name] = b
return b return b
def HasSection(self, section, subsection = ''):
"""Does at least one key in section.subsection exist?
"""
try:
return subsection in self._sections[section]
except KeyError:
return False
@property
def _sections(self):
d = self._section_dict
if d is None:
d = {}
for name in self._cache.keys():
p = name.split('.')
if 2 == len(p):
section = p[0]
subsect = ''
else:
section = p[0]
subsect = '.'.join(p[1:-1])
if section not in d:
d[section] = set()
d[section].add(subsect)
self._section_dict = d
return d
@property @property
def _cache(self): def _cache(self):
if self._cache_dict is None: if self._cache_dict is None:
@ -443,11 +471,23 @@ class Branch(object):
def Save(self): def Save(self):
"""Save this branch back into the configuration. """Save this branch back into the configuration.
""" """
self._Set('merge', self.merge) if self._config.HasSection('branch', self.name):
if self.remote: if self.remote:
self._Set('remote', self.remote.name) self._Set('remote', self.remote.name)
else:
self._Set('remote', None)
self._Set('merge', self.merge)
else: else:
self._Set('remote', None) fd = open(self._config.file, 'ab')
try:
fd.write('[branch "%s"]\n' % self.name)
if self.remote:
fd.write('\tremote = %s\n' % self.remote.name)
if self.merge:
fd.write('\tmerge = %s\n' % self.merge)
finally:
fd.close()
def _Set(self, key, value): def _Set(self, key, value):
key = 'branch.%s.%s' % (self.name, key) key = 'branch.%s.%s' % (self.name, key)

View File

@ -30,6 +30,21 @@ from remote import Remote
from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
def _lwrite(path, content):
lock = '%s.lock' % path
fd = open(lock, 'wb')
try:
fd.write(content)
finally:
fd.close()
try:
os.rename(lock, path)
except OSError:
os.remove(lock)
raise
def _error(fmt, *args): def _error(fmt, *args):
msg = fmt % args msg = fmt % args
print >>sys.stderr, 'error: %s' % msg print >>sys.stderr, 'error: %s' % msg
@ -758,31 +773,54 @@ class Project(object):
def StartBranch(self, name): def StartBranch(self, name):
"""Create a new branch off the manifest's revision. """Create a new branch off the manifest's revision.
""" """
try: head = self.work_git.GetHead()
self.bare_git.rev_parse(R_HEADS + name) if head == (R_HEADS + name):
exists = True return True
except GitError:
exists = False;
if exists: all = self.bare_ref.all
if name == self.CurrentBranch: if (R_HEADS + name) in all:
return True cmd = ['checkout', name, '--']
else: return GitCommand(self,
cmd = ['checkout', name, '--'] cmd,
return GitCommand(self, cmd).Wait() == 0 capture_stdout = True).Wait() == 0
branch = self.GetBranch(name)
branch.remote = self.GetRemote(self.remote.name)
branch.merge = self.revision
rev = branch.LocalMerge
if rev in all:
revid = all[rev]
elif IsId(rev):
revid = rev
else: else:
branch = self.GetBranch(name) revid = None
branch.remote = self.GetRemote(self.remote.name)
branch.merge = self.revision
rev = branch.LocalMerge if head.startswith(R_HEADS):
cmd = ['checkout', '-b', branch.name, rev] try:
if GitCommand(self, cmd).Wait() == 0: head = all[head]
branch.Save() except KeyError:
return True head = None
else:
return False if revid and head and revid == head:
ref = os.path.join(self.gitdir, R_HEADS + name)
try:
os.makedirs(os.path.dirname(ref))
except OSError:
pass
_lwrite(ref, '%s\n' % revid)
_lwrite(os.path.join(self.worktree, '.git', HEAD),
'ref: %s%s\n' % (R_HEADS, name))
branch.Save()
return True
cmd = ['checkout', '-b', branch.name, rev]
if GitCommand(self,
cmd,
capture_stdout = True).Wait() == 0:
branch.Save()
return True
return False
def CheckoutBranch(self, name): def CheckoutBranch(self, name):
"""Checkout a local topic branch. """Checkout a local topic branch.