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 @@ class GitConfig(object): 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 @@ class GitConfig(object): 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 @@ class Branch(object): 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 remote import Remote 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 @@ class Project(object): 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.