D4312: New bookflow extension for bookmark-based branching
idlsoft (Sandu Turcan)
phabricator at mercurial-scm.org
Thu Aug 16 23:16:52 UTC 2018
idlsoft created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.
REPOSITORY
rHG Mercurial
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,188 @@
+initialize
+ $ alias hgg="hg --config extensions.bookflow=`dirname $TESTDIR`/hgext/bookflow.py"
+ $ make_changes() { d=`pwd`; [ ! -z $1 ] && cd $1; echo "test $(basename `pwd`)" >> test; hgg commit -Am"${2:-test}"; r=$?; cd $d; return $r; }
+ $ assert_clean() { ls -1 $1 | grep -v "test$" | cat;}
+ $ 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
+ $ hgg branch X
+ abort: Branching should be done using bookmarks:
+ hg bookmark X
+ [255]
+ $ hgg bookmark X
+ $ hgg bookmarks
+ * X 0:* (glob)
+ $ make_changes
+ $ hgg push ../a > /dev/null
+
+ $ hg bookmarks
+ \* X 1:* (glob)
+
+change a
+ $ cd ../a
+ $ hgg 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
+ $ hgg 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)
+ $ assert_clean
+ $ hg bookmarks
+ X 1:* (glob)
+
+check protection of @ bookmark
+ $ hgg bookmark @
+ $ hgg bookmarks
+ \* @ 2:* (glob)
+ X 1:* (glob)
+ $ make_changes
+ abort: Can't commit, bookmark @ is protected
+ [255]
+
+ $ assert_clean
+ $ hgg bookmarks
+ \* @ 2:* (glob)
+ X 1:* (glob)
+
+ $ hgg --config bookflow.protect= commit -Am"Updated test"
+
+ $ hgg bookmarks
+ \* @ 3:* (glob)
+ X 1:* (glob)
+
+check requirement for an active bookmark
+ $ hgg bookmark -i
+ $ hgg bookmarks
+ @ 3:* (glob)
+ X 1:* (glob)
+ $ make_changes
+ abort: Can't commit without an active bookmark
+ [255]
+ $ hgg revert test
+ $ rm test.orig
+ $ assert_clean
+
+
+make the bookmark move by updating it on a, and then pulling
+# add a commit to a
+ $ cd ../a
+ $ hg bookmark X
+ $ hgg bookmarks
+ \* X 2:* (glob)
+ $ make_changes
+ $ hgg bookmarks
+ * X 3:81af7977fdb9
+
+# go back to b, and check out X
+ $ cd ../b
+ $ hgg up X
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (activating bookmark X)
+ $ hgg bookmarks
+ @ 3:* (glob)
+ \* X 1:* (glob)
+
+# pull, this should move the bookmark forward, because it was changed remotely
+ $ hgg pull -u | grep "updating to active bookmark X"
+ updating to active bookmark X
+
+ $ hgg bookmarks
+ @ 3:* (glob)
+ * X 4:81af7977fdb9
+
+the bookmark should not move if it diverged from remote
+ $ assert_clean ../a
+ $ assert_clean ../b
+ $ make_changes ../a
+ $ make_changes ../b
+ $ assert_clean ../a
+ $ assert_clean ../b
+ $ hgg --cwd ../a bookmarks
+ * X 4:238292f60a57
+ $ hgg --cwd ../b bookmarks
+ @ 3:* (glob)
+ * X 5:096f7e86892d
+ $ cd ../b
+ $ # make sure we can't push after bookmarks diverged
+ $ hgg 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]
+ $ hgg pull -u | grep divergent
+ divergent bookmark X stored as X at default
+ 1 other divergent bookmarks for "X"
+ $ hgg bookmarks
+ @ 3:* (glob)
+ * X 5:096f7e86892d
+ X at default 6:238292f60a57
+ $ hgg id -in
+ 096f7e86892d 5
+ $ make_changes
+ $ assert_clean
+ $ hgg bookmarks
+ @ 3:* (glob)
+ * X 7:227f941aeb07
+ X at default 6:238292f60a57
+
+now merge with the remote bookmark
+ $ hgg merge X at default --tool :local > /dev/null
+ $ assert_clean
+ $ hgg commit -m"Merged with X at default"
+ $ hgg bookmarks
+ @ 3:* (glob)
+ * X 8:26fed9bb3219
+ $ hgg push -B X | grep bookmark
+ pushing to $TESTTMP/a (?)
+ updating bookmark X
+ $ cd ../a
+ $ hgg up > /dev/null
+ $ hgg bookmarks
+ * X 7:26fed9bb3219
+
+test hg pull when there is more than one descendant
+ $ cd ../a
+ $ hgg bookmark Z
+ $ hgg bookmark Y
+ $ make_changes . YY
+ $ hgg up Z > /dev/null
+ $ make_changes . ZZ
+ created new head
+ $ hgg 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
+ $ hgg log -r 'Y%Z' -T '{rev}\n' # revs in Y but not in Z
+ 8
+ $ hgg log -r 'Z%Y' -T '{rev}\n' # revs in Z but not in Y
+ 9
+ $ cd ../b
+ $ hgg pull -u > /dev/null
+ $ hgg id
+ b74a4149df25 tip Z
+ $ hgg bookmarks | grep \* # no active bookmark
+ [1]
diff --git a/hgext/bookflow.py b/hgext/bookflow.py
new file mode 100644
--- /dev/null
+++ b/hgext/bookflow.py
@@ -0,0 +1,84 @@
+"""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 MARK: create a new bookmark
+ :hg book MARK -r REV: move bookmark to revision (fast-forward)
+ :hg up|co MARK: switch to bookmark
+ :hg push -B .: push active bookmark
+"""
+from mercurial.i18n import _
+from mercurial import (
+ bookmarks,
+ error,
+ registrar,
+ commands,
+ extensions
+)
+
+MY_NAME = __name__[len('hgext_'):] if __name__.startswith('hgext_') else __name__
+
+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 not active and ui.configbool(MY_NAME, 'require_bookmark', True):
+ raise error.Abort(_('Can\'t commit without an active bookmark'))
+ elif active in ui.configlist(MY_NAME, 'protect'):
+ raise error.Abort(_('Can\'t commit, bookmark {} is protected').format(active))
+ 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_branch(orig, ui, repo, label=None, **opts):
+ if label and not opts.get('clean') and not opts.get('rev'):
+ raise error.Abort("Branching should be done using bookmarks:\nhg bookmark " + label)
+ return orig(ui, repo, label, **opts)
+
+
+def reposetup(ui, repo):
+ extensions.wrapfunction(bookmarks, 'update', bookmarks_update)
+ extensions.wrapfunction(bookmarks, 'addbookmarks', bookmarks_addbookmarks)
+ ui.setconfig('hooks', 'pretxncommit.' + MY_NAME, commit_hook, source=MY_NAME)
+
+
+def uisetup(ui):
+ extensions.wrapcommand(commands.table, 'commit', commands_commit)
+ if not ui.configbool(MY_NAME, 'enable_branches'):
+ extensions.wrapcommand(commands.table, 'branch', commands_branch)
To: idlsoft, #hg-reviewers
Cc: mercurial-devel
More information about the Mercurial-devel
mailing list