A little help from my friends

In chapter 1 we have learned about darcs being a distributed version control system, but until now we basically just worked with one local instance of our project. No other parties were involved. But whenever we are working on something big it’s a good thing when people participate. In this chapter I am going to introduce you how to work together with others.

Cloning

Until now we only used repositories that were freshly created by ourselves. But sometimes we want to start participating on a project that has already started, so initializing something new is not what we want. This is where cloning comes in. With cloning we can take a repository and make our own copy from it.

Let’s say you want to clone the source of this book, which is hosted on darcs hub. To clone this repository you simply issue darcs clone https://hub.darcs.net/raichoo/darcs-book and darcs will start copying the repository into a directory called darcs-book..

$ darcs clone https://hub.darcs.net/raichoo/darcs-book
Copying patches, to get lazy repository hit ctrl-C...
Finished cloning.

Huh, that’s an interesting message… I can hit CTRL-C to get a lazy repository. What does that mean? Well, quite simple. Repositories can get REALLY big and therefore it takes a lot of time to clone them, but what if you only wanted the newest changes and pull in everything else on demand? No need to pull in the entire history of a project all at once. A lazy repository does just that, and this reduces cloning time drastically. Cloning the entire history is only useful if you might not be able to access the Internet for some time and you really need to have access to the entire evolution of the project. So if you happen to get stuck on a particularly long clone, just hit CTRL-C and you are good to go, most of the time you will be interested in the current state of the repository anyways. Of course you can instruct darcs to do a lazy clone right away by add the --lazy flag.

$ darcs clone --lazy https://hub.darcs.net/raichoo/darcs-book
Finished cloning.

It’s a simple as that.

Using clone locally.

clone is useful for more than just copying data from a remote location. Sometime you might just want to have another copy of your project to record a entirely different set of patches in. This becomes especially interesting once we are able to modify the history of our project, but more on that in a later chapter.

In the section before we saw that the clone command takes a location from where you want to clone. This does not have to be a remote server, it can also be a directory. So let’s say I decided to work on the second edition of this book and I wanted to do that inside of a separate repository and pull patches from there in the future.

$ ls
darcs-book
$ darcs clone darcs-book darcs-book-2nd-edition
Copying patches, to get lazy repository hit ctrl-C...
Finished cloning.
$ ls
darcs-book darcs-book-2nd-edition

So, clone can take 2 arguments, one being the source repository and another one being the destination directory. So a source does not necessarily need to be a remote location, it can be a directory as well.

Pu-Push it real good!

Great, now we can have multiple copies of a repository and they can either be remote or local. We are slowly stepping up our game! Now, let’s start to exchange patches between those different copies. For this purpose darcs offers the push and pull commands. We are going to take a look at push first. To do this in a somewhat realistic example let’s setup simple scenario first. I’m going to do this locally but all of the upcoming examples also work with remote sources.

$ darcs init new-project
$ darcs clone new-project new-project-work
Copying patches, to get lazy repository hit ctrl-C...
Finished cloning.
$ cd new-project-work
$ touch some-file.txt
$ darcs add some-file.txt
Adding 'some-file.txt'
$ darcs record -a -m 'added some file'
Finished recording patch 'added some file'
$ darcs log
patch cfaa89a2b431c0642687d9bfa0bbb721015cc775
Author: raichoo@example.com
Date:   Fri Jun 29 13:02:03 CEST 2018
  * added some file

Here we have a repository called new-project and a copy of it which we call new-project-work, here we are going to do some actual work and once we are done we are going to push that to our project repository for publishing or whatever we choose to do with it.

In our new-project-work repository we have recorded a patch that is not present in our new-project repository which we consider as our main repository, everything in here is meant to be published to a larger audience. At some point we have decided that our new patch is ready to go and we want to publish it to the main repository.

Currently we are in the working tree of our new-project-work repository and we want to push to the new-project repository, this is how we would do that.

$ darcs push ../new-project
Pushing to "/tmp/darcs/new-project"...
patch cfaa89a2b431c0642687d9bfa0bbb721015cc775
Author: raichoo@example.com
Date:   Fri Jun 29 13:02:03 CEST 2018
  * added some file
Shall I push this patch? (1/1)  [ynW...], or ? for more options: y
Do you want to Push these patches? [Yglqk...], or ? for more options: y
Finished applying.
Push successful.

