]>
Commit | Line | Data |
---|---|---|
0678b649 SS |
1 | From: Junio C Hamano <gitster@pobox.com> |
2 | Date: Wed, 07 May 2014 13:15:39 -0700 | |
3 | Subject: Beginner question on "Pull is mostly evil" | |
4 | Abstract: This how-to explains a method for keeping a | |
5 | project's history correct when using git pull. | |
6 | Content-type: text/asciidoc | |
7 | ||
8 | Keep authoritative canonical history correct with git pull | |
9 | ========================================================== | |
10 | ||
11 | Sometimes a new project integrator will end up with project history | |
12 | that appears to be "backwards" from what other project developers | |
13 | expect. This howto presents a suggested integration workflow for | |
14 | maintaining a central repository. | |
15 | ||
16 | Suppose that that central repository has this history: | |
17 | ||
18 | ------------ | |
19 | ---o---o---A | |
20 | ------------ | |
21 | ||
22 | which ends at commit `A` (time flows from left to right and each node | |
23 | in the graph is a commit, lines between them indicating parent-child | |
24 | relationship). | |
25 | ||
26 | Then you clone it and work on your own commits, which leads you to | |
27 | have this history in *your* repository: | |
28 | ||
29 | ------------ | |
30 | ---o---o---A---B---C | |
31 | ------------ | |
32 | ||
33 | Imagine your coworker did the same and built on top of `A` in *his* | |
34 | repository in the meantime, and then pushed it to the | |
35 | central repository: | |
36 | ||
37 | ------------ | |
38 | ---o---o---A---X---Y---Z | |
39 | ------------ | |
40 | ||
41 | Now, if you `git push` at this point, because your history that leads | |
42 | to `C` lacks `X`, `Y` and `Z`, it will fail. You need to somehow make | |
43 | the tip of your history a descendant of `Z`. | |
44 | ||
45 | One suggested way to solve the problem is "fetch and then merge", aka | |
46 | `git pull`. When you fetch, your repository will have a history like | |
47 | this: | |
48 | ||
49 | ------------ | |
50 | ---o---o---A---B---C | |
51 | \ | |
52 | X---Y---Z | |
53 | ------------ | |
54 | ||
55 | Once you run merge after that, while still on *your* branch, i.e. `C`, | |
56 | you will create a merge `M` and make the history look like this: | |
57 | ||
58 | ------------ | |
59 | ---o---o---A---B---C---M | |
60 | \ / | |
61 | X---Y---Z | |
62 | ------------ | |
63 | ||
64 | `M` is a descendant of `Z`, so you can push to update the central | |
65 | repository. Such a merge `M` does not lose any commit in both | |
66 | histories, so in that sense it may not be wrong, but when people want | |
67 | to talk about "the authoritative canonical history that is shared | |
68 | among the project participants", i.e. "the trunk", they often view | |
69 | it as "commits you see by following the first-parent chain", and use | |
70 | this command to view it: | |
71 | ||
72 | ------------ | |
73 | $ git log --first-parent | |
74 | ------------ | |
75 | ||
76 | For all other people who observed the central repository after your | |
77 | coworker pushed `Z` but before you pushed `M`, the commit on the trunk | |
78 | used to be `o-o-A-X-Y-Z`. But because you made `M` while you were on | |
79 | `C`, `M`'s first parent is `C`, so by pushing `M` to advance the | |
80 | central repository, you made `X-Y-Z` a side branch, not on the trunk. | |
81 | ||
82 | You would rather want to have a history of this shape: | |
83 | ||
84 | ------------ | |
85 | ---o---o---A---X---Y---Z---M' | |
86 | \ / | |
87 | B-----------C | |
88 | ------------ | |
89 | ||
90 | so that in the first-parent chain, it is clear that the project first | |
91 | did `X` and then `Y` and then `Z` and merged a change that consists of | |
92 | two commits `B` and `C` that achieves a single goal. You may have | |
93 | worked on fixing the bug #12345 with these two patches, and the merge | |
94 | `M'` with swapped parents can say in its log message "Merge | |
95 | fix-bug-12345". Having a way to tell `git pull` to create a merge | |
96 | but record the parents in reverse order may be a way to do so. | |
97 | ||
98 | Note that I said "achieves a single goal" above, because this is | |
99 | important. "Swapping the merge order" only covers a special case | |
100 | where the project does not care too much about having unrelated | |
101 | things done on a single merge but cares a lot about first-parent | |
102 | chain. | |
103 | ||
104 | There are multiple schools of thought about the "trunk" management. | |
105 | ||
106 | 1. Some projects want to keep a completely linear history without any | |
107 | merges. Obviously, swapping the merge order would not match their | |
108 | taste. You would need to flatten your history on top of the | |
109 | updated upstream to result in a history of this shape instead: | |
110 | + | |
111 | ------------ | |
112 | ---o---o---A---X---Y---Z---B---C | |
113 | ------------ | |
114 | + | |
115 | with `git pull --rebase` or something. | |
116 | ||
117 | 2. Some projects tolerate merges in their history, but do not worry | |
118 | too much about the first-parent order, and allow fast-forward | |
119 | merges. To them, swapping the merge order does not hurt, but | |
120 | it is unnecessary. | |
121 | ||
122 | 3. Some projects want each commit on the "trunk" to do one single | |
123 | thing. The output of `git log --first-parent` in such a project | |
124 | would show either a merge of a side branch that completes a single | |
125 | theme, or a single commit that completes a single theme by itself. | |
126 | If your two commits `B` and `C` (or they may even be two groups of | |
127 | commits) were solving two independent issues, then the merge `M'` | |
128 | we made in the earlier example by swapping the merge order is | |
129 | still not up to the project standard. It merges two unrelated | |
130 | efforts `B` and `C` at the same time. | |
131 | ||
132 | For projects in the last category (Git itself is one of them), | |
133 | individual developers would want to prepare a history more like | |
134 | this: | |
135 | ||
136 | ------------ | |
137 | C0--C1--C2 topic-c | |
138 | / | |
139 | ---o---o---A master | |
140 | \ | |
141 | B0--B1--B2 topic-b | |
142 | ------------ | |
143 | ||
144 | That is, keeping separate topics on separate branches, perhaps like | |
145 | so: | |
146 | ||
147 | ------------ | |
148 | $ git clone $URL work && cd work | |
149 | $ git checkout -b topic-b master | |
150 | $ ... work to create B0, B1 and B2 to complete one theme | |
151 | $ git checkout -b topic-c master | |
152 | $ ... same for the theme of topic-c | |
153 | ------------ | |
154 | ||
155 | And then | |
156 | ||
157 | ------------ | |
158 | $ git checkout master | |
159 | $ git pull --ff-only | |
160 | ------------ | |
161 | ||
162 | would grab `X`, `Y` and `Z` from the upstream and advance your master | |
163 | branch: | |
164 | ||
165 | ------------ | |
166 | C0--C1--C2 topic-c | |
167 | / | |
168 | ---o---o---A---X---Y---Z master | |
169 | \ | |
170 | B0--B1--B2 topic-b | |
171 | ------------ | |
172 | ||
173 | And then you would merge these two branches separately: | |
174 | ||
175 | ------------ | |
176 | $ git merge topic-b | |
177 | $ git merge topic-c | |
178 | ------------ | |
179 | ||
180 | to result in | |
181 | ||
182 | ------------ | |
183 | C0--C1---------C2 | |
184 | / \ | |
185 | ---o---o---A---X---Y---Z---M---N | |
186 | \ / | |
187 | B0--B1-----B2 | |
188 | ------------ | |
189 | ||
190 | and push it back to the central repository. | |
191 | ||
192 | It is very much possible that while you are merging topic-b and | |
193 | topic-c, somebody again advanced the history in the central repository | |
194 | to put `W` on top of `Z`, and make your `git push` fail. | |
195 | ||
196 | In such a case, you would rewind to discard `M` and `N`, update the | |
197 | tip of your 'master' again and redo the two merges: | |
198 | ||
199 | ------------ | |
200 | $ git reset --hard origin/master | |
201 | $ git pull --ff-only | |
202 | $ git merge topic-b | |
203 | $ git merge topic-c | |
204 | ------------ | |
205 | ||
206 | The procedure will result in a history that looks like this: | |
207 | ||
208 | ------------ | |
209 | C0--C1--------------C2 | |
210 | / \ | |
211 | ---o---o---A---X---Y---Z---W---M'--N' | |
212 | \ / | |
213 | B0--B1---------B2 | |
214 | ------------ | |
215 | ||
216 | See also http://git-blame.blogspot.com/2013/09/fun-with-first-parent-history.html |