|
1
|
<title>Branching, Forking, Merging, and Tagging</title> |
|
2
|
<h2>Background</h2> |
|
3
|
|
|
4
|
In a simple and perfect world, the development of a project would proceed |
|
5
|
linearly, as shown in Figure 1. |
|
6
|
|
|
7
|
<verbatim type="pikchr center toggle"> |
|
8
|
ALL: [circle rad 0.1in thickness 1.5px "1" |
|
9
|
arrow right 40% |
|
10
|
circle same "2" |
|
11
|
arrow same |
|
12
|
circle same "3" |
|
13
|
arrow same |
|
14
|
circle same "4"] |
|
15
|
box invis "Figure 1" big fit with .n at .3cm below ALL.s |
|
16
|
</verbatim> |
|
17
|
|
|
18
|
Each circle represents a check-in. For the sake of clarity, the check-ins |
|
19
|
are given small consecutive numbers. In a real system, of course, the |
|
20
|
check-in numbers would be long hexadecimal hashes since it is not possible |
|
21
|
to allocate collision-free sequential numbers in a distributed system. |
|
22
|
But as sequential numbers are easier to read, we will substitute them for |
|
23
|
the long hashes in this document. |
|
24
|
|
|
25
|
The arrows in Figure 1 show the evolution of a project. The initial |
|
26
|
check-in is 1. Check-in 2 is derived from 1. In other words, check-in 2 |
|
27
|
was created by making edits to check-in 1 and then committing those edits. |
|
28
|
We say that 2 is a <i>child</i> of 1 |
|
29
|
and that 1 is a <i>parent</i> of 2. |
|
30
|
Check-in 3 is derived from check-in 2, making |
|
31
|
3 a child of 2. We say that 3 is a <i>descendant</i> of both 1 and 2 and that 1 |
|
32
|
and 2 are both <i>ancestors</i> of 3. |
|
33
|
|
|
34
|
<h2 id="dag">DAGs</h2> |
|
35
|
|
|
36
|
The graph of check-ins is a |
|
37
|
[http://en.wikipedia.org/wiki/Directed_acyclic_graph | directed acyclic graph], |
|
38
|
commonly shortened to <i>DAG</i>. Check-in 1 is the <i>root</i> of the DAG |
|
39
|
since it has no ancestors. Check-in 4 is a <i>leaf</i> of the DAG since |
|
40
|
it has no descendants. (We will give a more precise definition later of |
|
41
|
"leaf.") |
|
42
|
|
|
43
|
Alas, reality often interferes with the simple linear development of a |
|
44
|
project. Suppose two programmers make independent modifications to check-in 2. |
|
45
|
After both changes are committed, the check-in graph looks like Figure 2: |
|
46
|
|
|
47
|
<verbatim type="pikchr center toggle"> |
|
48
|
ALL: [circle rad 0.1in thickness 1.5px "1" |
|
49
|
arrow right 40% |
|
50
|
circle same "2" |
|
51
|
circle same "3" at 2nd circle+(.4,.3) |
|
52
|
arrow from 2nd circle to 3rd circle chop |
|
53
|
circle same "4" at 2nd circle+(.4,-.3) |
|
54
|
arrow from 2nd circle to 4th circle chop] |
|
55
|
box invis "Figure 2" big fit with .n at .3cm below ALL.s |
|
56
|
</verbatim> |
|
57
|
|
|
58
|
The graph in Figure 2 has two leaves: check-ins 3 and 4. Check-in 2 has |
|
59
|
two children, check-ins 3 and 4. We call this state a <i>fork</i>. |
|
60
|
|
|
61
|
Fossil tries to prevent forks, primarily through its |
|
62
|
"[./concepts.wiki#workflow | autosync]" mechanism. |
|
63
|
|
|
64
|
Suppose two programmers named Alice and |
|
65
|
Bob are each editing check-in 2 separately. Alice finishes her edits |
|
66
|
and commits her changes first, resulting in check-in 3. When Bob later |
|
67
|
attempts to commit his changes, Fossil verifies that check-in 2 is still |
|
68
|
a leaf. Fossil sees that check-in 3 has occurred and aborts Bob's commit |
|
69
|
attempt with a message "would fork." This allows Bob to do a "fossil |
|
70
|
update" to pull in Alice's changes, merging them into his own |
|
71
|
changes. After merging, Bob commits check-in 4 as a child of check-in 3. |
|
72
|
The result is a linear graph as shown in Figure 1. This is how CVS |
|
73
|
works. This is also how Fossil works in autosync mode. |
|
74
|
|
|
75
|
But perhaps Bob is off-network when he does his commit, so he has no way |
|
76
|
of knowing that Alice has already committed her changes. Or, it could |
|
77
|
be that Bob has turned off "autosync" mode in Fossil. Or, maybe Bob |
|
78
|
just doesn't want to merge in Alice's changes before he has saved his |
|
79
|
own, so he forces the commit to occur using the "--allow-fork" option to |
|
80
|
the <b>[/help/commit | fossil commit]</b> command. For any of these |
|
81
|
reasons, two commits against check-in 2 have occurred, so the DAG now |
|
82
|
has two leaves. |
|
83
|
|
|
84
|
In such a condition, a person working with this repository has a |
|
85
|
dilemma: which version of the project is the "latest" in the sense of |
|
86
|
having the most features and the most bug fixes? When there is more |
|
87
|
than one leaf in the graph, you don't really know, which is why we |
|
88
|
would ideally prefer to have linear check-in graphs. |
|
89
|
|
|
90
|
Fossil resolves such problems using the check-in time on the leaves to |
|
91
|
decide which leaf to use as the parent of new leaves. When a branch is |
|
92
|
forked as in Figure 2, Fossil will choose check-in 4 as the parent for a |
|
93
|
later check-in 5, but <i>only</i> if it has sync'd that check-in down |
|
94
|
into the local repository. If autosync is disabled or the user is |
|
95
|
off-network when that fifth check-in occurs so that check-in 3 is the |
|
96
|
latest on that branch at the time within that clone of the repository, |
|
97
|
Fossil will make check-in 3 the parent of check-in 5! We show practical |
|
98
|
consequences of this [#bad-fork | later in this article]. |
|
99
|
|
|
100
|
Fossil also uses a forked branch's leaf check-in timestamps when |
|
101
|
checking out that branch: it gives you the fork with the latest |
|
102
|
check-in, which in turn selects which parent your next check-in will be |
|
103
|
a child of. This situation means development on that branch can fork |
|
104
|
into two independent lines of development, based solely on which branch |
|
105
|
tip is newer at the time the next user starts his work on it. |
|
106
|
|
|
107
|
Because of these potential problems, we strongly recommend that you do |
|
108
|
not intentionally create forks on long-lived shared working branches |
|
109
|
with "--allow-fork". (Prime example: trunk.) The inverse case — |
|
110
|
intentional forks on short-lived single-developer branches — is far |
|
111
|
easier to justify, since presumably the lone developer is never confused |
|
112
|
about why there are two or more leaves on that branch. Further |
|
113
|
justifications for intentional forking are [#forking | given below]. |
|
114
|
|
|
115
|
Let us return to Figure 2. To resolve such situations before they can |
|
116
|
become a real problem, Alice can use the <b>[/help/merge | fossil |
|
117
|
merge]</b> command to merge Bob's changes into her local copy of |
|
118
|
check-in 3. Without arguments, that command merges all leaves on the |
|
119
|
current branch. Alice can then verify that the merge is sensible and if |
|
120
|
so, commit the results as check-in 5. This results in a DAG as shown in |
|
121
|
Figure 3. |
|
122
|
|
|
123
|
<verbatim type="pikchr center toggle"> |
|
124
|
ALL: [circle rad 0.1in thickness 1.5px "1" |
|
125
|
arrow right 40% |
|
126
|
circle same "2" |
|
127
|
circle same "3" at 2nd circle+(.4,.3) |
|
128
|
arrow from 2nd circle to 3rd circle chop |
|
129
|
circle same "4" at 2nd circle+(.4,-.3) |
|
130
|
arrow from 2nd circle to 4th circle chop |
|
131
|
circle same "5" at 3rd circle+(.4,-.3) |
|
132
|
arrow from 3rd circle to 5th circle chop |
|
133
|
arrow dashed .03 from 4th circle to 5th circle chop] |
|
134
|
box invis "Figure 3" big fit with .n at .2cm below ALL.s |
|
135
|
</verbatim> |
|
136
|
|
|
137
|
Check-in 5 is a child of check-in 3 because it was created by editing |
|
138
|
check-in 3, but since check-in 5 also inherits the changes from check-in 4 by |
|
139
|
virtue of the merge, we say that check-in 5 is a <i>merge child</i> |
|
140
|
of check-in 4 and that it is a <i>direct child</i> of check-in 3. |
|
141
|
The graph is now back to a single leaf, check-in 5. |
|
142
|
|
|
143
|
We have already seen that if Fossil is in autosync mode then Bob would |
|
144
|
have been warned about the potential fork the first time he tried to |
|
145
|
commit check-in 4. If Bob had updated his local check-out to merge in |
|
146
|
Alice's check-in 3 changes, then committed, the fork would have |
|
147
|
never occurred. The resulting graph would have been linear, as shown |
|
148
|
in Figure 1. |
|
149
|
|
|
150
|
Realize that the graph of Figure 1 is a subset of Figure 3. If you hold your |
|
151
|
hand over the ④ in Figure 3, it looks |
|
152
|
exactly like Figure 1 except that the leaf has a different check-in |
|
153
|
number. That is just a notational difference: the two check-ins |
|
154
|
have exactly the same content. |
|
155
|
|
|
156
|
Inversely, Figure 3 is a |
|
157
|
superset of Figure 1. The check-in 4 of Figure 3 captures additional |
|
158
|
state which is omitted from Figure 1. Check-in 4 of Figure 3 holds a |
|
159
|
copy of Bob's local checkout before he merged in Alice's changes. That |
|
160
|
snapshot of Bob's changes, which is independent of Alice's changes, is |
|
161
|
omitted from Figure 1. |
|
162
|
|
|
163
|
Some people say that the development approach taken in |
|
164
|
Figure 3 is better because it preserves this extra intermediate state. |
|
165
|
Others say that the approach taken in Figure 1 is better because it is |
|
166
|
much easier to visualize linear development and because the |
|
167
|
merging happens automatically instead of as a separate manual step. We |
|
168
|
will not take sides in that debate. We will simply point out that |
|
169
|
Fossil enables you to do it either way. |
|
170
|
|
|
171
|
<h2 id="branching">The Alternative to Forking: Branching</h2> |
|
172
|
|
|
173
|
Having more than one leaf in the check-in DAG is called a "fork." This |
|
174
|
is usually undesirable and either avoided entirely, |
|
175
|
as in Figure 1, or else quickly resolved as shown in Figure 3. |
|
176
|
But sometimes, one does want to have multiple leaves. For example, a project |
|
177
|
might have one leaf that is the latest version of the project under |
|
178
|
development and another leaf that is the latest version that has been |
|
179
|
tested. |
|
180
|
When multiple leaves are desirable, we call this <i>branching</i> |
|
181
|
instead of <i>forking</i>: |
|
182
|
|
|
183
|
Figure 4 shows an example of a project where there are two branches, one |
|
184
|
for development work and another for testing. |
|
185
|
|
|
186
|
<verbatim type="pikchr center toggle"> |
|
187
|
ALL: [circle rad 0.1in thickness 1.5px fill white "1" |
|
188
|
arrow 40% |
|
189
|
C2: circle same "2" |
|
190
|
arrow same |
|
191
|
circle same "3" |
|
192
|
arrow same |
|
193
|
C5: circle same "5" |
|
194
|
arrow same |
|
195
|
C7: circle same "7" |
|
196
|
arrow same |
|
197
|
C8: circle same "8" |
|
198
|
arrow same |
|
199
|
C10: circle same "10" |
|
200
|
C4: circle same at 3rd circle-(0,.35) "4" |
|
201
|
C6: circle same at (1/2 way between C5 and C7,C4) "6" |
|
202
|
C9: circle same at (1/2 way between C8 and C10,C4) "9" |
|
203
|
arrow from C2 to C4 chop |
|
204
|
arrow from C4 to C6 chop |
|
205
|
arrow from C6 to C9 chop |
|
206
|
arrow dashed 0.03 from C6 to C7 chop |
|
207
|
arrow same from C9 to C10 |
|
208
|
layer = 0 |
|
209
|
box fill 0x9bcdfc color 0x9bcdfc wid (C10.e.x - C2.w.x) ht C6.height*1.5 at C6.c |
|
210
|
box invis "test" fit with .sw at last box.sw] |
|
211
|
box invis "Figure 4" big with .n at 0 below ALL.s |
|
212
|
</verbatim> |
|
213
|
|
|
214
|
Figure 4 diagrams the following scenario: the project starts and |
|
215
|
progresses to a point where (at check-in 2) |
|
216
|
it is ready to enter testing for its first release. |
|
217
|
In a real project, of course, there might be hundreds or thousands of |
|
218
|
check-ins before a project reaches this point, but for simplicity of |
|
219
|
presentation we will say that the project is ready after check-in 2. |
|
220
|
The project then splits into two branches that are used by separate |
|
221
|
teams. The testing team, using the blue branch, finds and fixes a few |
|
222
|
bugs with check-ins 6 and 9. Meanwhile, the development |
|
223
|
team, working on the top uncolored branch, |
|
224
|
is busy adding features for the second |
|
225
|
release. Of course, the development team would like to take advantage of |
|
226
|
the bug fixes implemented by the testing team, so periodically the |
|
227
|
changes in the test branch are merged into the dev branch. This is |
|
228
|
shown by the dashed merge arrows between check-ins 6 and 7 and between |
|
229
|
check-ins 9 and 10. |
|
230
|
|
|
231
|
In both Figures 2 and 4, check-in 2 has two children. In Figure 2, |
|
232
|
we call this a "fork." In diagram 4, we call it a "branch." What is |
|
233
|
the difference? As far as the internal Fossil data structures are |
|
234
|
concerned, there is no difference. The distinction is in the intent. |
|
235
|
In Figure 2, the fact that check-in 2 has multiple children is an |
|
236
|
accident that stems from concurrent development. In Figure 4, giving |
|
237
|
check-in 2 multiple children is a deliberate act. To a good |
|
238
|
approximation, we define forking to be by accident and branching to |
|
239
|
be by intent. Apart from that, they are the same. |
|
240
|
|
|
241
|
When the fork is intentional, it helps humans to understand what is |
|
242
|
going on if we <i>name</i> the forks. This is not essential to Fossil's |
|
243
|
internal data model, but humans have trouble working with long-lived |
|
244
|
branches identified only by the commit ID currently at its tip, being a |
|
245
|
long string of hex digits. Therefore, Fossil conflates two concepts: |
|
246
|
branching as intentional forking and the naming of forks as branches. |
|
247
|
They are in fact separate concepts, but since Fossil is intended to be |
|
248
|
used primarily by humans, we combine them in Fossil's human user |
|
249
|
interfaces. |
|
250
|
|
|
251
|
<p class="blockquote"> |
|
252
|
<b>Key Distinction:</b> A branch is a <i>named, intentional</i> fork. |
|
253
|
</p> |
|
254
|
|
|
255
|
Unnamed forks <i>may</i> be intentional, but most of the time, they're |
|
256
|
accidental and left unnamed. |
|
257
|
|
|
258
|
Fossil offers two primary ways to create named, intentional forks, |
|
259
|
a.k.a. branches. First: |
|
260
|
|
|
261
|
<pre> |
|
262
|
$ fossil commit --branch my-new-branch-name |
|
263
|
</pre> |
|
264
|
|
|
265
|
This is the method we recommend for most cases: it creates a branch as |
|
266
|
part of a check-in using the version in the current checkout directory |
|
267
|
as its basis. (This is normally the tip of the current branch, though |
|
268
|
it doesn't have to be. You can create a branch from an ancestor check-in |
|
269
|
on a branch as well.) After making this branch-creating |
|
270
|
check-in, your local working directory is switched to that branch, so |
|
271
|
that further check-ins occur on that branch as well, as children of the |
|
272
|
tip check-in on that branch. |
|
273
|
|
|
274
|
The second, more complicated option is: |
|
275
|
|
|
276
|
<pre> |
|
277
|
$ fossil branch new my-new-branch-name trunk |
|
278
|
$ fossil update my-new-branch-name |
|
279
|
$ fossil commit |
|
280
|
</pre> |
|
281
|
|
|
282
|
Not only is this three commands instead of one, the first of which is |
|
283
|
longer than the entire simpler command above, you must give the second command |
|
284
|
before creating any check-ins, because until you do, your local working |
|
285
|
directory remains on the same branch it was on at the time you issued |
|
286
|
the command, so that the commit would otherwise put the new material on |
|
287
|
the original branch instead of the new one. |
|
288
|
|
|
289
|
In addition to those problems, the second method is a violation of the |
|
290
|
[https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it|YAGNI |
|
291
|
Principle]. We recommend that you wait until you actually need the |
|
292
|
branch before you create it using the first command above. |
|
293
|
|
|
294
|
The "trunk" is just another named branch in Fossil. It is simply |
|
295
|
the default branch name for the first check-in and every check-in made as |
|
296
|
one of its direct descendants. It is special only in that it is Fossil's |
|
297
|
default when it has no better idea of which branch you mean. |
|
298
|
|
|
299
|
|
|
300
|
<h2 id="forking">Justifications For Forking</h2> |
|
301
|
|
|
302
|
The primary cases where forking is justified over branching are all when |
|
303
|
it is done purely in software in order to avoid losing information: |
|
304
|
|
|
305
|
<ol> |
|
306
|
<li><p id="offline">By Fossil itself when two users check in children to the same |
|
307
|
leaf of a branch, as in Figure 2. |
|
308
|
<br><br> |
|
309
|
If the fork occurs because |
|
310
|
autosync is disabled on one or both of the repositories or because |
|
311
|
the user doing the check-in has no network connection at the moment |
|
312
|
of the commit, Fossil has no way of knowing that it is creating a |
|
313
|
fork until the two repositories are later synchronized.</p></li> |
|
314
|
|
|
315
|
<li><p id="dist-clone">By Fossil when the cloning hierarchy is more |
|
316
|
than 2 levels deep. |
|
317
|
<br><br> |
|
318
|
[./sync.wiki|Fossil's synchronization protocol] is a two-party |
|
319
|
negotiation; syncs don't automatically propagate up the clone tree |
|
320
|
beyond that. Because of that, if you have a master repository and |
|
321
|
Alice clones it, then Bobby clones from Alice's repository, a |
|
322
|
check-in by Bobby that autosyncs with Alice's repo will <i>not</i> |
|
323
|
also autosync with the master repo. The master doesn't get a copy of |
|
324
|
Bobby's check-in until Alice <i>separately</i> syncs with the master. |
|
325
|
If Carol cloned from the master repo and checks something in that |
|
326
|
creates a fork relative to Bobby's check-in, the master repo won't |
|
327
|
know about that fork until Alice syncs her repo with the master. |
|
328
|
Even then, realize that Carol still won't know about the fork until |
|
329
|
she subsequently syncs with the master repo. |
|
330
|
<br><br> |
|
331
|
One way to deal with this is to just accept it as a fact of using a |
|
332
|
[https://en.wikipedia.org/wiki/Distributed_version_control|Distributed |
|
333
|
Version Control System] like Fossil. |
|
334
|
<br><br> |
|
335
|
Another option, which we recommend you consider carefully, is to |
|
336
|
make it a local policy that check-ins be made only directly against the master |
|
337
|
repo or one of its immediate child clones so that the autosync |
|
338
|
algorithm can do its job most effectively. Any clones deeper than |
|
339
|
that should be treated as read-only and thus get a copy of the new |
|
340
|
state of the world only once these central repos have negotiated |
|
341
|
that new state. This policy avoids a class of inadvertent fork you |
|
342
|
might not need to tolerate. Since [#bad-fork|forks on long-lived |
|
343
|
shared working branches can end up dividing a team's development |
|
344
|
effort], a team may easily justify this restriction on distributed |
|
345
|
cloning.</p></li> |
|
346
|
|
|
347
|
<li><p id="automation">You've automated Fossil, so you use |
|
348
|
<b>fossil commit --allow-fork</b> commands to prevent Fossil from |
|
349
|
refusing the check-in simply because it would create a fork. |
|
350
|
<br><br> |
|
351
|
If you are writing such a tool — e.g. a shell script to make |
|
352
|
multiple manipulations on a Fossil repo — it's better to make it |
|
353
|
smart enough to detect this condition and cope with it, such as |
|
354
|
by making a call to <b>[/help/update | fossil update]</b> |
|
355
|
and checking for a merge conflict. That said, if the alternative is |
|
356
|
losing information, you may feel justified in creating forks that an |
|
357
|
interactive user must later manually clean up with <b>fossil merge</b> |
|
358
|
commands.</p></li> |
|
359
|
</ol> |
|
360
|
|
|
361
|
That leaves only one case where we can recommend use of "--allow-fork" |
|
362
|
by interactive users: when you're working on a personal branch so that |
|
363
|
creating a dual-tipped branch isn't going to cause any other user an |
|
364
|
inconvenience or risk [#bad-fork | inadvertently forking the development |
|
365
|
effort]. In such a case, the lone developer working on that branch is |
|
366
|
not confused, since the fork in development is intentional. Sometimes it |
|
367
|
simply makes no sense to bother creating a name, cluttering the global |
|
368
|
branch namespace, simply to convert an intentional fork into a "branch." |
|
369
|
This is especially the case when the fork is short-lived. |
|
370
|
|
|
371
|
There's a common generalization of that case: you're a solo developer, |
|
372
|
so that the problems with branching vs forking simply don't matter. In |
|
373
|
that case, feel free to use "--allow-fork" as much as you like. |
|
374
|
|
|
375
|
|
|
376
|
<h2 id="fix">Fixing Forks</h2> |
|
377
|
|
|
378
|
If your local checkout is on a forked branch, you can usually fix a fork |
|
379
|
automatically with: |
|
380
|
|
|
381
|
<pre> |
|
382
|
$ fossil merge |
|
383
|
</pre> |
|
384
|
|
|
385
|
Normally you need to pass arguments to <b>fossil merge</b> to tell it |
|
386
|
what you want to merge into the current basis view of the repository, |
|
387
|
but without arguments, the command seeks out and fixes forks. |
|
388
|
|
|
389
|
|
|
390
|
<h2 id="tags">Tags And Properties</h2> |
|
391
|
|
|
392
|
Tags and properties are used in Fossil to help express the intent, and |
|
393
|
thus to distinguish between forks and branches. Figure 5 shows the |
|
394
|
same scenario as Figure 4 but with tags and properties added: |
|
395
|
|
|
396
|
<verbatim type="pikchr center toggle"> |
|
397
|
ALL: [arrowht = 0.07 |
|
398
|
C1: circle rad 0.1in thickness 1.5px fill white "1" |
|
399
|
arrow 40% |
|
400
|
C2: circle same "2" |
|
401
|
arrow same |
|
402
|
circle same "3" |
|
403
|
arrow same |
|
404
|
C5: circle same "5" |
|
405
|
arrow same |
|
406
|
C7: circle same "7" |
|
407
|
arrow same |
|
408
|
C8: circle same "8" |
|
409
|
arrow same |
|
410
|
C10: circle same "10" |
|
411
|
C4: circle same at 3rd circle-(0,.35) "4" |
|
412
|
C6: circle same at (1/2 way between C5 and C7,C4) "6" |
|
413
|
C9: circle same at (1/2 way between C8 and C10,C4) "9" |
|
414
|
arrow from C2 to C4 chop |
|
415
|
arrow from C4 to C6 chop |
|
416
|
arrow from C6 to C9 chop |
|
417
|
arrow dashed 0.03 from C6 to C7 chop |
|
418
|
arrow same from C9 to C10 |
|
419
|
layer = 0 |
|
420
|
box fill 0x9bcdfc color 0x9bcdfc wid (C10.e.x - C2.w.x) ht C6.height*1.5 at C6.c |
|
421
|
text " test" above ljust at last box.sw |
|
422
|
box fill lightgray "branch=trunk" "sym-trunk" fit with .ne at C1-(0.05,0.3); |
|
423
|
line color gray from last box.ne to C1 chop |
|
424
|
box same "branch=test" "sym-test" "bgcolor=blue" "cancel=sym-trunk" fit \ |
|
425
|
with .n at C4-(0,0.3) |
|
426
|
line color gray from last box.n to C4 chop |
|
427
|
box same "sym-release-1.0" "closed" fit with .n at C9-(0,0.3) |
|
428
|
line color gray from last box.n to C9 chop] |
|
429
|
box invis "Figure 5" big fit with .n at 0.2cm below ALL.s |
|
430
|
</verbatim> |
|
431
|
|
|
432
|
A <i>tag</i> is a name that is attached to a check-in. A |
|
433
|
<i>property</i> is a name/value pair. Internally, Fossil implements |
|
434
|
tags as properties with a NULL value. So, tags and properties really |
|
435
|
are much the same thing, and henceforth we will use the word "tag" |
|
436
|
to mean either a tag or a property. |
|
437
|
|
|
438
|
A tag can be a one-time tag, a propagating tag or a cancellation tag. |
|
439
|
A one-time tag only applies to the check-in to which it is attached. A |
|
440
|
propagating tag applies to the check-in to which it is attached and also |
|
441
|
to all direct descendants of that check-in. A <i>direct descendant</i> |
|
442
|
is a descendant through direct children. Tag propagation does not |
|
443
|
cross merges. Tag propagation also stops as soon |
|
444
|
as it encounters another check-in with the same tag. A cancellation tag |
|
445
|
is attached to a single check-in in order to either override a one-time |
|
446
|
tag that was previously placed on that same check-in, or to block |
|
447
|
tag propagation from an ancestor. |
|
448
|
|
|
449
|
The initial check-in of every repository has two propagating tags. In |
|
450
|
Figure 5, that initial check-in is check-in 1. The <b>branch</b> tag |
|
451
|
tells (by its value) what branch the check-in is a member of. |
|
452
|
The default branch is called "trunk." All tags that begin with "<b>sym-</b>" |
|
453
|
are symbolic name tags. When a symbolic name tag is attached to a |
|
454
|
check-in, that allows you to refer to that check-in by its symbolic |
|
455
|
name rather than by its hexadecimal hash name. When a symbolic name |
|
456
|
tag propagates (as does the <b>sym-trunk</b> tag) then referring to that |
|
457
|
name is the same as referring to the most recent check-in with that name. |
|
458
|
Thus the two tags on check-in 1 cause all descendants to be in the |
|
459
|
"trunk" branch and to have the symbolic name "trunk." |
|
460
|
|
|
461
|
Check-in 4 has a <b>branch</b> tag which changes the name of the branch |
|
462
|
to "test." The branch tag on check-in 4 propagates to check-ins 6 and 9. |
|
463
|
But because tag propagation does not follow merge links, the <b>branch=test</b> |
|
464
|
tag does not propagate to check-ins 7, 8, or 10. Note also that the |
|
465
|
<b>branch</b> tag on check-in 4 blocks the propagation of <b>branch=trunk</b> |
|
466
|
so that it cannot reach check-ins 6 or 9. This causes check-ins 4, 6, and |
|
467
|
9 to be in the "test" branch and all others to be in the "trunk" branch. |
|
468
|
|
|
469
|
Check-in 4 also has a <b>sym-test</b> tag, which gives the symbolic name |
|
470
|
"test" to check-ins 4, 6, and 9. Because tags do not propagate across |
|
471
|
merges, check-ins 7, 8, and 10 do not inherit the <b>sym-test</b> tag and |
|
472
|
are hence not known by the name "test." |
|
473
|
To prevent the <b>sym-trunk</b> tag from propagating from check-in 1 |
|
474
|
into check-ins 4, 6, and 9, there is a cancellation tag for |
|
475
|
<b>sym-trunk</b> on check-in 4. The net effect is that |
|
476
|
check-ins on the trunk go by the symbolic name of "trunk" and check-ins |
|
477
|
on the test branch go by the symbolic name "test." |
|
478
|
|
|
479
|
The <b>bgcolor=blue</b> tag on check-in 4 causes the background color |
|
480
|
of timelines to be blue for check-in 4 and its direct descendants. |
|
481
|
|
|
482
|
Figure 5 also shows two one-time tags on check-in 9. (The diagram does |
|
483
|
not make a graphical distinction between one-time and propagating tags.) |
|
484
|
The <b>sym-release-1.0</b> tag means that check-in 9 can be referred to |
|
485
|
using the more meaningful name "release-1.0." The <b>closed</b> tag means |
|
486
|
that check-in 9 is a "closed leaf." A closed leaf is a leaf that should |
|
487
|
never have direct children. |
|
488
|
|
|
489
|
<h2 id="bad-fork">How Can Forks Divide Development Effort?</h2> |
|
490
|
|
|
491
|
[#dist-clone|Above], we stated that forks carry a risk that development |
|
492
|
effort on a branch can be divided among the forks. It might not be |
|
493
|
immediately obvious why this is so. To see it, consider this swim lane |
|
494
|
diagram: |
|
495
|
|
|
496
|
<verbatim type="pikchr center toggle"> |
|
497
|
$laneh = 0.75 |
|
498
|
|
|
499
|
ALL: [ |
|
500
|
# Draw the lanes |
|
501
|
down |
|
502
|
box width 3.5in height $laneh fill 0xacc9e3 |
|
503
|
box same fill 0xc5d8ef |
|
504
|
box same as first box |
|
505
|
box same as 2nd box |
|
506
|
line from 1st box.sw+(0.2,0) up until even with 1st box.n \ |
|
507
|
"Alan" above aligned |
|
508
|
line from 2nd box.sw+(0.2,0) up until even with 2nd box.n \ |
|
509
|
"Betty" above aligned |
|
510
|
line from 3rd box.sw+(0.2,0) up until even with 3rd box.n \ |
|
511
|
"Charlie" above aligned |
|
512
|
line from 4th box.sw+(0.2,0) up until even with 4th box.n \ |
|
513
|
"Darlene" above aligned |
|
514
|
|
|
515
|
# fill in content for the Alice lane |
|
516
|
right |
|
517
|
A1: circle rad 0.1in at end of first line + (0.2,-0.2) \ |
|
518
|
fill white thickness 1.5px "1" |
|
519
|
arrow right 50% |
|
520
|
circle same "2" |
|
521
|
arrow right until even with first box.e - (0.65,0.0) |
|
522
|
ellipse "future" fit fill white height 0.2 width 0.5 thickness 1.5px |
|
523
|
A3: circle same at A1+(0.8,-0.3) "3" fill 0xc0c0c0 |
|
524
|
arrow from A1 to last circle chop "fork!" below aligned |
|
525
|
|
|
526
|
# content for the Betty lane |
|
527
|
B1: circle same as A1 at A1-(0,$laneh) "1" |
|
528
|
arrow right 50% |
|
529
|
circle same "2" |
|
530
|
arrow right until even with first ellipse.w |
|
531
|
ellipse same "future" |
|
532
|
B3: circle same at A3-(0,$laneh) "3" |
|
533
|
arrow right 50% |
|
534
|
circle same as A3 "4" |
|
535
|
arrow from B1 to 2nd last circle chop |
|
536
|
|
|
537
|
# content for the Charlie lane |
|
538
|
C1: circle same as A1 at B1-(0,$laneh) "1" |
|
539
|
arrow 50% |
|
540
|
circle same "2" |
|
541
|
arrow right 0.8in "goes" "offline" |
|
542
|
C5: circle same as A3 "5" |
|
543
|
arrow right until even with first ellipse.w \ |
|
544
|
"back online" above "pushes 5" below "pulls 3 & 4" below |
|
545
|
ellipse same "future" |
|
546
|
|
|
547
|
# content for the Darlene lane |
|
548
|
D1: circle same as A1 at C1-(0,$laneh) "1" |
|
549
|
arrow 50% |
|
550
|
circle same "2" |
|
551
|
arrow right until even with C5.w |
|
552
|
circle same "5" |
|
553
|
arrow 50% |
|
554
|
circle same as A3 "6" |
|
555
|
arrow right until even with first ellipse.w |
|
556
|
ellipse same "future" |
|
557
|
D3: circle same as B3 at B3-(0,2*$laneh) "3" |
|
558
|
arrow 50% |
|
559
|
circle same "4" |
|
560
|
arrow from D1 to D3 chop |
|
561
|
] |
|
562
|
box invis "Figure 6" big fit with .n at 0.2cm below ALL.s |
|
563
|
</verbatim> |
|
564
|
|
|
565
|
This is a happy, cooperating team. That is an important restriction on |
|
566
|
our example, because you must understand that this sort of problem can |
|
567
|
arise without any malice, selfishness, or willful ignorance in sight. |
|
568
|
All users on this diagram start out with the same view of the |
|
569
|
repository, cloned from the same master repo, and all of them are |
|
570
|
working toward their shared vision of a unified future. |
|
571
|
|
|
572
|
All users, except possibly Alan, start out with the same two initial |
|
573
|
check-ins in their local working clones, 1 & 2. It might be that Alan |
|
574
|
starts out with only check-in 1 in his local clone, but we'll deal with |
|
575
|
that detail later. |
|
576
|
|
|
577
|
It doesn't matter which branch this happy team is working on, only that |
|
578
|
our example makes the most sense if you think of it as a long-lived shared |
|
579
|
working branch like trunk. Each user makes |
|
580
|
only one check-in, shaded light gray in the diagram. |
|
581
|
|
|
582
|
<h3 id="bf-alan">Step 1: Alan</h3> |
|
583
|
|
|
584
|
Alan sets the stage for this problem by creating a |
|
585
|
fork from check-in 1 as check-in 3. How and why Alan did this doesn't |
|
586
|
affect what happens next, though we will walk through the possible cases |
|
587
|
and attempt to assign blame [#post-mortem|in the <i>post mortem</i>]. |
|
588
|
For now, you can assume that Alan did this out of unavoidable ignorance. |
|
589
|
|
|
590
|
<h3 id="bf-betty">Step 2: Betty</h3> |
|
591
|
|
|
592
|
Because Betty's local clone is autosyncing with |
|
593
|
the same upstream repository as Alan's clone, there are a number of ways |
|
594
|
she can end up seeing Alan's check-in 3 as the latest on that branch: |
|
595
|
|
|
596
|
<ol> |
|
597
|
<li><p>The working check-out directory she's using at the moment was |
|
598
|
on a different branch at the time Alan made check-in 3, so Fossil |
|
599
|
sees that as the tip at the time she switches her working directory |
|
600
|
to that branch with a <b>fossil update $BRANCH</b> command. (There is an |
|
601
|
implicit autosync in that command, if the option was enabled at the |
|
602
|
time of the update.)</p></li> |
|
603
|
|
|
604
|
<li><p>The same thing, only in a fresh checkout directory with a |
|
605
|
<b>[/help/open | fossil open $REPO $BRANCH]</b> command.</p></li> |
|
606
|
|
|
607
|
<li><p>Alan makes his check-in 3 while Betty has check-in 1 or 2 as |
|
608
|
the tip in her local clone, but because she's working with an |
|
609
|
autosync'd connection to the same upstream repository as Alan, on |
|
610
|
attempting what will become check-in 4, she gets the "would fork" |
|
611
|
message from <b>fossil commit</b>, so she dutifully updates her clone |
|
612
|
and tries again, moving her work to be a child of the new tip, |
|
613
|
check-in 3. (If she doesn't update, she creates a <i>second</i> |
|
614
|
fork, which simply complicates matters beyond what we need here for |
|
615
|
our illustration.)</p></li> |
|
616
|
</ol> |
|
617
|
|
|
618
|
For our purposes here, it doesn't really matter which one happened. All |
|
619
|
that matters is that Alan's check-in 3 becomes the parent of Betty's |
|
620
|
check-in 4 because it was the newest tip of the working branch at the |
|
621
|
time Betty does her check-in. |
|
622
|
|
|
623
|
<h3 id="bf-charlie">Step 3: Charlie</h3> |
|
624
|
|
|
625
|
Meanwhile, Charlie went offline after syncing |
|
626
|
his repo with check-in 2 as the latest on that branch. When he checks |
|
627
|
his changes in, it is as a child of 2, not of 4, because Charlie doesn't |
|
628
|
know about check-ins 3 & 4 yet. He does this at an absolute wall clock |
|
629
|
time <i>after</i> Alan and Betty made their check-ins, so when Charlie |
|
630
|
comes back online and pushes his check-in 5 to the master repository and |
|
631
|
learns about check-ins 3 and 4 during Fossil sync, Charlie inadvertently |
|
632
|
revives the other side of the fork. |
|
633
|
|
|
634
|
<h3 id="bf-darlene">Step 4: Darlene</h3> |
|
635
|
|
|
636
|
Darlene sees all of this, because she joins in |
|
637
|
on the work on this branch after Alan, Betty, and Charlie made their |
|
638
|
check-ins and pushed them to the master repository. She's taking one of |
|
639
|
the same three steps as we [#bf-betty|outlined for Betty above]. |
|
640
|
Regardless of her path to this view, it happens after Charlie pushed his |
|
641
|
check-in 5 to the master repo, so Darlene sees that as the latest on the |
|
642
|
branch, causing her work to be saved as a child of check-in 5, not of |
|
643
|
check-in 4, as it would if Charlie didn't come back online and sync |
|
644
|
before Darlene started work on that branch. |
|
645
|
|
|
646
|
<h3 id="post-mortem">Post Mortem</h3> |
|
647
|
|
|
648
|
The end result of all of this is that even though everyone makes only one check-in |
|
649
|
and no one disables autosync without genuine need, |
|
650
|
half of the check-ins end up on one side of the fork and half on |
|
651
|
the other. |
|
652
|
|
|
653
|
A future user — his mother calls him Edward, but please call him Eddie — |
|
654
|
can then join in on the work on this branch and end up on <i>either</i> side of |
|
655
|
the fork. If Eddie joins in with the state of the repository as drawn |
|
656
|
above, he'll end up on the top side of the fork, because check-in 6 is |
|
657
|
the latest, but if Alan or Betty makes a seventh check-in to that branch |
|
658
|
first, it will be as a child of check-in 4 since that's the version in |
|
659
|
their local check-out directories. Since that check-in 7 will then be the latest, |
|
660
|
Eddie will end up on the bottom side of the fork instead. |
|
661
|
|
|
662
|
In all of this, realize that neither side of the fork is obviously |
|
663
|
"correct." Every participant was doing the right thing by their own |
|
664
|
lights at the time they made their lone check-in. |
|
665
|
|
|
666
|
Who, then, is to blame? |
|
667
|
|
|
668
|
We can only blame the consequences of creating the fork on Alan if he |
|
669
|
did so on purpose, as by passing "--allow-fork" when creating a check-in |
|
670
|
on a shared working branch. Alan might have created it inadvertently by |
|
671
|
going offline while check-in 1 was the tip of the branch in his local |
|
672
|
clone, so that by the time he made his check-in 3, check-in 2 had |
|
673
|
arrived at the shared parent repository from someone else. (Francine?) |
|
674
|
When Alan rejoins the network and does an autosync, he learns about |
|
675
|
check-in 2. Since his #3 is already checked into his local clone because |
|
676
|
autosync was off or blocked, the sync creates an unavoidable fork. We |
|
677
|
can't blame either Alan or Francine here: they were both doing the right |
|
678
|
thing given their imperfect view of the state of the global situation. |
|
679
|
|
|
680
|
The same is true of Betty, Charlie, and Darlene. None of them tried to |
|
681
|
create a fork, and none of them chose a side in this fork to participate |
|
682
|
in. They just took Fossil's default and assumed it was correct. |
|
683
|
|
|
684
|
The only blame I can assign here is on any of these users who believed |
|
685
|
forks couldn't happen before this did occur, and I blame them only for |
|
686
|
their avoidable ignorance. (You, dear reader, have been ejected from |
|
687
|
that category by reading this very document.) Any time someone can work |
|
688
|
without getting full coordination from every other clone of the repo, |
|
689
|
forks are possible. Given enough time, they're all but inevitable. This |
|
690
|
is a general property of DVCSes, not just of Fossil. |
|
691
|
|
|
692
|
This sort of consequence is why forks on shared working branches are |
|
693
|
bad, which is why [./concepts.wiki#workflow|Fossil tries so hard to avoid them], why it warns you |
|
694
|
about it when they do occur, and why it makes it relatively [#fix|quick and |
|
695
|
painless to fix them] when they do occur. |
|
696
|
|
|
697
|
|
|
698
|
<h2>Review Of Terminology</h2> |
|
699
|
|
|
700
|
<dl> |
|
701
|
<dt><b>Branch</b></dt> |
|
702
|
<dd><p>A branch is a set of check-ins with the same value for their |
|
703
|
"branch" property.</p></dd> |
|
704
|
<dt><b>Leaf</b></dt> |
|
705
|
<dd><p>A leaf is a check-in with no children in the same branch.</p></dd> |
|
706
|
<dt><b>Closed Leaf</b></dt> |
|
707
|
<dd><p>A closed leaf is any leaf with the <b>closed</b> tag. These leaves |
|
708
|
are intended to never be extended with descendants and hence are omitted |
|
709
|
from lists of leaves in the command-line and web interface.</p></dd> |
|
710
|
<dt><b>Open Leaf</b></dt> |
|
711
|
<dd><p>A open leaf is a leaf that is not closed.</p></dd> |
|
712
|
<dt><b>Fork</b></dt> |
|
713
|
<dd><p>A fork is when a check-in has two or more direct (non-merge) |
|
714
|
children in the same branch.</p></dd> |
|
715
|
<dt><b>Branch Point</b></dt> |
|
716
|
<dd><p>A branch point occurs when a check-in has two or more direct (non-merge) |
|
717
|
children in different branches. A branch point is similar to a fork, |
|
718
|
except that the children are in different branches.</p></dd> |
|
719
|
</dl> |
|
720
|
|
|
721
|
Check-in 4 of Figure 3 is not a leaf because it has a child (check-in 5) |
|
722
|
in the same branch. Check-in 9 of Figure 5 also has a child (check-in 10) |
|
723
|
but that child is in a different branch, so check-in 9 is a leaf. Because |
|
724
|
of the <b>closed</b> tag on check-in 9, it is a closed leaf. |
|
725
|
|
|
726
|
Check-in 2 of Figure 3 is considered a "fork" |
|
727
|
because it has two children in the same branch. Check-in 2 of Figure 5 |
|
728
|
also has two children, but each child is in a different branch, hence in |
|
729
|
Figure 5, check-in 2 is considered a "branch point." |
|
730
|
|
|
731
|
<h2>Differences With Other DVCSes</h2> |
|
732
|
|
|
733
|
<h3 id="single">Single DAG</h3> |
|
734
|
|
|
735
|
Fossil keeps all check-ins on a single DAG. Branches are identified with |
|
736
|
tags. This means that check-ins can be freely moved between branches |
|
737
|
simply by altering their tags. |
|
738
|
|
|
739
|
Most other DVCSes maintain a separate DAG for each branch. |
|
740
|
|
|
741
|
<h3 id="unique">Branch Names Need Not Be Unique</h3> |
|
742
|
|
|
743
|
Fossil does not require that branch names be unique, as in some VCSes, |
|
744
|
most notably Git. Just as with unnamed branches (which we call forks) |
|
745
|
Fossil resolves such ambiguities using the timestamps on the latest |
|
746
|
check-in in each branch. If you have two branches named "foo" and you say |
|
747
|
<b>fossil update foo</b>, you get the tip of the "foo" branch with the most |
|
748
|
recent check-in. |
|
749
|
|
|
750
|
This fact is helpful because it means you can reuse branch names, which |
|
751
|
is especially useful with utility branches. There are several of these |
|
752
|
in the SQLite and Fossil repositories: "broken-build," "declined," |
|
753
|
"mistake," etc. As you might guess from these names, such branch names |
|
754
|
are used in renaming the tip of one branch to shunt it off away from the |
|
755
|
mainline of that branch due to some human error. (See |
|
756
|
<b>[/help/amend | fossil |
|
757
|
amend]</b> and the Fossil UI check-in amendment features.) This is a |
|
758
|
workaround for Fossil's [./shunning.wiki|normal inability to forget |
|
759
|
history]: we usually don't want to actually <i>remove</i> history, but |
|
760
|
would like to sometimes set some of it aside under a new label. |
|
761
|
|
|
762
|
Because some VCSes can't cope with duplicate branch names, Fossil |
|
763
|
collapses such names down on export using the same time stamp based |
|
764
|
arbitration logic, so that only the branch with the newest check-in gets |
|
765
|
the branch name in the export. |
|
766
|
|
|
767
|
All of the above is true of tags in general, not just branches. |
|
768
|
|