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.
Commit
18fc69710671d9f163a5e7659791289242e1d27732374f86e382ba87f72ab0f2
Parent
8f96db71d3fec99…
1 file changed
+81
-30
+81
-30
| --- www/fossil-v-git.wiki | ||
| +++ www/fossil-v-git.wiki | ||
| @@ -697,13 +697,16 @@ | ||
| 697 | 697 | |
| 698 | 698 | <h3 id="history">2.7 What you should have done vs. What you actually did</h3> |
| 699 | 699 | |
| 700 | 700 | Git puts a lot of emphasis on maintaining |
| 701 | 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 | |
| 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 | |
| 705 | 708 | the development of a project should have looked like had there been no |
| 706 | 709 | mistakes. |
| 707 | 710 | |
| 708 | 711 | Fossil, in contrast, puts more emphasis on recording exactly what happened, |
| 709 | 712 | including all of the messy errors, dead-ends, experimental branches, and |
| @@ -716,69 +719,117 @@ | ||
| 716 | 719 | Like Git, Fossil has an [/help?cmd=amend|amend command] for modifying |
| 717 | 720 | prior commits, but unlike in Git, this works not by replacing data in |
| 718 | 721 | the repository, but by adding a correction record to the repository that |
| 719 | 722 | affects how later Fossil operations present the corrected data. The old |
| 720 | 723 | 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. | |
| 724 | 770 | |
| 725 | 771 | One commentator characterized Git as recording history according to |
| 726 | 772 | the victors, whereas Fossil records history as it actually happened. |
| 727 | 773 | |
| 728 | -We go into more detail on this topic in a separate article, | |
| 729 | -[./rebaseharm.md | Rebase Considered Harmful]. | |
| 730 | - | |
| 731 | 774 | |
| 732 | 775 | <h3 id="testing">2.8 Test Before Commit</h3> |
| 733 | 776 | |
| 734 | 777 | One of the things that falls out of Git's default separation of commit |
| 735 | 778 | from push is that there are several Git sub-commands that jump straight |
| 736 | 779 | to the commit step before a change could possibly be tested. Fossil, by |
| 737 | 780 | contrast, makes the equivalent change to the local working check-out |
| 738 | 781 | only, requiring a separate check-in step to commit the change. This |
| 739 | 782 | 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]. | |
| 741 | 785 | |
| 742 | 786 | The prime example in Git is rebasing: the change happens to the local |
| 743 | 787 | repository immediately if successful, even though you haven't tested the |
| 744 | 788 | 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 | |
| 753 | 796 | [https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing |
| 754 | 797 | | the golden rule of rebasing]. |
| 755 | 798 | |
| 756 | 799 | Lesser examples are the Git <tt>merge</tt>, <tt>cherry-pick</tt>, and |
| 757 | 800 | <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. | |
| 761 | 807 | |
| 762 | 808 | 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 | |
| 764 | 813 | applies the proposed merge to the local working directory only, |
| 765 | 814 | requiring a separate check-in step before the change is committed to the |
| 766 | 815 | repository. This gives you a chance to test the change first, |
| 767 | 816 | 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. | |
| 769 | 819 | |
| 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 | |
| 773 | 823 | unless you give the optional <tt>--edit</tt> flag to override it. |
| 774 | 824 | |
| 775 | 825 | 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. | |
| 780 | 831 | |
| 781 | 832 | Incidentally, this is a good example of Git's messy command design. |
| 782 | 833 | These three commands: |
| 783 | 834 | |
| 784 | 835 | <pre> |
| 785 | 836 |
| --- 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 |