D4312: New bookflow extension for bookmark-based branching
idlsoft (Sandu Turcan)
phabricator at mercurial-scm.org
Mon Oct 15 13:48:58 UTC 2018
idlsoft updated this revision to Diff 12149.
CHANGES SINCE LAST UPDATE
https://phab.mercurial-scm.org/D4312?vs=10789&id=12149
REVISION DETAIL
https://phab.mercurial-scm.org/D4312
AFFECTED FILES
hgext/bookflow.py
tests/test-bookflow.t
CHANGE DETAILS
diff --git a/tests/test-bookflow.t b/tests/test-bookflow.t
new file mode 100644
--- /dev/null
+++ b/tests/test-bookflow.t
@@ -0,0 +1,284 @@
+initialize
+ $ make_changes() { d=`pwd`; [ ! -z $1 ] && cd $1; echo "test $(basename `pwd`)" >> test; hg commit -Am"${2:-test}"; r=$?; cd $d; return $r; }
+ $ ls -1a
+ .
+ ..
+ $ hg init a
+ $ cd a
+ $ echo 'test' > test; hg commit -Am'test'
+ adding test
+
+clone to b
+
+ $ mkdir ../b
+ $ cd ../b
+ $ hg clone ../a .
+ updating to branch default
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo "[extensions]" >> .hg/hgrc
+ $ echo "bookflow=" >> .hg/hgrc
+ $ hg branch X
+ abort: creating named branches is disabled and you should use bookmarks
+ (see 'hg help bookflow')
+ [255]
+ $ hg bookmark X
+ $ hg bookmarks
+ * X 0:* (glob)
+ $ hg bookmark X
+ abort: bookmark X already exists, to move use the --rev option
+ [255]
+ $ make_changes
+ $ hg push ../a -q
+
+ $ hg bookmarks
+ \* X 1:* (glob)
+
+change a
+ $ cd ../a
+ $ hg up
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo 'test' >> test; hg commit -Am'test'
+
+
+pull in b
+ $ cd ../b
+ $ hg pull -u
+ pulling from $TESTTMP/a
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files
+ new changesets * (glob)
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (leaving bookmark X)
+ $ hg status
+ $ hg bookmarks
+ X 1:* (glob)
+
+check protection of @ bookmark
+ $ hg bookmark @
+ $ hg bookmarks
+ \* @ 2:* (glob)
+ X 1:* (glob)
+ $ make_changes
+ abort: cannot commit, bookmark @ is protected
+ [255]
+
+ $ hg status
+ M test
+ $ hg bookmarks
+ \* @ 2:* (glob)
+ X 1:* (glob)
+
+ $ hg --config bookflow.protect= commit -Am"Updated test"
+
+ $ hg bookmarks
+ \* @ 3:* (glob)
+ X 1:* (glob)
+
+check requirement for an active bookmark
+ $ hg bookmark -i
+ $ hg bookmarks
+ @ 3:* (glob)
+ X 1:* (glob)
+ $ make_changes
+ abort: cannot commit without an active bookmark
+ [255]
+ $ hg revert test
+ $ rm test.orig
+ $ hg status
+
+
+make the bookmark move by updating it on a, and then pulling
+# add a commit to a
+ $ cd ../a
+ $ hg bookmark X
+ $ hg bookmarks
+ \* X 2:* (glob)
+ $ make_changes
+ $ hg bookmarks
+ * X 3:81af7977fdb9
+
+# go back to b, and check out X
+ $ cd ../b
+ $ hg up X
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (activating bookmark X)
+ $ hg bookmarks
+ @ 3:* (glob)
+ \* X 1:* (glob)
+
+# pull, this should move the bookmark forward, because it was changed remotely
+ $ hg pull -u | grep "updating to active bookmark X"
+ updating to active bookmark X
+
+ $ hg bookmarks
+ @ 3:* (glob)
+ * X 4:81af7977fdb9
+
+the bookmark should not move if it diverged from remote
+ $ hg -R ../a status
+ $ hg -R ../b status
+ $ make_changes ../a
+ $ make_changes ../b
+ $ hg -R ../a status
+ $ hg -R ../b status
+ $ hg -R ../a bookmarks
+ * X 4:238292f60a57
+ $ hg -R ../b bookmarks
+ @ 3:* (glob)
+ * X 5:096f7e86892d
+ $ cd ../b
+ $ # make sure we cannot push after bookmarks diverged
+ $ hg push -B X | grep abort
+ abort: push creates new remote head * with bookmark 'X'! (glob)
+ (pull and merge or see 'hg help push' for details about pushing new heads)
+ [1]
+ $ hg pull -u | grep divergent
+ divergent bookmark X stored as X at default
+ 1 other divergent bookmarks for "X"
+ $ hg bookmarks
+ @ 3:* (glob)
+ * X 5:096f7e86892d
+ X at default 6:238292f60a57
+ $ hg id -in
+ 096f7e86892d 5
+ $ make_changes
+ $ hg status
+ $ hg bookmarks
+ @ 3:* (glob)
+ * X 7:227f941aeb07
+ X at default 6:238292f60a57
+
+now merge with the remote bookmark
+ $ hg merge X at default --tool :local -q
+ $ hg status
+ M test
+ $ hg commit -m"Merged with X at default"
+ $ hg bookmarks
+ @ 3:* (glob)
+ * X 8:26fed9bb3219
+ $ hg push -B X | grep bookmark
+ pushing to $TESTTMP/a (?)
+ updating bookmark X
+ $ cd ../a
+ $ hg up -q
+ $ hg bookmarks
+ * X 7:26fed9bb3219
+
+test hg pull when there is more than one descendant
+ $ cd ../a
+ $ hg bookmark Z
+ $ hg bookmark Y
+ $ make_changes . YY
+ $ hg up Z -q
+ $ make_changes . ZZ
+ created new head
+ $ hg bookmarks
+ X 7:26fed9bb3219
+ Y 8:131e663dbd2a
+ * Z 9:b74a4149df25
+ $ hg log -r 'p1(Y)' -r 'p1(Z)' -T '{rev}\n' # prove that Y and Z share the same parent
+ 7
+ $ hg log -r 'Y%Z' -T '{rev}\n' # revs in Y but not in Z
+ 8
+ $ hg log -r 'Z%Y' -T '{rev}\n' # revs in Z but not in Y
+ 9
+ $ cd ../b
+ $ hg pull -uq
+ $ hg id
+ b74a4149df25 tip Z
+ $ hg bookmarks | grep \* # no active bookmark
+ [1]
+
+
+test shelving
+ $ cd ../a
+ $ echo anotherfile > anotherfile # this change should not conflict
+ $ hg add anotherfile
+ $ hg commit -m"Change in a"
+ $ cd ../b
+ $ hg up Z | grep Z
+ (activating bookmark Z)
+ $ hg book | grep \* # make sure active bookmark
+ \* Z 10:* (glob)
+ $ echo "test b" >> test
+ $ hg diff --stat
+ test | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+ $ hg --config extensions.shelve= shelve
+ shelved as Z
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg pull -uq
+ $ hg --trace --config extensions.shelve= unshelve
+ unshelving change 'Z'
+ rebasing shelved changes
+ $ hg diff --stat
+ test | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+
+make the bookmark move by updating it on a, and then pulling with a local change
+# add a commit to a
+ $ cd ../a
+ $ hg up -C X |fgrep "activating bookmark X"
+ (activating bookmark X)
+# go back to b, and check out X
+ $ cd ../b
+ $ hg up -C X |fgrep "activating bookmark X"
+ (activating bookmark X)
+# update and push from a
+ $ make_changes ../a
+ created new head
+ $ echo "more" >> test
+ $ hg pull -u 2>&1 | fgrep -v TESTTMP| fgrep -v "searching for changes" | fgrep -v adding
+ pulling from $TESTTMP/a
+ added 1 changesets with 0 changes to 0 files (+1 heads)
+ updating bookmark X
+ new changesets * (glob)
+ updating to active bookmark X
+ merging test
+ warning: conflicts while merging test! (edit, then use 'hg resolve --mark')
+ 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+ use 'hg resolve' to retry unresolved file merges
+ $ hg update -Cq
+ $ rm test.orig
+
+make sure that commits aren't possible if working directory is not pointing to active bookmark
+ $ hg -R ../a status
+ $ hg -R ../b status
+ $ hg -R ../a id -i
+ 36a6e592ec06
+ $ hg -R ../a book | grep X
+ \* X \d+:36a6e592ec06 (re)
+ $ hg -R ../b id -i
+ 36a6e592ec06
+ $ hg -R ../b book | grep X
+ \* X \d+:36a6e592ec06 (re)
+ $ make_changes ../a
+ $ hg -R ../a book | grep X
+ \* X \d+:f73a71c992b8 (re)
+ $ cd ../b
+ $ hg pull 2>&1 | grep -v add|grep -v pulling|grep -v searching|grep -v changeset
+ updating bookmark X
+ (run 'hg update' to get a working copy)
+ working directory out of sync with active bookmark, run 'hg up X'
+ $ hg id -i # we're still on the old commit
+ 36a6e592ec06
+ $ hg book | grep X # while the bookmark moved
+ \* X \d+:f73a71c992b8 (re)
+ $ make_changes
+ abort: cannot commit, working directory out of sync with active bookmark
+ (run 'hg up X')
+ [255]
+ $ hg up -Cq -r . # cleanup local changes
+ $ hg status
+ $ hg id -i # we're still on the old commit
+ 36a6e592ec06
+ $ hg up X -q
+ $ hg id -i # now we're on X
+ f73a71c992b8
+ $ hg book | grep X
+ \* X \d+:f73a71c992b8 (re)
+
diff --git a/hgext/bookflow.py b/hgext/bookflow.py
new file mode 100644
--- /dev/null
+++ b/hgext/bookflow.py
@@ -0,0 +1,91 @@
+"""implements bookmark-based branching
+
+ - Disables creation of new branches (config: enable_branches=False).
+ - Requires an active bookmark on commit (config: require_bookmark=True).
+ - Doesn't move the active bookmark on update, only on commit.
+ - Requires '--rev' for moving an existing bookmark.
+ - Protects special bookmarks (config: protect=@).
+
+ flow related commands
+
+ :hg book NAME: create a new bookmark
+ :hg book NAME -r REV: move bookmark to revision (fast-forward)
+ :hg up|co NAME: switch to bookmark
+ :hg push -B .: push active bookmark
+"""
+from mercurial.i18n import _
+from mercurial import (
+ bookmarks,
+ error,
+ registrar,
+ commands,
+ extensions
+)
+
+MY_NAME = 'bookflow'
+
+configtable = {}
+configitem = registrar.configitem(configtable)
+
+configitem(MY_NAME, 'protect', ['@'])
+configitem(MY_NAME, 'require-bookmark', True)
+configitem(MY_NAME, 'enable-branches', False)
+
+cmdtable = {}
+command = registrar.command(cmdtable)
+
+def commit_hook(ui, repo, **kwargs):
+ active = repo._bookmarks.active
+ if active:
+ if active in ui.configlist(MY_NAME, 'protect'):
+ raise error.Abort(_('cannot commit, bookmark {} is protected').format(active))
+ if not cwd_at_bookmark(repo, active):
+ raise error.Abort(_('cannot commit, working directory out of sync with active bookmark'), hint=_("run 'hg up {}'").format(active))
+ elif ui.configbool(MY_NAME, 'require-bookmark', True):
+ raise error.Abort(_('cannot commit without an active bookmark'))
+ return 0
+
+def bookmarks_update(orig, repo, parents, node):
+ if len(parents) == 2:
+ # called during commit
+ return orig(repo, parents, node)
+ else:
+ # called during update
+ return False
+
+def bookmarks_addbookmarks(orig, repo, tr, names, rev=None, force=False, inactive=False):
+ if not rev:
+ marks = repo._bookmarks
+ for name in names:
+ if name in marks:
+ raise error.Abort(_("bookmark {} already exists, to move use the --rev option").format(name))
+ return orig(repo, tr, names, rev, force, inactive)
+
+def commands_commit(orig, ui, repo, *args, **opts):
+ commit_hook(ui, repo)
+ return orig(ui, repo, *args, **opts)
+
+def commands_pull(orig, ui, repo, *args, **opts):
+ rc = orig(ui, repo, *args, **opts)
+ active = repo._bookmarks.active
+ if active and not cwd_at_bookmark(repo, active):
+ ui.warn(_("working directory out of sync with active bookmark, run 'hg up {}'").format(active))
+ return rc
+
+def commands_branch(orig, ui, repo, label=None, **opts):
+ if label and not opts.get(r'clean') and not opts.get(r'rev'):
+ raise error.Abort(_("creating named branches is disabled and you should use bookmarks"), hint="see 'hg help bookflow'")
+ return orig(ui, repo, label, **opts)
+
+def cwd_at_bookmark(repo, mark):
+ mark_id = repo._bookmarks[mark]
+ cur_id = repo.lookup('.')
+ return cur_id == mark_id
+
+def uisetup(ui):
+ extensions.wrapfunction(bookmarks, 'update', bookmarks_update)
+ extensions.wrapfunction(bookmarks, 'addbookmarks', bookmarks_addbookmarks)
+ extensions.wrapcommand(commands.table, 'commit', commands_commit)
+ extensions.wrapcommand(commands.table, 'pull', commands_pull)
+ if not ui.configbool(MY_NAME, 'enable-branches'):
+ extensions.wrapcommand(commands.table, 'branch', commands_branch)
To: idlsoft, #hg-reviewers, pulkit, marcink
Cc: markand, marcink, durin42, jwatt, pulkit, mercurial-devel
More information about the Mercurial-devel
mailing list