##// END OF EJS Templates
bookflow: new extension for bookmark-based branching...
idlsoft -
r40869:9cec7a36 default
parent child Browse files
Show More
@@ -0,0 +1,103 b''
1 """implements bookmark-based branching (EXPERIMENTAL)
2
3 - Disables creation of new branches (config: enable_branches=False).
4 - Requires an active bookmark on commit (config: require_bookmark=True).
5 - Doesn't move the active bookmark on update, only on commit.
6 - Requires '--rev' for moving an existing bookmark.
7 - Protects special bookmarks (config: protect=@).
8
9 flow related commands
10
11 :hg book NAME: create a new bookmark
12 :hg book NAME -r REV: move bookmark to revision (fast-forward)
13 :hg up|co NAME: switch to bookmark
14 :hg push -B .: push active bookmark
15 """
16 from __future__ import absolute_import
17
18 from mercurial.i18n import _
19 from mercurial import (
20 bookmarks,
21 commands,
22 error,
23 extensions,
24 registrar,
25 )
26
27 MY_NAME = 'bookflow'
28
29 configtable = {}
30 configitem = registrar.configitem(configtable)
31
32 configitem(MY_NAME, 'protect', ['@'])
33 configitem(MY_NAME, 'require-bookmark', True)
34 configitem(MY_NAME, 'enable-branches', False)
35
36 cmdtable = {}
37 command = registrar.command(cmdtable)
38
39 def commit_hook(ui, repo, **kwargs):
40 active = repo._bookmarks.active
41 if active:
42 if active in ui.configlist(MY_NAME, 'protect'):
43 raise error.Abort(
44 _('cannot commit, bookmark {} is protected').format(active))
45 if not cwd_at_bookmark(repo, active):
46 raise error.Abort(
47 _('cannot commit, working directory out of sync with active bookmark'),
48 hint=_("run 'hg up {}'").format(active))
49 elif ui.configbool(MY_NAME, 'require-bookmark', True):
50 raise error.Abort(_('cannot commit without an active bookmark'))
51 return 0
52
53 def bookmarks_update(orig, repo, parents, node):
54 if len(parents) == 2:
55 # called during commit
56 return orig(repo, parents, node)
57 else:
58 # called during update
59 return False
60
61 def bookmarks_addbookmarks(
62 orig, repo, tr, names, rev=None, force=False, inactive=False):
63 if not rev:
64 marks = repo._bookmarks
65 for name in names:
66 if name in marks:
67 raise error.Abort(
68 _("bookmark {} already exists, to move use the --rev option"
69 ).format(name))
70 return orig(repo, tr, names, rev, force, inactive)
71
72 def commands_commit(orig, ui, repo, *args, **opts):
73 commit_hook(ui, repo)
74 return orig(ui, repo, *args, **opts)
75
76 def commands_pull(orig, ui, repo, *args, **opts):
77 rc = orig(ui, repo, *args, **opts)
78 active = repo._bookmarks.active
79 if active and not cwd_at_bookmark(repo, active):
80 ui.warn(_(
81 "working directory out of sync with active bookmark, run 'hg up {}'"
82 ).format(active))
83 return rc
84
85 def commands_branch(orig, ui, repo, label=None, **opts):
86 if label and not opts.get(r'clean') and not opts.get(r'rev'):
87 raise error.Abort(
88 _("creating named branches is disabled and you should use bookmarks"),
89 hint="see 'hg help bookflow'")
90 return orig(ui, repo, label, **opts)
91
92 def cwd_at_bookmark(repo, mark):
93 mark_id = repo._bookmarks[mark]
94 cur_id = repo.lookup('.')
95 return cur_id == mark_id
96
97 def uisetup(ui):
98 extensions.wrapfunction(bookmarks, 'update', bookmarks_update)
99 extensions.wrapfunction(bookmarks, 'addbookmarks', bookmarks_addbookmarks)
100 extensions.wrapcommand(commands.table, 'commit', commands_commit)
101 extensions.wrapcommand(commands.table, 'pull', commands_pull)
102 if not ui.configbool(MY_NAME, 'enable-branches'):
103 extensions.wrapcommand(commands.table, 'branch', commands_branch)
@@ -0,0 +1,292 b''
1 initialize
2 $ make_changes() {
3 > d=`pwd`
4 > [ ! -z $1 ] && cd $1
5 > echo "test `basename \`pwd\``" >> test
6 > hg commit -Am"${2:-test}"
7 > r=$?
8 > cd $d
9 > return $r
10 > }
11 $ ls -1a
12 .
13 ..
14 $ hg init a
15 $ cd a
16 $ echo 'test' > test; hg commit -Am'test'
17 adding test
18
19 clone to b
20
21 $ mkdir ../b
22 $ cd ../b
23 $ hg clone ../a .
24 updating to branch default
25 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
26 $ echo "[extensions]" >> .hg/hgrc
27 $ echo "bookflow=" >> .hg/hgrc
28 $ hg branch X
29 abort: creating named branches is disabled and you should use bookmarks
30 (see 'hg help bookflow')
31 [255]
32 $ hg bookmark X
33 $ hg bookmarks
34 * X 0:* (glob)
35 $ hg bookmark X
36 abort: bookmark X already exists, to move use the --rev option
37 [255]
38 $ make_changes
39 $ hg push ../a -q
40
41 $ hg bookmarks
42 \* X 1:* (glob)
43
44 change a
45 $ cd ../a
46 $ hg up
47 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
48 $ echo 'test' >> test; hg commit -Am'test'
49
50
51 pull in b
52 $ cd ../b
53 $ hg pull -u
54 pulling from $TESTTMP/a
55 searching for changes
56 adding changesets
57 adding manifests
58 adding file changes
59 added 1 changesets with 1 changes to 1 files
60 new changesets * (glob)
61 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
62 (leaving bookmark X)
63 $ hg status
64 $ hg bookmarks
65 X 1:* (glob)
66
67 check protection of @ bookmark
68 $ hg bookmark @
69 $ hg bookmarks
70 \* @ 2:* (glob)
71 X 1:* (glob)
72 $ make_changes
73 abort: cannot commit, bookmark @ is protected
74 [255]
75
76 $ hg status
77 M test
78 $ hg bookmarks
79 \* @ 2:* (glob)
80 X 1:* (glob)
81
82 $ hg --config bookflow.protect= commit -Am"Updated test"
83
84 $ hg bookmarks
85 \* @ 3:* (glob)
86 X 1:* (glob)
87
88 check requirement for an active bookmark
89 $ hg bookmark -i
90 $ hg bookmarks
91 @ 3:* (glob)
92 X 1:* (glob)
93 $ make_changes
94 abort: cannot commit without an active bookmark
95 [255]
96 $ hg revert test
97 $ rm test.orig
98 $ hg status
99
100
101 make the bookmark move by updating it on a, and then pulling
102 # add a commit to a
103 $ cd ../a
104 $ hg bookmark X
105 $ hg bookmarks
106 \* X 2:* (glob)
107 $ make_changes
108 $ hg bookmarks
109 * X 3:81af7977fdb9
110
111 # go back to b, and check out X
112 $ cd ../b
113 $ hg up X
114 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
115 (activating bookmark X)
116 $ hg bookmarks
117 @ 3:* (glob)
118 \* X 1:* (glob)
119
120 # pull, this should move the bookmark forward, because it was changed remotely
121 $ hg pull -u | grep "updating to active bookmark X"
122 updating to active bookmark X
123
124 $ hg bookmarks
125 @ 3:* (glob)
126 * X 4:81af7977fdb9
127
128 the bookmark should not move if it diverged from remote
129 $ hg -R ../a status
130 $ hg -R ../b status
131 $ make_changes ../a
132 $ make_changes ../b
133 $ hg -R ../a status
134 $ hg -R ../b status
135 $ hg -R ../a bookmarks
136 * X 4:238292f60a57
137 $ hg -R ../b bookmarks
138 @ 3:* (glob)
139 * X 5:096f7e86892d
140 $ cd ../b
141 $ # make sure we cannot push after bookmarks diverged
142 $ hg push -B X | grep abort
143 abort: push creates new remote head * with bookmark 'X'! (glob)
144 (pull and merge or see 'hg help push' for details about pushing new heads)
145 [1]
146 $ hg pull -u | grep divergent
147 divergent bookmark X stored as X@default
148 1 other divergent bookmarks for "X"
149 $ hg bookmarks
150 @ 3:* (glob)
151 * X 5:096f7e86892d
152 X@default 6:238292f60a57
153 $ hg id -in
154 096f7e86892d 5
155 $ make_changes
156 $ hg status
157 $ hg bookmarks
158 @ 3:* (glob)
159 * X 7:227f941aeb07
160 X@default 6:238292f60a57
161
162 now merge with the remote bookmark
163 $ hg merge X@default --tool :local -q
164 $ hg status
165 M test
166 $ hg commit -m"Merged with X@default"
167 $ hg bookmarks
168 @ 3:* (glob)
169 * X 8:26fed9bb3219
170 $ hg push -B X | grep bookmark
171 pushing to $TESTTMP/a (?)
172 updating bookmark X
173 $ cd ../a
174 $ hg up -q
175 $ hg bookmarks
176 * X 7:26fed9bb3219
177
178 test hg pull when there is more than one descendant
179 $ cd ../a
180 $ hg bookmark Z
181 $ hg bookmark Y
182 $ make_changes . YY
183 $ hg up Z -q
184 $ make_changes . ZZ
185 created new head
186 $ hg bookmarks
187 X 7:26fed9bb3219
188 Y 8:131e663dbd2a
189 * Z 9:b74a4149df25
190 $ hg log -r 'p1(Y)' -r 'p1(Z)' -T '{rev}\n' # prove that Y and Z share the same parent
191 7
192 $ hg log -r 'Y%Z' -T '{rev}\n' # revs in Y but not in Z
193 8
194 $ hg log -r 'Z%Y' -T '{rev}\n' # revs in Z but not in Y
195 9
196 $ cd ../b
197 $ hg pull -uq
198 $ hg id
199 b74a4149df25 tip Z
200 $ hg bookmarks | grep \* # no active bookmark
201 [1]
202
203
204 test shelving
205 $ cd ../a
206 $ echo anotherfile > anotherfile # this change should not conflict
207 $ hg add anotherfile
208 $ hg commit -m"Change in a"
209 $ cd ../b
210 $ hg up Z | grep Z
211 (activating bookmark Z)
212 $ hg book | grep \* # make sure active bookmark
213 \* Z 10:* (glob)
214 $ echo "test b" >> test
215 $ hg diff --stat
216 test | 1 +
217 1 files changed, 1 insertions(+), 0 deletions(-)
218 $ hg --config extensions.shelve= shelve
219 shelved as Z
220 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
221 $ hg pull -uq
222 $ hg --trace --config extensions.shelve= unshelve
223 unshelving change 'Z'
224 rebasing shelved changes
225 $ hg diff --stat
226 test | 1 +
227 1 files changed, 1 insertions(+), 0 deletions(-)
228
229
230 make the bookmark move by updating it on a, and then pulling with a local change
231 # add a commit to a
232 $ cd ../a
233 $ hg up -C X |fgrep "activating bookmark X"
234 (activating bookmark X)
235 # go back to b, and check out X
236 $ cd ../b
237 $ hg up -C X |fgrep "activating bookmark X"
238 (activating bookmark X)
239 # update and push from a
240 $ make_changes ../a
241 created new head
242 $ echo "more" >> test
243 $ hg pull -u 2>&1 | fgrep -v TESTTMP| fgrep -v "searching for changes" | fgrep -v adding
244 pulling from $TESTTMP/a
245 added 1 changesets with 0 changes to 0 files (+1 heads)
246 updating bookmark X
247 new changesets * (glob)
248 updating to active bookmark X
249 merging test
250 warning: conflicts while merging test! (edit, then use 'hg resolve --mark')
251 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
252 use 'hg resolve' to retry unresolved file merges
253 $ hg update -Cq
254 $ rm test.orig
255
256 make sure that commits aren't possible if working directory is not pointing to active bookmark
257 $ hg -R ../a status
258 $ hg -R ../b status
259 $ hg -R ../a id -i
260 36a6e592ec06
261 $ hg -R ../a book | grep X
262 \* X \d+:36a6e592ec06 (re)
263 $ hg -R ../b id -i
264 36a6e592ec06
265 $ hg -R ../b book | grep X
266 \* X \d+:36a6e592ec06 (re)
267 $ make_changes ../a
268 $ hg -R ../a book | grep X
269 \* X \d+:f73a71c992b8 (re)
270 $ cd ../b
271 $ hg pull 2>&1 | grep -v add | grep -v pulling | grep -v searching | grep -v changeset
272 updating bookmark X
273 (run 'hg update' to get a working copy)
274 working directory out of sync with active bookmark, run 'hg up X'
275 $ hg id -i # we're still on the old commit
276 36a6e592ec06
277 $ hg book | grep X # while the bookmark moved
278 \* X \d+:f73a71c992b8 (re)
279 $ make_changes
280 abort: cannot commit, working directory out of sync with active bookmark
281 (run 'hg up X')
282 [255]
283 $ hg up -Cq -r . # cleanup local changes
284 $ hg status
285 $ hg id -i # we're still on the old commit
286 36a6e592ec06
287 $ hg up X -q
288 $ hg id -i # now we're on X
289 f73a71c992b8
290 $ hg book | grep X
291 \* X \d+:f73a71c992b8 (re)
292
General Comments 0
You need to be logged in to leave comments. Login now