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