Rewriting History

Amending patches

When you start using version control you will quickly encounter situations where you have recorded a patch and realize that you have made a mistake. For example you have recorded a patch with a typo or you have forgotten to state a dependency in a patch. That’s really super annoying. What you could do now is record a new patch correcting that error but that would make our history more and more confusing as time progresses and this kind patches start to accumulate. This is where darcs amend comes in handy, it let’s you change a patch the you have already recorded without creating a new one.

As an example we are going to pretend that we accidentally didn’t state the dependency of our use B patch in the above section correctly, so our repository once again looks like this.

So let’s use amend to right that wrong.

$ darcs amend --ask-deps -p 'use B'
patch 610c31ea41237cd407464ff41720d04bce214ef2
Author: raichoo@example.com
Date:   Mon Jul  9 20:05:30 CEST 2018
  * use B
Shall I amend this patch? [yNjk...], or ? for more options: y
patch 376b1bc0febb10802797a5afe7d55e35e42ac89f
Author: raichoo@example.com
Date:   Mon Jul  9 19:04:01 CEST 2018
  * change B
Shall I depend on this patch? (1/2)  [ynW...], or ? for more options: y
Will not ask whether to depend on 1 already decided patch.
Do you want to Depend on these patches? [Yglqk...], or ? for more options: y
Finished amending patch:
patch 9890bbfa50839674d45c416395bdb7e3bcd0ac00
Author: raichoo@example.com
Date:   Mon Jul  9 20:07:22 CEST 2018
  * use B

We have used the -p flag to state a pattern for the patch we want to amend and instructed darcs to prompt us for dependencies. We don’t need to specify a pattern but sometimes if we know exactly what patch we want to amend it can certainly speed up the process. After selecting the patch we want to take care of, darcs will prompt us for changes just like it would to when recording a change. Our repository now looks like we want it to.

amend works very much like record but rather than recording a new patch it prompts you for an existing patch that you intend to improve first and then proceeds just like we are used to when recording patches. We can add more changes to our patch, change hunks, messages, author etcetera. Keep in mind that you should not rewrite patches that have already been pushed to a public repository since this action is local only, pushed patches will remain in your remote repository even if you amend them locally. If you want to change the state of a remote repository your best option is to record a new patch and push it there.

You also cannot amend patches that are a dependency of another patch. That would be like pulling a rug away from under their feet. There are more powerful tools that let you do even that, but that’s something for a later chapter.

Another example that is a bit more common deals with typos. Typos are probably the most annoying thing when you deal with recording patches. They show up in parts of your projects as well as patch names. Heck, they are pretty much everywhere, just look at this patch gone horribly wrong.

$ darcs log -v
patch a7f5a2efe162787b3e7f6ceb215f53fa300f6590
Author: raichoo@example.com
Date:   Tue Jul 10 19:41:56 CEST 2018
  * initial rceord
    addfile ./Main.hs
    hunk ./Main.hs 1
    +main = "Hello Wrold!"

Oh boy, looks like we weren’t quite awake yet when we recorded that patch. We misspelled “World” as well as “record”. That’s quite embarrassing, surely we don’t want to push that! Thankfully we can still amend that patch before its publication.

$ sed -i '' 's/Wrold/World/' Main.hs
$ darcs amend -m 'initial record'
patch a7f5a2efe162787b3e7f6ceb215f53fa300f6590
Author: raichoo@example.com
Date:   Tue Jul 10 19:41:56 CEST 2018
  * initial rceord
Shall I amend this patch? [yNjk...], or ? for more options: y
hunk ./Main.hs 1
-main = "Hello Wrold!"
+main = "Hello World!"
Shall I record this change? (1/1)  [ynW...], or ? for more options: y
Do you want to Record these changes? [Yglqk...], or ? for more options: y
Finished amending patch:
patch 1e00a2684025c2ee9e1cbbb809d71d8167482458
Author: raichoo@example.com
Date:   Tue Jul 10 19:45:57 CEST 2018
  * initial record

Patch quality is certainly an important thing to take care of, so be sure to double check before you publish something important.

Unrecording patches

Sometimes we want to get rid of patches without getting rid of the work we have done. Maybe we want to fold a bunch of patches into one, or maybe we want to organize our patches a little differently. Whatever the case, sometimes some patches need to be removed from our local repository and unrecord is how we can do that.

Let’s say we are currently working on a new feature in our project and our history currently looks something like this.

We started out with a Haskell program just printing out “Hello!” and then we got the feature request asking us to add two more messages to the output. First we added some code that additionally prints out “World!”. At that point wanted to capture our progress, so we decided to record a patch and call it WIP. Then we proceeded to add the other message requested in the feature and record that as well.

$ cat Main.hs
main = do
  putStrLn "Hello!"
  putStrLn "World!"
  putStrLn "Everyone!"
