Autogenerated HTML docs for v2.44.0-616-g548fe3
[git-htmldocs.git] / howto / keep-canonical-history-correct.txt
blob5f800fd85a3dbd4218bb0aaa2bd4de08a3c71434
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
8 Keep authoritative canonical history correct with git pull
9 ==========================================================
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.
16 Suppose that that central repository has this history:
18 ------------
19     ---o---o---A
20 ------------
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).
26 Then you clone it and work on your own commits, which leads you to
27 have this history in *your* repository:
29 ------------
30     ---o---o---A---B---C
31 ------------
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:
37 ------------
38     ---o---o---A---X---Y---Z
39 ------------
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`.
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:
49 ------------
50     ---o---o---A---B---C
51                 \
52                  X---Y---Z
53 ------------
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:
58 ------------
59     ---o---o---A---B---C---M
60                 \         /
61                  X---Y---Z
62 ------------
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:
72 ------------
73     $ git log --first-parent
74 ------------
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.
82 You would rather want to have a history of this shape:
84 ------------
85     ---o---o---A---X---Y---Z---M'
86                 \             /
87                  B-----------C
88 ------------
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.
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.
104 There are multiple schools of thought about the "trunk" management.
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:
111 ------------
112     ---o---o---A---X---Y---Z---B---C
113 ------------
115 with `git pull --rebase` or something.
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.
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.
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:
136 ------------
137                  C0--C1--C2     topic-c
138                 /
139     ---o---o---A                master
140                 \
141                  B0--B1--B2     topic-b
142 ------------
144 That is, keeping separate topics on separate branches, perhaps like
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 ------------
155 And then
157 ------------
158     $ git checkout master
159     $ git pull --ff-only
160 ------------
162 would grab `X`, `Y` and `Z` from the upstream and advance your master
163 branch:
165 ------------
166                  C0--C1--C2     topic-c
167                 /
168     ---o---o---A---X---Y---Z    master
169                 \
170                  B0--B1--B2     topic-b
171 ------------
173 And then you would merge these two branches separately:
175 ------------
176     $ git merge topic-b
177     $ git merge topic-c
178 ------------
180 to result in
182 ------------
183                  C0--C1---------C2
184                 /                 \
185     ---o---o---A---X---Y---Z---M---N
186                 \             /
187                  B0--B1-----B2
188 ------------
190 and push it back to the central repository.
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.
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:
199 ------------
200     $ git reset --hard origin/master
201     $ git pull --ff-only
202     $ git merge topic-b
203     $ git merge topic-c
204 ------------
206 The procedure will result in a history that looks like this:
208 ------------
209                  C0--C1--------------C2
210                 /                     \
211     ---o---o---A---X---Y---Z---W---M'--N'
212                 \                 /
213                  B0--B1---------B2
214 ------------
216 See also https://git-blame.blogspot.com/2013/09/fun-with-first-parent-history.html