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) | |||||