Darcs is a patch-centric version control system. In Darcs, there is no “correct” linear history of a repository – rather, there is a poset of patches. That means that most of the time you are pushing and pulling changes you can cherry-pick patches without a problem. However, in some cases you cannot perform a pull (or some other operation on the repository) smoothly. Sometimes it is necessary to rewrite the “history” – i.e. modify a patch that is a dependency of one or more other patches. For those cases darcs rebase comes in handy. To put it in the words of the implementor “Rebase is a workaround for cases where commutation doesn’t do enough”.

A repository can change it’s state from rebase-in-progress back to normal if there are no suspended patches left. However, be aware that you cannot unsuspend a patch if you have unrecorded changes in the repository. In light of this, I suggest recording a temporary patch with current changes

darcs record -am DRAFT

You can suspend that patch at the beginning of your rebase process and apply it at the end.

General overview of rebase

darcs rebase is an operation (or, rather, a family of operations) that allows one to make changes “deep down” in the repository history. One of the crucial things that allows for rebase to work is the fact that since darcs 2.10 patches can be suspended. When one performs any of the darcs rebase commands, the repository moves to a special rebase-in-progress state. In this state repository contains a pristine, a set of patches, a working copy, and — in addition to all the usual stuff — a set of suspended patches. Suspended patches are not active in the repository — that is, they are not applied.

Let’s go over the rebase subcommands

rebase log/rebase changes

This is simple: list the suspended patches

rebase suspend

Moves selected patches into the suspended state. Once the patch is suspended it is no longer active in the repository.

Note: once you suspend a patch, it changes its identity. That means that even if you suspend a patch and unsuspend it immediately, you will get a different repository that you have started with. Let this be a good reason (one of a couple!) for doing rebase on a separate branch.

> cat file test 123 > darcs rebase suspend patch 64523bc4622fad02a4bdb9261887628b7997ebdd Author: Daniil Frumin Date: Thu Jul 23 18:49:30 MSK 2015 * 123 Shall I suspend this patch? (1/5) [ynW…], or ? for more options: y patch cc54d7cf4b9e3d13a24ce0b1b77b76581d98d75d Author: Daniil Frumin Date: Thu Jul 23 18:43:53 MSK 2015 * Test Shall I suspend this patch? (2/5) [ynW…], or ? for more options: d Rebase in progress: 1 suspended patches > darcs rebase log patch 64523bc4622fad02a4bdb9261887628b7997ebdd Author: Daniil Frumin Date: Thu Jul 23 18:49:30 MSK 2015 * 123 Shall I view this patch? (1/?) [yN…], or ? for more options: y [123 Daniil Frumin **20150723154930 Ignore-this: 43e09e6503ac74688e74441dc29bce25 ] hunk ./file 2 +123 Rebase in progress: 1 suspended patches > cat file test

rebase unsuspend

Does the opposite of suspend : applies a suspended patch to the repository and changes its state to normal.

> darcs rebase unsuspend patch 64523bc4622fad02a4bdb9261887628b7997ebdd Author: Daniil Frumin Date: Thu Jul 23 18:49:30 MSK 2015 * 123 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!

rebase apply

Rebase apply takes a patch bundle and tries to apply all the patches in the bundle to the current repository. If a patch from the bundle conflicts with a local patch, then the local patch gets suspended. You will thus have a chance to resolve the conflict by amending your conflicting patches, at a price of.. well, changing the identity of your local patches.

rebase pull

Sort of like rebase apply , but instead of a patch bundle it obtains the patches from a remote repository.

Specifically, rebase pull applies all the remote patches, one-by-one, suspending any local patches that conflict. We will see more of rebase pull in the second example.

Example 1: suspending local changes

Imagine the following situation: at point A you add a configuration file to your repository, then you record a patch B that updates the settings in the configuration file. After that you make some more records before you realize that you’ve included by accident your private password in patch A! You want to get rid of it in your entire history, but you can’t just unrecord A, because B depends on A, and possibly some other patches depend on B.

The contents of the configuration file after patch A:

port = 6667 host = irc.freenode.net password = awesomevcs

Patch B, diff:

@@ -1,3 +1,4 @@ -port = 6667 +port = 6697 +usessl = True host = irc.freenode.net password = awesomevcs

