We use a fork model in our organization, and I am trying to merge the updates from an upstream branch to my local branch.
This is my git set up.
kartik@ ~/sourcecode/myproject (defect-875) $ git remote -v
origin [email protected]/kartik/myproject.git (fetch)
origin [email protected]/kartik/myproject.git (push)
upstream [email protected]:MyOrg/myproject.git (fetch)
upstream [email protected]:MyOrg/myproject.git (push)
The upstream has two branches
-
master
-
devel
I need to sync my local branch with devel
branch upstream.
This is what I have tried
git fetch upstream
git merge upstream/defect-875
where defect-875 is my local branch.
I always get this error message
merge: upstream/defect-875 – not something we can merge
I also tried this
kartik@ ~/sourcecode/myproject (defect-875) $ git merge upstream/devel -m "Merging from seo redirect."
merge: upstream/devel - not something we can merge
How do I resolve this issue?
2
Answers
I think you should say
git merge upstream/devel -m "Merging from devel"
(having checked out defect-875, just so that we are on the same page).eftshift0’s answer is the right one here, I think, but it sounds like you should learn a few Git tricks.
The first thing to know about Git is that it’s mostly about commits. Branches—or really, branch names (see What exactly do we mean by "branch"?)—exist in Git so that Git, and we, can find the commits.
You probably already know that a commit is, in effect, a snapshot of a source tree. (If you didn’t, well, now you do!) But it has a little bit more as well. First, every commit has a unique “true name” in the form of its hash ID. A branch name is a way to express the true name of some commit—the hash ID—and
git rev-parse
will show you the actual hash ID:These big ugly IDs seem random, but are actually completely determined by the contents of the commit itself. These contents are pretty short—here’s the above commit, with
@<address>
munged a bit to cut down on spam:The tree line above is how the commit saves the source snapshot (by using another Git internal object). The author and committer lines tell us who made the commit and when—1536096807 means
Tue Sep 4 14:33:27 2018
. The parent line gives the big ugly hash ID of the commit that comes before this commit.We say that each of these things that has a hash ID in it points to the object. For much more on this, see Think Like (a) Git, but for this answer, let me just draw it out like this:
The name
master
points to this commit, which points to the earlier commite9983f8965c0875cc6727e9644c84ff1dfc99372
. That earlier commit points to a still-earlier commit, and so on.All of these names and objects are local to your own repository
You have all of these branch names, like
master
anddefect-875
, in your own repository. They do not depend on anything outside your own Git. They point to commits that you have locally as well, and these commits—including their snapshots and their parent commits and those commit’s snapshots, all the way back in history—do not depend on anything outside your own Git either.But you do get some commits from some other Git(s). You do this by having your Git call up those Gits, using the Internet. Their Git repository has their commits with their branch names. The commits all have unique hash IDs, but if they have a commit that has the same hash ID—the same true name—as some commit that you have, then they and you by definition have the same commit.
git fetch
gets the commits they have that you don’tTheir branch names do not have to match, but the commit hash IDs do have to match. Either they match, and you have the commit, or they have a commit that you don’t. So your Git calls up their Git and says: What names do you have and what are their commit hash IDs? Their Git gives your Git this list, and your Git says to itself: ah, I have this one or hm, I don’t have that one. Your Git then gives their Git a list of commits that they have, that your Git wants, and their Git packages up these commits—including their snapshots—and sends them over.
At the end of this process, you have all of your commits (including any commits you already had that they have) plus all of their commits. Note that they send you not just the tip-most commits—the ones to which the branch names point directly—but also the parent commit, the parent’s parent, and so on, as needed so that you get commits that connect back to your own commits.
git fetch
also gets their branch names; now what?You also have their branch names. But these are their branch names, so now your Git renames these things. If your Git called up their Git using
git fetch upstream
, your Git renames theirmaster
, calling itupstream/master
, and renames theirdevel
, calling itupstream/devel
.We can draw the commits like this, using round
o
s to stand in for the actual hash IDs, although I have to now start guessing how many commits they have that you don’t. Before you rungit fetch upstream
, you have something like this:where your
defect-875
commit points back to the tip of yourmaster
branch, which points back to some earlier commit, and so on. Once you rungit fetch upstream
, though, you have something more like this:(it’s getting too hard to draw in the arrows so I’ve replaced most of them with lines—just remember that they always point backwards).
Now you can merge
When you run
git merge
, you must tell your Git which commit to merge with. You usually do this by giving it a branch name, or a remote-tracking name likeupstream/devel
. This name resolves to a commit hash ID—you can rungit rev-parse
to see how that works, just as I showed above. It works becausegit fetch
obtained theirdevel
and renamed it to be your ownupstream/devel
. If you have not yet rungit fetch upstream
, you must do that first, so that your Git has their commits and has renamed theirdevel
to become yourupstream/devel
.Merging
At this point, all the work takes place in your own repository. Let’s say my drawings have been accurate; but let’s simplify this to just the interesting names, and attach the word
HEAD
to the one you have checked out right now. I’ll also put in one-letter names for three interesting commits:Running
git merge upstream/devel
will find your current orHEAD
commitL
(for Left or Local or--ours
); your commit labeledupstream/devel
, which is commitR
(for Right or Remote or--theirs
); and useL
andR
to work back to the common starting point, which is commitB
(for Base).Your Git will then, in effect, run two
git diff
commands, to see what you changed—what’s different inB
vsL
—and to see what they changed, i.e., what’s different inB
vsR
:Git now combines—merges—these two sets of changes. If they combine easily, Git applies the combined changes to the snapshot associated with commit
B
, and makes a new commit out of the result. This is a merge commit, a special kind of commit. What makes it special is simply that it has two parents instead of one: it lists your own commitL
first, as its first parent, and then their commitR
as its second parent.It might help a little if we flip the drawing upside down, so I will do that here. The result looks like this:
This new commit
M
exists on your owndefect-875
branch.Sending the new commits to
upstream
You can now, if you are authorized anyway, use
git push
to create adefect-875
branch on the Git repository your Git refers-to asupstream
, using:This has your Git call up their Git, offer to them a list of commits that you have that they don’t—which in this case is exactly the two commits
L
andM
—and then suggest to their Git that they create a branch nameddefect-875
, using commitM
as its tip commit.If they obey all these requests and suggestions, your Git will remember that they did that, and add to your own set of names, the name
upstream/defect-875
:Your own branch
defect-875
is not changed in any way: your namedefect-875
still identifies your commitM
(by its actual hash ID, whatever that is). You have merely given their Git these two commits and had their Git set their namedefect-875
to match yours.If you like, you can now set the upstream for your own branch
defect-875
to the nameupstream/defect-875
:If you want to do both when you run
git push
, you can do all of them at once by adding the-u
flag to yourgit push
:but that’s just a convenience optimization.