During the mail thread about "Pull is mostly evil" a user asked how
the first parent could become reversed.

This howto explains how the first parent can get reversed when viewed
by the project and then explains a method to keep the history correct.

Signed-off-by: Stephen P. Smith <isch...@cox.net>
---
 Documentation/Makefile                             |   1 +
 .../howto/keep-canonical-history-correct.txt       | 207 +++++++++++++++++++++
 2 files changed, 208 insertions(+)
 create mode 100644 Documentation/howto/keep-canonical-history-correct.txt

diff --git a/Documentation/Makefile b/Documentation/Makefile
index fc6b2cf..cea0e7a 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -59,6 +59,7 @@ SP_ARTICLES += howto/recover-corrupted-blob-object
 SP_ARTICLES += howto/recover-corrupted-object-harder
 SP_ARTICLES += howto/rebuild-from-update-hook
 SP_ARTICLES += howto/rebase-from-internal-branch
+SP_ARTICLES += howto/keep-canonical-history-correct
 SP_ARTICLES += howto/maintain-git
 API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt 
technical/api-index.txt, $(wildcard technical/api-*.txt)))
 SP_ARTICLES += $(API_DOCS)
diff --git a/Documentation/howto/keep-canonical-history-correct.txt 
b/Documentation/howto/keep-canonical-history-correct.txt
new file mode 100644
index 0000000..dd310ea
--- /dev/null
+++ b/Documentation/howto/keep-canonical-history-correct.txt
@@ -0,0 +1,207 @@
+From: Junio C Hamano <gits...@pobox.com>
+Date: Wed, 07 May 2014 13:15:39 -0700
+Subject: Beginner question on "Pull is mostly evil"
+Abstract: This how-to explains a method for keeping a project's history 
correct when using git pull.
+Content-type: text/asciidoc
+
+Keep authoritative canonical history correct with git pull
+==========================================================
+
+Suppose that that central repository has this history:
+
+------------
+    ---o---o---A
+------------
+
+which ends at commit A (time flows from left to right and each node in
+the graph is a commit, lines between them indicating parent-child
+relationship).
+
+Then you clone it and work on your own commits, which leads you to
+have this in *your* repository:
+
+------------
+    ---o---o---A---B---C
+------------
+
+Imagine your coworker did the same and built on top of A in *his*
+repository this history in the meantime, and then pushed it to the
+central repository:
+
+------------
+    ---o---o---A---X---Y---Z
+------------
+
+Now, if you "git push" at this point, beause your history that leads
+to C lack X, Y and Z, it will fail.  You need to somehow make the
+tip of your history a descendant of Z.
+
+One suggested way to solve the problem is "fetch and then merge".
+If you fetch, your repository will have a history like this:
+
+------------
+    ---o---o---A---B---C
+                \
+                 X---Y---Z
+------------
+
+And then if you did merge after that, while still on *your* branch,
+i.e. C, you will create a merge M and make the history look like
+this:
+
+------------
+    ---o---o---A---B---C---M
+                \         /
+                 X---Y---Z
+------------
+
+M is a descendant of Z, so you can push to update the central
+repository.  Such a merge M does not lose any commit in both
+histories, so in that sense it may not be wrong, but when people
+would want to talk about "the authoritative canonical history that
+is shared among the project participants", i.e. "the trunk", the way
+they often use is to do:
+
+------------
+    $ git log --first-parent
+------------
+
+For all other people who observed the central repository after your
+coworker pushed Z but before you pushed M, the commit on the trunk
+used to be "o-o-A-X-Y-Z".  But because you made M while you were on
+C, M's first parent is C, so by pushing M to advance the central
+repository, you made X-Y-Z a side branch, not on the trunk.
+
+You would rather want to have a history of this shape:
+
+------------
+    ---o---o---A---X---Y---Z---M'
+                \             /
+                 B-----------C
+------------
+
+so that in the first-parent chain, it is clear that the project
+first did X and then Y and then Z and merged a change that consists
+of two commits B and C that achieves a single goal.  You may have
+worked on fixing the bug #12345 with these two patches, and the
+merge M' with swapped parents can say in its log message "Merge
+'fix-bug-12345'".
+
+Note that I said "achieves a single goal" above, because this is
+important.  "swapping the merge order" only covers a special case
+where the project does not care too much about having unrelated
+things done on a single merge but cares a lot about first-parent
+chain.
+
+There are multiple schools of thought about the "trunk" management.
+
+ 1. Some projects want to keep a completely linear history without
+    any merges.  Obviously, swapping the merge order would not help
+    their taste.  You would need to flatten your history on top of
+    the updated upstream to result in a history of this shape
+    instead:
++
+------------
+    ---o---o---A---X---Y---Z---B---C
+------------
++
+    with "git pull --rebase" or something.
+
+ 2. Some projects tolerate merges in their history, but do not worry
+    too much about the first-parent order, and allows fast-forward
+    merges.  To them, swapping the merge order does not hurt, but
+    it is unnecessary.
+
+ 3. Some projects want each commit on the "trunk" to do one single
+    thing.  The output of "git log --first-parent" in such a project
+    would show either a merge of a side branch that completes a
+    single theme, or a single commit that completes a single theme
+    by itself.  If your two commits B and C (or they may even be two
+    groups of commits) were solving two independent issues, then the
+    merge M' we made in the earlier example by swapping the merge
+    order is still not up to the project standard.  It merges two
+    unrelated efforts B and C at the same time.
+
+For projects in the last category (git itself is one of them),
+individual developers would want to prepare a history more like
+this:
+
+------------
+                 C0--C1--C2     topic-c
+                /
+    ---o---o---A                master
+                \
+                 B0--B1--B2     topic-b
+------------
+
+That is, keeping separate topics on separate branches, perhaps like
+so:
+
+------------
+    $ git clone $URL work && cd work
+    $ git checkout -b topic-b master
+    $ ... work to create B0, B1 and B2 to complete one theme
+    $ git checkout -b topic-c master
+    $ ... same for the theme of topic-c
+------------
+
+And then
+
+------------
+    $ git checkout master
+    $ git pull --ff-only
+------------
+
+would grab X, Y and Z from the upstream and advance your master
+branch:
+
+------------
+                 C0--C1--C2
+                /
+    ---o---o---A---X---Y---Z
+                \
+                 B0--B1--B2
+------------
+
+And then you would merge these two branches separately:
+
+------------
+    $ git merge topic-b
+    $ git merge topic-c
+------------
+
+to result in
+
+------------
+                 C0--C1---------C2
+                /                 \
+    ---o---o---A---X---Y---Z---M---N
+                \             /
+                 B0--B1-----B2
+------------
+
+and push it back to the central repository.
+
+It is very much possible that while you are merging topic-b and
+topic-c, somebody again advanced the history in the central
+repository to put W on top of Z, and make your "git push" fail.
+
+In such a case, you would rewind to discard M and N, update the tip
+of your 'master' again and redo the two merges:
+
+------------
+    $ git reset --hard origin/master
+    $ git pull --ff-only
+    $ git merge topic-b
+    $ git merge topic-c
+------------
+
+------------
+                 C0--C1--------------C2
+                /                     \
+    ---o---o---A---X---Y---Z---W---M'--N
+                \                 /
+                 B0--B1---------B2
+------------
+
+See http://git-blame.blogspot.com/2012/03/fun-with-first-parent.html
-- 
2.0.0.rc1

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to