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>
diff --git a/git_config.py b/git_config.py
index 78069c5..7aad80d 100644
--- a/git_config.py
+++ b/git_config.py
@@ -57,6 +57,7 @@
self.file = file
self.defaults = defaults
self._cache_dict = None
+ self._section_dict = None
self._remotes = {}
self._branches = {}
self._pickle = os.path.join(
@@ -168,6 +169,33 @@
self._branches[b.name] = 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
def _cache(self):
if self._cache_dict is None:
@@ -443,11 +471,23 @@
def Save(self):
"""Save this branch back into the configuration.
"""
- self._Set('merge', self.merge)
- if self.remote:
- self._Set('remote', self.remote.name)
+ if self._config.HasSection('branch', self.name):
+ if self.remote:
+ self._Set('remote', self.remote.name)
+ else:
+ self._Set('remote', None)
+ self._Set('merge', self.merge)
+
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):
key = 'branch.%s.%s' % (self.name, key)
diff --git a/project.py b/project.py
index 79ade3b..8d6e4b6 100644
--- a/project.py
+++ b/project.py
@@ -30,6 +30,21 @@
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):
msg = fmt % args
print >>sys.stderr, 'error: %s' % msg
@@ -758,31 +773,54 @@
def StartBranch(self, name):
"""Create a new branch off the manifest's revision.
"""
- try:
- self.bare_git.rev_parse(R_HEADS + name)
- exists = True
- except GitError:
- exists = False;
+ head = self.work_git.GetHead()
+ if head == (R_HEADS + name):
+ return True
- if exists:
- if name == self.CurrentBranch:
- return True
- else:
- cmd = ['checkout', name, '--']
- return GitCommand(self, cmd).Wait() == 0
+ all = self.bare_ref.all
+ if (R_HEADS + name) in all:
+ cmd = ['checkout', name, '--']
+ return GitCommand(self,
+ cmd,
+ 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:
- branch = self.GetBranch(name)
- branch.remote = self.GetRemote(self.remote.name)
- branch.merge = self.revision
+ revid = None
- rev = branch.LocalMerge
- cmd = ['checkout', '-b', branch.name, rev]
- if GitCommand(self, cmd).Wait() == 0:
- branch.Save()
- return True
- else:
- return False
+ if head.startswith(R_HEADS):
+ try:
+ head = all[head]
+ except KeyError:
+ head = None
+
+ 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):
"""Checkout a local topic branch.