Index: hgext/bookflow.py =================================================================== --- hgext/bookflow.py +++ hgext/bookflow.py @@ -22,7 +22,7 @@ extensions ) -MY_NAME = __name__[len('hgext_'):] if __name__.startswith('hgext_') else __name__ +MY_NAME = 'bookflow' configtable = {} configitem = registrar.configitem(configtable) @@ -36,10 +36,13 @@ 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)) + if active: + if active in ui.configlist(MY_NAME, 'protect'): + 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 @@ -51,34 +54,43 @@ # 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)) + 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.\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): - if label and not opts.get('clean') and not opts.get('rev'): - raise error.Abort("Branching should be done using bookmarks:\nhg bookmark " + label) + 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 {}").format(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) - + # commit hook conflicts with shelving + # ui.setconfig('hooks', 'pretxncommit.' + MY_NAME, commit_hook, source=MY_NAME) def uisetup(ui): 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) Index: tests/test-bookflow.t =================================================================== --- tests/test-bookflow.t +++ tests/test-bookflow.t @@ -1,6 +1,5 @@ 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; } + $ make_changes() { d=`pwd`; [ ! -z $1 ] && cd $1; echo "test $(basename `pwd`)" >> test; hg commit -Am"${2:-test}"; r=$?; cd $d; return $r; } $ assert_clean() { ls -1 $1 | grep -v "test$" | cat;} $ ls -1a . @@ -17,29 +16,31 @@ $ 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: + $ echo "[extensions]" >> .hg/hgrc + $ echo "bookflow=" >> .hg/hgrc + $ hg branch X + abort: branching should be done using bookmarks: hg bookmark X [255] - $ hgg bookmark X - $ hgg bookmarks + $ hg bookmark X + $ hg bookmarks * X 0:* (glob) $ make_changes - $ hgg push ../a > /dev/null + $ hg push ../a > /dev/null $ hg bookmarks \* X 1:* (glob) change a $ cd ../a - $ hgg up + $ 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 - $ hgg pull -u + $ hg pull -u pulling from $TESTTMP/a searching for changes adding changesets @@ -54,34 +55,34 @@ X 1:* (glob) check protection of @ bookmark - $ hgg bookmark @ - $ hgg bookmarks + $ hg bookmark @ + $ hg bookmarks \* @ 2:* (glob) X 1:* (glob) $ make_changes - abort: Can't commit, bookmark @ is protected + abort: can't commit, bookmark @ is protected [255] $ assert_clean - $ hgg bookmarks + $ hg bookmarks \* @ 2:* (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) X 1:* (glob) check requirement for an active bookmark - $ hgg bookmark -i - $ hgg bookmarks + $ hg bookmark -i + $ hg bookmarks @ 3:* (glob) X 1:* (glob) $ make_changes - abort: Can't commit without an active bookmark + abort: can't commit without an active bookmark [255] - $ hgg revert test + $ hg revert test $ rm test.orig $ assert_clean @@ -90,26 +91,26 @@ # add a commit to a $ cd ../a $ hg bookmark X - $ hgg bookmarks + $ hg bookmarks \* X 2:* (glob) $ make_changes - $ hgg bookmarks + $ hg bookmarks * X 3:81af7977fdb9 # go back to b, and check out X $ cd ../b - $ hgg up X + $ hg up X 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (activating bookmark X) - $ hgg bookmarks + $ hg 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" + $ hg pull -u | grep "updating to active bookmark X" updating to active bookmark X - $ hgg bookmarks + $ hg bookmarks @ 3:* (glob) * X 4:81af7977fdb9 @@ -120,69 +121,160 @@ $ make_changes ../b $ assert_clean ../a $ assert_clean ../b - $ hgg --cwd ../a bookmarks + $ hg --cwd ../a bookmarks * X 4:238292f60a57 - $ hgg --cwd ../b bookmarks + $ hg --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 + $ 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] - $ hgg pull -u | grep divergent + $ hg pull -u | grep divergent divergent bookmark X stored as X@default 1 other divergent bookmarks for "X" - $ hgg bookmarks + $ hg bookmarks @ 3:* (glob) * X 5:096f7e86892d X@default 6:238292f60a57 - $ hgg id -in + $ hg id -in 096f7e86892d 5 $ make_changes $ assert_clean - $ hgg bookmarks + $ hg bookmarks @ 3:* (glob) * X 7:227f941aeb07 X@default 6:238292f60a57 now merge with the remote bookmark - $ hgg merge X@default --tool :local > /dev/null + $ hg merge X@default --tool :local > /dev/null $ assert_clean - $ hgg commit -m"Merged with X@default" - $ hgg bookmarks + $ hg commit -m"Merged with X@default" + $ hg bookmarks @ 3:* (glob) * X 8:26fed9bb3219 - $ hgg push -B X | grep bookmark + $ hg push -B X | grep bookmark pushing to $TESTTMP/a (?) updating bookmark X $ cd ../a - $ hgg up > /dev/null - $ hgg bookmarks + $ hg up > /dev/null + $ hg bookmarks * X 7:26fed9bb3219 test hg pull when there is more than one descendant $ cd ../a - $ hgg bookmark Z - $ hgg bookmark Y + $ hg bookmark Z + $ hg bookmark Y $ make_changes . YY - $ hgg up Z > /dev/null + $ hg up Z > /dev/null $ make_changes . ZZ created new head - $ hgg bookmarks + $ 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 - $ 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 - $ 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 $ cd ../b - $ hgg pull -u > /dev/null - $ hgg id + $ hg pull -u > /dev/null + $ hg id b74a4149df25 tip Z - $ hgg bookmarks | grep \* # no active bookmark + $ 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 -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) +