def list(self, arg=None): assert not arg or arg == 'for-push' fetch = Git.config('cinnabar.fetch') if fetch: heads = [unhexlify(fetch)] branchmap = {None: heads} bookmarks = {} elif self._repo.capable('batch'): if hasattr(self._repo, 'commandexecutor'): with self._repo.commandexecutor() as e: branchmap = e.callcommand('branchmap', {}) heads = e.callcommand('heads', {}) bookmarks = e.callcommand('listkeys', {'namespace': 'bookmarks'}) branchmap = branchmap.result() heads = heads.result() bookmarks = bookmarks.result() elif hasattr(self._repo, 'iterbatch'): batch = self._repo.iterbatch() batch.branchmap() batch.heads() batch.listkeys('bookmarks') batch.submit() branchmap, heads, bookmarks = batch.results() else: batch = self._repo.batch() branchmap = batch.branchmap() heads = batch.heads() bookmarks = batch.listkeys('bookmarks') batch.submit() branchmap = branchmap.value heads = heads.value bookmarks = bookmarks.value if heads == ['\0' * 20]: heads = [] else: while True: branchmap = self._repo.branchmap() heads = self._repo.heads() if heads == ['\0' * 20]: heads = [] # Some branch heads can be non-heads topologically, but if # some heads don't appear in the branchmap, then something # was pushed to the repo between branchmap() and heads() if set(heads).issubset(set(chain(*branchmap.values()))): break bookmarks = self._repo.listkeys('bookmarks') self._bookmarks = bookmarks branchmap = self._branchmap = BranchMap(self._store, branchmap, heads) self._has_unknown_heads = bool(self._branchmap.unknown_heads()) if self._graft and self._has_unknown_heads and not arg: self._store.prepare_graft() get_heads = set(branchmap.heads()) & branchmap.unknown_heads() getbundle(self._repo, self._store, get_heads, branchmap.names()) # We may have failed to graft all changesets, in which case we # skipped them. If that's what happened, we want to create a # new branchmap containing all we do know about, so that we can # avoid telling git about things we don't know, because if we # didn't, it would ask for them, and subsequently fail because # they are missing. # Since we can't know for sure what the right tips might be for # each branch, we won't expose the tips. This means we don't # need to care about the order of the heads for the new # branchmap. self._has_unknown_heads = any(not (self._store.changeset_ref(h)) for h in get_heads) if self._has_unknown_heads: new_branchmap = { branch: set(h for h in branchmap.heads(branch)) for branch in branchmap.names() } new_branchmap = { branch: set(h for h in branchmap.heads(branch) if h not in branchmap.unknown_heads()) for branch in branchmap.names() } new_heads = set(h for h in branchmap.heads() if h not in branchmap.unknown_heads()) for status, head, branch in self._store._hgheads.iterchanges(): branch_heads = new_branchmap.get(branch) if status == VersionedDict.REMOVED: if branch_heads and head in branch_heads: branch_heads.remove(head) if head in new_heads: new_heads.remove(head) else: if not branch_heads: branch_heads = new_branchmap[branch] = set() branch_heads.add(head) new_heads.add(head) branchmap = self._branchmap = BranchMap( self._store, new_branchmap, list(new_heads)) refs = {} for branch in sorted(branchmap.names()): branch_tip = branchmap.tip(branch) for head in sorted(branchmap.heads(branch)): if head == branch_tip: continue refs['refs/heads/branches/%s/%s' % (branch, head)] = head if branch_tip: refs['refs/heads/branches/%s/tip' % branch] = branch_tip for name, sha1 in sorted(bookmarks.iteritems()): if sha1 == NULL_NODE_ID: continue ref = self._store.changeset_ref(sha1) if self._graft and not ref: continue refs['refs/heads/bookmarks/%s' % name] = sha1 if fetch: refs['hg/revs/%s' % fetch] = fetch if '@' in bookmarks: self._HEAD = 'bookmarks/@' head = bookmarks.get('@', branchmap.tip('default')) if self._graft and head: head = self._store.changeset_ref(head) if head: refs['HEAD'] = '@refs/heads/%s' % self._HEAD self._refs = {sanitize_branch_name(k): v for k, v in refs.iteritems()} for k, v in sorted(self._refs.iteritems()): if k.startswith('refs/heads/branches/'): v = self._store.changeset_ref(v) or self._branchmap.git_sha1(v) elif not v.startswith('@'): v = self._store.changeset_ref(v) or '?' if not self._graft or v != '?': self._helper.write('%s %s\n' % (v, k)) self._helper.write('\n') self._helper.flush()
def import_(self, *refs): # If anything wrong happens at any time, we risk git picking # the existing refs/cinnabar refs, so remove them preventively. for sha1, ref in Git.for_each_ref('refs/cinnabar/refs/heads', 'refs/cinnabar/hg', 'refs/cinnabar/HEAD'): Git.delete_ref(ref) def resolve_head(head): resolved = self._refs.get(head) if resolved is None: return resolved if resolved.startswith('@'): return self._refs.get(resolved[1:]) return resolved wanted_refs = { k: v for k, v in ((h, resolve_head(h)) for h in refs) if v } heads = wanted_refs.values() if not heads: heads = self._branchmap.heads() try: # Mercurial can be an order of magnitude slower when creating # a bundle when not giving topological heads, which some of # the branch heads might not be. # http://bz.selenic.com/show_bug.cgi?id=4595 # So, when we're pulling all branch heads, just ask for the # topological heads instead. # `heads` might contain known heads, if e.g. the remote has # never been pulled from, but we happen to have some of its # heads locally already. if self._has_unknown_heads: unknown_heads = self._branchmap.unknown_heads() if set(heads).issuperset(unknown_heads): heads = set(self._branchmap.heads()) & unknown_heads getbundle(self._repo, self._store, heads, self._branchmap.names()) except Exception: wanted_refs = {} raise finally: for ref, value in wanted_refs.iteritems(): ref = 'refs/cinnabar/' + ref Git.update_ref(ref, self._store.changeset_ref(value)) self._store.close() self._helper.write('done\n') self._helper.flush() if self._remote.name: if Git.config('fetch.prune', self._remote.name) != 'true': prune = 'remote.%s.prune' % self._remote.name sys.stderr.write( 'It is recommended that you set "%(conf)s" or ' '"fetch.prune" to "true".\n' ' git config %(conf)s true\n' 'or\n' ' git config fetch.prune true\n' % {'conf': prune}) if self._store.tag_changes: sys.stderr.write('\nRun the following command to update tags:\n') sys.stderr.write(' git fetch --tags hg::tags: tag "*"\n')
def list(self, arg=None): assert not arg or arg == b'for-push' fetch = (Git.config('cinnabar.fetch') or b'').split() if fetch: heads = [unhexlify(f) for f in fetch] branchmap = {None: heads} bookmarks = {} elif self._repo.capable(b'batch'): if hasattr(self._repo, 'commandexecutor'): with self._repo.commandexecutor() as e: branchmap = e.callcommand(b'branchmap', {}) heads = e.callcommand(b'heads', {}) bookmarks = e.callcommand(b'listkeys', { b'namespace': b'bookmarks' }) branchmap = branchmap.result() heads = heads.result() bookmarks = bookmarks.result() elif hasattr(self._repo, b'iterbatch'): batch = self._repo.iterbatch() batch.branchmap() batch.heads() batch.listkeys(b'bookmarks') batch.submit() branchmap, heads, bookmarks = batch.results() else: batch = self._repo.batch() branchmap = batch.branchmap() heads = batch.heads() bookmarks = batch.listkeys(b'bookmarks') batch.submit() branchmap = branchmap.value heads = heads.value bookmarks = bookmarks.value if heads == [b'\0' * 20]: heads = [] else: while True: branchmap = self._repo.branchmap() heads = self._repo.heads() if heads == [b'\0' * 20]: heads = [] # Some branch heads can be non-heads topologically, but if # some heads don't appear in the branchmap, then something # was pushed to the repo between branchmap() and heads() if set(heads).issubset( set(chain(*(v for _, v in iteritems(branchmap))))): break bookmarks = self._repo.listkeys(b'bookmarks') self._bookmarks = bookmarks branchmap = self._branchmap = BranchMap(self._store, branchmap, heads) self._has_unknown_heads = bool(self._branchmap.unknown_heads()) if self._graft and self._has_unknown_heads and not arg: self._store.prepare_graft() get_heads = set(branchmap.heads()) & branchmap.unknown_heads() getbundle(self._repo, self._store, get_heads, branchmap.names()) # We may have failed to graft all changesets, in which case we # skipped them. If that's what happened, we want to create a # new branchmap containing all we do know about, so that we can # avoid telling git about things we don't know, because if we # didn't, it would ask for them, and subsequently fail because # they are missing. # Since we can't know for sure what the right tips might be for # each branch, we won't expose the tips. This means we don't # need to care about the order of the heads for the new # branchmap. self._has_unknown_heads = any(not(self._store.changeset_ref(h)) for h in get_heads) if self._has_unknown_heads: new_branchmap = { branch: set(h for h in branchmap.heads(branch)) for branch in branchmap.names() } new_branchmap = { branch: set(h for h in branchmap.heads(branch) if h not in branchmap.unknown_heads()) for branch in branchmap.names() } new_heads = set(h for h in branchmap.heads() if h not in branchmap.unknown_heads()) for status, head, branch in self._store._hgheads.iterchanges(): branch_heads = new_branchmap.get(branch) if status == VersionedDict.REMOVED: if branch_heads and head in branch_heads: branch_heads.remove(head) if head in new_heads: new_heads.remove(head) else: if not branch_heads: branch_heads = new_branchmap[branch] = set() branch_heads.add(head) new_heads.add(head) branchmap = self._branchmap = BranchMap( self._store, new_branchmap, list(new_heads)) refs_style = None refs_styles = ('bookmarks', 'heads', 'tips') if not fetch and branchmap.heads(): refs_config = 'cinnabar.refs' if arg == b'for-push': if Git.config('cinnabar.pushrefs', remote=self._remote.name): refs_config = 'cinnabar.pushrefs' refs_style = ConfigSetFunc(refs_config, refs_styles, remote=self._remote.name, default='all') refs_style = refs_style or (lambda x: True) self._refs_style = refs_style refs = {} if refs_style('heads') or refs_style('tips'): if refs_style('heads') and refs_style('tips'): self._head_template = b'refs/heads/branches/%s/%s' self._tip_template = b'refs/heads/branches/%s/tip' elif refs_style('heads') and refs_style('bookmarks'): self._head_template = b'refs/heads/branches/%s/%s' elif refs_style('heads'): self._head_template = b'refs/heads/%s/%s' elif refs_style('tips') and refs_style('bookmarks'): self._tip_template = b'refs/heads/branches/%s' elif refs_style('tips'): self._tip_template = b'refs/heads/%s' for branch in sorted(branchmap.names()): branch_tip = branchmap.tip(branch) if refs_style('heads'): for head in sorted(branchmap.heads(branch)): if head == branch_tip and refs_style('tips'): continue refs[self._head_template % (branch, head)] = head if branch_tip and refs_style('tips'): refs[self._tip_template % branch] = branch_tip if refs_style('bookmarks'): if refs_style('heads') or refs_style('tips'): self._bookmark_template = b'refs/heads/bookmarks/%s' else: self._bookmark_template = b'refs/heads/%s' for name, sha1 in sorted(iteritems(bookmarks)): if sha1 == NULL_NODE_ID: continue ref = self._store.changeset_ref(sha1) if self._graft and not ref: continue refs[self._bookmark_template % name] = sha1 for f in fetch: refs[b'hg/revs/%s' % f] = f head_ref = None if refs_style('bookmarks') and b'@' in bookmarks: head_ref = self._bookmark_template % b'@' elif refs_style('tips'): head_ref = self._tip_template % b'default' elif refs_style('heads'): head_ref = self._head_template % ( b'default', branchmap.tip(b'default')) if head_ref: head = refs.get(head_ref) if self._graft and head: head = self._store.changeset_ref(head) if head: refs[b'HEAD'] = b'@%s' % head_ref self._refs = {sanitize_branch_name(k): v for k, v in iteritems(refs)} head_prefix = strip_suffix((self._head_template or b''), b'%s/%s') for k, v in sorted(iteritems(self._refs)): if head_prefix and k.startswith(head_prefix): v = self._store.changeset_ref(v) or self._branchmap.git_sha1(v) elif not v.startswith(b'@'): v = self._store.changeset_ref(v) or b'?' if not self._graft or v != b'?': self._helper.write(b'%s %s\n' % (v, k)) self._helper.write(b'\n') self._helper.flush()