darcs first figures out which patches are not present in the destination repository and prompts us for each one of them if we really want to push that patch. Once we are done it asks us if we really want to push these patches and after confirming all our selected patches are being transferred to the destination repository.

Let’s take a quick look at the patches that are now inside our new-project repository, remember that we have just created it and there were no patches in there to begin with.

$ cd ../new-project
$ darcs log
patch cfaa89a2b431c0642687d9bfa0bbb721015cc775
Author: raichoo@example.com
Date:   Fri Jun 29 13:02:03 CEST 2018
  * added some file

Nice, as you can see our patch has been transferred from new-project-work to the new-project repository. If you take a look at the working tree you will also find that all the changes contained in our transferred patches have been applied. Piece of cake!

Keep in mind that the destination does not need to be just another folder on our own computer, it can as well be a remote destination.

Pulling

But what if the patches we are interested in are on a remote machine to which we cannot simple log in and issue a push. Well, for that we have pull. Let’s pretend that we didn’t push the patch to new-project like we just did. This time we are going to pull it in from new-project-work to new-project rather then the other way around. So now we are in the working tree of new-project.

$ darcs pull ../new-project-work
HINT: if you want to change the default remote repository to
      /tmp/darcs/new-project-work,
      quit now and issue the same command with the --set-default flag.
patch cfaa89a2b431c0642687d9bfa0bbb721015cc775
Author: raichoo@example.com
Date:   Fri Jun 29 13:02:03 CEST 2018
  * added some file
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
Finished pulling.
$ darcs log
patch cfaa89a2b431c0642687d9bfa0bbb721015cc775
Author: raichoo@example.com
Date:   Fri Jun 29 13:02:03 CEST 2018
  * added some file

So now rather than pushing to another location we can pull our patches in from, let’s say a website like darcshub.

Wait a minute though, there is something in the output that we didn’t talk about yet. darcs emits a HINT message which tells us something about a default remote repository. What does that mean? A default remote repository allows is to omit the destination when pushing and pulling. To check if you have one set you can issue darcs show repo.

$ darcs show repo
         Format: hashed, darcs-2
           Root: /tmp/darcs/new-project
   PristineType: HashedPristine
          Cache: thisrepo:/tmp/darcs/new-project, cache:/home/raichoo/.cache/darcs
     PatchIndex: enabled, but not yet created
    Num Patches: 1
      Weak Hash: cfaa89a2b431c0642687d9bfa0bbb721015cc775

This gives us some information about our repository, but I can’t spot a default repository here. Let’s use the --set-default flag just like darcs told us to in the hint.

$ darcs pull --set-default ../new-project-work
No remote patches to pull in!
darcs show repo
         Format: hashed, darcs-2
           Root: /tmp/darcs/new-project
   PristineType: HashedPristine
          Cache: thisrepo:/tmp/darcs/new-project, cache:/home/raichoo/.cache/darcs
     PatchIndex: enabled, but not yet created
 Default Remote: /tmp/darcs/new-project-work
    Num Patches: 1
      Weak Hash: cfaa89a2b431c0642687d9bfa0bbb721015cc775

Here we go! Now we do not have to explicitly state the remote repository we are working with when we just want to talk to the default remote repository.

$ darcs pull
Pulling from "/tmp/darcs/new-project-work"...
No remote patches to pull in!
$ darcs push
Pushing to "/tmp/darcs/new-project-work"...
No recorded local patches to push!

Of course we can still explicitly specify other repositories if we want to communicate with a different repository, we can also always set a different default remote repository just by using the --set-default flag on a push or pull. This saves us from memorizing all the remote repositories we are working with and thus makes life a little easier for us.

Weak Hashes

There is something else that is interesting in the darcs show repo output. Namely the weak hash. What in tarnation does that mean and why is it useful? Basically it’s useful to communicate if your repository has the same state as someone else’s. Let me explain. When we first got to know darcs we’ve learned that a repository’s state is defined by a set of changes, and therefore the order in which one has acquired these changes does not matter. So if I were to work on a project with my friend gimbar and they would pull in patches in a different order than I, how would we know that our repositories are actually in the same state? Well, as you might have guessed, that’s what the weak hash is for. In chapter 3 we have learned that every patch can be identified by a hash, the weak hash is generated from the hashes of all the patches in a repository in a way that does not care about their order. So if two repositories are in the same state, meaning they have the same set of changes, and each patch has a hash that is supposed to be unique to that hash, they are bound to have the same weak hash. Take a look.