You cannot just amend patch A, because the patch B depends on A:

> darcs amend patch 1925d640f1f3180cb5b9e64260c1b5f374fce4ca Author: Daniil Frumin Date: Tue Jul 21 13:23:07 MSK 2015 * B Shall I amend this patch? [yNjk…], or ? for more options: n Skipping depended-upon patch: patch 22d7c8da83141f8b1f80bdd3eff02064d4f45c6b Author: Daniil Frumin Date: Tue Jul 21 13:22:24 MSK 2015 * A Cancelling amend since no patch was selected.

What we will have to do is temporarily suspend patch B, amend patch A, and then unsuspend B.

> darcs rebase suspend patch 1925d640f1f3180cb5b9e64260c1b5f374fce4ca Author: Daniil Frumin Date: Tue Jul 21 13:23:07 MSK 2015 * B Shall I suspend this patch? (1/2) [ynW…], or ? for more options: y patch 22d7c8da83141f8b1f80bdd3eff02064d4f45c6b Author: Daniil Frumin Date: Tue Jul 21 13:22:24 MSK 2015 * A Shall I suspend this patch? (2/2) [ynW…], or ? for more options: d Rebase in progress: 1 suspended patches

At this point, the state of our repository is the following: there is one (active) patch A, and one suspended patch B.

> darcs rebase changes -a patch 4c5d45230dc146932b21964aea938e2a978523eb Author: Daniil Frumin Date: Tue Jul 21 13:28:58 MSK 2015 * B Rebase in progress: 1 suspended patches > darcs changes -a patch 21f56dfb425e4c49787bae5db4f8869e96787fb2 Author: Daniil Frumin Date: Tue Jul 21 13:28:49 MSK 2015 * A Rebase in progress: 1 suspended patches > cat config port = 6667 host = irc.freenode.net password = awesomevcs > $EDITOR config # remove the password bit > darcs amend patch 22d7c8da83141f8b1f80bdd3eff02064d4f45c6b Author: Daniil Frumin Date: Tue Jul 21 13:22:24 MSK 2015 * A Shall I amend this patch? [yNjk…], or ? for more options: y hunk ./config 3 -password = awesomevcs 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 21f56dfb425e4c49787bae5db4f8869e96787fb2 Author: Daniil Frumin Date: Tue Jul 21 13:28:49 MSK 2015 * A Rebase in progress: 1 suspended patches

Now that we’ve removed the password from the history, we can safely unsuspend patch B (in this particular situation we actually know that applying B to the current state of the repository won’t be a problem, because B does not conflict with our modified A)

> darcs rebase unsuspend patch 1925d640f1f3180cb5b9e64260c1b5f374fce4ca Author: Daniil Frumin Date: Tue Jul 21 13:23:07 MSK 2015 * B 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!

And that’s done!

> cat config port = 6697 usessl = True host = irc.freenode.net

You may use this rebase strategy for removing sensitive information from the repository, for removing that 1GB binary .iso that you added to your repository by accident, or for combining two patches into one deep down in the patchset.

Example 2: developing against a changing upstream – rebase pull

Imagine you have a fork R’ of a repository R that you are working on. You are implementing a feature that involves a couple of commits. During your work you record a commit L1 that refractors some common datum from modules A.hs and B.hs. You proceed with your work recording a patch L2. At this point you realise that after you forked R, the upstream recorded two more patches U1 and U2, messing with the common datum in A.hs. If you just pull U1 into your fork R’, you will have a conflict, that you will have to resolve by recording another patch on top.

S / \ / \ L1 U1

Note: if you run darcs rebase pull in R’, then the only patches that will be suspended are the ones which are already in R’. Because suspended patches gain new identity, make sure that you do not have other people’s conflicting patches present in R’.

The way to solve this would be to first do darcs rebase pull , which would suspend the conflicting patches, and then start unsuspending the patches one by one, making sure that you fix any conflicts that may arise after each unsuspend.

Consider a concrete example with two repositories rep1 and rep1_0 .

