def dorebase(ui, repo, src, destctx): rebase.rebase( ui, repo, rev=[revsetlang.formatspec(b'%ld', src)], dest=revsetlang.formatspec(b'%d', destctx.rev()), )
def _cachedgetoldworkingcopyparent(repo, wkpnode): if not util.safehasattr(repo, '_undooldworkingparentcache'): repo._undooldworkingparentcache = {} cache = repo._undooldworkingparentcache key = wkpnode if key not in cache: oldworkingparent = _readnode(repo, "workingparent.i", wkpnode) oldworkingparent = filter(None, oldworkingparent.split("\n")) oldwkprevstring = revsetlang.formatspec('%ls', oldworkingparent) urepo = repo.unfiltered() cache[key] = smartset.baseset(urepo.revs(oldwkprevstring)) return cache[key]
def _localbranch(repo, subset, x): """``_localbranch(changectx)`` localbranch changesets Returns all commits within the same localbranch as the changeset(s). A local branch is all draft changesets that are connected, uninterupted by public changesets. Any draft commit within a branch, or a public commit at the base of the branch, can be passed used to identify localbranches. """ # executed on an filtered repo args = revset.getargsdict(x, 'branchrevset', 'changectx') revstring = revsetlang.getstring(args.get('changectx'), _('localbranch argument must be a changectx')) revs = repo.revs(revstring) # we assume that there is only a single rev if repo[revs.first()].phase() == phases.public: querystring = revsetlang.formatspec('(children(%d) & draft())::', revs.first()) else: querystring = revsetlang.formatspec('((::%ld) & draft())::', revs) return subset & smartset.baseset(repo.revs(querystring))
def getscratchbranchparts(repo, peer, outgoing, ui, bookmark): if not outgoing.missing: raise error.Abort(_(b'no commits to push')) if scratchbranchparttype not in bundle2.bundle2caps(peer): raise error.Abort( _(b'no server support for %r') % scratchbranchparttype ) _validaterevset( repo, revsetlang.formatspec(b'%ln', outgoing.missing), bookmark ) supportedversions = changegroup.supportedoutgoingversions(repo) # Explicitly avoid using '01' changegroup version in infinitepush to # support general delta supportedversions.discard(b'01') cgversion = min(supportedversions) _handlelfs(repo, outgoing.missing) cg = changegroup.makestream(repo, outgoing, cgversion, b'push') params = {} params[b'cgversion'] = cgversion if bookmark: params[b'bookmark'] = bookmark # 'prevbooknode' is necessary for pushkey reply part params[b'bookprevnode'] = b'' bookmarks = repo._bookmarks if bookmark in bookmarks: params[b'bookprevnode'] = hex(bookmarks[bookmark]) # Do not send pushback bundle2 part with bookmarks if remotenames extension # is enabled. It will be handled manually in `_push()` if not isremotebooksenabled(ui): params[b'pushbackbookmarks'] = b'1' parts = [] # .upper() marks this as a mandatory part: server will abort if there's no # handler parts.append( bundle2.bundlepart( scratchbranchparttype.upper(), advisoryparams=pycompat.iteritems(params), data=cg, ) ) return parts
def _cachedgetolddrafts(repo, draftnode, obsnode): if not util.safehasattr(repo, '_undoolddraftcache'): repo._undoolddraftcache = {} cache = repo._undoolddraftcache key = draftnode + obsnode if key not in cache: olddraftheads = _readnode(repo, "draftheads.i", draftnode) oldheadslist = olddraftheads.split("\n") oldobs = _readnode(repo, "draftobsolete.i", obsnode) oldobslist = filter(None, oldobs.split("\n")) oldlogrevstring = revsetlang.formatspec( '(draft() & ancestors(%ls)) - %ls', oldheadslist, oldobslist) urepo = repo.unfiltered() cache[key] = smartset.baseset(urepo.revs(oldlogrevstring)) return cache[key]
def debugbruterebase(ui, repo, source, dest): """for every non-empty subset of source, run rebase -r subset -d dest Print one line summary for each subset. Assume obsstore is enabled. """ srevs = list(repo.revs(source)) with repo.wlock(), repo.lock(): repolen = len(repo) cl = repo.changelog def getdesc(rev): result = cl.changelogrevision(rev).description if rev >= repolen: result += b"'" return result for i in xrange(1, 2**len(srevs)): subset = [rev for j, rev in enumerate(srevs) if i & (1 << j) != 0] spec = revsetlang.formatspec(b'%ld', subset) tr = repo.transaction(b'rebase') tr.report = lambda x: 0 # hide "transaction abort" ui.pushbuffer() try: rebase.rebase(ui, repo, dest=dest, rev=[spec]) except error.Abort as ex: summary = b'ABORT: %s' % ex except Exception as ex: summary = b'CRASH: %s' % ex else: # short summary about new nodes cl = repo.changelog descs = [] for rev in xrange(repolen, len(repo)): desc = b'%s:' % getdesc(rev) for prev in cl.parentrevs(rev): if prev > -1: desc += getdesc(prev) descs.append(desc) descs.sort() summary = b' '.join(descs) ui.popbuffer() repo.vfs.tryunlink(b'rebasestate') subsetdesc = b''.join(getdesc(rev) for rev in subset) ui.write((b'%s: %s\n') % (subsetdesc.rjust(len(srevs)), summary)) tr.abort()
def oldworkingparenttemplate(context, mapping, args): """String. Workingcopyparent reverseindex repo states ago.""" reverseindex = templater.evalinteger(context, mapping, args[0], _('undonecommits needs an integer argument')) repo = mapping['ctx']._repo ctx = mapping['ctx'] repo = repo.unfiltered() revstring = revsetlang.formatspec('oldworkingcopyparent(%d)', reverseindex) revs = repo.revs(revstring) tonode = repo.changelog.node nodes = [tonode(x) for x in revs] if ctx.node() in nodes: result = ctx.hex() else: result = None return result
def createrebasepart(repo, peer, outgoing, onto, newhead): if not outgoing.missing: raise error.Abort(_('no changesets to rebase')) if rebaseparttype not in bundle2.bundle2caps(peer): raise error.Abort(_('no server support for %r') % rebaseparttype) validaterevset(repo, revsetlang.formatspec('%ln', outgoing.missing)) cg = changegroup.makestream(repo, outgoing, '01', 'push') # Explicitly notify the server what obsmarker versions the client supports # so the client could receive marker from the server. # # The core mercurial logic will do the right thing (enable obsmarker # capabilities in the pushback bundle) if obsmarker exchange is enabled # client-side. # # But we want the marker without enabling marker exchange, and our server # could reply a marker without exchange or even obsstore enabled. So we # bypass the "standard" way of capabilities check by sending the supported # versions directly in our own part. Note: do not enable "exchange" because # it has an unwanted side effect: pushing markers from client to server. # # "createmarkers" is all we need to be able to write a new marker. if obsolete.isenabled(repo, obsolete.createmarkersopt): obsmarkerversions = '\0'.join(str(v) for v in obsolete.formats) else: obsmarkerversions = '' # .upper() marks this as a mandatory part: server will abort if there's no # handler return bundle2.bundlepart( rebaseparttype.upper(), mandatoryparams={ 'onto': onto, 'newhead': repr(newhead), }.items(), advisoryparams={ # advisory: (old) server could ignore this without error 'obsmarkerversions': obsmarkerversions, }.items(), data = cg)
def restack(ui, repo, rebaseopts=None): """Repair a situation in which one or more changesets in a stack have been obsoleted (thereby leaving their descendants in the stack unstable) by finding any such changesets and rebasing their descendants onto the latest version of each respective changeset. """ rebaseopts = (rebaseopts or {}).copy() # TODO: Remove config override after https://phab.mercurial-scm.org/D1063 config = {('experimental', 'rebase.multidest'): True} with ui.configoverride(config), repo.wlock(), repo.lock(): # Find drafts connected to the current stack via either changelog or # obsolete graph. Note: "draft() & ::." is optimized by D441. # 1. Connect drafts via changelog revs = list(repo.revs('(draft() & ::.)::')) if not revs: # "." is probably public. Check its direct children. revs = repo.revs('draft() & children(.)') if not revs: ui.status(_('nothing to restack\n')) return 1 # 2. Connect revs via obsolete graph revs = list(repo.revs('successors(%ld)+allpredecessors(%ld)', revs, revs)) # 3. Connect revs via changelog again to cover missing revs revs = list(repo.revs('(draft() & ::%ld)::', revs)) rebaseopts['rev'] = [revsetlang.formatspec('%ld', revs)] rebaseopts['dest'] = '_destrestack(SRC)' rebase.rebase(ui, repo, **rebaseopts) # Ensure that we always end up on the latest version of the # current changeset. Usually, this will be taken care of # by the rebase operation. However, in some cases (such as # if we are on the precursor of the base changeset) the # rebase will not update to the latest version, so we need # to do this manually. successor = repo.revs('allsuccessors(.)').last() if successor is not None: commands.update(ui, repo, rev=successor)
def _findnextdelta(repo, reverseindex, branch, direction): # finds closest repos state making changes to branch in direction # input: # repo: mercurial.localrepo # reverseindex: positive int for index.i # branch: string changectx (commit hash) # direction: positive or negative int # output: # int index with next branch delta # this is the first repo state that makes a changectx, bookmark or working # copy parent change that effects the given branch if 0 == direction: # no infinite cycles guarantee raise error.ProgrammingError repo = repo.unfiltered() # current state try: nodedict = _readindex(repo, reverseindex) except IndexError: raise error.Abort(_("index out of bounds")) alphaworkingcopyparent = _readnode(repo, "workingparent.i", nodedict["workingparent"]) alphabookstring = _readnode(repo, "bookmarks.i", nodedict["bookmarks"]) incrementalindex = reverseindex spec = revsetlang.formatspec("_localbranch(%s)", branch) hexnodes = tohexnode(repo, spec) done = False while not done: # move index incrementalindex += direction # check this index try: nodedict = _readindex(repo, incrementalindex) except IndexError: raise error.Abort(_("index out of bounds")) # skip interupted commands if 'True' == nodedict['unfinished']: break # check wkp, commits, bookmarks workingcopyparent = _readnode(repo, "workingparent.i", nodedict["workingparent"]) bookstring = _readnode(repo, "bookmarks.i", nodedict["bookmarks"]) # local changes in respect to visible changectxs # disjunctive union of present and old = changes # intersection of changes and local = localchanges localctxchanges = revsetlang.formatspec( '((olddraft(%d) + olddraft(%d)) -' '(olddraft(%d) and olddraft(%d)))' ' and _localbranch(%s)', incrementalindex, reverseindex, incrementalindex, reverseindex, branch) done = done or repo.revs(localctxchanges) if done: # perf boost break # bookmark changes if alphabookstring != bookstring: diff = set(alphabookstring.split("\n")) ^\ set(bookstring.split("\n")) for mark in diff: if mark: kv = mark.rsplit(" ", 1) # was or will the mark be in the localbranch if kv[1] in hexnodes: done = True break # working copy parent changes # for workingcopyparent, only changes within the scope are interesting if alphaworkingcopyparent != workingcopyparent: done = done or (workingcopyparent in hexnodes and alphaworkingcopyparent in hexnodes) return incrementalindex
def _logdraftheads(repo, tr): spec = revsetlang.formatspec('heads(draft())') hexnodes = tohexnode(repo, spec) revstring = "\n".join(sorted(hexnodes)) return writelog(repo, tr, "draftheads.i", revstring)
def revs(self, expr, *args): expr = revsetlang.formatspec(expr, *args) m = revset.match(None, expr) return m(self)
def _logdraftobsolete(repo, tr): spec = revsetlang.formatspec('draft() & obsolete()') hexnodes = tohexnode(repo, spec) revstring = "\n".join(sorted(hexnodes)) return writelog(repo, tr, "draftobsolete.i", revstring)
def smartlogrevset(repo, subset, x): """``smartlog([master], [recentdays=N])`` Changesets relevent to you. 'master' is the head of the public branch. Unnamed heads will be hidden unless it's within 'recentdays'. """ args = revset.getargsdict(x, 'smartlogrevset', 'master recentdays') if 'master' in args: masterstring = revsetlang.getstring(args['master'], _('master must be a string')) else: masterstring = '' recentdays = revsetlang.getinteger(args.get('recentdays'), _("recentdays should be int"), -1) revs = set() heads = set() rev = repo.changelog.rev ancestor = repo.changelog.ancestor node = repo.changelog.node parentrevs = repo.changelog.parentrevs books = bookmarks.bmstore(repo) ignore = re.compile(repo.ui.config('smartlog', 'ignorebookmarks', '!')) for b in books: if not ignore.match(b): heads.add(rev(books[b])) # add 'interesting' remote bookmarks as well remotebooks = set() if util.safehasattr(repo, 'names') and 'remotebookmarks' in repo.names: ns = repo.names['remotebookmarks'] remotebooks = set(ns.listnames(repo)) for name in _reposnames(repo.ui): if name in remotebooks: heads.add(rev(ns.namemap(repo, name)[0])) heads.update(repo.revs('.')) global hiddenchanges headquery = 'head()' if remotebooks: # When we have remote bookmarks, only show draft heads, since public # heads should have a remote bookmark indicating them. This allows us # to force push server bookmarks to new locations, and not have the # commits clutter the user's smartlog. headquery = 'heads(draft())' allheads = set(repo.revs(headquery)) if recentdays >= 0: recentquery = revsetlang.formatspec('%r & date(-%d)', headquery, recentdays) recentrevs = set(repo.revs(recentquery)) hiddenchanges += len(allheads - heads) - len(recentrevs - heads) heads.update(recentrevs) else: heads.update(allheads) masterrevset = _masterrevset(repo.ui, repo, masterstring) masterrev = _masterrev(repo, masterrevset) if masterrev is None: masterrev = repo['tip'].rev() masternode = node(masterrev) # Find all draft ancestors and latest public ancestor of heads # that are not in master. # We don't want to draw all public commits because there can be too # many of them. # Don't use revsets, they are too slow for head in heads: anc = rev(ancestor(node(head), masternode)) queue = [head] while queue: current = queue.pop(0) if current not in revs: revs.add(current) # stop as soon as we find public commit ispublic = repo[current].phase() == phases.public if current != anc and not ispublic: parents = parentrevs(current) for p in parents: if p > anc: queue.append(p) # add context: master, current commit, and the common ancestor revs.add(masterrev) return subset & revs
def _smartlog(ui, repo, *pats, **opts): masterstring = opts.get('master') masterrevset = _masterrevset(ui, repo, masterstring) revs = set() global hiddenchanges hiddenchanges = 0 global commit_info commit_info = opts.get('commit_info') if not opts.get('rev'): if opts.get('all'): recentdays = -1 else: recentdays = 14 masterrev = _masterrev(repo, masterrevset) revstring = revsetlang.formatspec('smartlog(%s, %s)', masterrev or '', recentdays) revs.update(scmutil.revrange(repo, [revstring])) else: revs.update(scmutil.revrange(repo, opts.get('rev'))) try: masterrev = repo.revs('.').first() except error.RepoLookupError: masterrev = revs[0] if -1 in revs: revs.remove(-1) # It's important that these function caches come after the revsets above, # because the revsets may cause extra nodes to become visible, which in # turn invalidates the changelog instance. rev = repo.changelog.rev ancestor = repo.changelog.ancestor node = repo.changelog.node # Find lowest common ancestors of revs. If we have multiple roots in the # repo the following will find one ancestor per group of revs with the # same root. ancestors = set() for r in revs: added = False for anc in list(ancestors): lca = rev(ancestor(node(anc), node(r))) if lca != -1: if anc != lca: ancestors.discard(anc) ancestors.add(lca) added = True if not added: ancestors.add(r) revs |= ancestors revs = sorted(list(revs), reverse=True) if len(revs) == 0: return # Print it! overrides = {} if ui.config('experimental', 'graphstyle.grandparent', '2.') == '|': overrides[('experimental', 'graphstyle.grandparent')] = '2.' with ui.configoverride(overrides, 'smartlog'): revdag = getdag(ui, repo, revs, masterrev) displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True) ui.pager('smartlog') cmdutil.displaygraph( ui, repo, revdag, displayer, graphmod.asciiedges, None, None) try: with open(repo.vfs.join('completionhints'), 'w+') as f: for rev in revdag: commit_hash = rev[2].node() # Skip fakectxt nodes if commit_hash != '...': f.write(nodemod.short(commit_hash) + '\n') except IOError: # No write access. No big deal. pass if hiddenchanges: msg = _( "note: hiding %s old heads without bookmarks\n") % hiddenchanges hint = _("(use --all to see them)\n") ui.warn(msg) ui.warn(hint)
def _smartlog(ui, repo, *pats, **opts): masterstring = opts.get('master') masterrevset = _masterrevset(ui, repo, masterstring) revs = set() global hiddenchanges hiddenchanges = 0 global commit_info commit_info = opts.get('commit_info') if not opts.get('rev'): if opts.get('all'): recentdays = -1 else: recentdays = 14 revstring = revsetlang.formatspec('smartlog(%s, %s)', masterrevset, recentdays) revs.update(scmutil.revrange(repo, [revstring])) masterrev = _masterrev(repo, masterrevset) else: revs.update(scmutil.revrange(repo, opts.get('rev'))) try: masterrev = repo.revs('.').first() except error.RepoLookupError: masterrev = revs[0] if -1 in revs: revs.remove(-1) # It's important that these function caches come after the revsets above, # because the revsets may cause extra nodes to become visible, which in # turn invalidates the changelog instance. rev = repo.changelog.rev ancestor = repo.changelog.ancestor node = repo.changelog.node # Find lowest common ancestors of revs. If we have multiple roots in the # repo the following will find one ancestor per group of revs with the # same root. ancestors = set() for r in revs: added = False for anc in list(ancestors): lca = rev(ancestor(node(anc), node(r))) if lca != -1: if anc != lca: ancestors.discard(anc) ancestors.add(lca) added = True if not added: ancestors.add(r) revs |= ancestors revs = sorted(list(revs), reverse=True) if len(revs) == 0: return # Print it! overrides = {} if ui.config('experimental', 'graphstyle.grandparent') == '|': overrides[('experimental', 'graphstyle.grandparent')] = '2.' with ui.configoverride(overrides, 'smartlog'): revdag = getdag(ui, repo, revs, masterrev) displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True) ui.pager('smartlog') cmdutil.displaygraph( ui, repo, revdag, displayer, graphmod.asciiedges, None, None) try: with open(repo.vfs.join('completionhints'), 'w+') as f: for rev in revdag: commit_hash = rev[2].node() # Skip fakectxt nodes if commit_hash != '...': f.write(nodemod.short(commit_hash) + '\n') except IOError: # No write access. No big deal. pass if hiddenchanges: msg = _( "note: hiding %s old heads without bookmarks\n") % hiddenchanges hint = _("(use --all to see them)\n") ui.warn(msg) ui.warn(hint)
def dorebase(ui, repo, src, dest): rebase.rebase(ui, repo, rev=[revsetlang.formatspec('%ld', src)], dest=revsetlang.formatspec('%d', dest))
def smartlogrevset(repo, subset, x): """``smartlog([master], [recentdays=N])`` Changesets relevent to you. 'master' is the head of the public branch. Unnamed heads will be hidden unless it's within 'recentdays'. """ args = revset.getargsdict(x, 'smartlogrevset', 'master recentdays') if 'master' in args: masterstring = revsetlang.getstring(args['master'], _('master must be a string')) else: masterstring = '' recentdays = revsetlang.getinteger(args.get('recentdays'), _("recentdays should be int"), -1) revs = set() heads = set() rev = repo.changelog.rev branchinfo = repo.changelog.branchinfo ancestor = repo.changelog.ancestor node = repo.changelog.node parentrevs = repo.changelog.parentrevs books = bookmarks.bmstore(repo) ignore = re.compile(repo.ui.config('smartlog', 'ignorebookmarks', '!')) for b in books: if not ignore.match(b): heads.add(rev(books[b])) # add 'interesting' remote bookmarks as well remotebooks = set() if util.safehasattr(repo, 'names') and 'remotebookmarks' in repo.names: ns = repo.names['remotebookmarks'] remotebooks = set(ns.listnames(repo)) for name in _reposnames(repo.ui): if name in remotebooks: heads.add(rev(ns.namemap(repo, name)[0])) heads.update(repo.revs('.')) global hiddenchanges headquery = 'head() & branch(.)' if remotebooks: # When we have remote bookmarks, only show draft heads, since public # heads should have a remote bookmark indicating them. This allows us # to force push server bookmarks to new locations, and not have the # commits clutter the user's smartlog. headquery = 'draft() &' + headquery allheads = set(repo.revs(headquery)) if recentdays >= 0: recentquery = revsetlang.formatspec('%r & date(-%d)', headquery, recentdays) recentrevs = set(repo.revs(recentquery)) hiddenchanges += len(allheads - heads) - len(recentrevs - heads) heads.update(recentrevs) else: heads.update(allheads) branches = set() for head in heads: branches.add(branchinfo(head)[0]) masterrevset = _masterrevset(repo.ui, repo, masterstring) masterrev = _masterrev(repo, masterrevset) if masterrev is None: masterbranch = None else: masterbranch = branchinfo(masterrev)[0] for branch in branches: if branch != masterbranch: try: rs = 'first(reverse(branch("%s")) & public())' % branch branchmaster = repo.revs(rs).first() if branchmaster is None: # local-only (draft) branch rs = 'branch("%s")' % branch branchmaster = repo.revs(rs).first() except Exception: branchmaster = repo.revs('tip').first() else: branchmaster = masterrev # Find all draft ancestors and latest public ancestor of heads # that are not in master. # We don't want to draw all public commits because there can be too # many of them. # Don't use revsets, they are too slow for head in heads: if branchinfo(head)[0] != branch: continue anc = rev(ancestor(node(head), node(branchmaster))) queue = [head] while queue: current = queue.pop(0) if current not in revs: revs.add(current) # stop as soon as we find public commit ispublic = repo[current].phase() == phases.public if current != anc and not ispublic: parents = parentrevs(current) for p in parents: if p > anc: queue.append(p) # add context: master, current commit, and the common ancestor revs.add(branchmaster) # get common branch ancestor if branch != masterbranch: anc = None for r in revs: if branchinfo(r)[0] != branch: continue if anc is None: anc = r else: anc = rev(ancestor(node(anc), node(r))) if anc: revs.add(anc) return subset & revs
def _preview(ui, repo, reverseindex): # Print smartlog like preview of undo # Input: # ui: # repo: mercurial.localrepo # Output: # returns 1 on index error, 0 otherwise # override "UNDOINDEX" as a variable usable in template if not _gapcheck(ui, repo, reverseindex): repo.ui.status(_("WARN: missing history between present and this" " state\n")) overrides = { ('templates', 'UNDOINDEX'): str(reverseindex), } opts = {} opts["template"] = "{undopreview}" repo = repo.unfiltered() try: nodedict = _readindex(repo, reverseindex) curdict = _readindex(repo, reverseindex) except IndexError: return 1 bookstring = _readnode(repo, "bookmarks.i", nodedict["bookmarks"]) oldmarks = bookstring.split("\n") oldpairs = set() for mark in oldmarks: kv = mark.rsplit(" ", 1) if len(kv) == 2: oldpairs.update(kv) bookstring = _readnode(repo, "bookmarks.i", curdict["bookmarks"]) curmarks = bookstring.split("\n") curpairs = set() for mark in curmarks: kv = mark.rsplit(" ", 1) if len(kv) == 2: curpairs.update(kv) diffpairs = oldpairs.symmetric_difference(curpairs) # extract hashes from diffpairs bookdiffs = [] for kv in diffpairs: bookdiffs += kv[0] revstring = revsetlang.formatspec( "ancestor(olddraft(0), olddraft(%s)) +" "(draft() & ::((olddraft(0) - olddraft(%s)) + " "(olddraft(%s) - olddraft(0)) + %ls + '.' + " "oldworkingcopyparent(%s)))", reverseindex, reverseindex, reverseindex, bookdiffs, reverseindex) opts['rev'] = [revstring] try: with ui.configoverride(overrides): cmdutil.graphlog(ui, repo, None, opts) # informative output nodedict = _readindex(repo, reverseindex) time = _readnode(repo, "date.i", nodedict["date"]) time = util.datestr([float(x) for x in time.split(" ")]) except IndexError: # don't print anything return 1 try: nodedict = _readindex(repo, reverseindex - 1) commandstr = _readnode(repo, "command.i", nodedict["command"]) commandlist = commandstr.split("\0")[1:] commandstr = " ".join(commandlist) uimessage = _('undo to %s, before %s\n') % (time, commandstr) repo.ui.status((uimessage)) except IndexError: repo.ui.status(_("most recent state: undoing here won't change" " anything\n")) return 0
def _computerelative(repo, reverseindex, absolute=False, branch=""): # allows for relative undos using # redonode storage # allows for branch undos using # findnextdelta logic if reverseindex != 0: sign = reverseindex / abs(reverseindex) else: sign = None if not absolute: try: # attempt to get relative shift nodebranch = repo.vfs.read("undolog/redonode").split("\0") hexnode = nodebranch[0] try: oldbranch = nodebranch[1] except IndexError: oldbranch = "" rlog = _getrevlog(repo, 'index.i') rev = rlog.rev(bin(hexnode)) shiftedindex = _invertindex(rlog, rev) except (IOError, error.RevlogError): # no shift shiftedindex = 0 oldbranch = "" else: shiftedindex = 0 oldbranch = "" if not branch: if not oldbranch: reverseindex = shiftedindex + reverseindex # else: previous command was branch undo # perform absolute undo (no shift) else: # check if relative branch if (branch != oldbranch) and (oldbranch != ""): rootdelta = revsetlang.formatspec( 'roots(_localbranch(%s)) - roots(_localbranch(%s))', branch, oldbranch) if repo.revs(rootdelta): # different group of commits shiftedindex = 0 # from shifted index, find reverse index # of states that change # branch # remember that reverseindex can be negative sign = reverseindex / abs(reverseindex) for count in range(abs(reverseindex)): shiftedindex = _findnextdelta(repo, shiftedindex, branch, direction=sign) reverseindex = shiftedindex # skip interupted commands if sign: done = False rlog = _getrevlog(repo, 'index.i') while not done: indexdict = _readindex(repo, reverseindex, rlog) if 'True' == indexdict['unfinished']: reverseindex += sign else: done = True return reverseindex
def _undoto(ui, repo, reverseindex, keep=False, branch=None): # undo to specific reverseindex # requires inhibit extension # branch is a changectx hash (potentially short form) # which identifies its branch via localbranch revset if repo != repo.unfiltered(): raise error.ProgrammingError(_("_undoto expects unfilterd repo")) try: nodedict = _readindex(repo, reverseindex) except IndexError: raise error.Abort(_("index out of bounds")) # bookmarks bookstring = _readnode(repo, "bookmarks.i", nodedict["bookmarks"]) booklist = bookstring.split("\n") if branch: spec = revsetlang.formatspec('_localbranch(%s)', branch) branchcommits = tohexnode(repo, spec) else: branchcommits = False # copy implementation for bookmarks itercopy = [] for mark in repo._bookmarks.iteritems(): itercopy.append(mark) bmremove = [] for mark in itercopy: if not branchcommits or hex(mark[1]) in branchcommits: bmremove.append((mark[0], None)) repo._bookmarks.applychanges(repo, repo.currenttransaction(), bmremove) bmchanges = [] for mark in booklist: if mark: kv = mark.rsplit(" ", 1) if not branchcommits or\ kv[1] in branchcommits or\ (kv[0], None) in bmremove: bmchanges.append((kv[0], bin(kv[1]))) repo._bookmarks.applychanges(repo, repo.currenttransaction(), bmchanges) # working copy parent workingcopyparent = _readnode(repo, "workingparent.i", nodedict["workingparent"]) if not keep: if not branchcommits or workingcopyparent in branchcommits: # bailifchanged is run, so this should be safe hg.clean(repo, workingcopyparent, show_stats=False) elif not branchcommits or workingcopyparent in branchcommits: # keeps working copy files prednode = bin(workingcopyparent) predctx = repo[prednode] changedfiles = [] wctx = repo[None] wctxmanifest = wctx.manifest() predctxmanifest = predctx.manifest() dirstate = repo.dirstate diff = predctxmanifest.diff(wctxmanifest) changedfiles.extend(diff.iterkeys()) with dirstate.parentchange(): dirstate.rebuild(prednode, predctxmanifest, changedfiles) # we want added and removed files to be shown # properly, not with ? and ! prefixes for filename, data in diff.iteritems(): if data[0][0] is None: dirstate.add(filename) if data[1][0] is None: dirstate.remove(filename) # visible changesets addedrevs = revsetlang.formatspec('olddraft(0) - olddraft(%d)', reverseindex) removedrevs = revsetlang.formatspec('olddraft(%d) - olddraft(0)', reverseindex) if not branch: smarthide(repo, addedrevs, removedrevs) revealcommits(repo, removedrevs) else: localadds = revsetlang.formatspec('(olddraft(0) - olddraft(%d)) and' ' _localbranch(%s)', reverseindex, branch) localremoves = revsetlang.formatspec('(olddraft(%d) - olddraft(0)) and' ' _localbranch(%s)', reverseindex, branch) smarthide(repo, localadds, removedrevs) smarthide(repo, addedrevs, localremoves, local=True) revealcommits(repo, localremoves) # informative output time = _readnode(repo, "date.i", nodedict["date"]) time = util.datestr([float(x) for x in time.split(" ")]) nodedict = _readindex(repo, reverseindex - 1) commandstr = _readnode(repo, "command.i", nodedict["command"]) commandlist = commandstr.split("\0")[1:] commandstr = " ".join(commandlist) uimessage = _('undone to %s, before %s\n') % (time, commandstr) repo.ui.status((uimessage))
def _donehexnodes(repo, reverseindex): repo = repo.unfiltered() revstring = revsetlang.formatspec('olddraft(%d)', reverseindex) revs = repo.revs(revstring) tonode = repo.changelog.node return [tonode(x) for x in revs]