From: Junio C Hamano Date: Mon, 7 Nov 2005 07:29:35 +0000 (-0800) Subject: Documentation: talk about guts of merge in tutorial. X-Git-Tag: v0.99.9f~2^2 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=44760f1d559ae2087e53be6971ad4f1a8f18f1d7;p=git.git Documentation: talk about guts of merge in tutorial. While discussing Jon's ASCII art on merge operations with him, I realized that the tutorial stops talking about the plumbing details halfway. So fill in the gory details, and update the examples to use 'git-merge', not 'git-resolve'. Signed-off-by: Junio C Hamano --- diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt index 214673db0..6d2c153cf 100644 --- a/Documentation/tutorial.txt +++ b/Documentation/tutorial.txt @@ -301,7 +301,7 @@ all with a sequence of simple shell commands: ------------------------------------------------ tree=$(git-write-tree) commit=$(echo 'Initial commit' | git-commit-tree $tree) -git-update-ref HEAD $(commit) +git-update-ref HEAD $commit ------------------------------------------------ which will say: @@ -836,14 +836,14 @@ source. Anyway, let's exit `gitk` (`^Q` or the File menu), and decide that we want to merge the work we did on the `mybranch` branch into the `master` branch (which is currently our `HEAD` too). To do that, there's a nice -script called `git resolve`, which wants to know which branches you want +script called `git merge`, which wants to know which branches you want to resolve and what the merge is all about: ------------ -git resolve HEAD mybranch "Merge work in mybranch" +git merge "Merge work in mybranch" HEAD mybranch ------------ -where the third argument is going to be used as the commit message if +where the first argument is going to be used as the commit message if the merge can be resolved automatically. Now, in this case we've intentionally created a situation where the @@ -851,12 +851,14 @@ merge will need to be fixed up by hand, though, so git will do as much of it as it can automatically (which in this case is just merge the `example` file, which had no differences in the `mybranch` branch), and say: - Simple merge failed, trying Automatic merge - Auto-merging hello. + Trying really trivial in-index merge... + fatal: Merge requires file-level merging + Nope. + ... merge: warning: conflicts during merge ERROR: Merge conflict in hello. fatal: merge program failed - Automatic merge failed, fix up by hand + Automatic merge failed/prevented; fix up by hand which is way too verbose, but it basically tells you that it failed the really trivial merge ("Simple merge") and did an "Automatic merge" @@ -928,7 +930,7 @@ resolve to get the "upstream changes" back to your branch. ------------ git checkout mybranch -git resolve HEAD master "Merge upstream changes." +git merge "Merge upstream changes." HEAD master ------------ This outputs something like this (the actual commit object names @@ -1103,6 +1105,155 @@ the above are equivalent to: . `git pull http://www.kernel.org/pub/.../jgarzik/netdev-2.6.git e100` +How does the merge work? +------------------------ + +We said this tutorial shows what plumbing does to help you cope +with the porcelain that isn't flushing, but we so far did not +talk about how the merge really works. If you are following +this tutorial the first time, I'd suggest to skip to "Publishing +your work" section and come back here later. + +OK, still with me? To give us an example to look at, let's go +back to the earlier repository with "hello" and "example" file, +and bring ourselves back to the pre-merge state: + +------------ +$ git show-branch --more=3 master mybranch +! [master] Merge work in mybranch + * [mybranch] Merge work in mybranch +-- +++ [master] Merge work in mybranch +++ [master^2] Some work. +++ [master^] Some fun. +------------ + +Remember, before running `git merge`, our `master` head was at +"Some fun." commit, while our `mybranch` head was at "Some +work." commit. + +------------ +$ git checkout mybranch +$ git reset --hard master^2 +$ git checkout master +$ git reset --hard master^ +------------ + +After rewinding, the commit structure should look like this: + +------------ +$ git show-branch +* [master] Some fun. + ! [mybranch] Some work. +-- + + [mybranch] Some work. ++ [master] Some fun. +++ [mybranch^] New day. +------------ + +Now we are ready to experiment with the merge by hand. + +`git merge` command, when merging two branches, uses 3-way merge +algorithm. First, it finds the common ancestor between them. +The command it uses is `git-merge-base`: + +------------ +$ mb=$(git-merge-base HEAD mybranch) +------------ + +The command writes the commit object name of the common ancestor +to the standard output, so we captured its output to a variable, +because we will be using it in the next step. BTW, the common +ancestor commit is the "New day." commit in this case. You can +tell it by: + +------------ +$ git-name-rev $mb +my-first-tag +------------ + +After finding out a common ancestor commit, the second step is +this: + +------------ +$ git-read-tree -m -u $mb HEAD mybranch +------------ + +This is the same `git-read-tree` command we have already seen, +but it takes three trees, unlike previous examples. This reads +the contents of each tree into different 'stage' in the index +file (the first tree goes to stage 1, the second stage 2, +etc.). After reading three trees into three stages, the paths +that are the same in all three stages are 'collapsed' into stage +0. Also paths that are the same in two of three stages are +collapsed into stage 0, taking the SHA1 from either stage 2 or +stage 3, whichever is different from stage 1 (i.e. only one side +changed from the common ancestor). + +After 'collapsing' operation, paths that are different in three +trees are left in non-zero stages. At this point, you can +inspect the index file with this command: + +------------ +$ git-ls-files --stage +100644 7f8b141b65fdcee47321e399a2598a235a032422 0 example +100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1 hello +100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2 hello +100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello +------------ + +In our example of only two files, we did not have unchanged +files so only 'example' resulted in collapsing, but in real-life +large projects, only small number of files change in one commit, +and this 'collapsing' tends to trivially merge most of the paths +fairly quickly, leaving only the real changes in non-zero stages. + +To look at only non-zero stages, use `\--unmerged` flag: + +------------ +$ git-ls-files --unmerged +100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1 hello +100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2 hello +100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello +------------ + +The next step of merging is to merge these three versions of the +file, using 3-way merge. This is done by giving +`git-merge-one-file` command as one of the arguments to +`git-merge-index` command: + +------------ +$ git-merge-index git-merge-one-file hello +Auto-merging hello. +merge: warning: conflicts during merge +ERROR: Merge conflict in hello. +fatal: merge program failed +------------ + +`git-merge-one-file` script is called with parameters to +describe those three versions, and is responsible to leave the +merge results in the working tree and register it in the index +file. It is a fairly straightforward shell script, and +eventually calls `merge` program from RCS suite to perform the +file-level 3-way merge. In this case, `merge` detects +conflicts, and the merge result with conflict marks is left in +the working tree, while the index file is updated with the +version from the current branch (this is to make `git diff` +useful after this step). This can be seen if you run `ls-files +--stage` again at this point: + +------------ +$ git-ls-files --stage +100644 7f8b141b65fdcee47321e399a2598a235a032422 0 example +100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 0 hello +------------ + +As you can see, there is no unmerged paths in the index file. +This is the state of the index file and the working file after +`git merge` returns control back to you, leaving the conflicting +merge for you to resolve. + + Publishing your work --------------------