$ darcs log -v
patch 4f43a522ae0162dcef8dab6aab0d354ede209767
Author: raichoo@example.com
Date:   Sun Jul  8 14:51:56 CEST 2018
  * WIP
    hunk ./Main.hs 4
    +  putStrLn "Everyone!"

patch b7cf79bc1e3454f7dfdee86a480e72f312b43a14
Author: raichoo@example.com
Date:   Sun Jul  8 14:51:42 CEST 2018
  * WIP
    hunk ./Main.hs 3
    +  putStrLn "World!"

patch faf56a95edb01004d10f3bd4b72ad8d2b5ad2044
Author: raichoo@example.com
Date:   Sun Jul  8 14:51:10 CEST 2018
  * initial record
    addfile ./Main.hs
    hunk ./Main.hs 1
    +main = do
    +  putStrLn "Hello!"

That’s not a very useful history and really not fit for publishing since it’s showing our work in progress rather than a finished patch. It certainly served its purpose, but it really needs to be cleaned up before we show it to the rest of the world. We have recorded the state of our working tree whenever we felt confident and then moved onward to the next step. We want to squash the two WIP patches into a single patch. The first thing we do is that we unrecord them. Since we have named all our “work in progress” patches WIP we can specify a pattern for unrecord using the -p flag so darcs will only ask us if it should unrecord patches that have a name matching this pattern.

$ darcs unrecord -p WIP
patch 4f43a522ae0162dcef8dab6aab0d354ede209767
Author: raichoo@example.com
Date:   Sun Jul  8 14:51:56 CEST 2018
  * WIP
Shall I unrecord this patch? (1/2)  [ynW...], or ? for more options: y
patch b7cf79bc1e3454f7dfdee86a480e72f312b43a14
Author: raichoo@example.com
Date:   Sun Jul  8 14:51:42 CEST 2018
  * WIP
Shall I unrecord this patch? (2/2)  [ynW...], or ? for more options: y
Do you want to Unrecord these patches? [Yglqk...], or ? for more options: y
Finished unrecording.
$ darcs log
patch faf56a95edb01004d10f3bd4b72ad8d2b5ad2044
Author: raichoo@example.com
Date:   Sun Jul  8 14:51:10 CEST 2018
  * initial record

Okay, good. We are now back with only one patch in our repository. We have decided that this is the only thing that we want to keep and re-record our work on top of that. Since unrecord does not change our working tree still contains our work.

$ cat Main.hs
main = do
  putStrLn "Hello!"
  putStrLn "World!"
  putStrLn "Everyone!"

Now all the modifications that we have done to our project since the initial record are now unrecorded again and darcs whatsnew will report them as such.

$ darcs whatsnew
hunk ./Main.hs 3
+  putStrLn "World!"
+  putStrLn "Everyone!"

So let’s create a new patch single patch out of these changes and call it add cool new feature.

$ darcs record -a -m 'add cool new feature'
Finished recording patch 'add cool new feature'
$ darcs log -v
patch 07729b85848c949e03ae856005a8c90bf42c785a
Author: raichoo@example.com
Date:   Sun Jul  8 15:11:42 CEST 2018
  * add cool new feature
    hunk ./Main.hs 3
    +  putStrLn "World!"
    +  putStrLn "Everyone!"

patch faf56a95edb01004d10f3bd4b72ad8d2b5ad2044
Author: raichoo@example.com
Date:   Sun Jul  8 14:51:10 CEST 2018
  * initial record
    addfile ./Main.hs
    hunk ./Main.hs 1
    +main = do
    +  putStrLn "Hello!"

That’s better. We are now happy with our new patch. Again, keep in mind that you should not do this with patches you have already published. Just like amend this operation is local only.

Unrecording individual changes

In the previous sections we have met amend and unrecord and sometimes it would be great if we could bring those two functionalities together. Image you have recorded a patch but instead of adding something to it by using amend you would like to get rid of a change in that patch. Right now we only know how to add things to a patch by using amend or unrecord to get right of the whole thing. So here’s a situation where things went a bit wrong. We have recorded a patch and added a change that we didn’t really want.

patch 319dfd6efb2f69bcb5205263aa716cfe6e5d54ea
Author: raichoo@example.com
Date:   Mon Jul 16 20:20:28 CEST 2018
  * adding some stuff
    hunk ./file.txt 1
    +I want to add this
    hunk ./file.txt 3
    +but not this…

So one possibility would be to unrecord the whole patch and record it again, this time without the change that we didn’t want to have in there. In this situation that might not be that big of an issue, but if we had a larger patch and only wanted to get rid of a single line this approach is unacceptable. However, amend has a very handy --unrecord flag that allows us to unrecord individual changes from a patch. Super useful!

