Fossil SCM

Reworked sections 2.7 and 2.8 in fossil-v-git to be clearer about history rewriting: commit + autosync vs. commit + push, rebase + drop vs. shunning, etc.

wyoung 2020-11-05 02:10 trunk
Commit 18fc69710671d9f163a5e7659791289242e1d27732374f86e382ba87f72ab0f2
1 file changed +81 -30
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -697,13 +697,16 @@
697697
698698
<h3 id="history">2.7 What you should have done vs. What you actually did</h3>
699699
700700
Git puts a lot of emphasis on maintaining
701701
a "clean" check-in history. Extraneous and experimental branches by
702
-individual developers often never make it into the main repository. And
703
-branches are often rebased before being pushed, to make
704
-it appear as if development had been linear. Git strives to record what
702
+individual developers often never make it into the main repository.
703
+Branches may be rebased before being pushed to make
704
+it appear as if development had been linear, or "squashed" to make it
705
+appear that multiple commits were made as a single commit.
706
+There are [https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History |
707
+other history rewriting mechanisms in Git] as well. Git strives to record what
705708
the development of a project should have looked like had there been no
706709
mistakes.
707710
708711
Fossil, in contrast, puts more emphasis on recording exactly what happened,
709712
including all of the messy errors, dead-ends, experimental branches, and
@@ -716,69 +719,117 @@
716719
Like Git, Fossil has an [/help?cmd=amend|amend command] for modifying
717720
prior commits, but unlike in Git, this works not by replacing data in
718721
the repository, but by adding a correction record to the repository that
719722
affects how later Fossil operations present the corrected data. The old
720723
information is still there in the repository, it is just overridden from
721
-the amendment point forward. For extreme situations, Fossil adds the
722
-[/doc/trunk/www/shunning.wiki|shunning mechanism], but it has strict
723
-limitations that prevent global history rewrites.
724
+the amendment point forward.
725
+
726
+Fossil lacks almost every other history rewriting mechanism listed on
727
+the Git documentation page linked above. [./rebaseharm.md | There is no
728
+rebase] in Fossil, on purpose, thus no way to reorder or copy commits
729
+around in the commit hash tree. There is no commit squashing, dropping,
730
+or interactive patch-based cherry-picking of commit elements in Fossil.
731
+There is nothing like Git's <tt>filter-branch</tt> in Fossil.
732
+
733
+The lone exception is deleting commits. Fossil has two methods for doing
734
+that, both of which have stringent limitations, on purpose.
735
+
736
+The first is [/doc/trunk/www/shunning.wiki | shunning]. See that
737
+document for details, but briefly, you only get mandatory compliance
738
+for shun requests within a single repository. Shun requests do not
739
+propagate automatically between repository clones. A Fossil repository
740
+administrator can <i>cooperatively</i> pull another repo's shun requests
741
+across a sync boundary, so that two admins can get together and agree to
742
+shun certain committed artifacts, but a person cannot force their local
743
+shun requests into another repo without having admin-level control over
744
+the receiving repo as well. Fossil's shun feature isn't for fixing up
745
+everyday bad commits, it's for dealing with extreme situations: public
746
+commits of secret material, ticket/wiki/forum spam, law enforcement
747
+takedown demands, etc.
748
+
749
+There is also the experimental [/help?cmd=purge | <tt>purge</tt>
750
+command], which differs from shunning in ways that aren't especially
751
+important in the context of this document. At a 30000 foot level, you
752
+can think of purging as useful only when you've turned off Fossil's
753
+autosync feature and want to pluck artifacts out of its hash tree before
754
+they get pushed. In that sense, it's approximately the same as
755
+<tt>git rebase -i, drop</tt>. However, given that Fossil defaults to
756
+having autosync enabled [#devorg | for good reason], the purge command
757
+isn't very useful in practice: once a commit has been pushed into
758
+another repo, shunning is more useful if you need to delete it from
759
+history.
760
+
761
+If these accommodations strike you as incoherent with respect to
762
+Fossil's philosophy of durable, unchanging commits, realize that if
763
+shunning and purging were removed from Fossil, you could still remove
764
+artifacts from the repository with SQL <tt>DELETE</tt> statements; the
765
+repository database file is, after all, directly modifiable, being
766
+writable by your user. Where the Fossil philosophy really takes hold is
767
+in making it difficult to violate the integrity of the hash tree.
768
+It's somewhat tangential, but the document [./blockchain.md | "Is Fossil
769
+a Blockchain?"] touches on this and related topics.
724770
725771
One commentator characterized Git as recording history according to
726772
the victors, whereas Fossil records history as it actually happened.
727773
728
-We go into more detail on this topic in a separate article,
729
-[./rebaseharm.md | Rebase Considered Harmful].
730
-
731774
732775
<h3 id="testing">2.8 Test Before Commit</h3>
733776
734777
One of the things that falls out of Git's default separation of commit
735778
from push is that there are several Git sub-commands that jump straight
736779
to the commit step before a change could possibly be tested. Fossil, by
737780
contrast, makes the equivalent change to the local working check-out
738781
only, requiring a separate check-in step to commit the change. This
739782
design difference falls naturally out of Fossil's default-enabled
740
-autosync feature.
783
+autosync feature and its philosophy of [#history | not offering history
784
+rewriting features].
741785
742786
The prime example in Git is rebasing: the change happens to the local
743787
repository immediately if successful, even though you haven't tested the
744788
change yet. It's possible to argue for such a design in a tool like Git
745
-which doesn't automatically push the change up to its parent, because
746
-you can still test the change before pushing local changes to the parent
747
-repo, but in the meantime you've made a durable change to your local Git
748
-repository. You must do something drastic like <tt>git
749
-reset --hard</tt> to revert that rebase if it causes a problem. If you
750
-push your rebased local repo up to the parent without testing first,
751
-you've now committed the error on a public branch, effectively a
752
-violation of
789
+since it lacks an autosync feature, because you can still test the
790
+change before pushing local changes to the parent repo, but in the
791
+meantime you've made a durable change to your local Git repository. You
792
+must do something drastic like <tt>git reset --hard</tt> to revert that
793
+rebase or rewrite history before pushing it if the rebase causes a
794
+problem. If you push your rebased local repo up to the parent without
795
+testing first, you cannot fix it without violating
753796
[https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing
754797
| the golden rule of rebasing].
755798
756799
Lesser examples are the Git <tt>merge</tt>, <tt>cherry-pick</tt>, and
757800
<tt>revert</tt> commands, all of which apply work from one branch onto
758
-another, and all of which do their work immediately without giving you
759
-an opportunity to test the change first locally unless you give the
760
-<tt>--no-commit</tt> option.
801
+another, and all of which commit their change to the local repository
802
+immediately without giving you
803
+an opportunity to test the change first unless you give the
804
+<tt>--no-commit</tt> option. Otherwise, you're back in the same boat:
805
+reset the local repository or rewrite history to fix things, then maybe
806
+retry.
761807
762808
Fossil cannot sensibly work that way because of its default-enabled
763
-autosync feature. Instead of jumping straight to the commit step, Fossil
809
+autosync feature and its purposeful paucity of commands for modifying
810
+commits, as discussed in [#history | the prior section].
811
+
812
+Instead of jumping straight to the commit step, Fossil
764813
applies the proposed merge to the local working directory only,
765814
requiring a separate check-in step before the change is committed to the
766815
repository. This gives you a chance to test the change first,
767816
either manually or by running your software's automatic tests. (Ideally,
768
-both!)
817
+both!) Thus, Fossil doesn't need rebase, squashing,
818
+<tt>reset --hard</tt>, or other Git commit mutating mechanisms.
769819
770
-Another difference is that because Fossil requires an explicit commit
771
-for a merge, it makes you give an explicit commit <i>message</i> for
772
-each merge, whereas Git writes that commit message itself by default
820
+Because Fossil requires an explicit commit for a merge, it has the nice
821
+side benefit that it makes you give an explicit commit <i>message</i>
822
+for each merge, whereas Git writes that commit message itself by default
773823
unless you give the optional <tt>--edit</tt> flag to override it.
774824
775825
We don't look at this difference as a workaround in Fossil for autosync,
776
-but instead as a test-first philosophical difference. When every commit
777
-is pushed to the parent repo by default, it encourages a working style
778
-in which every commit is tested first. We think this is an inherently
779
-good thing.
826
+but instead as a test-first philosophical difference:
827
+<tt>fossil commit</tt> is a <i>commitment</i>. When every commit is
828
+pushed to the parent repo by default, it encourages a working style in
829
+which every commit is tested first. It encourages thinking before
830
+acting. We believe this is an inherently good thing.
780831
781832
Incidentally, this is a good example of Git's messy command design.
782833
These three commands:
783834
784835
<pre>
785836
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -697,13 +697,16 @@
697
698 <h3 id="history">2.7 What you should have done vs. What you actually did</h3>
699
700 Git puts a lot of emphasis on maintaining
701 a "clean" check-in history. Extraneous and experimental branches by
702 individual developers often never make it into the main repository. And
703 branches are often rebased before being pushed, to make
704 it appear as if development had been linear. Git strives to record what
 
 
 
