основное руководство Git для разработчиков (A Git core tutorial for developers)
MERGING TWO BRANCHES
One of the ideas of having a branch is that you do some (possibly
experimental) work in it, and eventually merge it back to the
main branch. So assuming you created the above mybranch
that
started out being the same as the original master
branch, let's
make sure we're in that branch, and do some work there.
$ git switch mybranch
$ echo "Work, work, work" >>hello
$ git commit -m "Some work." -i hello
Here, we just added another line to hello
, and we used a
shorthand for doing both git update-index hello
and git commit
by
just giving the filename directly to git commit
, with an -i
flag
(it tells Git to include that file in addition to what you have
done to the index file so far when making the commit). The -m
flag is to give the commit log message from the command line.
Now, to make it a bit more interesting, let's assume that
somebody else does some work in the original branch, and simulate
that by going back to the master branch, and editing the same
file differently there:
$ git switch master
Here, take a moment to look at the contents of hello
, and notice
how they don't contain the work we just did in mybranch
— because
that work hasn't happened in the master
branch at all. Then do
$ echo "Play, play, play" >>hello
$ echo "Lots of fun" >>example
$ git commit -m "Some fun." -i hello example
since the master branch is obviously in a much better mood.
Now, you've got two branches, and you decide that you want to
merge the work done. Before we do that, let's introduce a cool
graphical tool that helps you view what's going on:
$ gitk --all
will show you graphically both of your branches (that's what the
--all
means: normally it will just show you your current HEAD
)
and their histories. You can also see exactly how they came to be
from a common 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 merge, which wants to know which
branches you want to resolve and what the merge is all about:
$ git merge -m "Merge work in mybranch" mybranch
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 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:
Auto-merging hello
CONFLICT (content): Merge conflict in hello
Automatic merge failed; fix conflicts and then commit the result.
It tells you that it did an "Automatic merge", which failed due
to conflicts in hello
.
Not to worry. It left the (trivial) conflict in hello
in the same
form you should already be well used to if you've ever used CVS,
so let's just open hello
in our editor (whatever that may be),
and fix it up somehow. I'd suggest just making it so that hello
contains all four lines:
Hello World
It's a new day for git
Play, play, play
Work, work, work
and once you're happy with your manual merge, just do a
$ git commit -i hello
which will very loudly warn you that you're now committing a
merge (which is correct, so never mind), and you can write a
small merge message about your adventures in git merge-land.
After you're done, start up gitk --all
to see graphically what
the history looks like. Notice that mybranch
still exists, and
you can switch to it, and continue to work with it if you want
to. The mybranch
branch will not contain the merge, but next time
you merge it from the master
branch, Git will know how you merged
it, so you'll not have to do that merge again.
Another useful tool, especially if you do not always work in
X-Window environment, is git show-branch
.
$ git show-branch --topo-order --more=1 master mybranch
* [master] Merge work in mybranch
! [mybranch] Some work.
--
- [master] Merge work in mybranch
*+ [mybranch] Some work.
* [master^] Some fun.
The first two lines indicate that it is showing the two branches
with the titles of their top-of-the-tree commits, you are
currently on master
branch (notice the asterisk *
character), and
the first column for the later output lines is used to show
commits contained in the master
branch, and the second column for
the mybranch
branch. Three commits are shown along with their
titles. All of them have non blank characters in the first column
(*
shows an ordinary commit on the current branch, -
is a merge
commit), which means they are now part of the master
branch. Only
the "Some work" commit has the plus +
character in the second
column, because mybranch
has not been merged to incorporate these
commits from the master branch. The string inside brackets before
the commit log message is a short name you can use to name the
commit. In the above example, master and mybranch are branch
heads. master^ is the first parent of master branch head. Please
see gitrevisions(7) if you want to see more complex cases.
Note
Without the --more=1 option, git show-branch would not output
the [master^] commit, as [mybranch] commit is a common
ancestor of both master and mybranch tips. Please see
git-show-branch(1) for details.
Note
If there were more commits on the master branch after the
merge, the merge commit itself would not be shown by git
show-branch by default. You would need to provide --sparse
option to make the merge commit visible in this case.
Now, let's pretend you are the one who did all the work in
mybranch
, and the fruit of your hard work has finally been merged
to the master
branch. Let's go back to mybranch
, and run git
merge to get the "upstream changes" back to your branch.
$ git switch mybranch
$ git merge -m "Merge upstream changes." master
This outputs something like this (the actual commit object names
would be different)
Updating from ae3a2da... to a80b4aa....
Fast-forward (no commit created; -m option ignored)
example | 1 +
hello | 1 +
2 files changed, 2 insertions(+)
Because your branch did not contain anything more than what had
already been merged into the master
branch, the merge operation
did not actually do a merge. Instead, it just updated the top of
the tree of your branch to that of the master
branch. This is
often called fast-forward merge.
You can run gitk --all
again to see how the commit ancestry looks
like, or run show-branch, which tells you this.
$ git show-branch master mybranch
! [master] Merge work in mybranch
* [mybranch] Merge work in mybranch
--
-- [master] Merge work in mybranch