$ darcs amend --unrecord
patch 319dfd6efb2f69bcb5205263aa716cfe6e5d54ea
Author: raichoo@example.com
Date:   Mon Jul 16 20:20:28 CEST 2018
  * adding some stuff
Shall I amend this patch? [yNjk...], or ? for more options: y
hunk ./file.txt 1
+I want to add this
Shall I unrecord this change? (1/2)  [ynW...], or ? for more options: n
hunk ./file.txt 3
+but not this…
Shall I unrecord this change? (2/2)  [ynW...], or ? for more options: y
Do you want to Unrecord these changes? [Yglqk...], or ? for more options: y
Finished amending patch:
patch 796db1adfd5a11c00feec4ee50fff63e4fd4ddd1
Author: raichoo@example.com
Date:   Mon Jul 16 20:25:22 CEST 2018
  * adding some stuff
$ darcs log -v --last 1
patch 796db1adfd5a11c00feec4ee50fff63e4fd4ddd1
Author: raichoo@example.com
Date:   Mon Jul 16 20:25:22 CEST 2018
  * adding some stuff
    hunk ./file.txt 1
    +I want to add this
$ darcs whatsnew
hunk ./file.txt 3
+but not this…

That’s a lot more convenient and this way we can make a lot less mistakes than if we were to re-record our patch.

OB-LIT-E-RATE!

Sometime we want to go even further than unrecord. For example when a feature we are working on turns out to be completely broken and we think that we are better off to start from scratch. Of course we could just do an unrecord followed by a revert to make the patches disappear from our repository and then make the changes disappear from our working tree. If that is what you want you can use obliterate. That command sounds scary for a reason. It basically throws away work permanently.

So here’s our repository from the first section again.

$ ls
A  B _darcs
$ darcs log
patch 9890bbfa50839674d45c416395bdb7e3bcd0ac00
Author: raichoo@example.com
Date:   Mon Jul  9 20:07:22 CEST 2018
  * use B

patch 376b1bc0febb10802797a5afe7d55e35e42ac89f
Author: raichoo@example.com
Date:   Mon Jul  9 19:04:01 CEST 2018
  * change B

patch 1f9b4b1566ce3410c70ad5231ff21abb0dfed04b
Author: raichoo@example.com
Date:   Mon Jul  9 19:02:49 CEST 2018
  * change A

patch 0d87b94c8d03fea3a2b613c43032df8da8cbff93
Author: raichoo@example.com
Date:   Mon Jul  9 19:02:02 CEST 2018
  * add B

patch 0b2f173ea79d311c34d8e321b6a8d86582d18ff4
Author: raichoo@example.com
Date:   Mon Jul  9 19:01:03 CEST 2018
  * add A

We now want to get rid of all of the changes involving the file B and we want to get rid of them for good.

$ darcs obliterate
patch 9890bbfa50839674d45c416395bdb7e3bcd0ac00
Author: raichoo@example.com
Date:   Mon Jul  9 20:07:22 CEST 2018
  * use B
Shall I obliterate this patch? (1/5)  [ynW...], or ? for more options: y
patch 376b1bc0febb10802797a5afe7d55e35e42ac89f
Author: raichoo@example.com
Date:   Mon Jul  9 19:04:01 CEST 2018
  * change B
Shall I obliterate this patch? (2/5)  [ynW...], or ? for more options: y
patch 1f9b4b1566ce3410c70ad5231ff21abb0dfed04b
Author: raichoo@example.com
Date:   Mon Jul  9 19:02:49 CEST 2018
  * change A
Shall I obliterate this patch? (3/5)  [ynW...], or ? for more options: n
patch 0d87b94c8d03fea3a2b613c43032df8da8cbff93
Author: raichoo@example.com
Date:   Mon Jul  9 19:02:02 CEST 2018
  * add B
Shall I obliterate this patch? (4/5)  [ynW...], or ? for more options: y
Will not ask whether to obliterate 1 already decided patch.
Do you want to Obliterate these patches? [Yglqk...], or ? for more options: y
Finished obliterating.
$ darcs log
patch 1f9b4b1566ce3410c70ad5231ff21abb0dfed04b
Author: raichoo@example.com
Date:   Mon Jul  9 19:02:49 CEST 2018
  * change A

patch 0b2f173ea79d311c34d8e321b6a8d86582d18ff4
Author: raichoo@example.com
Date:   Mon Jul  9 19:01:03 CEST 2018
  * add A
$ ls
A  _darcs

obliterate has done its name justice. All the patches involving B are gone as well as the related files in our working tree. Notice how darcs has kept all our changes regarding file A even though the changes to A and B where interleaved in our first darcs log output. But there is no relationship between those changes so darcs could get separate those two cleanly from one another.