705 the development of a project should have looked like had there been no
706 mistakes.
707
708 Fossil, in contrast, puts more emphasis on recording exactly what happened,
709 including all of the messy errors, dead-ends, experimental branches, and
@@ -716,69 +719,117 @@
716 Like Git, Fossil has an [/help?cmd=amend|amend command] for modifying
717 prior commits, but unlike in Git, this works not by replacing data in
718 the repository, but by adding a correction record to the repository that
719 affects how later Fossil operations present the corrected data. The old
720 information is still there in the repository, it is just overridden from
721 the amendment point forward. For extreme situations, Fossil adds the
722 [/doc/trunk/www/shunning.wiki|shunning mechanism], but it has strict
723 limitations that prevent global history rewrites.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
724
725 One commentator characterized Git as recording history according to
726 the victors, whereas Fossil records history as it actually happened.
727
728 We go into more detail on this topic in a separate article,
729 [./rebaseharm.md | Rebase Considered Harmful].
730
731
732 <h3 id="testing">2.8 Test Before Commit</h3>
733
734 One of the things that falls out of Git's default separation of commit
735 from push is that there are several Git sub-commands that jump straight
736 to the commit step before a change could possibly be tested. Fossil, by
737 contrast, makes the equivalent change to the local working check-out
738 only, requiring a separate check-in step to commit the change. This
739 design difference falls naturally out of Fossil's default-enabled
740 autosync feature.
 
