What git command is used to replace, and what git command do you use to join an x branch to the master branch?
-
1Are you asking for [git merge](https://stackoverflow.com/questions/5601931/what-is-the-best-and-safest-way-to-merge-a-git-branch-into-master)? – kapsiR Jan 11 '20 at 19:25
-
Yes, but there are variants of these and since I'm new, I want to be sure and I don't want to throw the project away. I wanted you to help me here to be sure. – Jan 11 '20 at 19:41
2 Answers
The git merge
command is the answer.
I try to explain you the command.
Starting from the definition of git merge
: The "merge" command is used to integrate changes from another branch.
Pay attention: The target of this integration (i.e. the branch that receives changes) is always the currently checked out HEAD branch.While Git can perform most integrations automatically, some changes will result in conflicts that have to be solved by the user.
Now some useful options:
--no-ff
Creates a merge commit even when a fast-forward would be possible.
--squash
Combines all integrated changes into a single commit, instead of preserving them as individual commits.
--abort
When a conflict occurs, this option can be used to abort the merge and restore the project's state as it was before starting the merge.
Now in your case(if I understand correctly) you want to merge x branch to the master. So the sequence can be this :
git checkout master
To switch to the master branch. Now if you work on remote repository is good practice to do :
git pull
To update local copy of your repository. Then you can do the merge safely:
git merge xBranch
--squash option is a good practice when merging, but is ok also without.
Now you maybe need to resolve some conflicts. Then you can do a simple commit with git add filename
and git commit
commands, and at the end, if your repository is remote you can do git push
.

- 3,381
- 2
- 15
- 35
TL;DR
From the command line, use git merge
, or maybe git merge --no-ff
. There is a lot to know before you start, though.
Long
If you think about Git in terms of branches, you're probably on the wrong path. Git is not really about branches. Sometimes I like to shock people a bit by saying Git doesn't have branches,1 and if by branch you mean things like a Mercurial or Subversion branch, or branches from virtually any other version control system that you know well, that's actually true, because the branches Git has are really weird, compared to other version control systems.
So what, then, is Git about? What does it store? The answer is: Git stores, and is all about, commits. The commit is the basic unit of Git storage.2 Of course, commits do store files, so that's how you get files. But the commit is the central figure in Git. You should think of Git in terms of commits. Branches—or rather, one kind of the thing that people mean when they say branch—are formed by commits. Branch names, which are another thing that people mean when they say branch, just let you find commits.
To make sense of all of this, it helps a lot to draw some pictures.
1The point of this is to get their attention. I'm not sure this is actually a good strategy.
2This is by analogy a bit like atoms being the basic unit of physical existence of "everyday stuff". You can break atoms down into component parts—electrons, protons, neutrons—but by themselves, they don't provide proper existence. You have to join them up into atoms before you have, say, iron (nucleus of 26 protons, plus neutrons and electrons as needed) or copper (29 protons) or oxygen (8 protons). (Then you have to join the atoms into molecules, too, so don't push on this analogy too hard. )
Drawing pictures of commits
Before we can draw a decent picture of a commit, we need to know a few things:
- What's the name of a commit?
- What's inside the commit?
The name of any commit is its hash ID. This hash ID, which is a big ugly string of letters and digits like 083378cc35c4dbcc607e4cdd24a5fca440163d17
,3 is found by creating the commit. Once it's assigned to that commit—whatever commit it is that you, or someone, have created—that hash ID now means that commit, and only that commit. No other commit can have that ID!4 Moreover, all Git software everywhere will agree that your commit, with that hash ID, gets that hash ID.
The bad thing about these hash IDs is that they are big and ugly and humans just can't deal with them (other than by things like cut-and-paste anyway: if Git shows you a hash ID, you can grab it with your mouse, and paste it into a second Git command). So we don't, and we usually don't have to. But they are the true names of commits.
Meanwhile, what's inside a commit comes in two parts. One part is a snapshot: a full copy of every one of your files. The other part is metadata, which is information about the commit. The metadata include the name of the person who made it: you, or whoever, from your user.name
setting. They include the email address of this person, from your user.email
setting. They include a date-and-time-stamp of when you, or whoever, made the commit. They include a commit log message explaining to future-you and other people why you made this commit. Most important for Git itself, though, the metadata include the raw hash ID(s) of the commit's parent commit(s). The parent of a commit is the commit that comes before this commit.
If we use single uppercase letters to stand in for the hash ID of a commit—commit A
being the first commit ever, B
being the second, and so on—we can draw a tiny three-commit repository like this:
A <-B <-C
Here C
is the last commit we made. Inside C
, Git stores the actual hash ID of earlier commit B
. Commit C
has a full snapshot of all of our files, stored in a compressed and read-only manner. It has the usual metadata: who made it, when, and why, plus the hash ID of B
. So, if we can find commit C
, we can use it to find B
. We say that commit C
points to B
.
Meanwhile, when we made B
, we did it from commit A
, so B
has a full snapshot of all files—presumably some of them are different from those in C
—and all the usual metadata. This time, commit B
points back to commit A
, though: A
is B
's parent. So from B
we can find A
.
Commit A
is a bit special. It has the same snapshot and log message and so on, but it has no parent. It is sui generis. It somehow appeared from nothing, some sort of spontaneous generation. Or, more simply, it just has no parent. Git calls this kind of commit a root commit and every nonempty repository has at least one (and usually only one).
But that's it! We've just successfully drawn a picture of three commits. As long as we don't care about the snapshots in the commits, or their actual hash IDs, this simple:
A <-B <-C
thing does the trick. The backwards-pointing arrows are because that's how Git really works: it starts at the end, and works backwards, going from commit to commit, until you either get tired of looking and make Git stop, or it reaches a root commit, where it can't go back further.
Because it's hard to draw good arrows in any direction (there are some in various fonts but they show up weirdly on some displays) I like to now get lazy and stop drawing the internal arrows:
A--B--C
Just remember that they point backwards.
3Technically, it's a 160-bit-long string, but we tend to see it as letters and digits. These hashes are from Secure Hash Algorithm 1 or SHA-1. The Git folks plan to move to another algorithm and there will be some sort of transition period where each commit gets two names, and there will be much confusion. But Git will handle this automatically, for the most part.
4The possibility of hash collisions, which won't happen by accident but which are now being engineered on purpose, is why Git is moving from SHA-1 to another more-secure algorithm.
Branch names just let us find commits
Note that, to use our tiny three-commit repository, we have to start by finding the last commit. We could list out every commit, find all the arrows, and group them together to figure out which one is last. We could maybe go by dates: list out all commits and find the one with the latest timestamp. There are a lot of things we could do here. But what Git actually does do, is use a branch name.
Remember that hash IDs look random. Add to the mix the fact that different commits get made on different computers, and sometimes one computer has the wrong time set in it, so that the dates get screwed up. Add any number of other confounding factors from real life, and there's no obvious way to find the last commit. But if we let a branch name tell us which commit is last, well, that's really easy ... and useful! Let's draw this in:
A--B--C <-- master
As with the commits that point to earlier commits, we say that the branch name points to the last commit. Not only that, we define things this way: A branch name contains the hash ID of the commit that we will claim is the last commit. Now that we've done that, we can have more than one "last" commit, even if the commits continue.
That is, let's take our three-commit repository and make a new branch name, develop
, and make develop
point to existing commit C
:
A--B--C <-- master, develop
Now let's add a new commit D
. We'll write some files into the new commit, put in our name and email address and the current date and time, and set up new commit D
to point back to existing commit C
:
A--B--C <-- master, develop
\
D
And, as our final trick of this git commit
operation, we'll have Git write D
's actual hash ID—whatever it may be—into the name develop
:
A--B--C <-- master
\
D <-- develop
The name develop
has stopped pointing to C
and started pointing to D
.
Let's make another name feature
that points not to D
but to C
:
A--B--C <-- master, feature
\
D <-- develop
It's getting confusing here, so let's attach a special name, HEAD
, to exactly one of these other names to remember which name we're using:
A--B--C <-- master, feature (HEAD)
\
D <-- develop
Now let's make a new commit E
in the usual way. New commit E
should point back to the commit we're using, which is C
, because feature
points to C
. So we get:
E <-- feature (HEAD)
/
A--B--C <-- master
\
D <-- develop
If we add another new commit F
without changing branches, we'll get:
E--F <-- feature (HEAD)
/
A--B--C <-- master
\
D <-- develop
Note that every time we make a new commit, the branch name moves forward. The special name HEAD
is still attached to it. None of the existing commits have changed at all. The new commit just points back to what used to be the branch tip, and now the branch tip is the new commit.
Switching branches
If we now git checkout master
, we're asking Git to switch from commit F
back to commit C
again, like this:
E--F <-- feature
/
A--B--C <-- master (HEAD)
\
D <-- develop
Note that none of the commits changed, and none of the branch names moved this time, but HEAD
is now attached to master
, and so commit C
is the current one. That's what we have out, and we'll see the files from C
in our work-area.
Note, too, that the files inside the commits, like the commits themselves, are frozen for all time. Nothing and no one can change any of those. They're in a special, read-only, Git-only, compressed and frozen format.5 Because they are read-only, the fact that every commit stores a full copy of every file is cheap: the copies of files in various commits match the copies in other commits, and since commit contents are frozen, Git can just re-use the earlier copies. We only ever add new commits, and the new commits re-use existing frozen files automatically. They only store new versions of files as copies.6
In order to work with our files, though, Git copies them out. The files you work with, when you work with Git, are in what Git calls your work-tree or working tree or any number of similar names. (Git used to use the phrase working directory as well, but this is easy to confuse with what the pwd
command, or print working directory, prints. So work-tree is a better name.)
Now you know what git checkout
does—or at least, part of what it does. The git checkout
command actually can do a whole lot of different things, probably too many, so in Git 2.23 the Git folks added a new command, git switch
. Switching to a branch means attach HEAD to it, and get files out so I can see and use them. The files themselves come from the tip commit of that branch, which is the commit whose hash ID is stored in the branch (or more precisely, the branch name).
5I like to call them freeze-dried.
6Eventually, even those get compressed further, when Git packs its internal objects. This is all invisible to normal use: you don't look at Git objects directly, you just git checkout
a commit and Git defrosts and rehydrates the files from it, into a usable form.
Which commits are in which branches?
This is where Git's true weirdness with branches shows up. In most other version control systems, if you make a commit on some branch, it stays on that branch. That is, if you have a diagram like this:
I--J <-- feature1
/
...--G--H <-- master
\
K--L <-- feature2
you can easily and correctly say that feature1
holds commits I
and J
, and feature2
holds commits K
and L
. In some not-Git VCS, that would be true forever. In Git, it's often not true. Moreover, what about the commits leading up to and including commit H
? Commit H
is the tip of master
—it's the last commit on that branch, at the moment—but in Git, commit H
is on all three branches now.
That is, by starting at the last commit of feature1
, which is J
, we can work backwards: J
, then I
, then H
, then G
, and so on. So branch feature1
contains all these commits, with its tip being J
.
Similarly, by starting at the tip of feature2
, which is L
, we can work backwards: L
, then K
, then H
—but not I
or J
—then G
, and so on. So branch feature2
contains all these commits; the only ones it doesn't contain are the two that are exclusive to feature1
.
Merging
Git's merge is perhaps where Git gets its main power. Commits are the raison d'être for Git's existence, but git merge
is probably one of the reasons you're using Git.7
When we run git merge
, we generally do it like this, at least from the command line:
git checkout somebranch # or now, git switch somebranch
git merge otherbranch
It seems like we're merging branches. But no! As with so many other things in Git, what seem like branch operations are really based on commits. Let's start with this drawing again, but leave the name master
out of it. We'll pick one feature to attach our HEAD to, and merge the other:
I--J <-- feature1 (HEAD)
/
...--G--H
\
K--L <-- feature2
This works by commits. You ran git merge feature2
, so Git finds commit L
. You're on feature1
, so Git finds the current commit using HEAD
: that's J
. So Git is supposed to merge J
and L
. These are the two tip commits we'd like merged.
Git now uses the commit graph—the thing we've been drawing all along here—to go from J
back to I
and back to H
, and from L
to K
to H
. Having gone backwards along both branches, the first commit that's on both branches is clearly H
. Not only that, but that's also clearly the best common commit.
The best common commit, which Git finds automatically, is the merge base for any git merge
operation. Git now needs to figure out, and merge, two things:
- What did we change? That is, what's the difference between the snapshot in
H
, and the one inJ
? - What did they change? That is, what's the difference between the snapshot in
H
, and the one inL
?
To find these, Git runs, in effect, two git diff
commands. A git diff
extracts the snapshots and compares them. When files match, git diff
says nothing about them. When they differ, git diff
compares the files, line-by-line, and figures out which lines match and which are different, and tells us about the differences.
So, given:
git diff --find-renames <hash-of-H> <hash-of-J>
Git will find out what we changed, going from the snapshot in H
to that in J
. Then, doing a second git diff
:
git diff --find-renames <hash-of-H> <hash-of-L>
Git will find out what they changed.
The merge process, or what I like to call merge as a verb, is the process of getting these diffs and combining them. We—or rather, Git—now apply the combined changes to the snapshot from the merge base. That is, after combining all of our changes with all of their changes, Git applies these to the snapshot in H
. That keeps our changes—the new snapshot will match our J
, to that extent—but adds their changes too: the new snapshot will match our J
, but with their changes added.
Once all this combining is done, so that the merge-as-a-verb process itself is complete, Git will make a merge commit. A merge commit is just like any other commit, except that it records both tip commits as its two parents. In other words, this new commit M
will record both J
, our commit, and L
, their commit, as its two parents:
I--J
/ \
...--G--H M <-- feature1 (HEAD)
\ /
K--L <-- feature2
Like every other commit, M
has a snapshot. It's the one made from combining our changes and their changes, and applying that to the merge base. Like every other commit, M
has a log message. Git will generate a not-very-good one, consisting of the phrase merge branch Y into X or merge branch Y or something along these lines.8 The only thing that is really special about this merge is that it has two parents.9
7Well, that and the ubiquity of sites like GitHub, probably. But the existence of these sites is enabled by Git's model of distributed committing and the way Git does merging. Mercurial does all this too, in a way that is easier for most people to use, but with Bitbucket moving from mainly-Hg to mainly-Git, Git seems to have won the VCS war, at least for now.
8It is not a bad idea to put in a better one. People traditionally don't, though. Git suppresses the into X
part if X
is the literal string master
. The git pull
command adds in a URL for for the merge branch Y
part. The URL may become invalid over time, and/or the branch names may change over time, which is why this message isn't very good.
9Technically, a merge commit is any commit with two or more parents. Git has support for what it calls an octopus merge, which is one with more than two parents. This doesn't do anything you can't do with normal two-parent merges, though, so even though, e.g., Mercurial doesn't have octopus merges, Mercurial can achieve everything Git can. But this gives rise to the classification scheme for commits: a root commit is one with no parent, a regular commit is one with one parent, and a merge commit is one with two or more parents. Aside from the number of parents—and the resulting graph—these commits are all the same, really: they all just store metadata and a snapshot.
Deleting a branch name after a merge
Here is the after-merge picture again:
I--J
/ \
...--G--H M <-- feature1 (HEAD)
\ /
K--L <-- feature2
Note that the branch name, feature1
, to which HEAD
is attached, moved in the usual way, to point to new commit M
. But since M
points back to both J
and L
, we can use it to find commit L
. We can then use commit L
to find commit K
. We no longer need the name feature2
to find commit L
. So we can just delete it:
I--J
/ \
...--G--H M <-- feature1 (HEAD)
\ /
K--L
The branch name feature2
is now just gone. The commits that were on feature2
are also on feature1
; now they're only on feature1
. The set of branch names that find some commit changes dynamically over time. So the branch names that contain a commit, such as K
or L
, may change!
This is why you should think about Git repositories in terms of their commits. The branch names don't really mean much.10
10Well, branch names can mean as much as you want them to. Glory! But as with Humpty Dumpty, you might confuse your audience if you force words to mean what you want, and they don't know that.
Not all git merge
commands merge
There's a trick in the graph above. We started with this:
I--J <-- feature1 (HEAD)
/
...--G--H
\
K--L <-- feature2
but what if we start with this instead:
I--J <-- feature1
/
...--G--H <-- master (HEAD)
\
K--L <-- feature2
and run git merge feature2
? Now we're asking Git to merge feature2
into master
, i.e., commit L
into commit H
. So let's start with both branch tips and work backwards, to find the first common commit.
From H
, let's not move at all yet. From L
, let's step back once to K
, then once more to H
. Aha! We've found the shared commit, the best one reachable from both branches.
If we do a real merge now, we'll compare the snapshot in H
, the merge base, to the snapshot in H
, the branch tip of master
, to see what we changed. Then we'll compare the snapshot in H
, the merge base, to the snapshot in L
, to see what they changed.
What will comparing H
to itself do? Try it out. Pick a commit in any Git repository, use git log
to get its hash ID, and run git diff <hash> <hash>
. What happens?
The answer, of course, is that the diff is empty. No files are changed. There's nothing to combine! That's absolutely guaranteed, because the merge base is the branch tip.
So, in this case, git merge
takes a short-cut unless you tell it not to. You can run git merge --no-ff
to force a real merge, which gives you this:
I--J <-- feature1
/
...--G--H------M <-- master (HEAD)
\ /
K--L <-- feature2
That is, Git makes a new merge commit as usual, with the two parents being the tip commits, H
and L
. The current branch name advances to point to the new commit, as usual.
If you don't force it, though, Git does what Git calls a fast-forward merge, which produces this:
I--J <-- feature1
/
...--G--H
\
K--L <-- master (HEAD), feature2
That is, Git doesn't make a new commit M
. It just moves the name master
forward—in the opposite direction of the internal backwards-pointing arrows—to land at commit L
. Now the two branch names, master
and feature2
, both identify existing commit L
.
Git will do a fast-forward, instead of an actual merge, when the merge base of the two branch tips is the current HEAD
commit. You can also find situations in which Git says that there is nothing to merge:
...--o--o <-- branch1
\
o--o <-- branch2 (HEAD)
Running git merge branch1
produces a complaint, and does nothing, because the commit that branch1
identifies is already contained within in branch2
.
Combining does not always go well
When you use git merge
, and it does a true merge—finds a merge base and runs two git diff
s and so on–Git will combine these change-sets. Usually, Git can do this on its own, because your changes and their changes don't overlap. But sometimes these changes do overlap, or at least touch each other (abut). In this case, Git will throw its metaphorical hands in the air and say: This is too hard! I give up! Help me Spock!
In general, what you do in this case is finish the merge: figure out what the correct combination of these changes is. This article is too long to go into all the details, but Git leaves you with a mess, in the index and in your work-tree, and you have to clean it up. Then you run git merge --continue
or git commit
, to finish the merge process and make the final merge commit.
Sometimes, Git thinks all went well, but the resulting merge is not actually correct. In this case it is up to you to decide how to handle it. You can just make a followup commit that fixes whatever went wrong, or you can "undo" the merge or do other tricky things.11 No method is completely satisfactory, but the simplest—just add a followup commit, with a good log message explaining exactly what happened—is, I think, usually the best approach.
Usually, everything just works ... but it's wise to test stuff. If you have a CI system, that will presumably test the result.
11You can, for instance, use git commit --amend
to make an evil merge. Note that --amend
doesn't actually change any existing commit—it literally can't do that—but what it does do works fine for regular commits, but can produce these so-called evil merges, when used on merge commits.

- 448,244
- 59
- 642
- 775