rep1_0 > darcs changes patch ebaccd5c36667b7e3ee6a49d25ef262f0c7edf2b Author: Daniil Frumin Date: Mon Jul 27 20:56:25 MSK 2015 * commit2 patch a7e0d92a53b0523d0224ef8ffae4362adf854485 Author: Daniil Frumin Date: Mon Jul 27 20:56:25 MSK 2015 * commit1 rep1_0 > darcs diff —from-patch=commit2 patch ebaccd5c36667b7e3ee6a49d25ef262f0c7edf2b Author: Daniil Frumin Date: Mon Jul 27 20:56:25 MSK 2015 * commit2 diff -rN -u old-rep1_0/dir1/file2 new-rep1_0/dir1/file2 — old-rep1_0/dir1/file2 1970-01-01 03:00:00.000000000 +0300 +++ new-rep1_0/dir1/file2 2015-07-28 12:25:54.000000000 +0300 @@ -0,0 +1 @@ +double whatsup rep1_0 > cd ../rep1 rep1 > darcs changes patch e3df0e23a3915910a81eb8181d7b3669e8f270a9 Author: Daniil Frumin Date: Tue Jul 28 12:27:55 MSK 2015 * commit2’ patch a7e0d92a53b0523d0224ef8ffae4362adf854485 Author: Daniil Frumin Date: Mon Jul 27 20:56:25 MSK 2015 * commit1 rep1 > darcs diff —from-patch=“commit2’” patch e3df0e23a3915910a81eb8181d7b3669e8f270a9 Author: Daniil Frumin Date: Tue Jul 28 12:27:55 MSK 2015 * commit2’ diff -rN -u old-rep1/dir1/file2 new-rep1/dir1/file2 — old-rep1/dir1/file2 1970-01-01 03:00:00.000000000 +0300 +++ new-rep1/dir1/file2 2015-07-28 12:28:39.000000000 +0300 @@ -0,0 +1 @@ +touch file2 \ No newline at end of file diff -rN -u old-rep1/file1 new-rep1/file1 — old-rep1/file1 2015-07-28 12:28:39.000000000 +0300 +++ new-rep1/file1 2015-07-28 12:28:39.000000000 +0300 @@ -1 +1 @@ -whatsup \ No newline at end of file +double whatsup \ No newline at end of file

The patch commit2 from rep1_0 conflicts with commit2’ from rep1 .

rep1 > darcs rebase pull ../rep1_0 patch ebaccd5c36667b7e3ee6a49d25ef262f0c7edf2b Author: Daniil Frumin Date: Mon Jul 27 20:56:25 MSK 2015 * commit2 Shall I pull this patch? (1/1) [ynW…], or ? for more options: y Do you want to pull these patches? [Yglqk…], or ? for more options: y The following local patches are in conflict: patch e3df0e23a3915910a81eb8181d7b3669e8f270a9 Author: Daniil Frumin Date: Tue Jul 28 12:27:55 MSK 2015 * commit2’ Shall I suspend this patch? (1/1) [ynW…], or ? for more options: y Do you want to suspend these patches? [Yglqk…], or ? for more options: y Finished pulling. Rebase in progress: 1 suspended patches

Now we have one patch — commit2’ — in the suspended state. We want to resolve the conflict by amending commit2’ . We will do that by unsuspending it and manually editing out the conflicting lines. This will also make it depend on commit2 .

rep1 > darcs rebase unsuspend patch e3df0e23a3915910a81eb8181d7b3669e8f270a9 Author: Daniil Frumin Date: Tue Jul 28 12:27:55 MSK 2015 * commit2’ 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: d We have conflicts in the following files: ./dir1/file2 Rebase finished! rep1 > cat dir1/file2 v v v v v v v ============= double whatsup ************* touch file2 ^ ^ ^ ^ ^ ^ ^ rep1 > $EDITOR dir1/file2 rep1 > darcs amend -a patch 40b3b4123c78dba6a6797feb619572072654a9cd Author: Daniil Frumin Date: Tue Jul 28 12:32:56 MSK 2015 * commit2’ Shall I amend this patch? [yNjk…], or ? for more options: y Finished amending patch: patch c35867259f187c1bc30310f1cacb34c1bb2cce41 Author: Daniil Frumin Date: Tue Jul 28 12:34:30 MSK 2015 * commit2’ rep1 > darcs mark-conflicts No conflicts to mark.

Another repository saved from conflicting patches, yay!