741
742 The prime example in Git is rebasing: the change happens to the local
743 repository immediately if successful, even though you haven't tested the
744 change yet. It's possible to argue for such a design in a tool like Git
745 which doesn't automatically push the change up to its parent, because
746 you can still test the change before pushing local changes to the parent
747 repo, but in the meantime you've made a durable change to your local Git
748 repository. You must do something drastic like <tt>git
749 reset --hard</tt> to revert that rebase if it causes a problem. If you
750 push your rebased local repo up to the parent without testing first,
751 you've now committed the error on a public branch, effectively a
752 violation of
753 [https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing
754 | the golden rule of rebasing].
755
756 Lesser examples are the Git <tt>merge</tt>, <tt>cherry-pick</tt>, and
757 <tt>revert</tt> commands, all of which apply work from one branch onto
758 another, and all of which do their work immediately without giving you
759 an opportunity to test the change first locally unless you give the
760 <tt>--no-commit</tt> option.
 
 
 
761
762 Fossil cannot sensibly work that way because of its default-enabled
763 autosync feature. Instead of jumping straight to the commit step, Fossil
 
 
 
764 applies the proposed merge to the local working directory only,
765 requiring a separate check-in step before the change is committed to the
766 repository. This gives you a chance to test the change first,
767 either manually or by running your software's automatic tests. (Ideally,
768 both!)
 
769
770 Another difference is that because Fossil requires an explicit commit
771 for a merge, it makes you give an explicit commit <i>message</i> for
772 each merge, whereas Git writes that commit message itself by default
773 unless you give the optional <tt>--edit</tt> flag to override it.
774
775 We don't look at this difference as a workaround in Fossil for autosync,
776 but instead as a test-first philosophical difference. When every commit
777 is pushed to the parent repo by default, it encourages a working style
778 in which every commit is tested first. We think this is an inherently
779 good thing.
 
