def bundle(args): '''create a mercurial bundle''' bundle_commits = list((c, p) for c, t, p in GitHgHelper.rev_list( '--topo-order', '--full-history', '--parents', '--reverse', *args.rev)) if bundle_commits: # TODO: better UX. For instance, this will fail with an exception when # the parent commit doesn't have mercurial metadata. GRAFT = { None: False, 'false': False, 'true': True, } try: graft = Git.config('cinnabar.graft', values=GRAFT) except InvalidConfig as e: logging.error(e.message) return 1 store = PushStore(graft=graft) if args.version == 1: b2caps = {} elif args.version == 2: b2caps = { 'HG20': (), 'changegroup': ('01', '02'), } with open(args.path, 'wb') as fh: if not b2caps: fh.write('HG10UN') for data in create_bundle(store, bundle_commits, b2caps): fh.write(data) store.close(rollback=True)
def main(args): parser = argparse.ArgumentParser() parser.add_argument('url', help='URL of Mercurial repo to push to') parser.add_argument('commit', help='Git commit to push') parser.add_argument('--config-file', action='append', help='Extra Mercurial config file to load') args = parser.parse_args(args) url = args.url commit = args.commit init_logging() if passwordmgr: repo = get_repo(Remote('hg::%s' % url, url)) else: from mercurial import hg ui = get_ui() for p in args.config_file or []: ui.readconfig(p, trust=True) repo = hg.peer(ui, {}, url) heads = (hexlify(h) for h in repo.heads()) store = PushStore() try: pushed = push(repo, store, {commit: (None, False)}, heads, ()) except Exception as e: # This is a common error. So rather than display the entire stack to # the user, exit with a well-defined exit code so the caller can # display a nice error message. if any(arg.startswith('Pushing merges is not supported yet') for arg in getattr(e, 'args', ())[:1]): return 127 raise commits = [] if pushed: for commit in pushed.iternodes(): changeset = store.hg_changeset(commit) ref = store.changeset_ref(changeset) new_data = type(ref) != str commits.append([commit, changeset, new_data]) # By now, cinnabar or its subprocesses should not be writing anything to # either stdout or stderr. Ensure stderr is flushed for _this_ process, # since git-mozreview uses the same file descriptor for both stdout and # stderr, and we want to try to avoid mixed output. sys.stderr.flush() for commit, changeset, new_data in commits: print('>result>', commit, changeset, new_data) sys.stdout.flush() return 0
def main(args): parser = argparse.ArgumentParser() parser.add_argument('url', help='URL of Mercurial repo to push to') parser.add_argument('commit', help='Git commit to push') parser.add_argument('--config-file', action='append', help='Extra Mercurial config file to load') args = parser.parse_args(args) url = args.url commit = args.commit init_logging() if passwordmgr: repo = get_repo(Remote('hg::%s' % url, url)) else: from mercurial import hg ui = get_ui() for p in args.config_file or []: ui.readconfig(p, trust=True) repo = hg.peer(ui, {}, url) heads = (hexlify(h) for h in repo.heads()) store = PushStore() pushed = push(repo, store, {commit: (None, False)}, heads, ()) commits = [] if pushed: for commit in pushed.iternodes(): changeset = store.hg_changeset(commit) ref = store.changeset_ref(changeset) new_data = type(ref) != str commits.append([commit, changeset, new_data]) # By now, cinnabar or its subprocesses should not be writing anything to # either stdout or stderr. Ensure stderr is flushed for _this_ process, # since git-mozreview uses the same file descriptor for both stdout and # stderr, and we want to try to avoid mixed output. sys.stderr.flush() for commit, changeset, new_data in commits: print('>result>', commit, changeset, new_data) sys.stdout.flush() return 0
def push(self, *refspecs): try: default = 'never' if self._graft else 'phase' values = { None: default, '': default, 'never': 'never', 'phase': 'phase', 'always': 'always', } data = Git.config('cinnabar.data', self._remote.name, values=values) except InvalidConfig as e: logging.error(e.message) return 1 pushes = {s.lstrip('+'): (d, s.startswith('+')) for s, d in (r.split(':', 1) for r in refspecs)} if not self._repo.capable('unbundle'): for source, (dest, force) in pushes.iteritems(): self._helper.write( 'error %s Remote does not support the "unbundle" ' 'capability\n' % dest) self._helper.write('\n') self._helper.flush() else: repo_heads = self._branchmap.heads() PushStore.adopt(self._store, self._graft) pushed = push(self._repo, self._store, pushes, repo_heads, self._branchmap.names(), self._dry_run) status = {} for source, (dest, _) in pushes.iteritems(): if dest.startswith('refs/tags/'): if source: status[dest] = 'Pushing tags is unsupported' else: status[dest] = \ 'Deleting remote tags is unsupported' continue if not dest.startswith(('refs/heads/bookmarks/', 'hg/bookmarks/')): if source: status[dest] = bool(len(pushed)) else: status[dest] = \ 'Deleting remote branches is unsupported' continue name = unquote(dest[21:]) if source: source = self._store.hg_changeset(Git.resolve_ref(source))\ or '' status[dest] = self._repo.pushkey( 'bookmarks', name, self._bookmarks.get(name, ''), source) for source, (dest, force) in pushes.iteritems(): if status[dest] is True: self._helper.write('ok %s\n' % dest) elif status[dest]: self._helper.write('error %s %s\n' % (dest, status[dest])) else: self._helper.write('error %s nothing changed on remote\n' % dest) self._helper.write('\n') self._helper.flush() if not pushed or self._dry_run: data = False elif data == 'always': data = True elif data == 'phase': phases = self._repo.listkeys('phases') drafts = {} if not phases.get('publishing', False): drafts = set(p for p, is_draft in phases.iteritems() if int(is_draft)) if not drafts: data = True else: def draft_commits(): for d in drafts: c = self._store.changeset_ref(d) if c: yield '^%s^@' % c for h in pushed.heads(): yield h args = ['--ancestry-path', '--topo-order'] args.extend(draft_commits()) pushed_drafts = tuple( c for c, t, p in GitHgHelper.rev_list(*args)) # Theoretically, we could have commits with no # metadata that the remote declares are public, while # the rest of our push is in a draft state. That is # however so unlikely that it's not worth the effort # to support partial metadata storage. data = not bool(pushed_drafts) elif data == 'never': data = False self._store.close(rollback=not data)
def push(self, *refspecs): try: default = 'never' if self._graft else 'phase' values = { None: default, '': default, 'never': 'never', 'phase': 'phase', 'always': 'always', } data = Git.config('cinnabar.data', self._remote.name, values=values) except InvalidConfig as e: logging.error(e.message) return 1 pushes = {s.lstrip('+'): (d, s.startswith('+')) for s, d in (r.split(':', 1) for r in refspecs)} if not self._repo.capable('unbundle'): for source, (dest, force) in pushes.iteritems(): self._helper.write( 'error %s Remote does not support the "unbundle" ' 'capability\n' % dest) self._helper.write('\n') self._helper.flush() else: repo_heads = self._branchmap.heads() PushStore.adopt(self._store, self._graft) pushed = push(self._repo, self._store, pushes, repo_heads, self._branchmap.names()) status = {} for source, (dest, _) in pushes.iteritems(): if dest.startswith('refs/tags/'): if source: status[dest] = 'Pushing tags is unsupported' else: status[dest] = \ 'Deleting remote tags is unsupported' continue if not dest.startswith('refs/heads/bookmarks/'): if source: status[dest] = bool(len(pushed)) else: status[dest] = \ 'Deleting remote branches is unsupported' continue name = unquote(dest[21:]) if source: source = self._store.hg_changeset(Git.resolve_ref(source))\ or '' status[dest] = self._repo.pushkey( 'bookmarks', name, self._bookmarks.get(name, ''), source) for source, (dest, force) in pushes.iteritems(): if status[dest] is True: self._helper.write('ok %s\n' % dest) elif status[dest]: self._helper.write('error %s %s\n' % (dest, status[dest])) else: self._helper.write('error %s nothing changed on remote\n' % dest) self._helper.write('\n') self._helper.flush() if not pushed: data = False elif data == 'always': data = True elif data == 'phase': phases = self._repo.listkeys('phases') drafts = {} if not phases.get('publishing', False): drafts = set(p for p, is_draft in phases.iteritems() if int(is_draft)) if not drafts: data = True else: def draft_commits(): for d in drafts: c = self._store.changeset_ref(d) if c: yield '^%s^@' % c for h in pushed.heads(): yield h args = ['rev-list', '--ancestry-path', '--topo-order', '--stdin'] pushed_drafts = tuple( Git.iter(*args, stdin=draft_commits())) # Theoretically, we could have commits with no # metadata that the remote declares are public, while # the rest of our push is in a draft state. That is # however so unlikely that it's not worth the effort # to support partial metadata storage. data = not bool(pushed_drafts) elif data == 'never': data = False self._store.close(rollback=not data)