diff options
Diffstat (limited to 'www/gitusers.md')
| -rw-r--r-- | www/gitusers.md | 1335 |
1 files changed, 1335 insertions, 0 deletions
diff --git a/www/gitusers.md b/www/gitusers.md new file mode 100644 index 0000000..94197b4 --- /dev/null +++ b/www/gitusers.md @@ -0,0 +1,1335 @@ +# Git to Fossil Translation Guide + +## Introduction + +Fossil shares many similarities with Git. In many cases, the +sub-commands are identical: [`fossil bisect`][fbis] does essentially the +same thing as [`git bisect`][gbis], for example. + +Yet, Fossil is not merely Git with a bunch of commands misspelled. If +that were the case, we could give you a two-column translation table +which would tell you [how to say things like “`git reset --hard HEAD`”](#reset) in +this funny ol’ Fossil dialect of Git and be done. The purpose of this +document is to cover all the cases where there is no simple 1:1 mapping, +usually because of intentional design differences in Fossil that prevent +it from working exactly like Git. We choose to explain these differences +since to understand the conversion, you need to know why each difference +exists. + +We focus on practical command examples here, leaving discussions of the +philosophical underpinnings that drive these command differences to [another +document][fvg]. The [case studies](#cs1) do get a bit philosophical, but +it is with the aim of illustrating how these Fossil design differences +cause Fossil to behave materially differently from Git in everyday +operation. + +We present this from the perspective of Git users moving to Fossil, but +it is also possible to read this document as a Fossil user who speaks +only pidgin Git, who may often have questions of the form, “Now how do I +do X in Git again?” + +This document’s authors are intimately familiar with Fossil, so it is +difficult for us to anticipate the perspective of people who are +intimately familiar with Git. If you have a lot of prior Git +experience, we welcome your contributions and questions on the [Fossil +Forum][ffor]. + +While we do try to explain Fossil-specific terminology inline here +as-needed, you may find it helpful to skim [the Fossil glossary][gloss]. +It will give you another take on our definitions here, and it may help +you to understand some of the other Fossil docs better. + +[fbis]: /help/bisect +[gbis]: https://git-scm.com/docs/git-bisect +[ffor]: https://fossil-scm.org/forum +[fvg]: ./fossil-v-git.wiki + + +<a id="mwd"></a> +## Repositories and Checkouts Are Distinct + +A repository and a check-out are distinct in Fossil, allowing them to be +stored in separate directory trees, whereas the two are commingled by +default with Git, with the repository stored in a `.git` subdirectory +underneath your working directory. This difference shows up in several +separate places when it comes to moving from Git to Fossil. + + + +#### <a id="cwork" name="scw"></a> Checkout Workflows + +A Fossil repository is an SQLite database storing the entire history of a +project. It is not normally stored inside the working tree. +A Fossil working tree — [also called a check-out](./glossary.md#check-out) — is a directory +that contains a snapshot of your project that you are currently working +on, extracted for you from the repository database file by the `fossil` +program. + +There are ways to +[emulate the Fossil working style in Git](#worktree), but because they’re not +designed into the core concept of the tool, Git tutorials usually +advocate a switch-in-place working mode instead, so that is how most +users end up working with Git. Contrast [Fossil’s check-out workflow +document][ckwf] to see the practical differences. + +There is one Git-specific detail we wish to add beyond what that +document already covers. This command: + + git checkout some-branch + +…is best given as: + + fossil update some-branch + +…in Fossil. There is a [`fossil checkout`][co] command, but it has +[several differences](./co-vs-up.md) that make it less broadly useful +than [`fossil update`][up] in everyday operation, so we recommend that +Git users moving to Fossil develop a habit of typing `fossil up` rather +than `fossil checkout`. That said, one of those differences does match +up with Git users’ expectations: `fossil checkout` doesn’t pull changes +from the remote repository into the local clone as `fossil update` does. +We think this is less broadly useful, but that’s the subject of the next +section. + +[ckwf]: ./ckout-workflows.md +[co]: /help/checkout + + +#### <a id="pullup"></a> Update vs Pull + +The closest equivalent to [`git pull`][gpull] is not +[`fossil pull`][fpull], but in fact [`fossil up`][up]. + +This is because +Fossil tends to follow the CVS command design: `cvs up` pulls +changes from the central CVS repository and merges them into the local +working directory, so that’s what `fossil up` does, too. (This design +choice also tends to make Fossil feel comfortable to Subversion +expatriates.) + +The `fossil pull` command is simply the reverse of +`fossil push`, so that `fossil sync` [is functionally equivalent +to](./sync.wiki#sync): + + fossil push ; fossil pull + +There is no implicit “and update the local working directory” step in Fossil’s +push, pull, or sync commands, as there is with `git pull`. + +Someone coming from the Git perspective may perceive that `fossil up` +has two purposes: + +* Without the optional `VERSION` argument, it updates the working + check-out to the tip of the current branch, as `git pull` does. + +* Given a `VERSION` argument, it updates to the named version. If that’s the + name of a branch, it updates to the *tip* of that branch, as + `git checkout BRANCH` does. + +In fact, these are the same operation, so they’re the same command in +Fossil. The first form simply allows the `VERSION` to be implicit: the +tip of the current branch. + +We think this is a more sensible command design than `git pull` vs +`git checkout`. ([…vs `git checkout` vs `git checkout`!][gcokoan]) + +[fpull]: /help/pull +[gpull]: https://git-scm.com/docs/git-pull +[gcokoan]: https://stevelosh.com/blog/2013/04/git-koans/#s2-one-thing-well + + +#### <a id="close" name="dotfile"></a> Closing a Check-Out + +The [`fossil close`][close] command dissociates a check-out directory from the +Fossil repository database, _nondestructively_ inverting [`fossil open`][open]. +(Contrast Git’s [closest alternative](#worktree), `git worktree remove`, which *is* +destructive!) This Fossil command does not +remove the managed files, and unless you give the `--force` +option, it won’t let you close the check-out with uncommitted changes to +those managed files. + +The `close` command also refuses to run without `--force` when you have +certain other precious per-checkout data that Fossil stores in the +`.fslckout` file at the root of a check-out directory. This is an SQLite +database that keeps track of local state such as what version you have +checked out, the contents of the [stash] for that working directory, the +[undo] buffers, per-checkout [settings][set], and so forth. The stash +and undo buffers are considered precious uncommitted changes, +so you have to force Fossil to discard these as part of closing the +check-out. + +Thus, `.fslckout` is not the same thing as `.git`! + +In native Windows builds of Fossil — that is, excluding Cygwin and WSL +builds, which follow POSIX conventions — this file is called `_FOSSIL_` +instead to get around the historical 3-character extension limit with +certain legacy filesystems. + +Closing a check-out directory is a rare operation. One use case +is that you’re about to delete the directory, so you want Fossil to forget about it +for the purposes of commands like [`fossil all`][all]. Even that isn’t +necessary, because Fossil will detect that this has happened and forget +the working directory for you. + +[all]: /help/all + + +#### <a id="worktree"></a> Git Worktrees + +There are at least three different ways to get [Fossil-style multiple +check-out directories][mcw] with Git. + +The old way is to simply symlink the `.git` directory between working +trees: + + mkdir ../foo-branch + ln -s ../actual-clone-dir/.git . + git checkout foo-branch + +The symlink trick has a number of problems, the largest being that +symlinks weren’t available on Windows until Vista, and until the Windows +10 Creators Update was released in spring of 2017, you had to be an +Administrator to use the feature besides. ([Source][wsyml]) Git 2.5 solved +this problem back when Windows XP was Microsoft’s current offering +by adding the `git-worktree` command: + + git worktree add ../foo-branch foo-branch + cd ../foo-branch + +That is approximately equivalent to this in Fossil: + + mkdir ../foo-branch + cd ../foo-branch + fossil open /path/to/repo.fossil foo-branch + +The Fossil alternative is wordier, but since this tends to be one-time setup, +not something you do everyday, the overhead is insignificant. This author keeps a “scratch” check-out +for cases where it’s inappropriate to reuse the “trunk” check-out, +isolating all of my expedient switch-in-place actions to that one +working directory. Since the other peer check-outs track long-lived +branches, and that set rarely changes once a development machine is set +up, I rarely pay the cost of these wordier commands. + +That then leads us to the closest equivalent in Git to [closing a Fossil +check-out](#close): + + git worktree remove . + +Note, however, that unlike `fossil close`, once the Git command +determines that there are no uncommitted changes, it blows away all of +the checked-out files! Fossil’s alternative is shorter, easier to +remember, and safer. + +There’s another way to get Fossil-like separate worktrees in Git: + + git clone --separate-git-dir repo.git https://example.com/repo + +This allows you to have your Git repository directory entirely separate +from your working tree, with `.git` in the check-out directory being a +file that points to `../repo.git`, in this example. + +[mcw]: ./ckout-workflows.md#mcw +[wsyml]: https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ + + +#### <a id="iip"></a> Init in Place + +To illustrate the differences that Fossil’s separation of repository +from working directory creates in practice, consider this common Git “init in place” +method for creating a new repository from an existing tree of files, +perhaps because you are placing that project under version control for +the first time: + + cd long-established-project + git init + git add * + git commit -m "Initial commit of project." + +The closest equivalent in Fossil is: + + cd long-established-project + fossil init .fsl + fossil open --force .fsl + fossil add * + fossil ci -m "Initial commit of project." + +Note that unlike in Git, you can abbreviate the “`commit`” command in +Fossil as “`ci`” for compatibility with CVS, Subversion, etc. + +This creates a `.fsl` repo DB at the root of the project check-out to +emulate the `.git` repo dir. We have to use the `--force` flag on +opening the new repo because Fossil expects you to open a repo into an +empty directory in order to avoid spamming the contents of a repo over +an existing directory full of files. Here, we know the directory +contains files that will soon belong in the repository, though, so we +override this check. From then on, Fossil works like Git, for the +purposes of this example. + +We’ve drawn this example to create a tight parallel between Fossil and +Git, not to commend this `.fsl`-at-project-root trick to you. A better +choice would be `~/museum/home/long-established-project.fossil`, if +you’re following [the directory scheme exemplified in the glossary](./glossary.md#repository). That said, it +does emphasize an earlier point: Fossil doesn’t care where you put the +repo DB file or what you name it. + + +[clone]: /help/clone +[close]: /help/close +[gloss]: ./glossary.md +[open]: /help/open +[set]: /help/setting +[server]: /help/server +[stash]: /help/stash +[undo]: /help/undo + + +## <a id="log"></a> Fossil’s Timeline Is the “Log” + +Git users often need to use the `git log` command to dig linearly through +commit histories due to its [weak data model][wdm], giving [O(n) +performance][ocomp]. + +Fossil parses a huge amount of information out of commits that allow it +to produce its [timeline CLI][tlc] and [its `/timeline` web view][tlw] +using indexed SQL lookups, which generally have the info you would have +to manually extract from `git log`, produced much more quickly than Git +can, all else being equal: operations over [SQLite’s B-tree data structures][btree] +generally run in O(log n) time, faster than O(n) for equal *n* when the +constants are equal. + +Yet the constants are *not* equal because Fossil +reads from a single disk file rather than visit potentially many +files in sequence as Git must, so the OS’s buffer cache can result in +[still better performance][35pct]. + +Unlike Git’s log, Fossil’s timeline shows info across all branches by +default, a feature for maintaining better situational awareness. +It is possible to restrict the timeline to a single branch using `fossil timeline -b`. +Similarly, to restrict the timeline using the web UI equivalent, +click the name of a branch on the `/timeline` or `/brlist` page. (Or +manually, by adding the `r=` query parameter.) Note that even in this +case, the Fossil timeline still shows other branches where they interact +with the one you’ve referenced in this way; again, better situational +awareness. + + +#### <a id="emu-log"></a> Emulating `git log` + +If you truly need a backwards-in-time-only view of history in Fossil to +emulate `git log`, this is as close as you can currently come: + + fossil timeline parents current + +Again, though, this isn’t restricted to a single branch, as `git log` +is. + +Another useful rough equivalent is: + + git log --raw + fossil time -v + +This shows what changed in each version, though Fossil’s view is more a +summary than a list of raw changes. To dig deeper into single commits, +you can use Fossil’s [`info` command][infoc] or its [`/info` view][infow]. + +Inversely, you may more exactly emulate the default `fossil timeline` +output with `git log --name-status`. + + +#### <a id="whatchanged"></a> What Changed? + +A related — though deprecated — command is `git whatchanged`, which gives results similar to +`git log --raw`, so we cover it here. + +Though there is no `fossil whatchanged` command, the same sort of +information is available. For example, to pull the current changes from +the remote repository and then inspect them before updating the local +working directory, you might say this in Git: + + git fetch + git whatchanged ..@{u} + +…which you can approximate in Fossil as: + + fossil pull + fossil up -n + fossil diff --from tip + +To invert the `diff` to show a more natural patch, the command needs to +be a bit more complicated, since you can’t currently give `--to` +without `--from`. + + fossil diff --from current --to tip + +Rather than use the “dry run” form of [the `update` command][up], you can +say: + + fossil timeline after current + +…or if you want to restrict the output to the current branch: + + fossil timeline descendants current + + +#### <a id="ckin-names"></a> Symbolic Check-In Names + +Note the use of [human-readable symbolic version names][scin] in Fossil +rather than [Git’s cryptic notations][gcn]. + +For a more dramatic example of this, let us ask Git, “What changed since the +beginning of last month?” being October 2020 as I write this: + + git log master@{2020-10-01}..HEAD + +That’s rather obscure! Fossil answers the same question with a simpler +command: + + fossil timeline after 2020-10-01 + +You may need to add `-n 0` to bypass the default output limit of +`fossil timeline`, 20 entries. Without that, this command reads +almost like English. + +Some Git users like to write commands like the above so: + + git log @{2020-10-01}..@ + +Is that better? “@” now means two different things: an at-time reference +and a shortcut for `HEAD`! + +If you are one of those that like short commands, Fossil’s method is +less cryptic: it lets you shorten words in most cases up to the point +that they become ambiguous. For example, you may abbreviate the last +`fossil` command in the prior section: + + fossil tim d c + +…beyond which the `timeline` command becomes ambiguous with `ticket`. + +Some Fossil users employ shell aliases, symlinks, or scripts to shorten +the command still further: + + alias f=fossil + f tim d c + +Granted, that’s rather obscure, but you you can also choose something +intermediate like “`f time desc curr`”, which is reasonably clear. + +[35pct]: https://www.sqlite.org/fasterthanfs.html +[btree]: https://sqlite.org/btreemodule.html +[gcn]: https://git-scm.com/docs/gitrevisions +[infoc]: /help/info +[infow]: /help/www/info +[ocomp]: https://www.bigocheatsheet.com/ +[tlc]: /help/timeline +[tlw]: /help/www/timeline +[up]: /help/update +[wdm]: ./fossil-v-git.wiki#durable + + +## <a id="dhead"></a> Detached HEAD State + +The SQL indexes in Fossil which we brought up above have a useful +side benefit: you cannot have a [detached HEAD state][gdh] in Fossil, +the source of untold pain and data loss in Git. It simply cannot be done +in Fossil, because the indexes always let us find our way back into the +hash tree. + + +## <a id="slcom"></a> Summary Line Convention in Commit Comments + +The Git convention of a [length-limited summary line][lsl] at the start +of commit comments is not enforced or obeyed by default in Fossil. +However, there is a setting under Admin → Timeline → “Truncate comment +at first blank line (Git-style)” to change this for `/timeline` +displays. Alternately, you could enable the “Allow block-markup in +timeline” setting under Admin → Timeline, then apply [local skin +customizations][cskin] to put that first comment in bold or whatever +suits. + +Because this isn’t a typical Fossil convention, you’re likely to find +other odd differences between it and Git-based infrastructure. For +instance, Vim doesn’t ship with syntax support for Fossil commit +messages if you set `EDITOR=vim` in your shell environment, so you won’t +get over-limit highlighting for first-line text beyond the 50th +character and such, because it doesn’t recognize Fossil commit messages +and apply similar rules as to Git commit messages. + +[cskin]: ./customskin.md +[lsl]: https://chris.beams.io/posts/git-commit/#limit-50 + + +<a id="autocommit"></a> +## Fossil Never Auto-Commits + +There are several features in Git besides its `commit` command that +produce a new commit to the repository, and by default, they do it +without prompting or even asking for a commit message. These include +Git’s [`rebase`](#rebase), `merge`, and [`cherrypick`](#cpickrev) +commands, plus the [commit splitting](#comsplit) sub-feature +“`git commit -p`”. + +Fossil never does this, on firm philosophical grounds: we wish to be +able to test that each potentially repository-changing command does not +break anything _before_ freezing it immutably into the [Merkle +tree](./blockchain.md). Where Fossil has equivalent commands, they +modify the checkout tree alone, requiring a separate `commit` command +afterward, withheld until the user has satisfied themselves that the +command’s result is correct. + +We believe this is the main reason Git lacks an [autosync](#autosync) +feature: making push a manual step gives the user a chance to rewrite +history after triggering one of these autocommits locally, should the +automatic commit fail to work out as expected. Fossil chooses the +inverse path under the philosophy that commits are *commitments,* not +something you’re allowed to go back and rewrite later. + +This is also why there is no automatic commit message writing feature in +Fossil, as in these autocommit-triggering Git commands. The user is +meant to write the commit message by hand after they are sure it’s +correct, in clear-headed retrospective fashion. Having the tool do it +prospectively before one can test the result is simply backwards. + + +<a id="staging"></a> +## There Is No Staging Area + +Fossil omits the "Git index" or "staging area" concept. When you +type "`fossil commit`" _all_ changes in your check-out are committed, +by default. There is no need for the "-a" option as with Git. + +If you only want to commit _some_ of the changes, list the names +of the files or directories you want to commit as arguments, like this: + + fossil commit src/feature.c doc/feature.md examples/feature + +Note that the last element is a directory name, meaning “any changed +file under the `examples/feature` directory.” + + +<a id="comsplit"></a> +## Commit Splitting + +[Git’s commit splitting features][gcspl] rely on +other features of Git that Fossil purposefully lacks, as covered in the +prior two sections: [autocommit](#autocommit) and [the staging +area](#staging). + +While there is no direct Fossil equivalent for +`git add -p`, `git commit -p`, or `git rebase -i`, you can get the same +effect by converting an uncommitted change set to a patch and then +running it through [Patchouli]. + +Rather than use `fossil diff -i` to produce such a patch, a safer and +more idiomatic method would be: + + fossil stash save -m 'my big ball-o-hackage' + fossil stash diff > my-changes.patch + +That stores your changes in the stash, then lets you operate on a copy +of that patch. Each time you re-run the second command, it will take the +current state of the working directory into account to produce a +potentially different patch, likely smaller because it leaves out patch +hunks already applied. + +In this way, the combination of working tree and stash replaces the need +for Git’s index feature. + +We believe we know how to do commit splitting in a way compatible with +the Fossil philosophy, without following Git’s ill-considered design +leads. It amounts to automating the above process through an interactive +variant of [`fossil stash apply`][stash], as currently prototyped in the +third-party tool [`fnc`][fnc] and [its interactive `stash` +command][fncsta]. We merely await someone’s [contribution][ctrb] of this +feature into Fossil proper. + +[ctrb]: https://fossil-scm.org/fossil/doc/trunk/www/contribute.wiki +[fnc]: https://fnc.bsdbox.org/ +[fncsta]: https://fnc.bsdbox.org/uv/doc/fnc.1.html#stash +[gcspl]: https://git-scm.com/docs/git-rebase#_splitting_commits +[Patchouli]: https://pypi.org/project/patchouli/ + + +<a id="bneed"></a> +## Create Branches at Point of Need, Rather Than Ahead of Need + +Fossil prefers that you create new branches as part of the first commit +on that branch: + + fossil commit --branch my-branch + +If that commit is successful, your local check-out directory is then +switched to the tip of that branch, so subsequent commits don’t need the +“`--branch`” option. You simply say `fossil commit` again to continue +adding commits to the tip of that branch. + +To switch back to the parent branch, say something like: + + fossil update trunk + +(This is approximately equivalent to `git checkout master`.) + +Fossil does also support the Git style, creating the branch ahead of +need: + + fossil branch new my-branch + fossil up my-branch + ...work on first commit... + fossil commit + +This is more verbose, giving the same overall effect though the initial +actions are inverted: create a new branch for the first commit, switch +the check-out directory to that branch, and make that first commit. As +above, subsequent commits are descendants of that initial branch commit. +We think you’ll agree that creating a branch as part of the initial +commit is simpler. + +Fossil also allows you to move a check-in to a different branch +*after* you commit it, using the "`fossil amend`" command. +For example: + + fossil amend current --branch my-branch + +This works by inserting a tag into the repository that causes the web UI +to relabel commits from that point forward with the new name. Like Git, +Fossil’s fundamental data structure is the interlinked DAG of commit +hashes; branch names are supplemental data for making it easier for the +humans to understand this DAG, so this command does not change the core +history of the project, only annotate it for better display to the +humans. + +(The version string “current” is one of the [special check-in names][scin] in Fossil. See +that document for the many other names you can give to “`amend`”, or +indeed to any other Fossil command documented to accept a `VERSION` or +`NAME` string.) + +[scin]: ./checkin_names.wiki + + +<a id="syncall"></a> +## Sync Is All-or-Nothing + +Fossil does not support the concept of syncing, pushing, or pulling +individual branches. When you sync/push/pull in Fossil, it +processes all artifacts in its hash tree: +branches, tags, wiki articles, tickets, forum posts, technotes… +This is [not quite “everything,” full stop][bu], but it’s close. +[Fossil is an AP-mode system][capt], which in this case means it works +*very hard* to ensure that all repos are as close to identical as it can +make them under this eventually-consistent design philosophy. + +Branch *names* sync automatically in Fossil, not just the +content of those branches. That means this common Git command: + + git push origin master + +…is simply this in Fossil: + + fossil push + +Fossil doesn’t need to be told what to push or where to push it: it just +keeps using the same remote server URL you gave it last +until you [tell it to do something different][rem]. It pushes all +branches, not just one named local branch. + +[capt]: ./cap-theorem.md +[rem]: /help/remote + + +<a id="autosync"></a> +## Autosync + +Fossil’s [autosync][wflow] feature, normally enabled, has no +equivalent in Git. If you want Fossil to behave like Git, you can turn +it off: + + fossil set autosync 0 + +Let’s say that you have a typical server-and-workstations model with two +working clones on different machines, that you have disabled autosync, +and that this common sequence then occurs: + +1. Alice commits to her local clone and *separately* pushes the change + up to Condor — their central server — in typical Git fashion. +2. Bob does the same. +3. Alice brings Bob’s changes down from Condor with “`fossil pull`,” sees + what he did to their shared working branch, and becomes most wrathful. + (Tsk, tsk.) + +We’ll get to what you do about this situation [below](#reset), but for +now let us focus on the fact that disabling autosync makes it easier for +[forks] to occur in the development history. If all three machines had +been online and syncing at the time the sequence above began, Bob would +have been warned in step 2 that committing to the central repo would +create a fork and would be invited to fix it before committing. +Likewise, it would allow Fossil to warn Alice about the new +tip-of-branch commit the next time she triggers an implicit autosync at +step 3, giving her a chance to bring Bob’s changes down in a +non-conflicting manner, allowing work to proceed with minimal fuss. + +Fossil, being a distributed version control system, cannot guarantee +that sequence of events. Because it allows Alice’s work to proceed +asynchronously, it gives her the chance to create *another* inadvertent +fork before she can trigger an autosync. This is not a serious problem; +Fossil resolves it the same way as with Bob, by inviting her to fix this +second fork in the same manner as it did with Bob. It gets both parties +back onto a single track as expeditiously as possible by moving the +synchronization point out of the expensive human-time workflow and into +the software system, where it’s cheapest to resolve. + +Autosync provides Fossil with most of the advantages of a centralized +version control system while retaining the advantages of distributed +version control: + +1. Your work stays synced up with your coworkers’ efforts as long as your + machine can connect to the remote repository. At need, you can go + off-network and continue work atop the last version you synced with + the remote. + +2. You get implicit off-machine backup of your commits. Unlike + centralized version control, though, you can still work while + disconnected; your changes will sync up with the remote once you get + back online. + +3. Because there is [little distinction][bu] between the clones in the Fossil + model — unlike in Git, where clones often quickly diverge from each + other, quite possibly on purpose — the backup advantage applies in inverse + as well: if the remote server falls over dead, one of those with a + clone of that repository can stand it back up, and everyone can get + back to work simply by re-pointing their local repo at the new + remote. If the failed remote comes back later, it can sync with the + new central version, then perhaps take over as the primary source of + truth once again. + +[bu]: ./backup.md +[forks]: ./branching.wiki +[setup]: ./caps/admin-v-setup.md#apsu +[wflow]: ./concepts.wiki#workflow + + +<a id="reset"></a> +## Resetting the Repository + +Extending from [the prior item](#syncall), you may correctly infer that +“[delete the project and download a fresh copy][x1597]” has no part in +the Fossil Way. Ideally, you should never find yourself forced into +desperate measures like this:(^Parsing the output of `fossil status` is +usually a mistake since it relies on a potentially unstable interface. +We make no guarantee that there will always be a line beginning with +“`repo`” and that it will be separated from the repository’s file name +by a colon. The simplified example above is also liable to become +confused by whitespace in file names.) + + +``` +$ repo=$(fossil status | grep ^repo | cut -f2 -d:) +$ url=$(fossil remote) +$ fossil close # Stop here and think if it warns you! +$ mv $repo ${repo}.old +$ fossil clone $url $repo +$ fossil open --force $repo +``` + +What, then, should you as a Git transplant do instead when you find +yourself reaching for “`git reset`”? + +Since the correct answer to that depends on why you think it’s a good +solution to your immediate problem, we’ll take our motivating scenario +from the problem setup above, where we discussed Fossil’s [autosync] +feature. Let us further say Alice’s pique results from a belief that +Bob’s commit is objectively wrong-headed and should be expunged +henceforth. Since Fossil goes out of its way to ensure that [commits are +durable][wdm], it should be no further surprise that there is no easier +method to reset Bob’s clone in favor of Alice’s than the above sequence +in Fossil’s command set. Except in extreme situations, we believe that +sort of thing is unnecessary. + +Instead, Bob can say something like this: + +``` +fossil amend --branch MISTAKE --hide --close -m "mea culpa" tip +fossil up trunk +fossil push +``` + +Unlike in Git, the “`amend`” command doesn’t modify prior committed +artifacts. Bob’s first command doesn’t delete anything, merely tells +Fossil to hide his mistake from timeline views by inserting a few new +records into the local repository to change how the client interprets +the data it finds there henceforth.(^One to change the tag marking this +commit’s branch name to “`MISTAKE`,” one to mark that branch as hidden, +and one to close it to further commits.) + +Bob’s second command switches his working directory back to the prior +commit on that branch. We’re presuming it was “`trunk`” for the sake of +the example, but it works for any parent branch name. The command works +because the name “`trunk`” now means something different to Fossil by +virtue of the first command. + +Bob’s third command pushes the changes up to the central machine to +inform everyone else of his amendment.(^Amendments don’t autosync in +Fossil because they don’t change any previous commits, allowing the +other clones to continue working safely with their existing commit +hashes.) + +In this scheme, Alice then needs to say “`fossil update trunk`” in order +to return her check-out’s parent commit to the previous version lest her +next attempted commit land atop this mistake branch. The fact that Bob +marked the branch as closed will prevent that from going through, cluing +Alice into what she needs to do to remedy the situation, but that merely +shows why it’s a better workflow if Alice makes the amendment herself: + +``` +fossil amend --branch MISTAKE --hide --close \ + -m "shunt Bob’s erroneous commit off" tip +fossil up trunk +fossil push +``` + +Then she can fire off an email listing Bob’s assorted failings and go +about her work. This asynchronous workflow solves the problem without +requiring explicit coordination with Bob. When he gets his email, he can +then say “`fossil up trunk`” himself, which by default will trigger an +autosync, pulling down Alice’s amendments and getting him back onto her +development track. + +Remember that [branch names need not be unique](#btnames) in Fossil. You +are free to reuse this “`MISTAKE`” branch name as often as you need to. + +[autosync]: #autosync +[x1597]: https://xkcd.com/1597/ + + +<a id="trunk"></a> +## The Main Branch Is Called "`trunk`" + +In Fossil, the default name for the main branch +is "`trunk`". The "`trunk`" branch in Fossil corresponds to the +"`master`" branch in stock Git or to [the “`main`” branch in GitHub][mbgh]. + +Because the `fossil git export` command has to work with both stock Git +and with GitHub, Fossil uses Git’s traditional default rather than +GitHub’s new default: your Fossil repo’s “trunk” branch becomes “master” +when [mirroring to GitHub][mirgh] unless you give the `--mainbranch` +option. + +We do not know what happens on subsequent exports if you later rename +this branch on the GitHub side. + +[mbgh]: https://github.com/github/renaming +[mirgh]: ./mirrortogithub.md + + +<a id="unmanaged"></a> +## Status Does Not Show Unmanaged Files + +The "`fossil status`" command shows you what files in your check-out have +been edited and scheduled for adding or removing at the next commit. +But unlike "`git status`", the "`fossil status`" command does not warn +you about unmanaged files in your local check-out. There is a separate +"`fossil extras`" command for that. + + +<a id="rebase"></a> +## There Is No Rebase + +Fossil does not support rebase, [on purpose][3]. + +This is a deliberate design decision that the Fossil community has +thought about carefully and discussed many times, resulting in the +linked document. If you are fond of rebase, you should read it carefully +before expressing your views: it not only answers many of the common +arguments in favor of rebase known at the time the document’s first +draft was written, it’s been revised multiple times to address less +common objections as well. Chances are not good that you are going to +come up with a new objection that we haven’t already considered and +addressed there. + +There is only one sub-feature of `git rebase` that is philosophically +compatible with Fossil yet which currently has no functional equivalent. +We [covered this and the workaround for its lack](#comsplit) above. + +[3]: ./rebaseharm.md + + +<a id="cdiff"></a> +## Colorized Diffs + +When you run `git diff` on an ANSI X3.64 capable terminal, it uses color +to distinguish insertions, deletions, and replacements, but as of this +writing, `fossil diff` produces traditional uncolored [unified diff +format][udiff] output, suitable for producing a [patch file][pfile]. + +There are [many methods](./colordiff.md) for solving this. +Viewed this way, Fossil doesn’t lack colorized diffs, it simply has +*one* method where they *aren’t* colorized. + +[pfile]: https://en.wikipedia.org/wiki/Patch_(Unix) +[udiff]: https://en.wikipedia.org/wiki/Diff#Unified_format + + +## <a id="show"></a> Showing Information About Commits + +While there is no direct equivalent to Git’s “`show`” command, similar +functionality is present in Fossil under other commands: + + +#### <a id="patch"></a> Show a Patch for a Commit + + git show -p COMMIT_ID + +…gives much the same output as + + fossil diff --checkin COMMIT_ID + +…only without the patch email header. Git comes out of the [LKML] world, +where emailing a patch is a normal thing to do. Fossil is [designed for +cohesive teams][devorg] where drive-by patches are rarer. + +You can use any of [Fossil’s special check-in names][scin] in place of +the `COMMIT_ID` in this and later examples. Fossil docs usually say +“`VERSION`” or “`NAME`” where this is allowed, since the version string +or name might not refer to a commit ID, but instead to a forum post, a +wiki document, etc. For instance, the following command answers the question “What did +I just commit?” + + fossil diff --checkin tip + +…or equivalently using a different symbolic commit name: + + fossil diff --from prev + +[devorg]: ./fossil-v-git.wiki#devorg +[LKML]: https://lkml.org/ + + +#### <a id="cmsg"></a> Show a Specific Commit Message + + git show -s COMMIT_ID + + +…is + + fossil time -n 1 COMMIT_ID + +…or with a shorter, more obvious command, though with more verbose output: + + fossil info COMMIT_ID + +The `fossil info` command isn’t otherwise a good equivalent to +`git show`; it just overlaps its functionality in some areas. Much of +what’s missing is present in the corresponding [`/info` web +view][infow], though. + + +#### <a id="dstat"></a> Diff Statistics + +Fossil’s closest internal equivalent to commands like +`git show --stat` is: + + fossil diff -i --from 2020-04-01 --numstat + +The `--numstat` output is a bit cryptic, so we recommend delegating +this task to [the widely-available `diffstat` tool][dst], which gives +a histogram in its default output mode rather than bare integers: + + fossil diff -i -v --from 2020-04-01 | diffstat + +We gave the `-i` flag in both cases to force Fossil to use its internal +diff implementation, bypassing [your local `diff-command` setting][dcset] +since the `--numstat` option has no effect when you have an external diff +command set. + +If you leave off the `-v` flag in the second example, the `diffstat` +output won’t include info about any newly-added files. + +[dcset]: https://fossil-scm.org/home/help/diff-command +[dst]: https://invisible-island.net/diffstat/diffstat.html + + +<a id="btnames"></a> +## Branch and Tag Names + +Fossil has no special restrictions on the names of tags and branches, +though you might want to keep [Git's tag and branch name restrictions][gcrf] +in mind if you plan on [mirroring your Fossil repository to GitHub][mirgh]. + +Fossil does not require tag and branch names to be unique. It is +common, for example, to put a “`release`” tag on every release for a +Fossil-hosted project. This does not create a conflict in Fossil, since +Fossil resolves the ambiguity in a predictable way: the newest match +wins. Therefore, “`fossil up release`” always gets you the current +release in a project that uses this tagging convention. + +[The `fossil git export` command][fge] squashes repeated tags down to a +single instance to avoid confusing Git, exporting only the newest tag, +emulating Fossil’s own ambiguity resolution rule as best it can within +Git’s limitations. + +[fge]: /help/git +[gcrf]: https://git-scm.com/docs/git-check-ref-format + + + + +<a id="cpickrev"></a> +## Cherry-Picking and Reverting Commits + +Git’s separate "`git cherry-pick`" and “`git revert`” commands are +options to the [`fossil merge` command][merge]: `--cherrypick` and +`--backout`, respectively. We view this as sensible, since these are +both merge operations, and the two actions differ only in direction. + +Unlike in Git, the Fossil file format remembers cherrypicks and backouts +and can later show them as dashed lines on the graphical timeline. + +[merge]: /help/merge + + + +<a id="mvrm"></a> +## File Moves and Renames Are Soft by Default + +The "[`fossil mv`][mv]" and "[`fossil rm`][rm]" commands work like they +do in CVS in that they schedule the changes for the next commit by +default: they do not actually rename or delete the files in your +check-out. + +If you don’t like that default, you can change it globally: + + fossil setting --global mv-rm-files 1 + +Now these commands behave like in Git in any Fossil repository where +this setting hasn’t been overridden locally. + +If you want to keep Fossil’s soft `mv/rm` behavior most of the time, you +can cast it away on a per-command basis: + + fossil mv --hard old-name new-name + +[mv]: /help/mv +[rm]: /help/rm + + +---- + + +## <a id="cvdate" name="cs1"></a> Case Study 1: Checking Out a Version by Date + +Let’s get into something a bit more complicated: a case study showing +how the concepts lined out above cause Fossil to materially differ in +day-to-day operation from Git. + +Why would you want to check out a version of a project by date? Perhaps +your customer gave you a vague bug report referencing only a +date rather than a version. Or, you may be poking semi-randomly through +history to find a “good” version to anchor the start point of a +[`fossil bisect`][fbis] operation. + +My search engine’s first result for “git checkout by date” is [this +highly-upvoted accepted Stack Overflow answer][gcod]. The first command +it gives is based on Git’s [`rev-parse` feature][grp]: + + git checkout master@{2020-03-17} + +There are a number of weaknesses in this command. From least to most +critical: + +1. It’s a bit cryptic. Leave off the refname or punctuation, and it + means something else. You cannot simplify the cryptic incantation in + the typical use case. + +2. A date string in Git without a time will be interpreted as + “[at the local wall clock time on the given date][gapxd],” so the + command means something different from one second to the next! This + can be a serious problem if there are multiple commits on that date, because + the command will give different results depending on the time of + day you run it. + +3. It gives misleading output if there is no close match for the date + in the local [reflog]. It starts out empty after a fresh clone, and + while it does build up as you use that clone, Git [automatically + prunes][gle] the reflog to 90 days of history by default. This means + the command above can give different results from one machine to the + next, or even from one day to the next on the same clone. + + The command won’t fail outright if the reflog can’t resolve the + given date: it simply gives the closest commit it can come up with, + even if it’s months or years out from your target! Sometimes it + gives a warning about the reflog not going back far enough to give a + useful result, and sometimes it doesn’t. If you’re on a fresh clone, + you are likely to get the “tip” commit’s revision ID no matter what + date value you give. + + Git tries its best, but because it’s working from a purgeable and + possibly-stale local cache, you cannot trust its results. + +Consequently, we cannot recommend this command at all. It’s unreliable even in the +best case. + +That same Stack Overflow answer therefore goes on to recommend an +entirely different command: + + git checkout $(git rev-list -n 1 --first-parent --before="2020-03-17" master) + +We believe you get such answers to Git help requests in part +because of its lack of an always-up-to-date [index into its log](#log) and in +part because of its “small tools loosely joined” design philosophy. This +sort of command is therefore composed piece by piece: + +<p style="text-align:center">◆ ◆ ◆</p> + +“Oh, I know, I’ll search the rev-list, which outputs commit IDs by +parsing the log backwards from `HEAD`! Easy!” + + git rev-list --before=2020-03-17 + +“Blast! Forgot the commit ID!” + + git rev-list --before=2020-03-17 master + +“Double blast! It just spammed my terminal with revision IDs! I need to +limit it to the single closest match: + + git rev-list -n 1 --before=2020-03-17 master + +“Okay, it gives me a single revision ID now, but is it what I’m after? +Let’s take a look…” + + git show $(git rev-list -n 1 --before=2020-03-17 master) + +“Oops, that’s giving me a merge commit, not what I want. +Off to search the web… Okay, it says I need to give either the +`--first-parent` or `--no-merges` flag to show only regular commits, +not merge-commits. Let’s try the first one:” + + git show $(git rev-list -n 1 --first-parent --before=2020-03-17 master) + +“Better. Let’s check it out:” + + git checkout $(git rev-list -n 1 --first-parent --before=2020-03-17 master) + +“Success, I guess?” + +<p style="text-align:center">◆ ◆ ◆</p> + +This vignette is meant to explain some of Git’s popularity: it rewards +the sort of people who enjoy puzzles, many of whom are software +developers and thus need a tool like Git. Too bad if you’re just a +normal user. + +And too bad if you’re a Windows user who doesn’t want to use [Git +Bash][gbash], since neither of the stock OS command shells have a +command interpolation feature needed to run that horrid command. + +This alternative command still has weakness #2 above: if you run the +second `git show` command above on [Git’s own repository][gitgh], your +results may vary because there were four non-merge commits to Git on the +17th of March, 2020. + +You may be asking with an exasperated huff, “What is your *point*, man?” +The point is that the equivalent in Fossil is simply: + + fossil up 2020-03-17 + +…which will *always* give the commit closest to midnight UTC on the 17th +of March, 2020, no matter whether you do it on a fresh clone or a stale +one. The answer won’t shift about from one clone to the next or from +one local time of day to the next. We owe this reliability and stability +to three Fossil design choices: + +* Parse timestamps from all commits on clone into a local commit index, + then maintain that index through subsequent commits and syncs. + +* Use an indexed SQL `ORDER BY` query to match timestamps to commit + IDs for a fast and consistent result. + +* Round incomplete timestamp strings up using [rules][frud] consistent across + computers and local time of day. + +[frud]: https://fossil-scm.org/home/file/src/name.c?ci=d2a59b03727bc3&ln=122-141 +[gbash]: https://appuals.com/what-is-git-bash/ +[gapxd]: https://github.com/git/git/blob/7f7ebe054a/date.c#L1298-L1300 +[gcod]: https://stackoverflow.com/a/6990682/142454 +[gdh]: https://www.git-tower.com/learn/git/faq/detached-head-when-checkout-commit/ +[gitgh]: https://github.com/git/git/ +[gle]: https://git-scm.com/docs/git-reflog#_options_for_expire +[gmc]: https://github.com/git/git/commit/67b0a24910fbb23c8f5e7a2c61c339818bc68296 +[grp]: https://git-scm.com/docs/git-rev-parse +[reflog]: https://git-scm.com/docs/git-reflog + +---- + +## <a id="morigin" name="cs2"></a> Case Study 2: Multiple "origin" Servers + +Now let us consider a common use case at the time of this writing — during the +COVID-19 pandemic — where you’re working from home a lot, going into the +office one part-day a week only to do things that have to be done +on-site at the office. Let us also say you have no remote +access back into the work LAN, such as because your site IT is paranoid +about security. You may still want off-machine backups of your commits +while working from home, +so you need the ability to quickly switch between the “home” and +“work” remote repositories, with your laptop acting as a kind of +[sneakernet][sn] link between the big development server at the office +and your family’s home NAS. + +#### Git Method + +We first need to clone the work repo down to our laptop, so we can work on it +at home: + + git clone https://dev-server.example.com/repo + cd repo + git remote rename origin work + +The last command is optional, strictly speaking. We could continue to +use Git’s default name for the work repo’s origin — sensibly enough +called “`origin`” — but it makes later commands harder to understand, so +we rename it here. This will also make the parallel with Fossil easier +to draw. + +The first time we go home after this, we have to reverse-clone the work +repo up to the NAS: + + ssh my-nas.local 'git init --bare /SHARES/dayjob/repo.git' + git push --all ssh://my-nas.local//SHARES/dayjob/repo.git + +Realize that this is carefully optimized down to these two long +commands. In practice, we’d expect a user typing these commands by hand from memory +to need to give four or more commands here instead. +Packing the “`git init`” call into the “`ssh`” call is something more +often done in scripts and documentation examples than done interactively, +which then necessitates a third command before the push, “`exit`”. +There’s also a good chance that you’ll forget the need for the `--bare` +option here to avoid a fatal complaint from Git that the laptop can’t +push into a non-empty repo. If you fall into this trap, among the many +that Git lays for newbies, you have to nuke the incorrectly initted +repo, search the web or Git man pages to find out about `--bare`, and try again. + +Having navigated that little minefield, +we can tell Git that there is a second origin, a “home” repo in +addition to the named “work” repo we set up earlier: + + git remote add home ssh://my-nas.local//SHARES/dayjob/repo.git + git config master.remote home + +We don’t have to push or pull because the remote repo is a complete +clone of the repo on the laptop at this point, so we can just get to +work now, committing along the way to get our work safely off-machine +and onto our home NAS, like so: + + git add + git commit + git push + +We didn’t need to give a remote name on the push because we told it the +new upstream is the home NAS earlier. + +Now Friday comes along, and one of your office-mates needs a feature +you’re working on. You agree to come into the office later that +afternoon to sync up via the dev server: + + git push work master # send your changes from home up + git pull work master # get your coworkers’ changes + +Alternately, we could add “`--set-upstream/-u work`” to the first +command if we were coming into work long enough to do several Git-based things, not just pop in and sync. +That would allow the second to be just “`git pull`”, but the cost is +that when returning home, you’d have to manually reset the upstream +again. + +This example also shows a consequence of that fact that +[Git doesn’t sync branch names](#syncall): you have to keep repeating +yourself like an obsequious supplicant: “Master, master.” Didn’t we +invent computers to serve humans, rather than the other way around? + + +#### Fossil Method + +Now we’re going to do the same thing using Fossil, with +the commands arranged in blocks corresponding to those above for comparison. + +We start the same way, cloning the work repo down to the laptop: + + fossil clone https://dev-server.example.com/repo + cd repo + fossil remote add work https://dev-server.example.com/repo + +We’ve chosen the new “`fossil clone URI`” syntax rather than separate +`clone` and `open` commands to make the parallel with Git clearer. [See +above](#mwd) for more on that topic. + +Our [`remote` command][rem] is longer than the Git equivalent because +Fossil currently has no short command +to rename an existing remote. Worse, unlike with Git, we can’t just keep +using the default remote name because Fossil uses that slot in its +configuration database to store the *current* remote name, so on +switching from work to home, the home URL will overwrite the work URL if +we don’t give it an explicit name first. + +Although the Fossil commands are longer, so far, keep it in perspective: +they’re one-time setup costs, +easily amortized to insignificance by the shorter day-to-day commands +below. + +On first beginning to work from home, we reverse-clone the Fossil repo +up to the NAS: + + rsync repo.fossil my-nas.local:/SHARES/dayjob/ + +Now we’re beginning to see the advantage of Fossil’s simpler model, +relative to the tricky “`git init && git push`” sequence above. +Fossil’s alternative is almost impossible to get +wrong: copy this to that. *Done.* + +We’re relying on the `rsync` feature that creates up to one level of +missing directory (here, `dayjob/`) on the remote. If you know in +advance that the remote directory already exists, you could use a +slightly shorter `scp` command instead. Even with the extra 2 characters +in the `rsync` form, it’s much shorter because a Fossil repository is a +single SQLite database file, not a tree containing a pile of assorted +files. Because of this, it works reliably without any of [the caveats +inherent in using `rsync` to clone a Git repo][grsync]. + +Now we set up the second remote, which is again simpler in the Fossil +case: + + fossil remote add home ssh://my-nas.local//SHARES/dayjob/repo.fossil + fossil remote home + +The first command is nearly identical to the Git version, but the second +is considerably simpler. And to be fair, you won’t find the +“`git config`” command above in all Git tutorials. The more common +alternative we found with web searches is even longer: +“`git push --set-upstream home master`”. + +Where Fossil really wins is in the next step, making the initial commit +from home: + + fossil ci + +It’s one short command for Fossil instead of three for Git — or two if +you abbreviate it as “`git commit -a && git push`” — because of Fossil’s +[autosync] feature and deliberate omission of a +[staging feature](#staging). + +The “Friday afternoon sync-up” case is simpler, too: + + fossil remote work + fossil sync + +Back at home, it’s simpler still: we may be able to do away with the second command, +saying just “`fossil remote home`” because the sync will happen as part +of the next commit, thanks once again to Fossil’s autosync feature. If +the working branch now has commits from other developers after syncing +with the central repository, though, you’ll want to say “`fossil up`” to +avoid creating an inadvertent fork in the branch. + +(Which is easy to fix if it occurs: `fossil merge` without arguments +means “merge open tips on the current branch.”) + +[grsync]: https://stackoverflow.com/q/1398018/142454 +[qs]: ./quickstart.wiki +[shwmd]: ./fossil-v-git.wiki#checkouts +[sn]: https://en.wikipedia.org/wiki/Sneakernet |
