~jan0sch/darcs-book
~jan0sch/darcs-book/en/08-rewriting-history.md
~jan0sch/darcs-book/en/08-rewriting-history.md
title: Rewriting History next: chapter09 prev: chapter07 toclink: yes ---
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.
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.
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.
Sometimes 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 separate those two cleanly from one
another.
There is also a way to obliterate
all patches that are not in a remote
repository. This is particularly useful if you have applied local patches to
your repository that clash with remote patches. To remove local-only patches
just add the --not-in-remote
flag.
rebase
are belong to us!Sometimes amend
just does not cut it. We might need to go deeper because we
have spotted a mistake we have made in a patch further down our dependency
chain. Imagine the following situation.
$ darcs log
patch 7f4723c5a539c75fce70dd7a3e7b12fcb284aa7b
Author: raichoo@example.com
Date: Mon Aug 17 12:16:01 CEST 2020
* C
patch 7ed8dfe47953186ca4d97cee32898e926bf5eb7e
Author: raichoo@example.com
Date: Mon Aug 17 12:15:24 CEST 2020
* B
patch d3fbcaf6e187b1e582caf031d39f29ddf332570b
Author: raichoo@example.com
Date: Mon Aug 17 12:15:08 CEST 2020
* A
As it turns out, I made a mistake in patch B
which I want to repair before
pushing my changes to the remote repository. So let's try to amend
B
.
$ darcs amend
patch 7f4723c5a539c75fce70dd7a3e7b12fcb284aa7b
Author: raichoo@example.com
Date: Mon Aug 17 12:16:01 CEST 2020
* C
Shall I amend this patch? [yNjk...], or ? for more options: n
Skipping depended-upon patch:
patch 7ed8dfe47953186ca4d97cee32898e926bf5eb7e
Author: raichoo@example.com
Date: Mon Aug 17 12:15:24 CEST 2020
* B
Skipping depended-upon patch:
patch d3fbcaf6e187b1e582caf031d39f29ddf332570b
Author: raichoo@example.com
Date: Mon Aug 17 12:15:08 CEST 2020
* A
Cancelling amend since no patch was selected.
Oh no! What happened? We cannot amend
B
. darcs
does not even prompt it as
an option here. Why is that? Let's take a look at the dependency graph to shed a
little light onto the situation and things will become a little clearer.
Ah! C
depends on B
and A
, so we can not simply amend
B
because we
would pull the rug from under C
's feet (remember that amend
changes patch
identity, but C
already depends on B
). What we could do is just unrecord
C
, then amend
B
and record
C
again. That would work, but it's not a
very good approach if you have to go multiple patches deeper. You don't want to
re-record
all of them again, it would be way too much work and also quite
error prone.
Here is where rebase
comes into play. As the name suggests, it allows us to
change the dependencies a patch it based on. Naturally this means we are changing
the patches identity that we are rebasing, since we are changing what they are
depending on.
So how does it work?
The rebase
command allows us to suspend
and unsuspend
patches. Suspending
means that we putting a patch on the side, it doesn't really apply to our
repository anymore, but it is still floating around in a suspended state though.
So we want to work on B
, that means that we first have to suspend C
.
$ darcs rebase suspend
patch 7f4723c5a539c75fce70dd7a3e7b12fcb284aa7b
Author: raichoo@example.com
Date: Mon Aug 17 12:16:01 CEST 2020
* C
Shall I suspend this patch? (1/3) [ynW...], or ? for more options: y
patch 7ed8dfe47953186ca4d97cee32898e926bf5eb7e
Author: raichoo@example.com
Date: Mon Aug 17 12:15:24 CEST 2020
* B
Shall I suspend this patch? (2/3) [ynW...], or ? for more options: d
Rebase in progress: 1 suspended patch
In the last line tells us that there is one suspended patch, waiting for us to
take care of it. If we log take a look at our log
we can see that C
is no
longer part of our repository. This is going to happen with every command that
we issue at this point, just to make sure that we do not forget about the
patches we are currently rebasing.
$ darcs log
patch 7ed8dfe47953186ca4d97cee32898e926bf5eb7e
Author: raichoo@example.com
Date: Mon Aug 17 12:15:24 CEST 2020
* B
patch d3fbcaf6e187b1e582caf031d39f29ddf332570b
Author: raichoo@example.com
Date: Mon Aug 17 12:15:08 CEST 2020
* A
Rebase in progress: 1 suspended patch
We can also take a look at all our suspended patches using darcs rebase log
.
$ darcs rebase log
patch 0919c58f931ffcc93bc5fbe41f8fdcc0a9fab1ba
Author: raichoo@example.com
Date: Mon Aug 17 12:16:01 CEST 2020
* C
Rebase in progress: 1 suspended patch
Perfect! C
is waiting for us to finish our work. We can now do all the
operations we want without taking our suspended patch into account. You might
also have realized that the hash of the suspended patch has changed. This
happens every time we suspend
a patch, so should not do this with patches you
have already published.
We have now brought B
into shape. It's time to unsuspend
C
again!
$ darcs rebase unsuspend
patch 0919c58f931ffcc93bc5fbe41f8fdcc0a9fab1ba
Author: raichoo@example.com
Date: Mon Aug 17 12:16:01 CEST 2020
* C
Shall I unsuspend this patch? (1/1) [ynW...], or ? for more options: y
Do you want to Unsuspend these patches? [Yglqk...], or ? for more options: y
Rebase finished!
$ darcs log
patch bcd766f6817c2c4757101db96f241e86cb47571f
Author: raichoo@example.com
Date: Mon Aug 17 14:20:14 CEST 2020
* C
patch 08d1ebf883423391e80152a9d0562b569df8b4cf
Author: raichoo@example.com
Date: Mon Aug 17 13:16:12 CEST 2020
* B
patch d3fbcaf6e187b1e582caf031d39f29ddf332570b
Author: raichoo@example.com
Date: Mon Aug 17 12:15:08 CEST 2020
* A
Great, we are back to where we started but we have adjusted B
to our liking.
Once we unsuspend
our patch, it changes its hash one last time and the
rebase
is completed.