|
1 .. Copyright © 2014 Greg Ward <greg@gerg.ca> |
|
2 |
|
3 ------------------------------ |
|
4 Evolve: Shared Mutable History |
|
5 ------------------------------ |
|
6 |
|
7 Once you have mastered the art of mutable history in a single |
|
8 repository, you might want to move up to the next level: *shared* |
|
9 mutable history. ``evolve`` lets you push and pull draft changesets |
|
10 between repositories along with their obsolescence markers. This opens |
|
11 up a number of interesting possibilities. |
|
12 |
|
13 The most common scenario is a single developer working across two |
|
14 computers. Say you're working on code that must be tested on a remote |
|
15 test server, probably in a rack somewhere, only accessible by SSH, and |
|
16 running an “enterprise-grade” (out-of-date) OS. But you probably |
|
17 prefer to write code locally: everything is setup the way you like it, |
|
18 and you can use your preferred editor, IDE, merge/diff tools, etc. |
|
19 |
|
20 Traditionally, your options are limited: either |
|
21 |
|
22 * (ab)use your source control system by committing half-working code |
|
23 in order to get it onto the remote test server, or |
|
24 * go behind source control's back by using ``rsync`` (or similar) to |
|
25 transfer your code back-and-forth until it is ready to commit |
|
26 |
|
27 The former is less bad with distributed version control systems like |
|
28 Mercurial, but it's still far from ideal. (One important version |
|
29 control “best practice” is that every commit should make things just a |
|
30 little bit better, i.e. you should never commit code that is worse |
|
31 than what came before.) The latter, avoiding version control entirely, |
|
32 means that you're walking a tightrope without a safety net. One |
|
33 accidental ``rsync`` in the wrong direction could destroy hours of |
|
34 work. |
|
35 |
|
36 Using Mercurial with ``evolve`` to share mutable history solves all of |
|
37 these problems. As with single-repository ``evolve``, you can commit |
|
38 whenever the code is demonstrably better, even if all the tests aren't |
|
39 passing yet—just ``hg amend`` when they are. And you can transfer |
|
40 those half-baked changesets between repositories to try things out on |
|
41 your test server before anything is carved in stone. |
|
42 |
|
43 A less common scenario is multiple developers sharing mutable history. |
|
44 (This is in fact how Mercurial itself is developed.) We'll cover this |
|
45 scenario later. But first, single-user sharing. |
|
46 |
|
47 Publishing and non-publishing repositories |
|
48 ------------------------------------------ |
|
49 |
|
50 The key to shared mutable history is to keep your changesets in |
|
51 *draft* phase as you pass them around. Recall that by default, ``hg |
|
52 push`` promotes changesets from *draft* to *public*, and public |
|
53 changesets are immutable. You can change this behaviour by |
|
54 reconfiguring the *target* repository so that it is non-publishing. |
|
55 (Short version: set ``phases.publish`` to ``false``. Long version |
|
56 follows.) |
|
57 |
|
58 Setting things up |
|
59 ----------------- |
|
60 |
|
61 We'll work an example with three local repositories, although in the |
|
62 real world they'd most likely be on three different computers. First, |
|
63 the public repository is where tested, polished changesets live, and |
|
64 it is where you push/pull changesets to/from the rest of your team. :: |
|
65 |
|
66 $ hg init public |
|
67 |
|
68 We'll need two clones where work gets done:: |
|
69 |
|
70 $ hg clone -q public test-repo |
|
71 $ hg clone -q test-repo dev-repo |
|
72 |
|
73 ``dev-repo`` is your local machine, with GUI merge tools and IDEs and |
|
74 everything configured just the way you like it. ``test-repo`` is the |
|
75 test server in a rack somewhere behind SSH. So for the most part, |
|
76 we'll develop in ``dev-repo``, push to ``test-repo``, test and polish |
|
77 there, and push to ``public``. |
|
78 |
|
79 The key to making this whole thing work is to make ``test-repo`` |
|
80 non-publishing:: |
|
81 |
|
82 $ cat >> test-repo/.hg/hgrc <<EOF |
|
83 [phases] |
|
84 publish = false |
|
85 EOF |
|
86 |
|
87 We also have to configure ``evolve`` in both ``test-repo`` and |
|
88 ``dev-repo``, so that we can amend and evolve in both of them. :: |
|
89 |
|
90 $ cat >> test-repo/.hg/hgrc <<EOF |
|
91 [extensions] |
|
92 rebase = |
|
93 evolve = /path/to/mutable-history/hgext/evolve.py |
|
94 EOF |
|
95 $ cat >> dev-repo/.hg/hgrc <<EOF |
|
96 [extensions] |
|
97 rebase = |
|
98 evolve = /path/to/mutable-history/hgext/evolve.py |
|
99 EOF |
|
100 |
|
101 Keep in mind that in real life, these repositories would probably be |
|
102 on separate computers, so you'd have to login to each one to configure |
|
103 each repository. |
|
104 |
|
105 To start things off, let's make one public, immutable changeset:: |
|
106 |
|
107 $ cd test-repo |
|
108 $ echo 'my new project' > file1 |
|
109 $ hg add file1 |
|
110 $ hg commit -m 'create new project' |
|
111 $ hg push -q |
|
112 |
|
113 and pull that into the development repository:: |
|
114 |
|
115 $ cd ../dev-repo |
|
116 $ hg pull -u |
|
117 |
|
118 Amending a shared changeset |
|
119 --------------------------- |
|
120 |
|
121 Everything you learned in the `user guide`_ applies to work done in |
|
122 ``dev-repo``. You can commit, amend, uncommit, evolve, and so forth |
|
123 just as before. |
|
124 |
|
125 .. _`user guide`: user-guide.html |
|
126 |
|
127 Things get different when you push changesets to ``test-repo``. Or |
|
128 rather, things stay the same, which *is* different: because we |
|
129 configured ``test-repo`` to be non-publishing, draft changesets stay |
|
130 draft when we push them to ``test-repo``. Importantly, they're also |
|
131 draft (mutable) in ``test-repo``. |
|
132 |
|
133 Let's commit a preliminary change and push it to ``test-repo`` for |
|
134 testing. :: |
|
135 |
|
136 $ echo 'fix fix fix' > file1 |
|
137 $ hg commit -m 'prelim change' |
|
138 $ hg push ../test-repo |
|
139 |
|
140 At this point, ``dev-repo`` and ``test-repo`` have the same changesets |
|
141 in the same phases: |
|
142 |
|
143 [figure SG01: rev 0:0dc9 public, rev 1:f649 draft, same on both repos] |
|
144 |
|
145 (You may notice a change in notation from the user guide: now |
|
146 changesets are labelled with their revision number and the first four |
|
147 digits of the 40-digit hexadecimal changeset ID. Mercurial revision |
|
148 numbers are never stable when working across repositories, especially |
|
149 when obsolescence is involved. We'll see why shortly.) |
|
150 |
|
151 Now let's switch to ``test-repo`` to test our change:: |
|
152 |
|
153 $ cd ../test-repo |
|
154 $ hg update |
|
155 |
|
156 Don't forget to ``hg update``! Pushing only adds changesets to a |
|
157 remote repository; it does not update the working directory (unless |
|
158 you have a hook that updates for you). |
|
159 |
|
160 Now let's imagine the tests failed because we didn't use proper |
|
161 punctuation and capitalization (oops). Let's amend our preliminary fix |
|
162 (and fix the lame commit message while we're at it):: |
|
163 |
|
164 $ echo 'Fix fix fix.' > file1 |
|
165 $ hg amend -m 'fix bug 37' |
|
166 |
|
167 Now we're in a funny intermediate state (figure 2): revision 1:f649 is |
|
168 obsolete in ``test-repo``, having been replaced by revision 3:60ff |
|
169 (revision 2:2a03 is another one of those temporary amend commits that |
|
170 we saw in the user guide)—but ``dev-repo`` knows nothing of these |
|
171 recent developments. |
|
172 |
|
173 [figure SG02: rev 0:0dc9 public, rev 1:f649, 2:2a03 obsolete, rev 3:60ff draft -- but dev-repo same as in SG01] |
|
174 |
|
175 Let's resynchronize:: |
|
176 |
|
177 $ cd ../dev-repo |
|
178 $ hg pull -u |
|
179 |
|
180 As seen in figure 3, this transfers the new changeset *and* the |
|
181 obsolescence marker for revision 1. However, it does *not* transfer |
|
182 the temporary amend commit, because it is obsolete. Push and pull |
|
183 transfer obsolesence markers between repositories, but they do not |
|
184 normally transfer obsolete changesets. |
|
185 |
|
186 [figure SG03: dev-repo grows new rev 2:60ff, marks 1:f649 obsolete] |
|
187 |
|
188 Because of this deliberately incomplete synchronization, revision |
|
189 numbers in ``test-repo`` and ``dev-repo`` are no longer consistent. We |
|
190 *must* use changeset IDs. |
|
191 |
|
192 Amend again, locally |
|
193 -------------------- |
|
194 |
|
195 This process can repeat. Perhaps you figure out a more elegant fix to |
|
196 the bug, and want to mutate history so nobody ever knows you had a |
|
197 less-than-perfect idea. We'll implement it locally in ``dev-repo`` and |
|
198 push to ``test-repo``:: |
|
199 |
|
200 $ echo 'Fix, fix, and fix.' > file1 |
|
201 $ hg amend |
|
202 $ hg push |
|
203 |
|
204 This time around, the temporary amend commit is in ``dev-repo``, and |
|
205 it is not transferred to ``test-repo``—the same as before, just in the |
|
206 opposite direction. Figure 4 shows the two repositories after amending |
|
207 in ``dev-repo`` and pushing to ``test-repo``. |
|
208 |
|
209 [figure SG04: each repo has one temporary amend commit, but they're different in each one] |
|
210 |
|
211 Let's hop over to ``test-repo`` to test the more elegant fix:: |
|
212 |
|
213 $ cd ../test-repo |
|
214 $ hg update -q |
|
215 |
|
216 This time, all the tests pass, so no further amendment is required. |
|
217 This bug fix is finished, so we push it to the public repository:: |
|
218 |
|
219 $ hg push |
|
220 [...] |
|
221 added 1 changesets with 1 changes to 1 files |
|
222 |
|
223 Note that only one changeset—the final version, after two |
|
224 amendments—was actually pushed. Again, Mercurial normally doesn't |
|
225 transfer obsolete changesets on push and pull. (Specifically, it |
|
226 doesn't transfer *hidden* changesets: roughly speaking, obsolete |
|
227 changesets with no non-obsolete descendants. If you're curious, see |
|
228 the `concept guide`_ for the precise definition of hidden.) |
|
229 |
|
230 .. _`concept guide`: concepts.html |
|
231 |
|
232 So the picture in ``public`` is much simpler than in either |
|
233 ``dev-repo`` or ``test-repo``. None of our missteps or amendments are |
|
234 visible publicly, just the final, beautifully polished changeset: |
|
235 |
|
236 [figure SG05: public repo with rev 0:0dc9, 1:de61, both public] |
|
237 |
|
238 There is one important step left to do. Because we pushed from |
|
239 ``test-repo`` to ``public``, the pushed changeset is in *public* phase |
|
240 in those two repositories. But ``dev-repo`` knows nothing of this: |
|
241 that changeset is still *draft* there. If we're not careful, we might |
|
242 mutate history in ``dev-repo``, obsoleting a changeset that is already |
|
243 public. Let's avoid that situation for now by pulling from |
|
244 ``test-repo`` down to ``dev-repo``:: |
|
245 |
|
246 $ cd ../dev-repo |
|
247 $ hg pull -u |
|
248 |
|
249 Getting into trouble |
|
250 -------------------- |
|
251 |
|
252 Mercurial with ``evolve`` is a powerful tool, and using powerful tools |
|
253 can have consequences. (You can cut yourself badly with a sharp knife, |
|
254 but every competent chef keeps several around. Ever try to chop onions |
|
255 with a spoon?) |
|
256 |
|
257 In the user guide, we saw examples of *unstable* changesets, which are |
|
258 the most common type of troubled changeset. (Recall that a |
|
259 non-obsolete changeset with obsolete ancestors is unstable.) |
|
260 |
|
261 Two other types of trouble can crop up: *bumped* and *divergent* |
|
262 changesets. Both are more likely with shared mutable history, |
|
263 especially mutable history shared by multiple developers. |
|
264 |
|
265 To demonstrate, let's start with the ``public`` repository as we left |
|
266 it in the last example, with two immutable changesets (figure 5 |
|
267 above). Two developers, Alice and Bob, start working from this point:: |
|
268 |
|
269 $ hg clone -q public alice |
|
270 $ hg clone -q public bob |
|
271 |
|
272 We need to configure Alice's and Bob's working repositories similar to |
|
273 ``test-repo``, i.e. make them non-publishing and enable ``evolve``:: |
|
274 |
|
275 $ cat >> alice/.hg/hgrc <<EOF |
|
276 [phases] |
|
277 publish = false |
|
278 [extensions] |
|
279 rebase = |
|
280 evolve = /path/to/mutable-history/hgext/evolve.py |
|
281 EOF |
|
282 $ cp alice/.hg/hgrc bob/.hg/hgrc |
|
283 |
|
284 Bumped changesets: only one gets on the plane |
|
285 --------------------------------------------- |
|
286 |
|
287 If two people show up at the airport with tickets for the same seat on |
|
288 the same plane, only one of them gets on the plane. The would-be |
|
289 traveller left behind in the airport terminal is said to have been |
|
290 *bumped*. |
|
291 |
|
292 Similarly, if Alice and Bob are collaborating on some mutable |
|
293 changesets, it's possible to get into a situation where an otherwise |
|
294 worthwhile changeset cannot be pushed to the public repository; it is |
|
295 bumped by an alternative changeset that happened to get there first. |
|
296 Let's demonstrate one way this could happen. |
|
297 |
|
298 It starts with Alice committing a bug fix. Right now, we don't yet |
|
299 know if this bug fix is good enough to push to the public repository, |
|
300 but it's good enough for Alice to commit. :: |
|
301 |
|
302 $ cd alice |
|
303 $ echo 'fix' > file2 |
|
304 $ hg commit -q -A -m 'fix bug 15' |
|
305 |
|
306 Now Bob has a bad idea: he decides to pull whatever Alice is working |
|
307 on and tweak her bug fix to his taste:: |
|
308 |
|
309 $ cd ../bob |
|
310 $ hg pull -q -u ../alice |
|
311 $ echo 'Fix.' > file2 |
|
312 $ hg amend -q -A -m 'fix bug 15 (amended)' |
|
313 |
|
314 (Note the lack of communication between Alice and Bob. Failing to |
|
315 communicate with your colleagues is a good way to get into trouble. |
|
316 Nevertheless, ``evolve`` can usually sort things out, as we will see.) |
|
317 |
|
318 [figure SG06: Bob's repo with one amendment] |
|
319 |
|
320 After some testing, Alice realizes her bug fix is just fine as it is: |
|
321 no need for further polishing and amending, this changeset is ready to |
|
322 publish. :: |
|
323 |
|
324 $ cd ../alice |
|
325 $ hg push -q |
|
326 |
|
327 This introduces a contradiction: in Bob's repository, changeset 2:e011 |
|
328 (his copy of Alice's fix) is obsolete, since Bob amended it. But in |
|
329 Alice's repository (and ``public``), that changeset is public: it is |
|
330 immutable, carved in stone for all eternity. No changeset can be both |
|
331 obsolete and public, so Bob is in for a surprise the next time he |
|
332 pulls from ``public``:: |
|
333 |
|
334 $ cd ../bob |
|
335 $ hg pull -q -u |
|
336 1 new bumped changesets |
|
337 |
|
338 Figure 7 shows what just happened to Bob's repository: changeset |
|
339 2:e011 is now public, so it can't be obsolete. When that changeset was |
|
340 obsolete, it made perfect sense for it to have a successor, namely |
|
341 Bob's amendment of Alice's fix (changeset 4:fe88). But it's illogical |
|
342 for a public changeset to have a successor, so 4:fe88 is in trouble: |
|
343 it has been *bumped*. |
|
344 |
|
345 [figure SG07: 2:e011 now public not obsolete, 4:fe88 now bumped] |
|
346 |
|
347 As usual when there's trouble in your repository, the solution is to |
|
348 evolve it:: |
|
349 |
|
350 $ hg evolve --all |
|
351 |
|
352 Figure 8 illustrate's Bob's repository after evolving away the bumped |
|
353 changeset. Ignoring the obsolete changesets, Bob now has a nice, |
|
354 clean, simple history. His amendment of Alice's bug fix lives on, as |
|
355 changeset 5:227d—albeit with a software-generated commit message. (Bob |
|
356 should probably amend that changeset to improve the commit message.) |
|
357 But the important thing is that his repository no longer has any |
|
358 troubled changesets, thanks to ``evolve``. |
|
359 |
|
360 [figure SG08: 5:227d is new, formerly bumped changeset 4:fe88 now hidden] |
|
361 |
|
362 Divergent changesets |
|
363 -------------------- |
|
364 |
|
365 In addition to *unstable* and *bumped*, there is a third kind of |
|
366 troubled changeset: *divergent*. When an obsolete changeset has two |
|
367 successors, those successors are divergent. |
|
368 |
|
369 To illustrate, let's start Alice and Bob at the same |
|
370 point—specifically, the point where Alice's repository currently |
|
371 stands. Bob's repository is a bit of a mess, so we'll throw it away |
|
372 and start him off with a copy of Alice's repository:: |
|
373 |
|
374 $ cd .. |
|
375 $ rm -rf bob |
|
376 $ cp -rp alice bob |
|
377 |
|
378 Now we'll have Bob commit a bug fix that could still be improved:: |
|
379 |
|
380 $ cd bob |
|
381 $ echo 'pretty good fix' >> file1 |
|
382 $ hg commit -u bob -m 'fix bug 24 (v1)' |
|
383 |
|
384 This time, Alice meddles with her colleague's work (still a bad |
|
385 idea):: |
|
386 |
|
387 $ cd ../alice |
|
388 $ hg pull -q -u ../bob |
|
389 $ echo 'better (alice)' >> file1 |
|
390 $ hg amend -u alice -m 'fix bug 24 (v2 by alice)' |
|
391 |
|
392 Here's where things change from the "bumped" scenario above: this |
|
393 time, the original author (Bob) decides to amend his changeset too. :: |
|
394 |
|
395 $ cd ../bob |
|
396 $ echo 'better (bob)' >> file1 |
|
397 $ hg amend -u bob -m 'fix bug 24 (v2 by bob)' |
|
398 |
|
399 At this point, the divergence exists, but only in theory: Bob's |
|
400 original changeset, 3:fe81, is obsolete and has two successors. But |
|
401 those successors are in different repositories, so the trouble is not |
|
402 visible to anyone yet. It will be as soon as one of our players pulls |
|
403 from the other's repository. Let's make Bob the victim again:: |
|
404 |
|
405 $ hg pull -q -u ../alice |
|
406 not updating: not a linear update |
|
407 (merge or update --check to force update) |
|
408 2 new divergent changesets |
|
409 |
|
410 The “not a linear update” is our first hint that something is wrong, |
|
411 but of course “2 new divergent changesets” is the real problem. Figure |
|
412 9 shows both problems. |
|
413 |
|
414 [figure SG09: bob's repo with 2 heads for the 2 divergent changesets, 5:fc16 and 6:694f; wc is at 5:fc16, hence update refused; both are successors of obsolete 3:fe81, hence divergence] |
|
415 |
|
416 Now we need to get out of trouble. Unfortunately, a `bug`_ in |
|
417 ``evolve`` means that the usual answer (run ``hg evolve --all``) does |
|
418 not work. Bob has to figure out the solution on his own: in this case, |
|
419 merge. To avoid distractions, we'll set ``HGMERGE`` to make Mercurial |
|
420 resolve any conflicts in favour of Bob. :: |
|
421 |
|
422 $ HGMERGE=internal:local hg merge |
|
423 $ hg commit -m merge |
|
424 |
|
425 .. _`bug`: https://bitbucket.org/marmoute/mutable-history/issue/48/ |
|
426 |
|
427 This is approximately what ``hg evolve`` would do in this |
|
428 circumstance, if not for that bug. One annoying difference is that |
|
429 Mercurial thinks the two divergent changesets are still divergent, |
|
430 which you can see with a simple revset query:: |
|
431 |
|
432 $ hg log -q -r 'divergent()' |
|
433 5:fc16901f4d7a |
|
434 6:694fd0f6b503 |
|
435 |
|
436 (That annoyance should go away when the bug is fixed.) |
|
437 |
|
438 Conclusion |
|
439 ---------- |
|
440 |
|
441 Mutable history is a powerful tool. Like a sharp knife, an experienced |
|
442 user can do wonderful things with it, much more wonderful than with a |
|
443 dull knife (never mind a rusty spoon). At the same time, an |
|
444 inattentive or careless user can do harm to himself or others. |
|
445 Mercurial with ``evolve`` goes to great lengths to limit the harm you |
|
446 can do by trying to handle all possible types of “troubled” |
|
447 changesets. But having a first-aid kit nearby does not excuse you from |
|
448 being careful with sharp knives. |
|
449 |
|
450 Mutable history shared across multiple repositories by a single |
|
451 developer is a natural extension of this model. Once you are used to |
|
452 using a single sharp knife on its own, it's pretty straightforward to |
|
453 chop onions and mushrooms using the same knife, or to alternate |
|
454 between two chopping boards with different knives. |
|
455 |
|
456 Mutable history shared by multiple developers is a scary place to go. |
|
457 Imagine a professional kitchen full of expert chefs tossing their |
|
458 favourite knives back and forth, with the occasional axe or chainsaw |
|
459 thrown in to spice things up. If you're confident that you *and your |
|
460 colleagues* can do it without losing a limb, go for it. But be sure to |
|
461 practice a lot first before you rely on it! |