diff --git a/hgext3rd/fbamend/__init__.py b/hgext3rd/fbamend/__init__.py --- a/hgext3rd/fbamend/__init__.py +++ b/hgext3rd/fbamend/__init__.py @@ -63,6 +63,7 @@ from . import ( common, fold, + hiddenoverride, metaedit, movement, prune, @@ -92,6 +93,7 @@ ] def uisetup(ui): + hiddenoverride.uisetup(ui) prune.uisetup(ui) entry = extensions.wrapcommand(commands.table, 'commit', commit) for opt in amendopts: diff --git a/hgext3rd/fbamend/hiddenoverride.py b/hgext3rd/fbamend/hiddenoverride.py new file mode 100644 --- /dev/null +++ b/hgext3rd/fbamend/hiddenoverride.py @@ -0,0 +1,80 @@ +# hiddenoverride.py - lightweight hidden-ness override +# +# Copyright 2017 Facebook, Inc. +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from __future__ import absolute_import + +from mercurial import ( + dispatch, + error, + extensions, + obsolete, + repoview, +) + +def uisetup(ui): + extensions.wrapfunction(repoview, 'pinnedrevs', pinnedrevs) + extensions.wrapfunction(dispatch, 'runcommand', runcommand) + extensions.wrapfunction(obsolete, 'createmarkers', createmarkers) + +def pinnedrevs(orig, repo): + revs = orig(repo) + nodemap = repo.changelog.nodemap + pinned = list(nodemap[n] for n in loadpinnednodes(repo) if n in nodemap) + revs.update(pinned) + return revs + +def loadpinnednodes(repo): + """yield pinned nodes that should be visible""" + if repo is None or not repo.local(): + return + # the "pinned nodes" file name is "obsinhibit" for compatibility reason + content = repo.svfs.tryread('obsinhibit') or '' + nodemap = repo.unfiltered().changelog.nodemap + offset = 0 + while True: + node = content[offset:offset + 20] + if not node: + break + if node in nodemap: + yield node + offset += 20 + +def savepinnednodes(repo, nodes): + with repo.svfs.open('obsinhibit', 'wb', atomictemp=True) as f: + f.write(''.join(nodes)) + +def runcommand(orig, lui, repo, cmd, fullargs, *args): + result = orig(lui, repo, cmd, fullargs, *args) + # after a command completes, make sure working copy parent and all + # bookmarks get "pinned". + if repo and repo.local(): + unfi = repo.unfiltered() + wnode = None + try: + # read dirstate directly to avoid dirstate object overhead + wnode = repo.vfs('dirstate').read(20) + except Exception: + pass + tounpin = getattr(unfi, '_tounpinnodes', set()) + pinned = set(loadpinnednodes(repo)) - tounpin + if wnode: + pinned.add(wnode) + pinned.update(unfi._bookmarks.values()) + savepinnednodes(repo, pinned) + return result + +def createmarkers(orig, repo, rels, *args, **kwargs): + # this is a way to unpin revs - precursors are unpinned + unfi = repo.unfiltered() + tounpin = getattr(unfi, '_tounpinnodes', set()) + for r in rels: + try: + tounpin.add(r[0].node()) + except error.RepoLookupError: + pass + unfi._tounpinnodes = tounpin + return orig(repo, rels, *args, **kwargs) diff --git a/tests/test-fbamend-hiddenoverride.t b/tests/test-fbamend-hiddenoverride.t new file mode 100644 --- /dev/null +++ b/tests/test-fbamend-hiddenoverride.t @@ -0,0 +1,58 @@ + $ cat >> $HGRCPATH << EOF + > [extensions] + > fbamend=$TESTDIR/../hgext3rd/fbamend + > drawdag=$RUNTESTDIR/drawdag.py + > [experimental] + > evolution = all + > EOF + + $ hg init + $ hg debugdrawdag <<'EOS' + > B C # amend: B -> C + > |/ + > A + > EOS + + $ rm .hg/localtags + $ hg log -G -T '{rev} {desc}\n' + o 2 C + | + o 0 A + + $ hg log -G -T '{rev} {desc}\n' --hidden + o 2 C + | + | x 1 B + |/ + o 0 A + +Changing working copy parent pins a node + + $ hg update 1 --hidden -q + $ hg update 0 -q + $ hg log -G -T '{rev} {desc}\n' + o 2 C + | + | x 1 B + |/ + @ 0 A + +Strip/prune unpins a node + + $ hg prune 1 -q + $ hg log -G -T '{rev} {desc}\n' + o 2 C + | + @ 0 A + +Bookmark pins nodes even after removed + + $ hg bookmark -ir 1 BOOK --hidden -q + $ hg bookmark -d BOOK -q + $ hg log -G -T '{rev} {desc}\n' + o 2 C + | + | x 1 B + |/ + @ 0 A + diff --git a/tests/test-fbamend-restack.t b/tests/test-fbamend-restack.t --- a/tests/test-fbamend-restack.t +++ b/tests/test-fbamend-restack.t @@ -435,6 +435,8 @@ | @ 4 Amended | + | x 1 add b + |/ o 0 add a Test situation with divergence. Restack should rebase unstable children @@ -476,6 +478,8 @@ | | o 3 successor 1 |/ + | x 1 add b + |/ o 0 add a Test situation with divergence due to an unamend. This should actually succeed @@ -582,6 +586,8 @@ | @ 5 add b | + | x 1 add b + |/ o 0 add a Test recursive restacking -- more complex case. This test is designed to @@ -673,4 +679,6 @@ |/ @ 5 add b | + | x 1 add b + |/ o 0 add a diff --git a/tests/test-fbamend.t b/tests/test-fbamend.t --- a/tests/test-fbamend.t +++ b/tests/test-fbamend.t @@ -373,6 +373,8 @@ | @ 6 3a4d2824efc1 message from exec | + | x 2 048e86baa19d message from exec + |/ | o 1 3f6197b00eba extra commit to test multiple heads |/ o 0 cb9a9f314b8b a