Details
Details
- Reviewers
- None
- Group Reviewers
hg-reviewers
Diff Detail
Diff Detail
- Repository
- rHG Mercurial
- Lint
Lint Skipped - Unit
Unit Tests Skipped
hg-reviewers |
Lint Skipped |
Unit Tests Skipped |
Path | Packages | |||
---|---|---|---|---|
M | hgext/bookflow.py (38 lines) | |||
M | tests/test-bookflow.t (186 lines) |
Commit | Parents | Author | Summary | Date |
---|---|---|---|---|
Sandu Turcan | Aug 22 2018, 9:52 AM |
from mercurial import ( | from mercurial import ( | ||||
bookmarks, | bookmarks, | ||||
error, | error, | ||||
registrar, | registrar, | ||||
commands, | commands, | ||||
extensions | extensions | ||||
) | ) | ||||
MY_NAME = __name__[len('hgext_'):] if __name__.startswith('hgext_') else __name__ | MY_NAME = 'bookflow' | ||||
configtable = {} | configtable = {} | ||||
configitem = registrar.configitem(configtable) | configitem = registrar.configitem(configtable) | ||||
configitem(MY_NAME, 'protect', ['@']) | configitem(MY_NAME, 'protect', ['@']) | ||||
configitem(MY_NAME, 'require_bookmark', True) | configitem(MY_NAME, 'require_bookmark', True) | ||||
configitem(MY_NAME, 'enable_branches', False) | configitem(MY_NAME, 'enable_branches', False) | ||||
cmdtable = {} | cmdtable = {} | ||||
command = registrar.command(cmdtable) | command = registrar.command(cmdtable) | ||||
def commit_hook(ui, repo, **kwargs): | def commit_hook(ui, repo, **kwargs): | ||||
active = repo._bookmarks.active | active = repo._bookmarks.active | ||||
if not active and ui.configbool(MY_NAME, 'require_bookmark', True): | if active: | ||||
raise error.Abort(_('Can\'t commit without an active bookmark')) | if active in ui.configlist(MY_NAME, 'protect'): | ||||
elif active in ui.configlist(MY_NAME, 'protect'): | raise error.Abort(_('can\'t commit, bookmark {} is protected').format(active)) | ||||
raise error.Abort(_('Can\'t commit, bookmark {} is protected').format(active)) | if not cwd_at_bookmark(repo, active): | ||||
raise error.Abort(_('can\'t commit, working directory out of sync with active bookmark.\nRun: hg up {}').format(active)) | |||||
elif ui.configbool(MY_NAME, 'require_bookmark', True): | |||||
raise error.Abort(_('can\'t commit without an active bookmark')) | |||||
return 0 | return 0 | ||||
def bookmarks_update(orig, repo, parents, node): | def bookmarks_update(orig, repo, parents, node): | ||||
if len(parents) == 2: | if len(parents) == 2: | ||||
# called during commit | # called during commit | ||||
return orig(repo, parents, node) | return orig(repo, parents, node) | ||||
else: | else: | ||||
# called during update | # called during update | ||||
return False | return False | ||||
def bookmarks_addbookmarks(orig, repo, tr, names, rev=None, force=False, inactive=False): | def bookmarks_addbookmarks(orig, repo, tr, names, rev=None, force=False, inactive=False): | ||||
if not rev: | if not rev: | ||||
marks = repo._bookmarks | marks = repo._bookmarks | ||||
for name in names: | for name in names: | ||||
if name in marks: | if name in marks: | ||||
raise error.Abort("Bookmark {} already exists, to move use the --rev option".format(name)) | raise error.Abort(_("bookmark {} already exists, to move use the --rev option").format(name)) | ||||
return orig(repo, tr, names, rev, force, inactive) | return orig(repo, tr, names, rev, force, inactive) | ||||
def commands_commit(orig, ui, repo, *args, **opts): | def commands_commit(orig, ui, repo, *args, **opts): | ||||
commit_hook(ui, repo) | commit_hook(ui, repo) | ||||
return orig(ui, repo, *args, **opts) | 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.\nRun: hg up {}\n").format(active)) | |||||
return rc | |||||
def cwd_at_bookmark(repo, mark): | |||||
mark_id = repo._bookmarks[mark] | |||||
cur_id = repo.lookup('.') | |||||
return cur_id == mark_id | |||||
def commands_branch(orig, ui, repo, label=None, **opts): | def commands_branch(orig, ui, repo, label=None, **opts): | ||||
if label and not opts.get('clean') and not opts.get('rev'): | if label and not opts.get(r'clean') and not opts.get(r'rev'): | ||||
raise error.Abort("Branching should be done using bookmarks:\nhg bookmark " + label) | raise error.Abort(_("branching should be done using bookmarks:\nhg bookmark {}").format(label)) | ||||
return orig(ui, repo, label, **opts) | return orig(ui, repo, label, **opts) | ||||
def reposetup(ui, repo): | def reposetup(ui, repo): | ||||
extensions.wrapfunction(bookmarks, 'update', bookmarks_update) | extensions.wrapfunction(bookmarks, 'update', bookmarks_update) | ||||
extensions.wrapfunction(bookmarks, 'addbookmarks', bookmarks_addbookmarks) | extensions.wrapfunction(bookmarks, 'addbookmarks', bookmarks_addbookmarks) | ||||
ui.setconfig('hooks', 'pretxncommit.' + MY_NAME, commit_hook, source=MY_NAME) | # commit hook conflicts with shelving | ||||
# ui.setconfig('hooks', 'pretxncommit.' + MY_NAME, commit_hook, source=MY_NAME) | |||||
def uisetup(ui): | def uisetup(ui): | ||||
extensions.wrapcommand(commands.table, 'commit', commands_commit) | extensions.wrapcommand(commands.table, 'commit', commands_commit) | ||||
extensions.wrapcommand(commands.table, 'pull', commands_pull) | |||||
if not ui.configbool(MY_NAME, 'enable_branches'): | if not ui.configbool(MY_NAME, 'enable_branches'): | ||||
extensions.wrapcommand(commands.table, 'branch', commands_branch) | extensions.wrapcommand(commands.table, 'branch', commands_branch) |
initialize | 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; hg commit -Am"${2:-test}"; r=$?; cd $d; return $r; } | ||||
$ 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;} | $ assert_clean() { ls -1 $1 | grep -v "test$" | cat;} | ||||
$ ls -1a | $ ls -1a | ||||
. | . | ||||
.. | .. | ||||
$ hg init a | $ hg init a | ||||
$ cd a | $ cd a | ||||
$ echo 'test' > test; hg commit -Am'test' | $ echo 'test' > test; hg commit -Am'test' | ||||
adding test | adding test | ||||
clone to b | clone to b | ||||
$ mkdir ../b | $ mkdir ../b | ||||
$ cd ../b | $ cd ../b | ||||
$ hg clone ../a . | $ hg clone ../a . | ||||
updating to branch default | updating to branch default | ||||
1 files updated, 0 files merged, 0 files removed, 0 files unresolved | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | ||||
$ hgg branch X | $ echo "[extensions]" >> .hg/hgrc | ||||
abort: Branching should be done using bookmarks: | $ echo "bookflow=" >> .hg/hgrc | ||||
$ hg branch X | |||||
abort: branching should be done using bookmarks: | |||||
hg bookmark X | hg bookmark X | ||||
[255] | [255] | ||||
$ hgg bookmark X | $ hg bookmark X | ||||
$ hgg bookmarks | $ hg bookmarks | ||||
* X 0:* (glob) | * X 0:* (glob) | ||||
$ make_changes | $ make_changes | ||||
$ hgg push ../a > /dev/null | $ hg push ../a > /dev/null | ||||
$ hg bookmarks | $ hg bookmarks | ||||
\* X 1:* (glob) | \* X 1:* (glob) | ||||
change a | change a | ||||
$ cd ../a | $ cd ../a | ||||
$ hgg up | $ hg up | ||||
1 files updated, 0 files merged, 0 files removed, 0 files unresolved | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | ||||
$ echo 'test' >> test; hg commit -Am'test' | $ echo 'test' >> test; hg commit -Am'test' | ||||
pull in b | pull in b | ||||
$ cd ../b | $ cd ../b | ||||
$ hgg pull -u | $ hg pull -u | ||||
pulling from $TESTTMP/a | pulling from $TESTTMP/a | ||||
searching for changes | searching for changes | ||||
adding changesets | adding changesets | ||||
adding manifests | adding manifests | ||||
adding file changes | adding file changes | ||||
added 1 changesets with 1 changes to 1 files | added 1 changesets with 1 changes to 1 files | ||||
new changesets * (glob) | new changesets * (glob) | ||||
1 files updated, 0 files merged, 0 files removed, 0 files unresolved | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | ||||
(leaving bookmark X) | (leaving bookmark X) | ||||
$ assert_clean | $ assert_clean | ||||
$ hg bookmarks | $ hg bookmarks | ||||
X 1:* (glob) | X 1:* (glob) | ||||
check protection of @ bookmark | check protection of @ bookmark | ||||
$ hgg bookmark @ | $ hg bookmark @ | ||||
$ hgg bookmarks | $ hg bookmarks | ||||
\* @ 2:* (glob) | \* @ 2:* (glob) | ||||
X 1:* (glob) | X 1:* (glob) | ||||
$ make_changes | $ make_changes | ||||
abort: Can't commit, bookmark @ is protected | abort: can't commit, bookmark @ is protected | ||||
[255] | [255] | ||||
$ assert_clean | $ assert_clean | ||||
$ hgg bookmarks | $ hg bookmarks | ||||
\* @ 2:* (glob) | \* @ 2:* (glob) | ||||
X 1:* (glob) | X 1:* (glob) | ||||
$ hgg --config bookflow.protect= commit -Am"Updated test" | $ hg --config bookflow.protect= commit -Am"Updated test" | ||||
$ hgg bookmarks | $ hg bookmarks | ||||
\* @ 3:* (glob) | \* @ 3:* (glob) | ||||
X 1:* (glob) | X 1:* (glob) | ||||
check requirement for an active bookmark | check requirement for an active bookmark | ||||
$ hgg bookmark -i | $ hg bookmark -i | ||||
$ hgg bookmarks | $ hg bookmarks | ||||
@ 3:* (glob) | @ 3:* (glob) | ||||
X 1:* (glob) | X 1:* (glob) | ||||
$ make_changes | $ make_changes | ||||
abort: Can't commit without an active bookmark | abort: can't commit without an active bookmark | ||||
[255] | [255] | ||||
$ hgg revert test | $ hg revert test | ||||
$ rm test.orig | $ rm test.orig | ||||
$ assert_clean | $ assert_clean | ||||
make the bookmark move by updating it on a, and then pulling | make the bookmark move by updating it on a, and then pulling | ||||
# add a commit to a | # add a commit to a | ||||
$ cd ../a | $ cd ../a | ||||
$ hg bookmark X | $ hg bookmark X | ||||
$ hgg bookmarks | $ hg bookmarks | ||||
\* X 2:* (glob) | \* X 2:* (glob) | ||||
$ make_changes | $ make_changes | ||||
$ hgg bookmarks | $ hg bookmarks | ||||
* X 3:81af7977fdb9 | * X 3:81af7977fdb9 | ||||
# go back to b, and check out X | # go back to b, and check out X | ||||
$ cd ../b | $ cd ../b | ||||
$ hgg up X | $ hg up X | ||||
1 files updated, 0 files merged, 0 files removed, 0 files unresolved | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | ||||
(activating bookmark X) | (activating bookmark X) | ||||
$ hgg bookmarks | $ hg bookmarks | ||||
@ 3:* (glob) | @ 3:* (glob) | ||||
\* X 1:* (glob) | \* X 1:* (glob) | ||||
# pull, this should move the bookmark forward, because it was changed remotely | # pull, this should move the bookmark forward, because it was changed remotely | ||||
$ hgg pull -u | grep "updating to active bookmark X" | $ hg pull -u | grep "updating to active bookmark X" | ||||
updating to active bookmark X | updating to active bookmark X | ||||
$ hgg bookmarks | $ hg bookmarks | ||||
@ 3:* (glob) | @ 3:* (glob) | ||||
* X 4:81af7977fdb9 | * X 4:81af7977fdb9 | ||||
the bookmark should not move if it diverged from remote | the bookmark should not move if it diverged from remote | ||||
$ assert_clean ../a | $ assert_clean ../a | ||||
$ assert_clean ../b | $ assert_clean ../b | ||||
$ make_changes ../a | $ make_changes ../a | ||||
$ make_changes ../b | $ make_changes ../b | ||||
$ assert_clean ../a | $ assert_clean ../a | ||||
$ assert_clean ../b | $ assert_clean ../b | ||||
$ hgg --cwd ../a bookmarks | $ hg --cwd ../a bookmarks | ||||
* X 4:238292f60a57 | * X 4:238292f60a57 | ||||
$ hgg --cwd ../b bookmarks | $ hg --cwd ../b bookmarks | ||||
@ 3:* (glob) | @ 3:* (glob) | ||||
* X 5:096f7e86892d | * X 5:096f7e86892d | ||||
$ cd ../b | $ cd ../b | ||||
$ # make sure we can't push after bookmarks diverged | $ # make sure we can't push after bookmarks diverged | ||||
$ hgg push -B X | grep abort | $ hg push -B X | grep abort | ||||
abort: push creates new remote head * with bookmark 'X'! (glob) | abort: push creates new remote head * with bookmark 'X'! (glob) | ||||
(pull and merge or see 'hg help push' for details about pushing new heads) | (pull and merge or see 'hg help push' for details about pushing new heads) | ||||
[1] | [1] | ||||
$ hgg pull -u | grep divergent | $ hg pull -u | grep divergent | ||||
divergent bookmark X stored as X@default | divergent bookmark X stored as X@default | ||||
1 other divergent bookmarks for "X" | 1 other divergent bookmarks for "X" | ||||
$ hgg bookmarks | $ hg bookmarks | ||||
@ 3:* (glob) | @ 3:* (glob) | ||||
* X 5:096f7e86892d | * X 5:096f7e86892d | ||||
X@default 6:238292f60a57 | X@default 6:238292f60a57 | ||||
$ hgg id -in | $ hg id -in | ||||
096f7e86892d 5 | 096f7e86892d 5 | ||||
$ make_changes | $ make_changes | ||||
$ assert_clean | $ assert_clean | ||||
$ hgg bookmarks | $ hg bookmarks | ||||
@ 3:* (glob) | @ 3:* (glob) | ||||
* X 7:227f941aeb07 | * X 7:227f941aeb07 | ||||
X@default 6:238292f60a57 | X@default 6:238292f60a57 | ||||
now merge with the remote bookmark | now merge with the remote bookmark | ||||
$ hgg merge X@default --tool :local > /dev/null | $ hg merge X@default --tool :local > /dev/null | ||||
$ assert_clean | $ assert_clean | ||||
$ hgg commit -m"Merged with X@default" | $ hg commit -m"Merged with X@default" | ||||
$ hgg bookmarks | $ hg bookmarks | ||||
@ 3:* (glob) | @ 3:* (glob) | ||||
* X 8:26fed9bb3219 | * X 8:26fed9bb3219 | ||||
$ hgg push -B X | grep bookmark | $ hg push -B X | grep bookmark | ||||
pushing to $TESTTMP/a (?) | pushing to $TESTTMP/a (?) | ||||
updating bookmark X | updating bookmark X | ||||
$ cd ../a | $ cd ../a | ||||
$ hgg up > /dev/null | $ hg up > /dev/null | ||||
$ hgg bookmarks | $ hg bookmarks | ||||
* X 7:26fed9bb3219 | * X 7:26fed9bb3219 | ||||
test hg pull when there is more than one descendant | test hg pull when there is more than one descendant | ||||
$ cd ../a | $ cd ../a | ||||
$ hgg bookmark Z | $ hg bookmark Z | ||||
$ hgg bookmark Y | $ hg bookmark Y | ||||
$ make_changes . YY | $ make_changes . YY | ||||
$ hgg up Z > /dev/null | $ hg up Z > /dev/null | ||||
$ make_changes . ZZ | $ make_changes . ZZ | ||||
created new head | created new head | ||||
$ hgg bookmarks | $ hg bookmarks | ||||
X 7:26fed9bb3219 | X 7:26fed9bb3219 | ||||
Y 8:131e663dbd2a | Y 8:131e663dbd2a | ||||
* Z 9:b74a4149df25 | * Z 9:b74a4149df25 | ||||
$ hg log -r 'p1(Y)' -r 'p1(Z)' -T '{rev}\n' # prove that Y and Z share the same parent | $ hg log -r 'p1(Y)' -r 'p1(Z)' -T '{rev}\n' # prove that Y and Z share the same parent | ||||
7 | 7 | ||||
$ hgg log -r 'Y%Z' -T '{rev}\n' # revs in Y but not in Z | $ hg log -r 'Y%Z' -T '{rev}\n' # revs in Y but not in Z | ||||
8 | 8 | ||||
$ hgg log -r 'Z%Y' -T '{rev}\n' # revs in Z but not in Y | $ hg log -r 'Z%Y' -T '{rev}\n' # revs in Z but not in Y | ||||
9 | 9 | ||||
$ cd ../b | $ cd ../b | ||||
$ hgg pull -u > /dev/null | $ hg pull -u > /dev/null | ||||
$ hgg id | $ hg id | ||||
b74a4149df25 tip Z | b74a4149df25 tip Z | ||||
$ hgg bookmarks | grep \* # no active bookmark | $ hg bookmarks | grep \* # no active bookmark | ||||
[1] | [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 -u > /dev/null | |||||
$ 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 > /dev/null | |||||
$ 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 -C > /dev/null | |||||
$ rm test.orig | |||||
make sure that commits aren't possible if working directory is not pointing to active bookmark | |||||
$ assert_clean ../a | |||||
$ assert_clean ../b | |||||
$ hg --cwd ../a id -i | |||||
36a6e592ec06 | |||||
$ hg --cwd ../a book | grep X | |||||
\* X \d+:36a6e592ec06 (re) | |||||
$ hg --cwd ../b id -i | |||||
36a6e592ec06 | |||||
$ hg --cwd ../b book | grep X | |||||
\* X \d+:36a6e592ec06 (re) | |||||
$ make_changes ../a | |||||
$ hg --cwd ../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: can't commit, working directory out of sync with active bookmark. | |||||
Run: hg up X | |||||
[255] | |||||
$ hg up -C -r . > /dev/null # cleanup local changes | |||||
$ assert_clean | |||||
$ hg id -i # we're still on the old commit | |||||
36a6e592ec06 | |||||
$ hg up X > /dev/null | |||||
$ hg id -i # now we're on X | |||||
f73a71c992b8 | |||||
$ hg book | grep X | |||||
\* X \d+:f73a71c992b8 (re) | |||||