$ darcs log
patch 1ae0f2d1bd00c9d72ac21eef7a26736ed76c27c8
Author: raichoo@example.com
Date:   Thu Jul  5 21:36:46 CEST 2018
  * A

patch 6f9774a3038e1226d0df9518b163cab517b29f27
Author: raichoo@example.com
Date:   Thu Jul  5 21:37:03 CEST 2018
  * B
$ darcs show repo
         Format: hashed, darcs-2
           Root: /tmp/darcs/X
   PristineType: HashedPristine
          Cache: thisrepo:/tmp/darcs/X, cache:/home/raichoo/.cache/darcs
     PatchIndex: enabled, but not yet created
    Num Patches: 2
      Weak Hash: 75778672be8edbf1fa1d8bf7cb45b9dbc0deb8ef
$ darcs log
patch 6f9774a3038e1226d0df9518b163cab517b29f27
Author: raichoo@example.com
Date:   Thu Jul  5 21:37:03 CEST 2018
  * B

patch 1ae0f2d1bd00c9d72ac21eef7a26736ed76c27c8
Author: raichoo@example.com
Date:   Thu Jul  5 21:36:46 CEST 2018
  * A
$ darcs show repo
         Format: hashed, darcs-2
           Root: /tmp/darcs/Y
   PristineType: HashedPristine
          Cache: thisrepo:/tmp/darcs/Y, cache:/home/raichoo/.cache/darcs
     PatchIndex: enabled, but not yet created
 Default Remote: /tmp/darcs/X
    Num Patches: 2
      Weak Hash: 75778672be8edbf1fa1d8bf7cb45b9dbc0deb8ef

Even though these two example repositories have patch A and B applied in a different order, they do have the same weak hash meaning they are in the same state.

Traveling without moving

Sometimes we don’t actually want to push or pull but rather just want to see what would happen if we were to do either of these operations. One of these situations might be that we want to know what changes we have that do not exist in the remote repository that we want to push to, or the set changes we would pull in from another repository. Both pull and push offer a --dry-run flag to suppress the effects of the actual operation. When we issue a command using the --dry-run flag it will only report the actions it would do without actually performing them.

$ darcs pull --dry-run raichoo@hub.darcs.net:darcs-fish
Would pull from "raichoo@hub.darcs.net:darcs-fish"...
Would pull the following changes:
patch 0dd46bd7a710d66136d7293b667984bdaffb1509
Author: raichoo@example.com
Date:   Mon Jun 18 12:31:32 CEST 2018
  * display move

Making no changes: this is a dry run.

You can see that there is one change in the remote repository that I do not have in my local copy yet. I can now decide if I really want to pull this change or not.

Instead of using --dry-run you can of course always say no to all of the changes when the interactive prompt asks you if you would like to pull them in. Whether you prefer this or --dry-run is totally up to you.

As a side note, the --dry-run flag is supported by other commands as well, it is a common way to tell an operation that it should only show me what its intentions are rather than actually performing an action. You can check the help section of the individual commands to find out of they support this flag.

Annotate

Once you start working with multiple people it becomes useful to know who had recorded what patches and how that affected a current file. Imagine you are working on an important and someone has written a patch you don’t fully understand, maybe it’s not a sufficiently documented change or you are just having a bad day and need the person who wrote that to help you understand what is going on.

Here we have a simple file but it presents us with an issue, who wrote which line? Thankfully annotate can be of assistance here.

$ cat text.txt
I wrote this!
I wrote this!
$ darcs annotate text.txt
1: patch abcb03dd0b39a08c6424700eac117f72fb465ea5
Author: raichoo@example.com
Date:   Fri Jun 29 14:09:20 CEST 2018
  * adding important information
2: patch 3f8ee5063a2697aeec8856246f7069ae8c9aafa5
Author: tauli@example.com
Date:   Fri Jun 29 14:09:57 CEST 2018
  * some more information

1:  raichoo@example.com   #1 | I wrote this!
2:    tauli@example.com   #2 | I wrote this!

The contents of the file does not help us a lot when we want to figure out who contributed which line to the file. But annotate presents us with a list of involved changes and who recorded them. This can be immensely useful when trying to find bugs or just figure out the right person to talk to about a particular piece of code. Quite a lot of power you got there, remember to use it responsibly. Fun fact: other version control systems tend to call this command blame, you can guess what some people are primarily using it for. Don’t be that person.