780
781 Incidentally, this is a good example of Git's messy command design.
782 These three commands:
783
784 <pre>
785
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -697,13 +697,16 @@
697
698 <h3 id="history">2.7 What you should have done vs. What you actually did</h3>
699
700 Git puts a lot of emphasis on maintaining
701 a "clean" check-in history. Extraneous and experimental branches by
702 individual developers often never make it into the main repository.
703 Branches may be rebased before being pushed to make
704 it appear as if development had been linear, or "squashed" to make it
705 appear that multiple commits were made as a single commit.
706 There are [https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History |
707 other history rewriting mechanisms in Git] as well. Git strives to record what
708 the development of a project should have looked like had there been no
709 mistakes.
710
711 Fossil, in contrast, puts more emphasis on recording exactly what happened,
712 including all of the messy errors, dead-ends, experimental branches, and
@@ -716,69 +719,117 @@
719 Like Git, Fossil has an [/help?cmd=amend|amend command] for modifying
720 prior commits, but unlike in Git, this works not by replacing data in
721 the repository, but by adding a correction record to the repository that
722 affects how later Fossil operations present the corrected data. The old
723 information is still there in the repository, it is just overridden from
724 the amendment point forward.
725
726 Fossil lacks almost every other history rewriting mechanism listed on
727 the Git documentation page linked above. [./rebaseharm.md | There is no
728 rebase] in Fossil, on purpose, thus no way to reorder or copy commits
729 around in the commit hash tree. There is no commit squashing, dropping,
730 or interactive patch-based cherry-picking of commit elements in Fossil.
731 There is nothing like Git's <tt>filter-branch</tt> in Fossil.
732
733 The lone exception is deleting commits. Fossil has two methods for doing
734 that, both of which have stringent limitations, on purpose.
735
736 The first is [/doc/trunk/www/shunning.wiki | shunning]. See that
737 document for details, but briefly, you only get mandatory compliance
738 for shun requests within a single repository. Shun requests do not
739 propagate automatically between repository clones. A Fossil repository
740 administrator can <i>cooperatively</i> pull another repo's shun requests
741 across a sync boundary, so that two admins can get together and agree to
742 shun certain committed artifacts, but a person cannot force their local
743 shun requests into another repo without having admin-level control over
744 the receiving repo as well. Fossil's shun feature isn't for fixing up
745 everyday bad commits, it's for dealing with extreme situations: public
746 commits of secret material, ticket/wiki/forum spam, law enforcement
747 takedown demands, etc.
748
749 There is also the experimental [/help?cmd=purge | <tt>purge</tt>
750 command], which differs from shunning in ways that aren't especially
751 important in the context of this document. At a 30000 foot level, you
752 can think of purging as useful only when you've turned off Fossil's
753 autosync feature and want to pluck artifacts out of its hash tree before
754 they get pushed. In that sense, it's approximately the same as
755 <tt>git rebase -i, drop</tt>. However, given that Fossil defaults to
756 having autosync enabled [#devorg | for good reason], the purge command
757 isn't very useful in practice: once a commit has been pushed into
758 another repo, shunning is more useful if you need to delete it from
759 history.
760
761 If these accommodations strike you as incoherent with respect to
762 Fossil's philosophy of durable, unchanging commits, realize that if
763 shunning and purging were removed from Fossil, you could still remove
764 artifacts from the repository with SQL <tt>DELETE</tt> statements; the
765 repository database file is, after all, directly modifiable, being
766 writable by your user. Where the Fossil philosophy really takes hold is
767 in making it difficult to violate the integrity of the hash tree.
768 It's somewhat tangential, but the document [./blockchain.md | "Is Fossil
769 a Blockchain?"] touches on this and related topics.
770
771 One commentator characterized Git as recording history according to
772 the victors, whereas Fossil records history as it actually happened.
773
 
 
 
774
775 <h3 id="testing">2.8 Test Before Commit</h3>
776
777 One of the things that falls out of Git's default separation of commit
778 from push is that there are several Git sub-commands that jump straight
779 to the commit step before a change could possibly be tested. Fossil, by
780 contrast, makes the equivalent change to the local working check-out
781 only, requiring a separate check-in step to commit the change. This
782 design difference falls naturally out of Fossil's default-enabled
783 autosync feature and its philosophy of [#history | not offering history
784 rewriting features].
785
786 The prime example in Git is rebasing: the change happens to the local
787 repository immediately if successful, even though you haven't tested the
788 change yet. It's possible to argue for such a design in a tool like Git
789 since it lacks an autosync feature, because you can still test the
790 change before pushing local changes to the parent repo, but in the
791 meantime you've made a durable change to your local Git repository. You
792 must do something drastic like <tt>git reset --hard</tt> to revert that
793 rebase or rewrite history before pushing it if the rebase causes a
794 problem. If you push your rebased local repo up to the parent without
795 testing first, you cannot fix it without violating
 
796 [https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing
797 | the golden rule of rebasing].
798
799 Lesser examples are the Git <tt>merge</tt>, <tt>cherry-pick</tt>, and
800 <tt>revert</tt> commands, all of which apply work from one branch onto
801 another, and all of which commit their change to the local repository
802 immediately without giving you
803 an opportunity to test the change first unless you give the
804 <tt>--no-commit</tt> option. Otherwise, you're back in the same boat:
805 reset the local repository or rewrite history to fix things, then maybe
806 retry.
807
808 Fossil cannot sensibly work that way because of its default-enabled
809 autosync feature and its purposeful paucity of commands for modifying
810 commits, as discussed in [#history | the prior section].
811
812 Instead of jumping straight to the commit step, Fossil
813 applies the proposed merge to the local working directory only,
814 requiring a separate check-in step before the change is committed to the
815 repository. This gives you a chance to test the change first,
816 either manually or by running your software's automatic tests. (Ideally,
817 both!) Thus, Fossil doesn't need rebase, squashing,
818 <tt>reset --hard</tt>, or other Git commit mutating mechanisms.
819
820 Because Fossil requires an explicit commit for a merge, it has the nice
821 side benefit that it makes you give an explicit commit <i>message</i>
822 for each merge, whereas Git writes that commit message itself by default
823 unless you give the optional <tt>--edit</tt> flag to override it.
824
825 We don't look at this difference as a workaround in Fossil for autosync,
826 but instead as a test-first philosophical difference:
827 <tt>fossil commit</tt> is a <i>commitment</i>. When every commit is
828 pushed to the parent repo by default, it encourages a working style in
829 which every commit is tested first. It encourages thinking before
830 acting. We believe this is an inherently good thing.
831
832 Incidentally, this is a good example of Git's messy command design.
833 These three commands:
834
835 <pre>
836

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button