|
1
|
# Git to Fossil Translation Guide |
|
2
|
|
|
3
|
## Introduction |
|
4
|
|
|
5
|
Fossil shares many similarities with Git. In many cases, the |
|
6
|
sub-commands are identical: [`fossil bisect`][fbis] does essentially the |
|
7
|
same thing as [`git bisect`][gbis], for example. |
|
8
|
|
|
9
|
Yet, Fossil is not merely Git with a bunch of commands misspelled. If |
|
10
|
that were the case, we could give you a two-column translation table |
|
11
|
which would tell you [how to say things like “`git reset --hard HEAD`”](#reset) in |
|
12
|
this funny ol’ Fossil dialect of Git and be done. The purpose of this |
|
13
|
document is to cover all the cases where there is no simple 1:1 mapping, |
|
14
|
usually because of intentional design differences in Fossil that prevent |
|
15
|
it from working exactly like Git. We choose to explain these differences |
|
16
|
since to understand the conversion, you need to know why each difference |
|
17
|
exists. |
|
18
|
|
|
19
|
We focus on practical command examples here, leaving discussions of the |
|
20
|
philosophical underpinnings that drive these command differences to [another |
|
21
|
document][fvg]. The [case studies](#cs1) do get a bit philosophical, but |
|
22
|
it is with the aim of illustrating how these Fossil design differences |
|
23
|
cause Fossil to behave materially differently from Git in everyday |
|
24
|
operation. |
|
25
|
|
|
26
|
We present this from the perspective of Git users moving to Fossil, but |
|
27
|
it is also possible to read this document as a Fossil user who speaks |
|
28
|
only pidgin Git, who may often have questions of the form, “Now how do I |
|
29
|
do X in Git again?” |
|
30
|
|
|
31
|
This document’s authors are intimately familiar with Fossil, so it is |
|
32
|
difficult for us to anticipate the perspective of people who are |
|
33
|
intimately familiar with Git. If you have a lot of prior Git |
|
34
|
experience, we welcome your contributions and questions on the [Fossil |
|
35
|
Forum][ffor]. |
|
36
|
|
|
37
|
While we do try to explain Fossil-specific terminology inline here |
|
38
|
as-needed, you may find it helpful to skim [the Fossil glossary][gloss]. |
|
39
|
It will give you another take on our definitions here, and it may help |
|
40
|
you to understand some of the other Fossil docs better. |
|
41
|
|
|
42
|
[fbis]: /help/bisect |
|
43
|
[gbis]: https://git-scm.com/docs/git-bisect |
|
44
|
[ffor]: https://fossil-scm.org/forum |
|
45
|
[fvg]: ./fossil-v-git.wiki |
|
46
|
|
|
47
|
|
|
48
|
<a id="mwd"></a> |
|
49
|
## Repositories and Checkouts Are Distinct |
|
50
|
|
|
51
|
A repository and a check-out are distinct in Fossil, allowing them to be |
|
52
|
stored in separate directory trees, whereas the two are commingled by |
|
53
|
default with Git, with the repository stored in a `.git` subdirectory |
|
54
|
underneath your working directory. This difference shows up in several |
|
55
|
separate places when it comes to moving from Git to Fossil. |
|
56
|
|
|
57
|
|
|
58
|
|
|
59
|
#### <a id="cwork" name="scw"></a> Checkout Workflows |
|
60
|
|
|
61
|
A Fossil repository is an SQLite database storing the entire history of a |
|
62
|
project. It is not normally stored inside the working tree. |
|
63
|
A Fossil working tree — [also called a check-out](./glossary.md#check-out) — is a directory |
|
64
|
that contains a snapshot of your project that you are currently working |
|
65
|
on, extracted for you from the repository database file by the `fossil` |
|
66
|
program. |
|
67
|
|
|
68
|
There are ways to |
|
69
|
[emulate the Fossil working style in Git](#worktree), but because they’re not |
|
70
|
designed into the core concept of the tool, Git tutorials usually |
|
71
|
advocate a switch-in-place working mode instead, so that is how most |
|
72
|
users end up working with Git. Contrast [Fossil’s check-out workflow |
|
73
|
document][ckwf] to see the practical differences. |
|
74
|
|
|
75
|
There is one Git-specific detail we wish to add beyond what that |
|
76
|
document already covers. This command: |
|
77
|
|
|
78
|
git checkout some-branch |
|
79
|
|
|
80
|
…is best given as: |
|
81
|
|
|
82
|
fossil update some-branch |
|
83
|
|
|
84
|
…in Fossil. There is a [`fossil checkout`][co] command, but it has |
|
85
|
[several differences](./co-vs-up.md) that make it less broadly useful |
|
86
|
than [`fossil update`][up] in everyday operation, so we recommend that |
|
87
|
Git users moving to Fossil develop a habit of typing `fossil up` rather |
|
88
|
than `fossil checkout`. That said, one of those differences does match |
|
89
|
up with Git users’ expectations: `fossil checkout` doesn’t pull changes |
|
90
|
from the remote repository into the local clone as `fossil update` does. |
|
91
|
We think this is less broadly useful, but that’s the subject of the next |
|
92
|
section. |
|
93
|
|
|
94
|
[ckwf]: ./ckout-workflows.md |
|
95
|
[co]: /help/checkout |
|
96
|
|
|
97
|
|
|
98
|
#### <a id="pullup"></a> Update vs Pull |
|
99
|
|
|
100
|
The closest equivalent to [`git pull`][gpull] is not |
|
101
|
[`fossil pull`][fpull], but in fact [`fossil up`][up]. |
|
102
|
|
|
103
|
This is because |
|
104
|
Fossil tends to follow the CVS command design: `cvs up` pulls |
|
105
|
changes from the central CVS repository and merges them into the local |
|
106
|
working directory, so that’s what `fossil up` does, too. (This design |
|
107
|
choice also tends to make Fossil feel comfortable to Subversion |
|
108
|
expatriates.) |
|
109
|
|
|
110
|
The `fossil pull` command is simply the reverse of |
|
111
|
`fossil push`, so that `fossil sync` [is functionally equivalent |
|
112
|
to](./sync.wiki#sync): |
|
113
|
|
|
114
|
fossil push ; fossil pull |
|
115
|
|
|
116
|
There is no implicit “and update the local working directory” step in Fossil’s |
|
117
|
push, pull, or sync commands, as there is with `git pull`. |
|
118
|
|
|
119
|
Someone coming from the Git perspective may perceive that `fossil up` |
|
120
|
has two purposes: |
|
121
|
|
|
122
|
* Without the optional `VERSION` argument, it updates the working |
|
123
|
check-out to the tip of the current branch, as `git pull` does. |
|
124
|
|
|
125
|
* Given a `VERSION` argument, it updates to the named version. If that’s the |
|
126
|
name of a branch, it updates to the *tip* of that branch, as |
|
127
|
`git checkout BRANCH` does. |
|
128
|
|
|
129
|
In fact, these are the same operation, so they’re the same command in |
|
130
|
Fossil. The first form simply allows the `VERSION` to be implicit: the |
|
131
|
tip of the current branch. |
|
132
|
|
|
133
|
We think this is a more sensible command design than `git pull` vs |
|
134
|
`git checkout`. ([…vs `git checkout` vs `git checkout`!][gcokoan]) |
|
135
|
|
|
136
|
[fpull]: /help/pull |
|
137
|
[gpull]: https://git-scm.com/docs/git-pull |
|
138
|
[gcokoan]: https://stevelosh.com/blog/2013/04/git-koans/#s2-one-thing-well |
|
139
|
|
|
140
|
|
|
141
|
#### <a id="close" name="dotfile"></a> Closing a Check-Out |
|
142
|
|
|
143
|
The [`fossil close`][close] command dissociates a check-out directory from the |
|
144
|
Fossil repository database, _nondestructively_ inverting [`fossil open`][open]. |
|
145
|
(Contrast Git’s [closest alternative](#worktree), `git worktree remove`, which *is* |
|
146
|
destructive!) This Fossil command does not |
|
147
|
remove the managed files, and unless you give the `--force` |
|
148
|
option, it won’t let you close the check-out with uncommitted changes to |
|
149
|
those managed files. |
|
150
|
|
|
151
|
The `close` command also refuses to run without `--force` when you have |
|
152
|
certain other precious per-checkout data that Fossil stores in the |
|
153
|
`.fslckout` file at the root of a check-out directory. This is an SQLite |
|
154
|
database that keeps track of local state such as what version you have |
|
155
|
checked out, the contents of the [stash] for that working directory, the |
|
156
|
[undo] buffers, per-checkout [settings][set], and so forth. The stash |
|
157
|
and undo buffers are considered precious uncommitted changes, |
|
158
|
so you have to force Fossil to discard these as part of closing the |
|
159
|
check-out. |
|
160
|
|
|
161
|
Thus, `.fslckout` is not the same thing as `.git`! |
|
162
|
|
|
163
|
In native Windows builds of Fossil — that is, excluding Cygwin and WSL |
|
164
|
builds, which follow POSIX conventions — this file is called `_FOSSIL_` |
|
165
|
instead to get around the historical 3-character extension limit with |
|
166
|
certain legacy filesystems. |
|
167
|
|
|
168
|
Closing a check-out directory is a rare operation. One use case |
|
169
|
is that you’re about to delete the directory, so you want Fossil to forget about it |
|
170
|
for the purposes of commands like [`fossil all`][all]. Even that isn’t |
|
171
|
necessary, because Fossil will detect that this has happened and forget |
|
172
|
the working directory for you. |
|
173
|
|
|
174
|
[all]: /help/all |
|
175
|
|
|
176
|
|
|
177
|
#### <a id="worktree"></a> Git Worktrees |
|
178
|
|
|
179
|
There are at least three different ways to get [Fossil-style multiple |
|
180
|
check-out directories][mcw] with Git. |
|
181
|
|
|
182
|
The old way is to simply symlink the `.git` directory between working |
|
183
|
trees: |
|
184
|
|
|
185
|
mkdir ../foo-branch |
|
186
|
ln -s ../actual-clone-dir/.git . |
|
187
|
git checkout foo-branch |
|
188
|
|
|
189
|
The symlink trick has a number of problems, the largest being that |
|
190
|
symlinks weren’t available on Windows until Vista, and until the Windows |
|
191
|
10 Creators Update was released in spring of 2017, you had to be an |
|
192
|
Administrator to use the feature besides. ([Source][wsyml]) Git 2.5 solved |
|
193
|
this problem back when Windows XP was Microsoft’s current offering |
|
194
|
by adding the `git-worktree` command: |
|
195
|
|
|
196
|
git worktree add ../foo-branch foo-branch |
|
197
|
cd ../foo-branch |
|
198
|
|
|
199
|
That is approximately equivalent to this in Fossil: |
|
200
|
|
|
201
|
mkdir ../foo-branch |
|
202
|
cd ../foo-branch |
|
203
|
fossil open /path/to/repo.fossil foo-branch |
|
204
|
|
|
205
|
The Fossil alternative is wordier, but since this tends to be one-time setup, |
|
206
|
not something you do everyday, the overhead is insignificant. This author keeps a “scratch” check-out |
|
207
|
for cases where it’s inappropriate to reuse the “trunk” check-out, |
|
208
|
isolating all of my expedient switch-in-place actions to that one |
|
209
|
working directory. Since the other peer check-outs track long-lived |
|
210
|
branches, and that set rarely changes once a development machine is set |
|
211
|
up, I rarely pay the cost of these wordier commands. |
|
212
|
|
|
213
|
That then leads us to the closest equivalent in Git to [closing a Fossil |
|
214
|
check-out](#close): |
|
215
|
|
|
216
|
git worktree remove . |
|
217
|
|
|
218
|
Note, however, that unlike `fossil close`, once the Git command |
|
219
|
determines that there are no uncommitted changes, it blows away all of |
|
220
|
the checked-out files! Fossil’s alternative is shorter, easier to |
|
221
|
remember, and safer. |
|
222
|
|
|
223
|
There’s another way to get Fossil-like separate worktrees in Git: |
|
224
|
|
|
225
|
git clone --separate-git-dir repo.git https://example.com/repo |
|
226
|
|
|
227
|
This allows you to have your Git repository directory entirely separate |
|
228
|
from your working tree, with `.git` in the check-out directory being a |
|
229
|
file that points to `../repo.git`, in this example. |
|
230
|
|
|
231
|
[mcw]: ./ckout-workflows.md#mcw |
|
232
|
[wsyml]: https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ |
|
233
|
|
|
234
|
|
|
235
|
#### <a id="iip"></a> Init in Place |
|
236
|
|
|
237
|
To illustrate the differences that Fossil’s separation of repository |
|
238
|
from working directory creates in practice, consider this common Git “init in place” |
|
239
|
method for creating a new repository from an existing tree of files, |
|
240
|
perhaps because you are placing that project under version control for |
|
241
|
the first time: |
|
242
|
|
|
243
|
cd long-established-project |
|
244
|
git init |
|
245
|
git add * |
|
246
|
git commit -m "Initial commit of project." |
|
247
|
|
|
248
|
The closest equivalent in Fossil is: |
|
249
|
|
|
250
|
cd long-established-project |
|
251
|
fossil init .fsl |
|
252
|
fossil open --force .fsl |
|
253
|
fossil add * |
|
254
|
fossil ci -m "Initial commit of project." |
|
255
|
|
|
256
|
Note that unlike in Git, you can abbreviate the “`commit`” command in |
|
257
|
Fossil as “`ci`” for compatibility with CVS, Subversion, etc. |
|
258
|
|
|
259
|
This creates a `.fsl` repo DB at the root of the project check-out to |
|
260
|
emulate the `.git` repo dir. We have to use the `--force` flag on |
|
261
|
opening the new repo because Fossil expects you to open a repo into an |
|
262
|
empty directory in order to avoid spamming the contents of a repo over |
|
263
|
an existing directory full of files. Here, we know the directory |
|
264
|
contains files that will soon belong in the repository, though, so we |
|
265
|
override this check. From then on, Fossil works like Git, for the |
|
266
|
purposes of this example. |
|
267
|
|
|
268
|
We’ve drawn this example to create a tight parallel between Fossil and |
|
269
|
Git, not to commend this `.fsl`-at-project-root trick to you. A better |
|
270
|
choice would be `~/museum/home/long-established-project.fossil`, if |
|
271
|
you’re following [the directory scheme exemplified in the glossary](./glossary.md#repository). That said, it |
|
272
|
does emphasize an earlier point: Fossil doesn’t care where you put the |
|
273
|
repo DB file or what you name it. |
|
274
|
|
|
275
|
|
|
276
|
[clone]: /help/clone |
|
277
|
[close]: /help/close |
|
278
|
[gloss]: ./glossary.md |
|
279
|
[open]: /help/open |
|
280
|
[set]: /help/setting |
|
281
|
[server]: /help/server |
|
282
|
[stash]: /help/stash |
|
283
|
[undo]: /help/undo |
|
284
|
|
|
285
|
|
|
286
|
## <a id="log"></a> Fossil’s Timeline Is the “Log” |
|
287
|
|
|
288
|
Git users often need to use the `git log` command to dig linearly through |
|
289
|
commit histories due to its [weak data model][wdm], giving [O(n) |
|
290
|
performance][ocomp]. |
|
291
|
|
|
292
|
Fossil parses a huge amount of information out of commits that allow it |
|
293
|
to produce its [timeline CLI][tlc] and [its `/timeline` web view][tlw] |
|
294
|
using indexed SQL lookups, which generally have the info you would have |
|
295
|
to manually extract from `git log`, produced much more quickly than Git |
|
296
|
can, all else being equal: operations over [SQLite’s B-tree data structures][btree] |
|
297
|
generally run in O(log n) time, faster than O(n) for equal *n* when the |
|
298
|
constants are equal. |
|
299
|
|
|
300
|
Yet the constants are *not* equal because Fossil |
|
301
|
reads from a single disk file rather than visit potentially many |
|
302
|
files in sequence as Git must, so the OS’s buffer cache can result in |
|
303
|
[still better performance][35pct]. |
|
304
|
|
|
305
|
Unlike Git’s log, Fossil’s timeline shows info across all branches by |
|
306
|
default, a feature for maintaining better situational awareness. |
|
307
|
It is possible to restrict the timeline to a single branch using `fossil timeline -b`. |
|
308
|
Similarly, to restrict the timeline using the web UI equivalent, |
|
309
|
click the name of a branch on the `/timeline` or `/brlist` page. (Or |
|
310
|
manually, by adding the `r=` query parameter.) Note that even in this |
|
311
|
case, the Fossil timeline still shows other branches where they interact |
|
312
|
with the one you’ve referenced in this way; again, better situational |
|
313
|
awareness. |
|
314
|
|
|
315
|
|
|
316
|
#### <a id="emu-log"></a> Emulating `git log` |
|
317
|
|
|
318
|
If you truly need a backwards-in-time-only view of history in Fossil to |
|
319
|
emulate `git log`, this is as close as you can currently come: |
|
320
|
|
|
321
|
fossil timeline parents current |
|
322
|
|
|
323
|
Again, though, this isn’t restricted to a single branch, as `git log` |
|
324
|
is. |
|
325
|
|
|
326
|
Another useful rough equivalent is: |
|
327
|
|
|
328
|
git log --raw |
|
329
|
fossil time -v |
|
330
|
|
|
331
|
This shows what changed in each version, though Fossil’s view is more a |
|
332
|
summary than a list of raw changes. To dig deeper into single commits, |
|
333
|
you can use Fossil’s [`info` command][infoc] or its [`/info` view][infow]. |
|
334
|
|
|
335
|
Inversely, you may more exactly emulate the default `fossil timeline` |
|
336
|
output with `git log --name-status`. |
|
337
|
|
|
338
|
|
|
339
|
#### <a id="whatchanged"></a> What Changed? |
|
340
|
|
|
341
|
A related — though deprecated — command is `git whatchanged`, which gives results similar to |
|
342
|
`git log --raw`, so we cover it here. |
|
343
|
|
|
344
|
Though there is no `fossil whatchanged` command, the same sort of |
|
345
|
information is available. For example, to pull the current changes from |
|
346
|
the remote repository and then inspect them before updating the local |
|
347
|
working directory, you might say this in Git: |
|
348
|
|
|
349
|
git fetch |
|
350
|
git whatchanged ..@{u} |
|
351
|
|
|
352
|
…which you can approximate in Fossil as: |
|
353
|
|
|
354
|
fossil pull |
|
355
|
fossil up -n |
|
356
|
fossil diff --from tip |
|
357
|
|
|
358
|
To invert the `diff` to show a more natural patch, the command needs to |
|
359
|
be a bit more complicated, since you can’t currently give `--to` |
|
360
|
without `--from`. |
|
361
|
|
|
362
|
fossil diff --from current --to tip |
|
363
|
|
|
364
|
Rather than use the “dry run” form of [the `update` command][up], you can |
|
365
|
say: |
|
366
|
|
|
367
|
fossil timeline after current |
|
368
|
|
|
369
|
…or if you want to restrict the output to the current branch: |
|
370
|
|
|
371
|
fossil timeline descendants current |
|
372
|
|
|
373
|
|
|
374
|
#### <a id="ckin-names"></a> Symbolic Check-In Names |
|
375
|
|
|
376
|
Note the use of [human-readable symbolic version names][scin] in Fossil |
|
377
|
rather than [Git’s cryptic notations][gcn]. |
|
378
|
|
|
379
|
For a more dramatic example of this, let us ask Git, “What changed since the |
|
380
|
beginning of last month?” being October 2020 as I write this: |
|
381
|
|
|
382
|
git log master@{2020-10-01}..HEAD |
|
383
|
|
|
384
|
That’s rather obscure! Fossil answers the same question with a simpler |
|
385
|
command: |
|
386
|
|
|
387
|
fossil timeline after 2020-10-01 |
|
388
|
|
|
389
|
You may need to add `-n 0` to bypass the default output limit of |
|
390
|
`fossil timeline`, 20 entries. Without that, this command reads |
|
391
|
almost like English. |
|
392
|
|
|
393
|
Some Git users like to write commands like the above so: |
|
394
|
|
|
395
|
git log @{2020-10-01}..@ |
|
396
|
|
|
397
|
Is that better? “@” now means two different things: an at-time reference |
|
398
|
and a shortcut for `HEAD`! |
|
399
|
|
|
400
|
If you are one of those that like short commands, Fossil’s method is |
|
401
|
less cryptic: it lets you shorten words in most cases up to the point |
|
402
|
that they become ambiguous. For example, you may abbreviate the last |
|
403
|
`fossil` command in the prior section: |
|
404
|
|
|
405
|
fossil tim d c |
|
406
|
|
|
407
|
…beyond which the `timeline` command becomes ambiguous with `ticket`. |
|
408
|
|
|
409
|
Some Fossil users employ shell aliases, symlinks, or scripts to shorten |
|
410
|
the command still further: |
|
411
|
|
|
412
|
alias f=fossil |
|
413
|
f tim d c |
|
414
|
|
|
415
|
Granted, that’s rather obscure, but you you can also choose something |
|
416
|
intermediate like “`f time desc curr`”, which is reasonably clear. |
|
417
|
|
|
418
|
[35pct]: https://www.sqlite.org/fasterthanfs.html |
|
419
|
[btree]: https://sqlite.org/btreemodule.html |
|
420
|
[gcn]: https://git-scm.com/docs/gitrevisions |
|
421
|
[infoc]: /help/info |
|
422
|
[infow]: /help/www/info |
|
423
|
[ocomp]: https://www.bigocheatsheet.com/ |
|
424
|
[tlc]: /help/timeline |
|
425
|
[tlw]: /help/www/timeline |
|
426
|
[up]: /help/update |
|
427
|
[wdm]: ./fossil-v-git.wiki#durable |
|
428
|
|
|
429
|
|
|
430
|
## <a id="dhead"></a> Detached HEAD State |
|
431
|
|
|
432
|
The SQL indexes in Fossil which we brought up above have a useful |
|
433
|
side benefit: you cannot have a [detached HEAD state][gdh] in Fossil, |
|
434
|
the source of untold pain and data loss in Git. It simply cannot be done |
|
435
|
in Fossil, because the indexes always let us find our way back into the |
|
436
|
hash tree. |
|
437
|
|
|
438
|
|
|
439
|
## <a id="slcom"></a> Summary Line Convention in Commit Comments |
|
440
|
|
|
441
|
The Git convention of a [length-limited summary line][lsl] at the start |
|
442
|
of commit comments is not enforced or obeyed by default in Fossil. |
|
443
|
However, there is a setting under Admin → Timeline → “Truncate comment |
|
444
|
at first blank line (Git-style)” to change this for `/timeline` |
|
445
|
displays. Alternately, you could enable the “Allow block-markup in |
|
446
|
timeline” setting under Admin → Timeline, then apply [local skin |
|
447
|
customizations][cskin] to put that first comment in bold or whatever |
|
448
|
suits. |
|
449
|
|
|
450
|
Because this isn’t a typical Fossil convention, you’re likely to find |
|
451
|
other odd differences between it and Git-based infrastructure. For |
|
452
|
instance, Vim doesn’t ship with syntax support for Fossil commit |
|
453
|
messages if you set `EDITOR=vim` in your shell environment, so you won’t |
|
454
|
get over-limit highlighting for first-line text beyond the 50th |
|
455
|
character and such, because it doesn’t recognize Fossil commit messages |
|
456
|
and apply similar rules as to Git commit messages. |
|
457
|
|
|
458
|
[cskin]: ./customskin.md |
|
459
|
[lsl]: https://chris.beams.io/posts/git-commit/#limit-50 |
|
460
|
|
|
461
|
|
|
462
|
<a id="autocommit"></a> |
|
463
|
## Fossil Never Auto-Commits |
|
464
|
|
|
465
|
There are several features in Git besides its `commit` command that |
|
466
|
produce a new commit to the repository, and by default, they do it |
|
467
|
without prompting or even asking for a commit message. These include |
|
468
|
Git’s [`rebase`](#rebase), `merge`, and [`cherrypick`](#cpickrev) |
|
469
|
commands, plus the [commit splitting](#comsplit) sub-feature |
|
470
|
“`git commit -p`”. |
|
471
|
|
|
472
|
Fossil never does this, on firm philosophical grounds: we wish to be |
|
473
|
able to test that each potentially repository-changing command does not |
|
474
|
break anything _before_ freezing it immutably into the [Merkle |
|
475
|
tree](./blockchain.md). Where Fossil has equivalent commands, they |
|
476
|
modify the checkout tree alone, requiring a separate `commit` command |
|
477
|
afterward, withheld until the user has satisfied themselves that the |
|
478
|
command’s result is correct. |
|
479
|
|
|
480
|
We believe this is the main reason Git lacks an [autosync](#autosync) |
|
481
|
feature: making push a manual step gives the user a chance to rewrite |
|
482
|
history after triggering one of these autocommits locally, should the |
|
483
|
automatic commit fail to work out as expected. Fossil chooses the |
|
484
|
inverse path under the philosophy that commits are *commitments,* not |
|
485
|
something you’re allowed to go back and rewrite later. |
|
486
|
|
|
487
|
This is also why there is no automatic commit message writing feature in |
|
488
|
Fossil, as in these autocommit-triggering Git commands. The user is |
|
489
|
meant to write the commit message by hand after they are sure it’s |
|
490
|
correct, in clear-headed retrospective fashion. Having the tool do it |
|
491
|
prospectively before one can test the result is simply backwards. |
|
492
|
|
|
493
|
|
|
494
|
<a id="staging"></a> |
|
495
|
## There Is No Staging Area |
|
496
|
|
|
497
|
Fossil omits the "Git index" or "staging area" concept. When you |
|
498
|
type "`fossil commit`" _all_ changes in your check-out are committed, |
|
499
|
by default. There is no need for the "-a" option as with Git. |
|
500
|
|
|
501
|
If you only want to commit _some_ of the changes, list the names |
|
502
|
of the files or directories you want to commit as arguments, like this: |
|
503
|
|
|
504
|
fossil commit src/feature.c doc/feature.md examples/feature |
|
505
|
|
|
506
|
Note that the last element is a directory name, meaning “any changed |
|
507
|
file under the `examples/feature` directory.” |
|
508
|
|
|
509
|
|
|
510
|
<a id="comsplit"></a> |
|
511
|
## Commit Splitting |
|
512
|
|
|
513
|
[Git’s commit splitting features][gcspl] rely on |
|
514
|
other features of Git that Fossil purposefully lacks, as covered in the |
|
515
|
prior two sections: [autocommit](#autocommit) and [the staging |
|
516
|
area](#staging). |
|
517
|
|
|
518
|
While there is no direct Fossil equivalent for |
|
519
|
`git add -p`, `git commit -p`, or `git rebase -i`, you can get the same |
|
520
|
effect by converting an uncommitted change set to a patch and then |
|
521
|
running it through [Patchouli]. |
|
522
|
|
|
523
|
Rather than use `fossil diff -i` to produce such a patch, a safer and |
|
524
|
more idiomatic method would be: |
|
525
|
|
|
526
|
fossil stash save -m 'my big ball-o-hackage' |
|
527
|
fossil stash diff > my-changes.patch |
|
528
|
|
|
529
|
That stores your changes in the stash, then lets you operate on a copy |
|
530
|
of that patch. Each time you re-run the second command, it will take the |
|
531
|
current state of the working directory into account to produce a |
|
532
|
potentially different patch, likely smaller because it leaves out patch |
|
533
|
hunks already applied. |
|
534
|
|
|
535
|
In this way, the combination of working tree and stash replaces the need |
|
536
|
for Git’s index feature. |
|
537
|
|
|
538
|
We believe we know how to do commit splitting in a way compatible with |
|
539
|
the Fossil philosophy, without following Git’s ill-considered design |
|
540
|
leads. It amounts to automating the above process through an interactive |
|
541
|
variant of [`fossil stash apply`][stash], as currently prototyped in the |
|
542
|
third-party tool [`fnc`][fnc] and [its interactive `stash` |
|
543
|
command][fncsta]. We merely await someone’s [contribution][ctrb] of this |
|
544
|
feature into Fossil proper. |
|
545
|
|
|
546
|
[ctrb]: https://fossil-scm.org/fossil/doc/trunk/www/contribute.wiki |
|
547
|
[fnc]: https://fnc.bsdbox.org/ |
|
548
|
[fncsta]: https://fnc.bsdbox.org/uv/doc/fnc.1.html#stash |
|
549
|
[gcspl]: https://git-scm.com/docs/git-rebase#_splitting_commits |
|
550
|
[Patchouli]: https://pypi.org/project/patchouli/ |
|
551
|
|
|
552
|
|
|
553
|
<a id="bneed"></a> |
|
554
|
## Create Branches at Point of Need, Rather Than Ahead of Need |
|
555
|
|
|
556
|
Fossil prefers that you create new branches as part of the first commit |
|
557
|
on that branch: |
|
558
|
|
|
559
|
fossil commit --branch my-branch |
|
560
|
|
|
561
|
If that commit is successful, your local check-out directory is then |
|
562
|
switched to the tip of that branch, so subsequent commits don’t need the |
|
563
|
“`--branch`” option. You simply say `fossil commit` again to continue |
|
564
|
adding commits to the tip of that branch. |
|
565
|
|
|
566
|
To switch back to the parent branch, say something like: |
|
567
|
|
|
568
|
fossil update trunk |
|
569
|
|
|
570
|
(This is approximately equivalent to `git checkout master`.) |
|
571
|
|
|
572
|
Fossil does also support the Git style, creating the branch ahead of |
|
573
|
need: |
|
574
|
|
|
575
|
fossil branch new my-branch |
|
576
|
fossil up my-branch |
|
577
|
...work on first commit... |
|
578
|
fossil commit |
|
579
|
|
|
580
|
This is more verbose, giving the same overall effect though the initial |
|
581
|
actions are inverted: create a new branch for the first commit, switch |
|
582
|
the check-out directory to that branch, and make that first commit. As |
|
583
|
above, subsequent commits are descendants of that initial branch commit. |
|
584
|
We think you’ll agree that creating a branch as part of the initial |
|
585
|
commit is simpler. |
|
586
|
|
|
587
|
Fossil also allows you to move a check-in to a different branch |
|
588
|
*after* you commit it, using the "`fossil amend`" command. |
|
589
|
For example: |
|
590
|
|
|
591
|
fossil amend current --branch my-branch |
|
592
|
|
|
593
|
This works by inserting a tag into the repository that causes the web UI |
|
594
|
to relabel commits from that point forward with the new name. Like Git, |
|
595
|
Fossil’s fundamental data structure is the interlinked DAG of commit |
|
596
|
hashes; branch names are supplemental data for making it easier for the |
|
597
|
humans to understand this DAG, so this command does not change the core |
|
598
|
history of the project, only annotate it for better display to the |
|
599
|
humans. |
|
600
|
|
|
601
|
(The version string “current” is one of the [special check-in names][scin] in Fossil. See |
|
602
|
that document for the many other names you can give to “`amend`”, or |
|
603
|
indeed to any other Fossil command documented to accept a `VERSION` or |
|
604
|
`NAME` string.) |
|
605
|
|
|
606
|
[scin]: ./checkin_names.wiki |
|
607
|
|
|
608
|
|
|
609
|
<a id="syncall"></a> |
|
610
|
## Sync Is All-or-Nothing |
|
611
|
|
|
612
|
Fossil does not support the concept of syncing, pushing, or pulling |
|
613
|
individual branches. When you sync/push/pull in Fossil, it |
|
614
|
processes all artifacts in its hash tree: |
|
615
|
branches, tags, wiki articles, tickets, forum posts, technotes… |
|
616
|
This is [not quite “everything,” full stop][bu], but it’s close. |
|
617
|
[Fossil is an AP-mode system][capt], which in this case means it works |
|
618
|
*very hard* to ensure that all repos are as close to identical as it can |
|
619
|
make them under this eventually-consistent design philosophy. |
|
620
|
|
|
621
|
Branch *names* sync automatically in Fossil, not just the |
|
622
|
content of those branches. That means this common Git command: |
|
623
|
|
|
624
|
git push origin master |
|
625
|
|
|
626
|
…is simply this in Fossil: |
|
627
|
|
|
628
|
fossil push |
|
629
|
|
|
630
|
Fossil doesn’t need to be told what to push or where to push it: it just |
|
631
|
keeps using the same remote server URL you gave it last |
|
632
|
until you [tell it to do something different][rem]. It pushes all |
|
633
|
branches, not just one named local branch. |
|
634
|
|
|
635
|
[capt]: ./cap-theorem.md |
|
636
|
[rem]: /help/remote |
|
637
|
|
|
638
|
|
|
639
|
<a id="autosync"></a> |
|
640
|
## Autosync |
|
641
|
|
|
642
|
Fossil’s [autosync][wflow] feature, normally enabled, has no |
|
643
|
equivalent in Git. If you want Fossil to behave like Git, you can turn |
|
644
|
it off: |
|
645
|
|
|
646
|
fossil set autosync 0 |
|
647
|
|
|
648
|
Let’s say that you have a typical server-and-workstations model with two |
|
649
|
working clones on different machines, that you have disabled autosync, |
|
650
|
and that this common sequence then occurs: |
|
651
|
|
|
652
|
1. Alice commits to her local clone and *separately* pushes the change |
|
653
|
up to Condor — their central server — in typical Git fashion. |
|
654
|
2. Bob does the same. |
|
655
|
3. Alice brings Bob’s changes down from Condor with “`fossil pull`,” sees |
|
656
|
what he did to their shared working branch, and becomes most wrathful. |
|
657
|
(Tsk, tsk.) |
|
658
|
|
|
659
|
We’ll get to what you do about this situation [below](#reset), but for |
|
660
|
now let us focus on the fact that disabling autosync makes it easier for |
|
661
|
[forks] to occur in the development history. If all three machines had |
|
662
|
been online and syncing at the time the sequence above began, Bob would |
|
663
|
have been warned in step 2 that committing to the central repo would |
|
664
|
create a fork and would be invited to fix it before committing. |
|
665
|
Likewise, it would allow Fossil to warn Alice about the new |
|
666
|
tip-of-branch commit the next time she triggers an implicit autosync at |
|
667
|
step 3, giving her a chance to bring Bob’s changes down in a |
|
668
|
non-conflicting manner, allowing work to proceed with minimal fuss. |
|
669
|
|
|
670
|
Fossil, being a distributed version control system, cannot guarantee |
|
671
|
that sequence of events. Because it allows Alice’s work to proceed |
|
672
|
asynchronously, it gives her the chance to create *another* inadvertent |
|
673
|
fork before she can trigger an autosync. This is not a serious problem; |
|
674
|
Fossil resolves it the same way as with Bob, by inviting her to fix this |
|
675
|
second fork in the same manner as it did with Bob. It gets both parties |
|
676
|
back onto a single track as expeditiously as possible by moving the |
|
677
|
synchronization point out of the expensive human-time workflow and into |
|
678
|
the software system, where it’s cheapest to resolve. |
|
679
|
|
|
680
|
Autosync provides Fossil with most of the advantages of a centralized |
|
681
|
version control system while retaining the advantages of distributed |
|
682
|
version control: |
|
683
|
|
|
684
|
1. Your work stays synced up with your coworkers’ efforts as long as your |
|
685
|
machine can connect to the remote repository. At need, you can go |
|
686
|
off-network and continue work atop the last version you synced with |
|
687
|
the remote. |
|
688
|
|
|
689
|
2. You get implicit off-machine backup of your commits. Unlike |
|
690
|
centralized version control, though, you can still work while |
|
691
|
disconnected; your changes will sync up with the remote once you get |
|
692
|
back online. |
|
693
|
|
|
694
|
3. Because there is [little distinction][bu] between the clones in the Fossil |
|
695
|
model — unlike in Git, where clones often quickly diverge from each |
|
696
|
other, quite possibly on purpose — the backup advantage applies in inverse |
|
697
|
as well: if the remote server falls over dead, one of those with a |
|
698
|
clone of that repository can stand it back up, and everyone can get |
|
699
|
back to work simply by re-pointing their local repo at the new |
|
700
|
remote. If the failed remote comes back later, it can sync with the |
|
701
|
new central version, then perhaps take over as the primary source of |
|
702
|
truth once again. |
|
703
|
|
|
704
|
[bu]: ./backup.md |
|
705
|
[forks]: ./branching.wiki |
|
706
|
[setup]: ./caps/admin-v-setup.md#apsu |
|
707
|
[wflow]: ./concepts.wiki#workflow |
|
708
|
|
|
709
|
|
|
710
|
<a id="reset"></a> |
|
711
|
## Resetting the Repository |
|
712
|
|
|
713
|
Extending from [the prior item](#syncall), you may correctly infer that |
|
714
|
“[delete the project and download a fresh copy][x1597]” has no part in |
|
715
|
the Fossil Way. Ideally, you should never find yourself forced into |
|
716
|
desperate measures like this:(^Parsing the output of `fossil status` is |
|
717
|
usually a mistake since it relies on a potentially unstable interface. |
|
718
|
We make no guarantee that there will always be a line beginning with |
|
719
|
“`repo`” and that it will be separated from the repository’s file name |
|
720
|
by a colon. The simplified example above is also liable to become |
|
721
|
confused by whitespace in file names.) |
|
722
|
|
|
723
|
|
|
724
|
``` |
|
725
|
$ repo=$(fossil status | grep ^repo | cut -f2 -d:) |
|
726
|
$ url=$(fossil remote) |
|
727
|
$ fossil close # Stop here and think if it warns you! |
|
728
|
$ mv $repo ${repo}.old |
|
729
|
$ fossil clone $url $repo |
|
730
|
$ fossil open --force $repo |
|
731
|
``` |
|
732
|
|
|
733
|
What, then, should you as a Git transplant do instead when you find |
|
734
|
yourself reaching for “`git reset`”? |
|
735
|
|
|
736
|
Since the correct answer to that depends on why you think it’s a good |
|
737
|
solution to your immediate problem, we’ll take our motivating scenario |
|
738
|
from the problem setup above, where we discussed Fossil’s [autosync] |
|
739
|
feature. Let us further say Alice’s pique results from a belief that |
|
740
|
Bob’s commit is objectively wrong-headed and should be expunged |
|
741
|
henceforth. Since Fossil goes out of its way to ensure that [commits are |
|
742
|
durable][wdm], it should be no further surprise that there is no easier |
|
743
|
method to reset Bob’s clone in favor of Alice’s than the above sequence |
|
744
|
in Fossil’s command set. Except in extreme situations, we believe that |
|
745
|
sort of thing is unnecessary. |
|
746
|
|
|
747
|
Instead, Bob can say something like this: |
|
748
|
|
|
749
|
``` |
|
750
|
fossil amend --branch MISTAKE --hide --close -m "mea culpa" tip |
|
751
|
fossil up trunk |
|
752
|
fossil push |
|
753
|
``` |
|
754
|
|
|
755
|
Unlike in Git, the “`amend`” command doesn’t modify prior committed |
|
756
|
artifacts. Bob’s first command doesn’t delete anything, merely tells |
|
757
|
Fossil to hide his mistake from timeline views by inserting a few new |
|
758
|
records into the local repository to change how the client interprets |
|
759
|
the data it finds there henceforth.(^One to change the tag marking this |
|
760
|
commit’s branch name to “`MISTAKE`,” one to mark that branch as hidden, |
|
761
|
and one to close it to further commits.) |
|
762
|
|
|
763
|
Bob’s second command switches his working directory back to the prior |
|
764
|
commit on that branch. We’re presuming it was “`trunk`” for the sake of |
|
765
|
the example, but it works for any parent branch name. The command works |
|
766
|
because the name “`trunk`” now means something different to Fossil by |
|
767
|
virtue of the first command. |
|
768
|
|
|
769
|
Bob’s third command pushes the changes up to the central machine to |
|
770
|
inform everyone else of his amendment.(^Amendments don’t autosync in |
|
771
|
Fossil because they don’t change any previous commits, allowing the |
|
772
|
other clones to continue working safely with their existing commit |
|
773
|
hashes.) |
|
774
|
|
|
775
|
In this scheme, Alice then needs to say “`fossil update trunk`” in order |
|
776
|
to return her check-out’s parent commit to the previous version lest her |
|
777
|
next attempted commit land atop this mistake branch. The fact that Bob |
|
778
|
marked the branch as closed will prevent that from going through, cluing |
|
779
|
Alice into what she needs to do to remedy the situation, but that merely |
|
780
|
shows why it’s a better workflow if Alice makes the amendment herself: |
|
781
|
|
|
782
|
``` |
|
783
|
fossil amend --branch MISTAKE --hide --close \ |
|
784
|
-m "shunt Bob’s erroneous commit off" tip |
|
785
|
fossil up trunk |
|
786
|
fossil push |
|
787
|
``` |
|
788
|
|
|
789
|
Then she can fire off an email listing Bob’s assorted failings and go |
|
790
|
about her work. This asynchronous workflow solves the problem without |
|
791
|
requiring explicit coordination with Bob. When he gets his email, he can |
|
792
|
then say “`fossil up trunk`” himself, which by default will trigger an |
|
793
|
autosync, pulling down Alice’s amendments and getting him back onto her |
|
794
|
development track. |
|
795
|
|
|
796
|
Remember that [branch names need not be unique](#btnames) in Fossil. You |
|
797
|
are free to reuse this “`MISTAKE`” branch name as often as you need to. |
|
798
|
|
|
799
|
[autosync]: #autosync |
|
800
|
[x1597]: https://xkcd.com/1597/ |
|
801
|
|
|
802
|
|
|
803
|
<a id="trunk"></a> |
|
804
|
## The Main Branch Is Called "`trunk`" |
|
805
|
|
|
806
|
In Fossil, the default name for the main branch |
|
807
|
is "`trunk`". The "`trunk`" branch in Fossil corresponds to the |
|
808
|
"`master`" branch in stock Git or to [the “`main`” branch in GitHub][mbgh]. |
|
809
|
|
|
810
|
Because the `fossil git export` command has to work with both stock Git |
|
811
|
and with GitHub, Fossil uses Git’s traditional default rather than |
|
812
|
GitHub’s new default: your Fossil repo’s “trunk” branch becomes “master” |
|
813
|
when [mirroring to GitHub][mirgh] unless you give the `--mainbranch` |
|
814
|
option. |
|
815
|
|
|
816
|
We do not know what happens on subsequent exports if you later rename |
|
817
|
this branch on the GitHub side. |
|
818
|
|
|
819
|
[mbgh]: https://github.com/github/renaming |
|
820
|
[mirgh]: ./mirrortogithub.md |
|
821
|
|
|
822
|
|
|
823
|
<a id="unmanaged"></a> |
|
824
|
## Status Does Not Show Unmanaged Files |
|
825
|
|
|
826
|
The "`fossil status`" command shows you what files in your check-out have |
|
827
|
been edited and scheduled for adding or removing at the next commit. |
|
828
|
But unlike "`git status`", the "`fossil status`" command does not warn |
|
829
|
you about unmanaged files in your local check-out. There is a separate |
|
830
|
"`fossil extras`" command for that. |
|
831
|
|
|
832
|
|
|
833
|
<a id="rebase"></a> |
|
834
|
## There Is No Rebase |
|
835
|
|
|
836
|
Fossil does not support rebase, [on purpose][3]. |
|
837
|
|
|
838
|
This is a deliberate design decision that the Fossil community has |
|
839
|
thought about carefully and discussed many times, resulting in the |
|
840
|
linked document. If you are fond of rebase, you should read it carefully |
|
841
|
before expressing your views: it not only answers many of the common |
|
842
|
arguments in favor of rebase known at the time the document’s first |
|
843
|
draft was written, it’s been revised multiple times to address less |
|
844
|
common objections as well. Chances are not good that you are going to |
|
845
|
come up with a new objection that we haven’t already considered and |
|
846
|
addressed there. |
|
847
|
|
|
848
|
There is only one sub-feature of `git rebase` that is philosophically |
|
849
|
compatible with Fossil yet which currently has no functional equivalent. |
|
850
|
We [covered this and the workaround for its lack](#comsplit) above. |
|
851
|
|
|
852
|
[3]: ./rebaseharm.md |
|
853
|
|
|
854
|
|
|
855
|
<a id="cdiff"></a> |
|
856
|
## Colorized Diffs |
|
857
|
|
|
858
|
When you run `git diff` on an ANSI X3.64 capable terminal, it uses color |
|
859
|
to distinguish insertions, deletions, and replacements, but as of this |
|
860
|
writing, `fossil diff` produces traditional uncolored [unified diff |
|
861
|
format][udiff] output, suitable for producing a [patch file][pfile]. |
|
862
|
|
|
863
|
There are [many methods](./colordiff.md) for solving this. |
|
864
|
Viewed this way, Fossil doesn’t lack colorized diffs, it simply has |
|
865
|
*one* method where they *aren’t* colorized. |
|
866
|
|
|
867
|
[pfile]: https://en.wikipedia.org/wiki/Patch_(Unix) |
|
868
|
[udiff]: https://en.wikipedia.org/wiki/Diff#Unified_format |
|
869
|
|
|
870
|
|
|
871
|
## <a id="show"></a> Showing Information About Commits |
|
872
|
|
|
873
|
While there is no direct equivalent to Git’s “`show`” command, similar |
|
874
|
functionality is present in Fossil under other commands: |
|
875
|
|
|
876
|
|
|
877
|
#### <a id="patch"></a> Show a Patch for a Commit |
|
878
|
|
|
879
|
git show -p COMMIT_ID |
|
880
|
|
|
881
|
…gives much the same output as |
|
882
|
|
|
883
|
fossil diff --checkin COMMIT_ID |
|
884
|
|
|
885
|
…only without the patch email header. Git comes out of the [LKML] world, |
|
886
|
where emailing a patch is a normal thing to do. Fossil is [designed for |
|
887
|
cohesive teams][devorg] where drive-by patches are rarer. |
|
888
|
|
|
889
|
You can use any of [Fossil’s special check-in names][scin] in place of |
|
890
|
the `COMMIT_ID` in this and later examples. Fossil docs usually say |
|
891
|
“`VERSION`” or “`NAME`” where this is allowed, since the version string |
|
892
|
or name might not refer to a commit ID, but instead to a forum post, a |
|
893
|
wiki document, etc. For instance, the following command answers the question “What did |
|
894
|
I just commit?” |
|
895
|
|
|
896
|
fossil diff --checkin tip |
|
897
|
|
|
898
|
…or equivalently using a different symbolic commit name: |
|
899
|
|
|
900
|
fossil diff --from prev |
|
901
|
|
|
902
|
[devorg]: ./fossil-v-git.wiki#devorg |
|
903
|
[LKML]: https://lkml.org/ |
|
904
|
|
|
905
|
|
|
906
|
#### <a id="cmsg"></a> Show a Specific Commit Message |
|
907
|
|
|
908
|
git show -s COMMIT_ID |
|
909
|
|
|
910
|
|
|
911
|
…is |
|
912
|
|
|
913
|
fossil time -n 1 COMMIT_ID |
|
914
|
|
|
915
|
…or with a shorter, more obvious command, though with more verbose output: |
|
916
|
|
|
917
|
fossil info COMMIT_ID |
|
918
|
|
|
919
|
The `fossil info` command isn’t otherwise a good equivalent to |
|
920
|
`git show`; it just overlaps its functionality in some areas. Much of |
|
921
|
what’s missing is present in the corresponding [`/info` web |
|
922
|
view][infow], though. |
|
923
|
|
|
924
|
|
|
925
|
#### <a id="dstat"></a> Diff Statistics |
|
926
|
|
|
927
|
Fossil’s closest internal equivalent to commands like |
|
928
|
`git show --stat` is: |
|
929
|
|
|
930
|
fossil diff -i --from 2020-04-01 --numstat |
|
931
|
|
|
932
|
The `--numstat` output is a bit cryptic, so we recommend delegating |
|
933
|
this task to [the widely-available `diffstat` tool][dst], which gives |
|
934
|
a histogram in its default output mode rather than bare integers: |
|
935
|
|
|
936
|
fossil diff -i -v --from 2020-04-01 | diffstat |
|
937
|
|
|
938
|
We gave the `-i` flag in both cases to force Fossil to use its internal |
|
939
|
diff implementation, bypassing [your local `diff-command` setting][dcset] |
|
940
|
since the `--numstat` option has no effect when you have an external diff |
|
941
|
command set. |
|
942
|
|
|
943
|
If you leave off the `-v` flag in the second example, the `diffstat` |
|
944
|
output won’t include info about any newly-added files. |
|
945
|
|
|
946
|
[dcset]: https://fossil-scm.org/home/help/diff-command |
|
947
|
[dst]: https://invisible-island.net/diffstat/diffstat.html |
|
948
|
|
|
949
|
|
|
950
|
<a id="btnames"></a> |
|
951
|
## Branch and Tag Names |
|
952
|
|
|
953
|
Fossil has no special restrictions on the names of tags and branches, |
|
954
|
though you might want to keep [Git's tag and branch name restrictions][gcrf] |
|
955
|
in mind if you plan on [mirroring your Fossil repository to GitHub][mirgh]. |
|
956
|
|
|
957
|
Fossil does not require tag and branch names to be unique. It is |
|
958
|
common, for example, to put a “`release`” tag on every release for a |
|
959
|
Fossil-hosted project. This does not create a conflict in Fossil, since |
|
960
|
Fossil resolves the ambiguity in a predictable way: the newest match |
|
961
|
wins. Therefore, “`fossil up release`” always gets you the current |
|
962
|
release in a project that uses this tagging convention. |
|
963
|
|
|
964
|
[The `fossil git export` command][fge] squashes repeated tags down to a |
|
965
|
single instance to avoid confusing Git, exporting only the newest tag, |
|
966
|
emulating Fossil’s own ambiguity resolution rule as best it can within |
|
967
|
Git’s limitations. |
|
968
|
|
|
969
|
[fge]: /help/git |
|
970
|
[gcrf]: https://git-scm.com/docs/git-check-ref-format |
|
971
|
|
|
972
|
|
|
973
|
|
|
974
|
|
|
975
|
<a id="cpickrev"></a> |
|
976
|
## Cherry-Picking and Reverting Commits |
|
977
|
|
|
978
|
Git’s separate "`git cherry-pick`" and “`git revert`” commands are |
|
979
|
options to the [`fossil merge` command][merge]: `--cherrypick` and |
|
980
|
`--backout`, respectively. We view this as sensible, since these are |
|
981
|
both merge operations, and the two actions differ only in direction. |
|
982
|
|
|
983
|
Unlike in Git, the Fossil file format remembers cherrypicks and backouts |
|
984
|
and can later show them as dashed lines on the graphical timeline. |
|
985
|
|
|
986
|
[merge]: /help/merge |
|
987
|
|
|
988
|
|
|
989
|
|
|
990
|
<a id="mvrm"></a> |
|
991
|
## File Moves and Renames Are Soft by Default |
|
992
|
|
|
993
|
The "[`fossil mv`][mv]" and "[`fossil rm`][rm]" commands work like they |
|
994
|
do in CVS in that they schedule the changes for the next commit by |
|
995
|
default: they do not actually rename or delete the files in your |
|
996
|
check-out. |
|
997
|
|
|
998
|
If you don’t like that default, you can change it globally: |
|
999
|
|
|
1000
|
fossil setting --global mv-rm-files 1 |
|
1001
|
|
|
1002
|
Now these commands behave like in Git in any Fossil repository where |
|
1003
|
this setting hasn’t been overridden locally. |
|
1004
|
|
|
1005
|
If you want to keep Fossil’s soft `mv/rm` behavior most of the time, you |
|
1006
|
can cast it away on a per-command basis: |
|
1007
|
|
|
1008
|
fossil mv --hard old-name new-name |
|
1009
|
|
|
1010
|
[mv]: /help/mv |
|
1011
|
[rm]: /help/rm |
|
1012
|
|
|
1013
|
|
|
1014
|
---- |
|
1015
|
|
|
1016
|
|
|
1017
|
## <a id="cvdate" name="cs1"></a> Case Study 1: Checking Out a Version by Date |
|
1018
|
|
|
1019
|
Let’s get into something a bit more complicated: a case study showing |
|
1020
|
how the concepts lined out above cause Fossil to materially differ in |
|
1021
|
day-to-day operation from Git. |
|
1022
|
|
|
1023
|
Why would you want to check out a version of a project by date? Perhaps |
|
1024
|
your customer gave you a vague bug report referencing only a |
|
1025
|
date rather than a version. Or, you may be poking semi-randomly through |
|
1026
|
history to find a “good” version to anchor the start point of a |
|
1027
|
[`fossil bisect`][fbis] operation. |
|
1028
|
|
|
1029
|
My search engine’s first result for “git checkout by date” is [this |
|
1030
|
highly-upvoted accepted Stack Overflow answer][gcod]. The first command |
|
1031
|
it gives is based on Git’s [`rev-parse` feature][grp]: |
|
1032
|
|
|
1033
|
git checkout master@{2020-03-17} |
|
1034
|
|
|
1035
|
There are a number of weaknesses in this command. From least to most |
|
1036
|
critical: |
|
1037
|
|
|
1038
|
1. It’s a bit cryptic. Leave off the refname or punctuation, and it |
|
1039
|
means something else. You cannot simplify the cryptic incantation in |
|
1040
|
the typical use case. |
|
1041
|
|
|
1042
|
2. A date string in Git without a time will be interpreted as |
|
1043
|
“[at the local wall clock time on the given date][gapxd],” so the |
|
1044
|
command means something different from one second to the next! This |
|
1045
|
can be a serious problem if there are multiple commits on that date, because |
|
1046
|
the command will give different results depending on the time of |
|
1047
|
day you run it. |
|
1048
|
|
|
1049
|
3. It gives misleading output if there is no close match for the date |
|
1050
|
in the local [reflog]. It starts out empty after a fresh clone, and |
|
1051
|
while it does build up as you use that clone, Git [automatically |
|
1052
|
prunes][gle] the reflog to 90 days of history by default. This means |
|
1053
|
the command above can give different results from one machine to the |
|
1054
|
next, or even from one day to the next on the same clone. |
|
1055
|
|
|
1056
|
The command won’t fail outright if the reflog can’t resolve the |
|
1057
|
given date: it simply gives the closest commit it can come up with, |
|
1058
|
even if it’s months or years out from your target! Sometimes it |
|
1059
|
gives a warning about the reflog not going back far enough to give a |
|
1060
|
useful result, and sometimes it doesn’t. If you’re on a fresh clone, |
|
1061
|
you are likely to get the “tip” commit’s revision ID no matter what |
|
1062
|
date value you give. |
|
1063
|
|
|
1064
|
Git tries its best, but because it’s working from a purgeable and |
|
1065
|
possibly-stale local cache, you cannot trust its results. |
|
1066
|
|
|
1067
|
Consequently, we cannot recommend this command at all. It’s unreliable even in the |
|
1068
|
best case. |
|
1069
|
|
|
1070
|
That same Stack Overflow answer therefore goes on to recommend an |
|
1071
|
entirely different command: |
|
1072
|
|
|
1073
|
git checkout $(git rev-list -n 1 --first-parent --before="2020-03-17" master) |
|
1074
|
|
|
1075
|
We believe you get such answers to Git help requests in part |
|
1076
|
because of its lack of an always-up-to-date [index into its log](#log) and in |
|
1077
|
part because of its “small tools loosely joined” design philosophy. This |
|
1078
|
sort of command is therefore composed piece by piece: |
|
1079
|
|
|
1080
|
<p style="text-align:center">◆ ◆ ◆</p> |
|
1081
|
|
|
1082
|
“Oh, I know, I’ll search the rev-list, which outputs commit IDs by |
|
1083
|
parsing the log backwards from `HEAD`! Easy!” |
|
1084
|
|
|
1085
|
git rev-list --before=2020-03-17 |
|
1086
|
|
|
1087
|
“Blast! Forgot the commit ID!” |
|
1088
|
|
|
1089
|
git rev-list --before=2020-03-17 master |
|
1090
|
|
|
1091
|
“Double blast! It just spammed my terminal with revision IDs! I need to |
|
1092
|
limit it to the single closest match: |
|
1093
|
|
|
1094
|
git rev-list -n 1 --before=2020-03-17 master |
|
1095
|
|
|
1096
|
“Okay, it gives me a single revision ID now, but is it what I’m after? |
|
1097
|
Let’s take a look…” |
|
1098
|
|
|
1099
|
git show $(git rev-list -n 1 --before=2020-03-17 master) |
|
1100
|
|
|
1101
|
“Oops, that’s giving me a merge commit, not what I want. |
|
1102
|
Off to search the web… Okay, it says I need to give either the |
|
1103
|
`--first-parent` or `--no-merges` flag to show only regular commits, |
|
1104
|
not merge-commits. Let’s try the first one:” |
|
1105
|
|
|
1106
|
git show $(git rev-list -n 1 --first-parent --before=2020-03-17 master) |
|
1107
|
|
|
1108
|
“Better. Let’s check it out:” |
|
1109
|
|
|
1110
|
git checkout $(git rev-list -n 1 --first-parent --before=2020-03-17 master) |
|
1111
|
|
|
1112
|
“Success, I guess?” |
|
1113
|
|
|
1114
|
<p style="text-align:center">◆ ◆ ◆</p> |
|
1115
|
|
|
1116
|
This vignette is meant to explain some of Git’s popularity: it rewards |
|
1117
|
the sort of people who enjoy puzzles, many of whom are software |
|
1118
|
developers and thus need a tool like Git. Too bad if you’re just a |
|
1119
|
normal user. |
|
1120
|
|
|
1121
|
And too bad if you’re a Windows user who doesn’t want to use [Git |
|
1122
|
Bash][gbash], since neither of the stock OS command shells have a |
|
1123
|
command interpolation feature needed to run that horrid command. |
|
1124
|
|
|
1125
|
This alternative command still has weakness #2 above: if you run the |
|
1126
|
second `git show` command above on [Git’s own repository][gitgh], your |
|
1127
|
results may vary because there were four non-merge commits to Git on the |
|
1128
|
17th of March, 2020. |
|
1129
|
|
|
1130
|
You may be asking with an exasperated huff, “What is your *point*, man?” |
|
1131
|
The point is that the equivalent in Fossil is simply: |
|
1132
|
|
|
1133
|
fossil up 2020-03-17 |
|
1134
|
|
|
1135
|
…which will *always* give the commit closest to midnight UTC on the 17th |
|
1136
|
of March, 2020, no matter whether you do it on a fresh clone or a stale |
|
1137
|
one. The answer won’t shift about from one clone to the next or from |
|
1138
|
one local time of day to the next. We owe this reliability and stability |
|
1139
|
to three Fossil design choices: |
|
1140
|
|
|
1141
|
* Parse timestamps from all commits on clone into a local commit index, |
|
1142
|
then maintain that index through subsequent commits and syncs. |
|
1143
|
|
|
1144
|
* Use an indexed SQL `ORDER BY` query to match timestamps to commit |
|
1145
|
IDs for a fast and consistent result. |
|
1146
|
|
|
1147
|
* Round incomplete timestamp strings up using [rules][frud] consistent across |
|
1148
|
computers and local time of day. |
|
1149
|
|
|
1150
|
[frud]: https://fossil-scm.org/home/file/src/name.c?ci=d2a59b03727bc3&ln=122-141 |
|
1151
|
[gbash]: https://appuals.com/what-is-git-bash/ |
|
1152
|
[gapxd]: https://github.com/git/git/blob/7f7ebe054a/date.c#L1298-L1300 |
|
1153
|
[gcod]: https://stackoverflow.com/a/6990682/142454 |
|
1154
|
[gdh]: https://www.git-tower.com/learn/git/faq/detached-head-when-checkout-commit/ |
|
1155
|
[gitgh]: https://github.com/git/git/ |
|
1156
|
[gle]: https://git-scm.com/docs/git-reflog#_options_for_expire |
|
1157
|
[gmc]: https://github.com/git/git/commit/67b0a24910fbb23c8f5e7a2c61c339818bc68296 |
|
1158
|
[grp]: https://git-scm.com/docs/git-rev-parse |
|
1159
|
[reflog]: https://git-scm.com/docs/git-reflog |
|
1160
|
|
|
1161
|
---- |
|
1162
|
|
|
1163
|
## <a id="morigin" name="cs2"></a> Case Study 2: Multiple "origin" Servers |
|
1164
|
|
|
1165
|
Now let us consider a common use case at the time of this writing — during the |
|
1166
|
COVID-19 pandemic — where you’re working from home a lot, going into the |
|
1167
|
office one part-day a week only to do things that have to be done |
|
1168
|
on-site at the office. Let us also say you have no remote |
|
1169
|
access back into the work LAN, such as because your site IT is paranoid |
|
1170
|
about security. You may still want off-machine backups of your commits |
|
1171
|
while working from home, |
|
1172
|
so you need the ability to quickly switch between the “home” and |
|
1173
|
“work” remote repositories, with your laptop acting as a kind of |
|
1174
|
[sneakernet][sn] link between the big development server at the office |
|
1175
|
and your family’s home NAS. |
|
1176
|
|
|
1177
|
#### Git Method |
|
1178
|
|
|
1179
|
We first need to clone the work repo down to our laptop, so we can work on it |
|
1180
|
at home: |
|
1181
|
|
|
1182
|
git clone https://dev-server.example.com/repo |
|
1183
|
cd repo |
|
1184
|
git remote rename origin work |
|
1185
|
|
|
1186
|
The last command is optional, strictly speaking. We could continue to |
|
1187
|
use Git’s default name for the work repo’s origin — sensibly enough |
|
1188
|
called “`origin`” — but it makes later commands harder to understand, so |
|
1189
|
we rename it here. This will also make the parallel with Fossil easier |
|
1190
|
to draw. |
|
1191
|
|
|
1192
|
The first time we go home after this, we have to reverse-clone the work |
|
1193
|
repo up to the NAS: |
|
1194
|
|
|
1195
|
ssh my-nas.local 'git init --bare /SHARES/dayjob/repo.git' |
|
1196
|
git push --all ssh://my-nas.local//SHARES/dayjob/repo.git |
|
1197
|
|
|
1198
|
Realize that this is carefully optimized down to these two long |
|
1199
|
commands. In practice, we’d expect a user typing these commands by hand from memory |
|
1200
|
to need to give four or more commands here instead. |
|
1201
|
Packing the “`git init`” call into the “`ssh`” call is something more |
|
1202
|
often done in scripts and documentation examples than done interactively, |
|
1203
|
which then necessitates a third command before the push, “`exit`”. |
|
1204
|
There’s also a good chance that you’ll forget the need for the `--bare` |
|
1205
|
option here to avoid a fatal complaint from Git that the laptop can’t |
|
1206
|
push into a non-empty repo. If you fall into this trap, among the many |
|
1207
|
that Git lays for newbies, you have to nuke the incorrectly initted |
|
1208
|
repo, search the web or Git man pages to find out about `--bare`, and try again. |
|
1209
|
|
|
1210
|
Having navigated that little minefield, |
|
1211
|
we can tell Git that there is a second origin, a “home” repo in |
|
1212
|
addition to the named “work” repo we set up earlier: |
|
1213
|
|
|
1214
|
git remote add home ssh://my-nas.local//SHARES/dayjob/repo.git |
|
1215
|
git config master.remote home |
|
1216
|
|
|
1217
|
We don’t have to push or pull because the remote repo is a complete |
|
1218
|
clone of the repo on the laptop at this point, so we can just get to |
|
1219
|
work now, committing along the way to get our work safely off-machine |
|
1220
|
and onto our home NAS, like so: |
|
1221
|
|
|
1222
|
git add |
|
1223
|
git commit |
|
1224
|
git push |
|
1225
|
|
|
1226
|
We didn’t need to give a remote name on the push because we told it the |
|
1227
|
new upstream is the home NAS earlier. |
|
1228
|
|
|
1229
|
Now Friday comes along, and one of your office-mates needs a feature |
|
1230
|
you’re working on. You agree to come into the office later that |
|
1231
|
afternoon to sync up via the dev server: |
|
1232
|
|
|
1233
|
git push work master # send your changes from home up |
|
1234
|
git pull work master # get your coworkers’ changes |
|
1235
|
|
|
1236
|
Alternately, we could add “`--set-upstream/-u work`” to the first |
|
1237
|
command if we were coming into work long enough to do several Git-based things, not just pop in and sync. |
|
1238
|
That would allow the second to be just “`git pull`”, but the cost is |
|
1239
|
that when returning home, you’d have to manually reset the upstream |
|
1240
|
again. |
|
1241
|
|
|
1242
|
This example also shows a consequence of that fact that |
|
1243
|
[Git doesn’t sync branch names](#syncall): you have to keep repeating |
|
1244
|
yourself like an obsequious supplicant: “Master, master.” Didn’t we |
|
1245
|
invent computers to serve humans, rather than the other way around? |
|
1246
|
|
|
1247
|
|
|
1248
|
#### Fossil Method |
|
1249
|
|
|
1250
|
Now we’re going to do the same thing using Fossil, with |
|
1251
|
the commands arranged in blocks corresponding to those above for comparison. |
|
1252
|
|
|
1253
|
We start the same way, cloning the work repo down to the laptop: |
|
1254
|
|
|
1255
|
fossil clone https://dev-server.example.com/repo |
|
1256
|
cd repo |
|
1257
|
fossil remote add work https://dev-server.example.com/repo |
|
1258
|
|
|
1259
|
We’ve chosen the new “`fossil clone URI`” syntax rather than separate |
|
1260
|
`clone` and `open` commands to make the parallel with Git clearer. [See |
|
1261
|
above](#mwd) for more on that topic. |
|
1262
|
|
|
1263
|
Our [`remote` command][rem] is longer than the Git equivalent because |
|
1264
|
Fossil currently has no short command |
|
1265
|
to rename an existing remote. Worse, unlike with Git, we can’t just keep |
|
1266
|
using the default remote name because Fossil uses that slot in its |
|
1267
|
configuration database to store the *current* remote name, so on |
|
1268
|
switching from work to home, the home URL will overwrite the work URL if |
|
1269
|
we don’t give it an explicit name first. |
|
1270
|
|
|
1271
|
Although the Fossil commands are longer, so far, keep it in perspective: |
|
1272
|
they’re one-time setup costs, |
|
1273
|
easily amortized to insignificance by the shorter day-to-day commands |
|
1274
|
below. |
|
1275
|
|
|
1276
|
On first beginning to work from home, we reverse-clone the Fossil repo |
|
1277
|
up to the NAS: |
|
1278
|
|
|
1279
|
rsync repo.fossil my-nas.local:/SHARES/dayjob/ |
|
1280
|
|
|
1281
|
Now we’re beginning to see the advantage of Fossil’s simpler model, |
|
1282
|
relative to the tricky “`git init && git push`” sequence above. |
|
1283
|
Fossil’s alternative is almost impossible to get |
|
1284
|
wrong: copy this to that. *Done.* |
|
1285
|
|
|
1286
|
We’re relying on the `rsync` feature that creates up to one level of |
|
1287
|
missing directory (here, `dayjob/`) on the remote. If you know in |
|
1288
|
advance that the remote directory already exists, you could use a |
|
1289
|
slightly shorter `scp` command instead. Even with the extra 2 characters |
|
1290
|
in the `rsync` form, it’s much shorter because a Fossil repository is a |
|
1291
|
single SQLite database file, not a tree containing a pile of assorted |
|
1292
|
files. Because of this, it works reliably without any of [the caveats |
|
1293
|
inherent in using `rsync` to clone a Git repo][grsync]. |
|
1294
|
|
|
1295
|
Now we set up the second remote, which is again simpler in the Fossil |
|
1296
|
case: |
|
1297
|
|
|
1298
|
fossil remote add home ssh://my-nas.local//SHARES/dayjob/repo.fossil |
|
1299
|
fossil remote home |
|
1300
|
|
|
1301
|
The first command is nearly identical to the Git version, but the second |
|
1302
|
is considerably simpler. And to be fair, you won’t find the |
|
1303
|
“`git config`” command above in all Git tutorials. The more common |
|
1304
|
alternative we found with web searches is even longer: |
|
1305
|
“`git push --set-upstream home master`”. |
|
1306
|
|
|
1307
|
Where Fossil really wins is in the next step, making the initial commit |
|
1308
|
from home: |
|
1309
|
|
|
1310
|
fossil ci |
|
1311
|
|
|
1312
|
It’s one short command for Fossil instead of three for Git — or two if |
|
1313
|
you abbreviate it as “`git commit -a && git push`” — because of Fossil’s |
|
1314
|
[autosync] feature and deliberate omission of a |
|
1315
|
[staging feature](#staging). |
|
1316
|
|
|
1317
|
The “Friday afternoon sync-up” case is simpler, too: |
|
1318
|
|
|
1319
|
fossil remote work |
|
1320
|
fossil sync |
|
1321
|
|
|
1322
|
Back at home, it’s simpler still: we may be able to do away with the second command, |
|
1323
|
saying just “`fossil remote home`” because the sync will happen as part |
|
1324
|
of the next commit, thanks once again to Fossil’s autosync feature. If |
|
1325
|
the working branch now has commits from other developers after syncing |
|
1326
|
with the central repository, though, you’ll want to say “`fossil up`” to |
|
1327
|
avoid creating an inadvertent fork in the branch. |
|
1328
|
|
|
1329
|
(Which is easy to fix if it occurs: `fossil merge` without arguments |
|
1330
|
means “merge open tips on the current branch.”) |
|
1331
|
|
|
1332
|
[grsync]: https://stackoverflow.com/q/1398018/142454 |
|
1333
|
[qs]: ./quickstart.wiki |
|
1334
|
[shwmd]: ./fossil-v-git.wiki#checkouts |
|
1335
|
[sn]: https://en.wikipedia.org/wiki/Sneakernet |
|
1336
|
|