Skip to content

Commit 0512ed4

Browse files
committed
small change to intro
1 parent f5aa304 commit 0512ed4

File tree

2 files changed

+257
-1
lines changed

2 files changed

+257
-1
lines changed
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
=== Rerere
2+
3+
The `git rerere` functionality is a bit of a hidden feature. The name stands for ``reuse recorded resolution'' and as the name implies, it allows you to ask Git to remember how you've resolved a hunk conflict so that the next time it sees the same conflict, Git can automatically resolve it for you.
4+
5+
There are a number of scenarios in which this functionality might be really handy. One of the examples that is mentioned in the documentation is if you want to make sure a long lived topic branch will merge cleanly but don't want to have a bunch of intermediate merge commits. With `rerere` turned on you can merge occasionally, resolve the conflicts, then back out the merge. If you do this continuously, then the final merge should be easy because `rerere` can just do everything for you automatically.
6+
7+
This same tactic can be used if you want to keep a branch rebased so you don't have to deal with the same rebasing conflicts each time you do it. Or if you want to take a branch that you merged and fixed a bunch of conflicts and then decide to rebase it instead - you likely won't have to do all the same conflicts again.
8+
9+
Another situation is where you merge a bunch of evolving topic branches together into a testable head occasionally, as the Git project itself often does. If the tests fail, you can rewind the merges and re-do them without the topic branch that made the tests fail without having to re-resolve the conflicts again.
10+
11+
To enable the `rerere` functionality, you simply have to run this config setting:
12+
13+
[source,shell]
14+
----
15+
$ git config --global rerere.enabled true
16+
----
17+
18+
You can also turn it on by creating the `.git/rr-cache` directory in a specific repository, but I think the config setting is clearer, and it can be done globally.
19+
20+
Now let's see a simple example. Let's say we have a file that looks like this:
21+
22+
[source,shell]
23+
----
24+
#! /usr/bin/env ruby
25+
26+
def hello
27+
puts 'hello world'
28+
end
29+
----
30+
31+
In one branch we change the word ``hello'' to ``hola'', then in another branch we change the ``world'' to ``mundo''.
32+
33+
image::images/rerere1.png[]
34+
35+
When we merge the two branches together, we'll get a merge conflict:
36+
37+
[source,shell]
38+
----
39+
$ git merge i18n-world
40+
Auto-merging hello.rb
41+
CONFLICT (content): Merge conflict in hello.rb
42+
Recorded preimage for 'hello.rb'
43+
Automatic merge failed; fix conflicts and then commit the result.
44+
----
45+
46+
You should notice the new line `Recorded preimage for FILE` in there. Otherwise it should look exactly like a normal merge conflict. At this point, `rerere` can tell us a few things. Normally, you might run `git status` at this point to see what all conflicted:
47+
48+
[source,shell]
49+
----
50+
$ git status
51+
# On branch master
52+
# Unmerged paths:
53+
# (use "git reset HEAD <file>..." to unstage)
54+
# (use "git add <file>..." to mark resolution)
55+
#
56+
# both modified: hello.rb
57+
#
58+
----
59+
60+
However, `git rerere` will also tell you what it has recorded the pre-merge state for with `git rerere status`:
61+
62+
[source,shell]
63+
----
64+
$ git rerere status
65+
hello.rb
66+
----
67+
68+
And `git rerere diff` will show the current state of the resolution - what you started with to resolve and what you've resolved it to.
69+
70+
[source,shell]
71+
----
72+
$ git rerere diff
73+
--- a/hello.rb
74+
+++ b/hello.rb
75+
@@ -1,11 +1,11 @@
76+
#! /usr/bin/env ruby
77+
78+
def hello
79+
-<<<<<<<
80+
- puts 'hello mundo'
81+
-=======
82+
+<<<<<<< HEAD
83+
puts 'hola world'
84+
->>>>>>>
85+
+=======
86+
+ puts 'hello mundo'
87+
+>>>>>>> i18n-world
88+
end
89+
----
90+
91+
Also (and this isn't really related to `rerere`), you can use `ls-files -u` to see the conflicted files and the before, left and right versions:
92+
93+
[source,shell]
94+
----
95+
$ git ls-files -u
96+
100644 39804c942a9c1f2c03dc7c5ebcd7f3e3a6b97519 1 hello.rb
97+
100644 a440db6e8d1fd76ad438a49025a9ad9ce746f581 2 hello.rb
98+
100644 54336ba847c3758ab604876419607e9443848474 3 hello.rb
99+
----
100+
101+
Now you can resolve it to just be `puts 'hola mundo'` and you can run the `rerere diff` command again to see what rerere will remember:
102+
103+
[source,shell]
104+
----
105+
$ git rerere diff
106+
--- a/hello.rb
107+
+++ b/hello.rb
108+
@@ -1,11 +1,7 @@
109+
#! /usr/bin/env ruby
110+
111+
def hello
112+
-<<<<<<<
113+
- puts 'hello mundo'
114+
-=======
115+
- puts 'hola world'
116+
->>>>>>>
117+
+ puts 'hola mundo'
118+
end
119+
----
120+
121+
So that basically says, when I see a hunk conflict in a `hello.rb` file that has ``hello mundo'' on one side and ``hola world'' on the other, resolve it to ``hola mundo''.
122+
123+
Now we can mark it as resolved and commit it:
124+
125+
[source,shell]
126+
----
127+
$ git add hello.rb
128+
$ git commit
129+
Recorded resolution for 'hello.rb'.
130+
[master 68e16e5] Merge branch 'i18n'
131+
----
132+
133+
You can see that it "Recorded resolution for FILE".
134+
135+
image::images/rerere2.png[]
136+
137+
Now, let's undo that merge and then rebase it on top of our master branch instead. We can move our branch back by using `reset` as we saw in <<_reset>>.
138+
139+
[source,shell]
140+
----
141+
$ git reset --hard HEAD^
142+
HEAD is now at ad63f15 i18n the hello
143+
----
144+
145+
Our merge is undone. Now let's rebase the topic branch.
146+
147+
[source,shell]
148+
----
149+
$ git checkout i18n-world
150+
Switched to branch 'i18n-world'
151+
152+
$ git rebase master
153+
First, rewinding head to replay your work on top of it...
154+
Applying: i18n one word
155+
Using index info to reconstruct a base tree...
156+
Falling back to patching base and 3-way merge...
157+
Auto-merging hello.rb
158+
CONFLICT (content): Merge conflict in hello.rb
159+
Resolved 'hello.rb' using previous resolution.
160+
Failed to merge in the changes.
161+
Patch failed at 0001 i18n one word
162+
----
163+
164+
Now, we got the same merge conflict like we expected, but take a look at the `Resolved FILE using previous resolution` line. If we look at the file, we'll see that it's already been resolved, there are no merge conflict markers in it.
165+
166+
[source,shell]
167+
----
168+
$ cat hello.rb
169+
#! /usr/bin/env ruby
170+
171+
def hello
172+
puts 'hola mundo'
173+
end
174+
----
175+
176+
Also, `git diff` will show you how it was automatically re-resolved:
177+
178+
[source,shell]
179+
----
180+
$ git diff
181+
diff --cc hello.rb
182+
index a440db6,54336ba..0000000
183+
--- a/hello.rb
184+
+++ b/hello.rb
185+
@@@ -1,7 -1,7 +1,7 @@@
186+
#! /usr/bin/env ruby
187+
188+
def hello
189+
- puts 'hola world'
190+
- puts 'hello mundo'
191+
++ puts 'hola mundo'
192+
end
193+
----
194+
195+
image::images/rerere3.png[]
196+
197+
You can also recreate the conflicted file state with the `checkout` command:
198+
199+
[source,shell]
200+
----
201+
$ git checkout --conflict=merge hello.rb
202+
$ cat hello.rb
203+
#! /usr/bin/env ruby
204+
205+
def hello
206+
<<<<<<< ours
207+
puts 'hola world'
208+
=======
209+
puts 'hello mundo'
210+
>>>>>>> theirs
211+
end
212+
----
213+
214+
You can actually have `checkout` do a couple of things in this situation to help you resolve conflicts. Another interesting value for that option is 'diff3', which will give you left, right and common to help you resolve the conflict manually.
215+
216+
[source,shell]
217+
----
218+
$ git checkout --conflict=diff3 hello.rb
219+
$ cat hello.rb
220+
#! /usr/bin/env ruby
221+
222+
def hello
223+
<<<<<<< ours
224+
puts 'hola world'
225+
|||||||
226+
puts 'hello world'
227+
=======
228+
puts 'hello mundo'
229+
>>>>>>> theirs
230+
end
231+
----
232+
233+
You can actually do that any time you have a merge conflict to get more context. For now though, you can re-resolve it by just running `rerere` again:
234+
235+
[source,shell]
236+
----
237+
$ git rerere
238+
Resolved 'hello.rb' using previous resolution.
239+
$ cat hello.rb
240+
#! /usr/bin/env ruby
241+
242+
def hello
243+
puts 'hola mundo'
244+
end
245+
----
246+
247+
We have re-resolved the file automatically using the `rerere` cached resolution. You can now add and continue the rebase to complete it.
248+
249+
[source,shell]
250+
----
251+
$ git add hello.rb
252+
$ git rebase --continue
253+
Applying: i18n one word
254+
----
255+
256+
So, if you do a lot of re-merges, or want to keep a topic branch up to date with your master branch without a ton of merges, or you rebase often, you can turn on `rerere` to help your life out a bit.

book/preface.asc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ At the time of publishing there were maybe a few thousand people using the site
2727
As I write this introduction, GitHub is announcing our 10 millionth hosted project, with nearly 5 million registered developer accounts and over 230 employees.
2828
Love it or hate it, GitHub has heavily changed large swaths of the Open Source community in a way that was barely conceivable when I sat down to write the first edition.
2929

30-
I wrote a small section in Pro Git on GitHub as an example of hosted Git which I was never very comfortable with.
30+
I wrote a small section in the original version of Pro Git about GitHub as an example of hosted Git which I was never very comfortable with.
3131
I didn't much like that I was writing what I felt was essentially a community resource and also talking about my company in it.
3232
While I still don't love that conflict of interests, the importance of GitHub in the Git community is unavoidable.
3333
Instead of an example of Git hosting, I have decided to turn that part of the book into more deeply describing what GitHub is and how to effectively use it.

0 commit comments

Comments
 (0)