~jan0sch/darcs-book
~jan0sch/darcs-book/en/06-conflict-management.md
~jan0sch/darcs-book/en/06-conflict-management.md
title: Conflict Management next: chapter07 prev: chapter05 toclink: yes ---
Conflicts are bound to happen when more than one person is working on something.
Two people might end up editing the same portion of text with different results.
When you have two people creating changes that change the same part of a project
and you pull them into your repository you will and up with a so called
conflict. darcs
does not know what change you prefer, so it will inform
you that a conflict has occurred and asks you to resolve it.
I have created a fairly simple piece of code that simply prints out This is a
test!
.
$ darcs log -v
patch 7561a52b319ae52ddd1bd321d53cc01fb14fa94c
Author: raichoo@example.com
Date: Sun Jul 1 19:32:26 CEST 2018
* initial record
addfile ./Main.hs
hunk ./Main.hs 1
+main = putStrLn "This is a test!"
Sandra and xanderio both clone my repository and make their own changes to the message that this program is supposed to print out.
xanderio is rather excited about the project and creates the following change.
$ darcs log -v --last 1
patch b769c1ae2bc57d0b8254deff9bb83c9c1000b93e
Author: xanderio@example.com
Date: Sun Jul 1 19:50:24 CEST 2018
* change message
hunk ./Main.hs 1
-main = putStrLn "This is a test!"
+main = putStrLn "This is the best test!"
Sandra has seen better things and thinks that the message should be a bit more modest, so she is creating a change that looks like this.
$ darcs log -v --last 1
patch 1891536411e86f9666219cc1a95f89587da08de3
Author: sandra@example.com
Date: Sun Jul 1 19:52:12 CEST 2018
* change message
hunk ./Main.hs 1
-main = putStrLn "This is a test!"
+main = putStrLn "This is a quite okay test!"
Obviously both have changed the same line of the project so when I pull in those
changes darcs
won't just decide on its own but rather ask me to do the
conflict resolution. That's a good thing, you don't want some random program
making decisions regarding your project, who knows what it might come up with!
Before we are going to pull in our changes let's check the status of our working tree. It'll become clear why I'm doing this, for now just roll along.
$ darcs status
No changes!
Okay, there are no unrecorded changes present so let's pull in xanderio's and
Sandra's changes. I can give pull
multiple repositories to pull
from. This
makes it quite convenient if you want to pull in changes from your entire team
all that once.
$ ls
Main.hs _darcs
$ darcs pull \
https://hub.darcs.net/xanderio/project \
https://hub.darcs.net/sandra/project
patch b769c1ae2bc57d0b8254deff9bb83c9c1000b93e
Author: xanderio@example.com
Date: Sun Jul 1 19:50:24 CEST 2018
* change message
Shall I pull this patch? (1/2) [ynW...], or ? for more options: y
patch 1891536411e86f9666219cc1a95f89587da08de3
Author: sandra@example.com
Date: Sun Jul 1 19:52:12 CEST 2018
* change message
Shall I pull this patch? (2/2) [ynW...], or ? for more options: y
Do you want to Pull these patches? [Yglqk...], or ? for more options: y
Backing up ./Main.hs(.~0~)
We have conflicts in the following files:
./Main.hs
Finished pulling.
$ ls
Main.hs Main.hs.~0~ _darcs
As we anticipated, we ended up with a conflict in Main.hs
. Also
another mysterious file by the name of Main.hs.~0~
appeared in our working
tree, we will take a look at that one later. First, let's take a look at what
that our repository looks like.
$ darcs log -s
patch 1891536411e86f9666219cc1a95f89587da08de3
Author: sandra@example.com
Date: Sun Jul 1 19:52:12 CEST 2018
* change message
M! ./Main.hs -1 +1
patch b769c1ae2bc57d0b8254deff9bb83c9c1000b93e
Author: xanderio@example.com
Date: Sun Jul 1 19:50:24 CEST 2018
* change message
M ./Main.hs -1 +1
patch 7561a52b319ae52ddd1bd321d53cc01fb14fa94c
Author: raichoo@example.com
Date: Sun Jul 1 19:32:26 CEST 2018
* initial record
A ./Main.hs
In the summary of Sandra's patch we can see an exclamation point. This tells us
that there are changes in our repository that conflict with the changes that
patch introduces. To see what line actually causes that problem let's take a
look at the verbose output of log
.
$ darcs log -v
patch 1891536411e86f9666219cc1a95f89587da08de3
Author: sandra@example.com
Date: Sun Jul 1 19:52:12 CEST 2018
* change message
conflictor [
hunk ./Main.hs 1
-main = putStrLn "This is a test!"
+main = putStrLn "This is the best test!"
]
|:
hunk ./Main.hs 1
-main = putStrLn "This is a test!"
+main = putStrLn "This is a quite okay test!"
patch b769c1ae2bc57d0b8254deff9bb83c9c1000b93e
Author: xanderio@example.com
Date: Sun Jul 1 19:50:24 CEST 2018
* change message
hunk ./Main.hs 1
-main = putStrLn "This is a test!"
+main = putStrLn "This is the best test!"
patch 7561a52b319ae52ddd1bd321d53cc01fb14fa94c
Author: raichoo@example.com
Date: Sun Jul 1 19:32:26 CEST 2018
* initial record
addfile ./Main.hs
hunk ./Main.hs 1
+main = putStrLn "This is a test!"
Here we can see that the conflict is marked with a conflictor which tells us
which changes are in conflict with one another. The line that xanderio's patch
changed is in direct conflict with Sandra's change and therefore that line is
marked with a conflictor. But what happened to your Main.hs
?
When a conflict occurs darcs
will mark these conflicts in our working tree.
Remember that we checked for unrecorded changed in the working tree before
pulling in the changes? Let's check again.
$ darcs status
M ./Main.hs +6
Interesting, darcs
seems to have changed something in our Main.hs
. If we
take a look at the file it's now going to look like this.
$ cat Main.hs
v v v v v v v
main = putStrLn "This is a test!"
=============
main = putStrLn "This is a quite okay test!"
*************
main = putStrLn "This is the best test!"
^ ^ ^ ^ ^ ^ ^
These are so called conflict markers. These markers consist out of multiple parts and there are a different delimiters separating those parts.
v v v v v v v
Marks the beginning of a conflict, it is followed by the initial state of our file, which is its state before the conflict occurred.
=============
Marks the end of the initial state, it is followed by a list of conflicting changes.
*************
This marker separates conflicting changes. Depending on how many changes we
have pulled in that conflict with one other there can be multiple changes
listed here. Each of them is separated by a marker like this. In our
example we just have those two conflicting changes but if we happen to pull
in from more repositories there might potentially be more.
^ ^ ^ ^ ^ ^ ^
Marks the end of a conflict.
darcs
also created a file called Main.hs.~0~
the captures the state of our
conflicting file before the we pulled in the changes.
$ cat Main.hs.~0~
main = putStrLn "This is a test!"
You can get rid of the conflict markers be simply issuing darcs revert
,
this does not solve your conflict but it takes you back to the state of the file
before the conflicting changes were applied. In our situation our Main.hs
would look like this.
$ darcs revert -a
Finished reverting.
$ cat Main.hs
main = putStrLn "This is a test!"
To get our conflict markers back we can always issue mark-conflicts
.
$ darcs mark-conflicts
Marking conflicts in: "Main.hs".
Finished marking conflicts.
$ cat Main.hs
v v v v v v v
main = putStrLn "This is a test!"
=============
main = putStrLn "This is a quite okay test!"
*************
main = putStrLn "This is the best test!"
^ ^ ^ ^ ^ ^ ^
Okay, now we know what the problem is but what's the solution? It's actually pretty simple. We just record a patch that resolves our conflict. The situation above calls a change that somewhat takes the two conflicting patches into account to resolve the issue.
$ echo 'main = putStrLn "This is a good test, Bront!"' > Main.hs
$ darcs record -m 'resolve "change message" conflict'
hunk ./Main.hs 1
-main = putStrLn "This is a test!"
+main = putStrLn "This is a good test, Bront!"
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 recording patch 'resolve "change message" conflict'
--skip-conflicts
!As you can see, conflicts can cause quite a lot of work, but when we pull in
work from others not every patch contained in the remote repository might cause
a conflict. So how about just pulling in those that don't cause any trouble for
now and continue working with those before tackling the task of resolving any
issues? Sounds nice and darcs pull
offers a flag that does exactly that,
--skip-conflicts
.
Let's pretend that we didn't pull
in any changes yet and now we are carefully
pulling them in from our other example repositories.
$ darcs pull https://hub.darcs.net/xanderio/project
patch b769c1ae2bc57d0b8254deff9bb83c9c1000b93e
Author: xanderio@example.com
Date: Sun Jul 1 19:50:24 CEST 2018
* change message
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 pull --skip-conflicts https://hub.darcs.net/sandra/project
Skipping some patches which would cause conflicts.
No remote patches to pull in!
$ darcs log
patch b769c1ae2bc57d0b8254deff9bb83c9c1000b93e
Author: xanderio@example.com
Date: Sun Jul 1 19:50:24 CEST 2018
* change message
patch 7561a52b319ae52ddd1bd321d53cc01fb14fa94c
Author: raichoo@example.com
Date: Sun Jul 1 19:32:26 CEST 2018
* initial record
As you can see we didn't pull in Sandra's patch that would have conflicted
with xanderio's. On the other hand if Sandra would have had other changes that
would not conflict we would have pulled them in only leaving out the culprit.
This is one of the nice and powerful properties of patch based version
control systems like darcs
.
replace
In chapter 3 we were introduced to the replace
change and until now it got
relatively little attention. I would like to show you a very nice application of
this kind of change that allows us to reduce the attack surface for conflicts.
As a simple example I'm going write a simple piece of Haskell code that only
contains a function called foo
.
$ cat Fun.hs
foo :: Int -> Int
foo x = x + 1
I'm working on this piece of code with my friends alexej and bodems. They both have a copy of my repository and apply the following changes to it.
alexej wants the foo
function to be a little more generic so they change the
type signature of the function to reflect that.
$ darcs log --last 1 -v
patch 9ca832bb0187d46d37e129b05837af39275246f4
Author: alexej@example.com
Date: Tue Jul 3 11:41:22 CEST 2018
* make foo more generic
hunk ./Fun.hs 1
-foo :: Int -> Int
+foo :: Num a => a -> a
By doing this alexej introduced a hunk
that replaces the first line of the
file with a new one that features the more generic type signature.
bodems thinks that the name foo
is just not descriptive enough. So they change
it by introducing the following change.
$ darcs log --last 1 -v
patch 592af55bbf1395376c745fffc47da598e3233149
Author: bodems@example.com
Date: Tue Jul 3 11:41:04 CEST 2018
* give foo an appropriate name
hunk ./Fun.hs 1
-foo :: Int -> Int
-foo x = x + 1
+inc :: Int -> Int
+inc x = x + 1
Once again darcs
uses a hunk change to reflect that change and swaps out
entire lines just to change the name.
Now I come along to pull in both of their changes. I'm careful and therefore
using the --dry-run
flag of pull
to take a brief look at their changes
first.
$ darcs pull --dry-run -v \
https://hub.darcs.net/bodems/project \
https://hub.darcs.net/alexej/project
They have the following patches to pull:
patch 592af55bbf1395376c745fffc47da598e3233149
Author: bodems@example.com
Date: Tue Jul 3 11:41:04 CEST 2018
* give foo an appropriate name
patch 9ca832bb0187d46d37e129b05837af39275246f4
Author: alexej@example.com
Date: Tue Jul 3 11:41:22 CEST 2018
* make foo more generic
Would pull the following changes:
patch 592af55bbf1395376c745fffc47da598e3233149
Author: bodems@example.com
Date: Tue Jul 3 11:41:04 CEST 2018
* give foo an appropriate name
hunk ./Fun.hs 1
-foo :: Int -> Int
-foo x = x + 1
+inc :: Int -> Int
+inc x = x + 1
patch 9ca832bb0187d46d37e129b05837af39275246f4
Author: alexej@example.com
Date: Tue Jul 3 11:41:22 CEST 2018
* make foo more generic
conflictor [
hunk ./Fun.hs 1
-foo :: Int -> Int
-foo x = x + 1
+inc :: Int -> Int
+inc x = x + 1
]
:
hunk ./Fun.hs 1
-foo :: Int -> Int
+foo :: Num a => a -> a
Making no changes: this is a dry run.
Oh snap! We have a conflict! Not really surprising when we consider that both changes are changing the sames lines of code.
Now thankfully Sandra comes along, being very good at solving conflicts they use
replace
instead of a plain hunk
.
$ darcs replace foo inc Fun.hs
$ darcs whatsnew
replace ./Fun.hs [A-Za-z_0-9] foo inc
$ darcs record -a -m 'give foo an appropriate name'
Finished recording patch 'give foo an appropriate name'
No when I pull in this patch instead of the one bodems made this is going to happen.
$ darcs pull --dry-run -v \
https://hub.darcs.net/sandra/project \
https://hub.darcs.net/alexej/project
They have the following patches to pull:
patch 9ea86844a018aeb9c7cf96657d0466960787b359
Author: sandra@example.com
Date: Tue Jul 3 12:00:09 CEST 2018
* give foo an appropriate name
patch 9ca832bb0187d46d37e129b05837af39275246f4
Author: alexej@example.com
Date: Tue Jul 3 11:41:22 CEST 2018
* make foo more generic
Would pull the following changes:
patch 9ea86844a018aeb9c7cf96657d0466960787b359
Author: sandra@example.com
Date: Tue Jul 3 12:00:09 CEST 2018
* give foo an appropriate name
replace ./Fun.hs [A-Za-z_0-9] foo inc
patch 9ca832bb0187d46d37e129b05837af39275246f4
Author: alexej@example.com
Date: Tue Jul 3 11:41:22 CEST 2018
* make foo more generic
hunk ./Fun.hs 1
-inc :: Int -> Int
+inc :: Num a => a -> a
Making no changes: this is a dry run.
VoilĂ ! No conflicts at all! darcs
could figure out that these changes do not
conflict because of the properties of the replace
change. If I'd pull
in
those patches my Fun.hs
would have changed based on both of my collaborator's
changes.
$ cat Fun.hs
inc :: Num a => a -> a
inc x = x + 1
replace
changes are quite unique to darcs
. I have yet to find another
version control system that features this. On the other hand they do not make a
lot of sense in snapshot based version control systems anyway, whereas in a
patch based system they do.