def metarewrite(repo, old, newbases, commitopts): """Return (nodeid, created) where nodeid is the identifier of the changeset generated by the rewrite process, and created is True if nodeid was actually created. If created is False, nodeid references a changeset existing before the rewrite call. """ wlock = lock = tr = None try: wlock = repo.wlock() lock = repo.lock() tr = repo.transaction('rewrite') updatebookmarks = bookmarksupdater(repo, old.node(), tr) message = cmdutil.logmessage(repo.ui, commitopts) if not message: message = old.description() user = commitopts.get('user') or old.user() date = commitopts.get('date') or None # old.date() extra = dict(commitopts.get('extra', old.extra())) extra['branch'] = old.branch() new = context.metadataonlyctx(repo, old, parents=newbases, text=message, user=user, date=date, extra=extra) if commitopts.get('edit'): new._text = cmdutil.commitforceeditor(repo, new, []) revcount = len(repo) newid = repo.commitctx(new) new = repo[newid] created = len(repo) != revcount updatebookmarks(newid) tr.close() return newid, created finally: lockmod.release(tr, lock, wlock)
def phabsend(ui, repo, *revs, **opts): """upload changesets to Phabricator If there are multiple revisions specified, they will be send as a stack with a linear dependencies relationship using the order specified by the revset. For the first time uploading changesets, local tags will be created to maintain the association. After the first time, phabsend will check obsstore and tags information so it can figure out whether to update an existing Differential Revision, or create a new one. If --amend is set, update commit messages so they have the ``Differential Revision`` URL, remove related tags. This is similar to what arcanist will do, and is more desired in author-push workflows. Otherwise, use local tags to record the ``Differential Revision`` association. The --confirm option lets you confirm changesets before sending them. You can also add following to your configuration file to make it default behaviour:: [phabsend] confirm = true phabsend will check obsstore and the above association to decide whether to update an existing Differential Revision, or create a new one. """ revs = list(revs) + opts.get(b'rev', []) revs = scmutil.revrange(repo, revs) if not revs: raise error.Abort(_(b'phabsend requires at least one changeset')) if opts.get(b'amend'): cmdutil.checkunfinished(repo) # {newnode: (oldnode, olddiff, olddrev} oldmap = getoldnodedrevmap(repo, [repo[r].node() for r in revs]) confirm = ui.configbool(b'phabsend', b'confirm') confirm |= bool(opts.get(b'confirm')) if confirm: confirmed = _confirmbeforesend(repo, revs, oldmap) if not confirmed: raise error.Abort(_(b'phabsend cancelled')) actions = [] reviewers = opts.get(b'reviewer', []) if reviewers: phids = user_group_phids(repo, reviewers) actions.append({b'type': b'reviewers.add', b'value': phids}) drevids = [] # [int] diffmap = {} # {newnode: diff} # Send patches one by one so we know their Differential Revision IDs and # can provide dependency relationship lastrevid = None for rev in revs: ui.debug(b'sending rev %d\n' % rev) ctx = repo[rev] acts = list(actions) reviewers = list(commitparser.parse_reviewers(ctx.description())) if reviewers: phids = user_group_phids(repo, reviewers) acts.append({b'type': b'reviewers.add', b'value': phids}) bugs = commitparser.parse_bugs(ctx.description()) if bugs: acts.append({b'type': b'bugzilla.bug-id', b'value': str(bugs[0]).encode('utf-8')}) # Get Differential Revision ID oldnode, olddiff, revid = oldmap.get(ctx.node(), (None, None, None)) if oldnode != ctx.node() or opts.get(b'amend'): # Create or update Differential Revision revision, diff = createdifferentialrevision( ctx, revid, lastrevid, oldnode, olddiff, acts) diffmap[ctx.node()] = diff newrevid = int(revision[r'object'][r'id']) if revid: action = b'updated' else: action = b'created' # Create a local tag to note the association, if commit message # does not have it already m = _differentialrevisiondescre.search(ctx.description()) if not m or int(m.group(b'id')) != newrevid: tagname = b'D%d' % newrevid tags.tag(repo, tagname, ctx.node(), message=None, user=None, date=None, local=True) else: # Nothing changed. But still set "newrevid" so the next revision # could depend on this one. newrevid = revid action = b'skipped' actiondesc = ui.label( {b'created': _(b'created'), b'skipped': _(b'skipped'), b'updated': _(b'updated')}[action], b'phabricator.action.%s' % action) drevdesc = ui.label(b'D%s' % newrevid, b'phabricator.drev') nodedesc = ui.label(bytes(ctx), b'phabricator.node') desc = ui.label(ctx.description().split(b'\n')[0], b'phabricator.desc') ui.write(_(b'%s - %s - %s: %s\n') % (drevdesc, actiondesc, nodedesc, desc)) drevids.append(newrevid) lastrevid = newrevid # Update commit messages and remove tags if opts.get(b'amend'): unfi = repo.unfiltered() drevs = callconduit(repo, b'differential.query', {b'ids': drevids}) with repo.wlock(), repo.lock(), repo.transaction(b'phabsend'): wnode = unfi[b'.'].node() mapping = {} # {oldnode: [newnode]} for i, rev in enumerate(revs): old = unfi[rev] drevid = drevids[i] drev = [d for d in drevs if int(d[r'id']) == drevid][0] newdesc = getdescfromdrev(drev) # Make sure commit message contain "Differential Revision" if old.description() != newdesc: parents = [ mapping.get(old.p1().node(), (old.p1(),))[0], mapping.get(old.p2().node(), (old.p2(),))[0], ] new = context.metadataonlyctx( repo, old, parents=parents, text=newdesc, user=old.user(), date=old.date(), extra=old.extra()) newnode = new.commit() mapping[old.node()] = [newnode] # Update diff property writediffproperties(unfi[newnode], diffmap[old.node()]) # Remove local tags since it's no longer necessary tagname = b'D%d' % drevid if tagname in repo.tags(): tags.tag(repo, tagname, nullid, message=None, user=None, date=None, local=True) scmutil.cleanupnodes(repo, mapping, b'phabsend') if wnode in mapping: unfi.setparents(mapping[wnode][0])
def phabsend(ui, repo, *revs, **opts): """upload changesets to Phabricator If there are multiple revisions specified, they will be send as a stack with a linear dependencies relationship using the order specified by the revset. For the first time uploading changesets, local tags will be created to maintain the association. After the first time, phabsend will check obsstore and tags information so it can figure out whether to update an existing Differential Revision, or create a new one. If --amend is set, update commit messages so they have the ``Differential Revision`` URL, remove related tags. This is similar to what arcanist will do, and is more desired in author-push workflows. Otherwise, use local tags to record the ``Differential Revision`` association. The --confirm option lets you confirm changesets before sending them. You can also add following to your configuration file to make it default behaviour:: [phabsend] confirm = true phabsend will check obsstore and the above association to decide whether to update an existing Differential Revision, or create a new one. """ revs = list(revs) + opts.get(b'rev', []) revs = scmutil.revrange(repo, revs) if not revs: raise error.Abort(_(b'phabsend requires at least one changeset')) if opts.get(b'amend'): cmdutil.checkunfinished(repo) # {newnode: (oldnode, olddiff, olddrev} oldmap = getoldnodedrevmap(repo, [repo[r].node() for r in revs]) confirm = ui.configbool(b'phabsend', b'confirm') confirm |= bool(opts.get(b'confirm')) if confirm: confirmed = _confirmbeforesend(repo, revs, oldmap) if not confirmed: raise error.Abort(_(b'phabsend cancelled')) actions = [] reviewers = opts.get(b'reviewer', []) if reviewers: phids = userphids(repo, reviewers) actions.append({b'type': b'reviewers.add', b'value': phids}) drevids = [] # [int] diffmap = {} # {newnode: diff} # Send patches one by one so we know their Differential Revision IDs and # can provide dependency relationship lastrevid = None for rev in revs: ui.debug(b'sending rev %d\n' % rev) ctx = repo[rev] # Get Differential Revision ID oldnode, olddiff, revid = oldmap.get(ctx.node(), (None, None, None)) if oldnode != ctx.node() or opts.get(b'amend'): # Create or update Differential Revision revision, diff = createdifferentialrevision( ctx, revid, lastrevid, oldnode, olddiff, actions) diffmap[ctx.node()] = diff newrevid = int(revision[r'object'][r'id']) if revid: action = b'updated' else: action = b'created' # Create a local tag to note the association, if commit message # does not have it already m = _differentialrevisiondescre.search(ctx.description()) if not m or int(m.group(b'id')) != newrevid: tagname = b'D%d' % newrevid tags.tag(repo, tagname, ctx.node(), message=None, user=None, date=None, local=True) else: # Nothing changed. But still set "newrevid" so the next revision # could depend on this one. newrevid = revid action = b'skipped' actiondesc = ui.label( {b'created': _(b'created'), b'skipped': _(b'skipped'), b'updated': _(b'updated')}[action], b'phabricator.action.%s' % action) drevdesc = ui.label(b'D%s' % newrevid, b'phabricator.drev') nodedesc = ui.label(bytes(ctx), b'phabricator.node') desc = ui.label(ctx.description().split(b'\n')[0], b'phabricator.desc') ui.write(_(b'%s - %s - %s: %s\n') % (drevdesc, actiondesc, nodedesc, desc)) drevids.append(newrevid) lastrevid = newrevid # Update commit messages and remove tags if opts.get(b'amend'): unfi = repo.unfiltered() drevs = callconduit(repo, b'differential.query', {b'ids': drevids}) with repo.wlock(), repo.lock(), repo.transaction(b'phabsend'): wnode = unfi[b'.'].node() mapping = {} # {oldnode: [newnode]} for i, rev in enumerate(revs): old = unfi[rev] drevid = drevids[i] drev = [d for d in drevs if int(d[r'id']) == drevid][0] newdesc = getdescfromdrev(drev) newdesc = encoding.unitolocal(newdesc) # Make sure commit message contain "Differential Revision" if old.description() != newdesc: parents = [ mapping.get(old.p1().node(), (old.p1(),))[0], mapping.get(old.p2().node(), (old.p2(),))[0], ] new = context.metadataonlyctx( repo, old, parents=parents, text=newdesc, user=old.user(), date=old.date(), extra=old.extra()) newnode = new.commit() mapping[old.node()] = [newnode] # Update diff property writediffproperties(unfi[newnode], diffmap[old.node()]) # Remove local tags since it's no longer necessary tagname = b'D%d' % drevid if tagname in repo.tags(): tags.tag(repo, tagname, nullid, message=None, user=None, date=None, local=True) scmutil.cleanupnodes(repo, mapping, b'phabsend', fixphase=True) if wnode in mapping: unfi.setparents(mapping[wnode][0])