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