skip to Main Content

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

  1. master

  2. 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


  1. 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).

    Login or Signup to reply.
  2. 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:

    $ git rev-parse master
    c05048d43925ab8edcb36663752c2b4541911231
    

    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:

    $ git cat-file -p c05048d43925ab8edcb36663752c2b4541911231 | sed 's/@/ /'
    tree a446a29ef7ca90d9c64825fb00a0f1e1a099ca18
    parent e9983f8965c0875cc6727e9644c84ff1dfc99372
    author Junio C Hamano <gitster pobox.com> 1536096807 -0700
    committer Junio C Hamano <gitster pobox.com> 1536096807 -0700
    
    Git 2.19-rc2
    
    Signed-off-by: Junio C Hamano <gitster pobox.com>
    

    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:

    ...  <-c05048d43925ab8edcb36663752c2b4541911231   <--master
    

    The name master points to this commit, which points to the earlier commit e9983f8965c0875cc6727e9644c84ff1dfc99372. 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 and defect-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’t

    Their 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 their master, calling it upstream/master, and renames their devel, calling it upstream/devel.

    We can draw the commits like this, using round os 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 run git fetch upstream, you have something like this:

    ... <-o <-o <-o   <-- master
                   
                    o   <-- defect-875
    

    where your defect-875 commit points back to the tip of your master branch, which points back to some earlier commit, and so on. Once you run git fetch upstream, though, you have something more like this:

                         o--o   <-- upstream/devel
                        /
                 o--o--o   <-- upstream/master
                /
    ...--o--o--o   <-- master
                
                 o   <-- defect-875
    

    (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 like upstream/devel. This name resolves to a commit hash ID—you can run git rev-parse to see how that works, just as I showed above. It works because git fetch obtained their devel and renamed it to be your own upstream/devel. If you have not yet run git fetch upstream, you must do that first, so that your Git has their commits and has renamed their devel to become your upstream/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:

                         o--R   <-- upstream/devel
                        /
                 o--o--o
                /
    ...--o--o--B
                
                 L   <-- defect-875 (HEAD)
    

    Running git merge upstream/devel will find your current or HEAD commit L (for Left or Local or --ours); your commit labeled upstream/devel, which is commit R (for Right or Remote or --theirs); and use L and R to work back to the common starting point, which is commit B (for Base).

    Your Git will then, in effect, run two git diff commands, to see what you changed—what’s different in B vs L—and to see what they changed, i.e., what’s different in B vs R:

    git diff --find-renames <hash-of-B> <hash-of-L>   # what we did
    git diff --find-renames <hash-of-B> <hash-of-R>   # what they did
    

    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 commit L first, as its first parent, and then their commit R 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:

                 L----------------M   <-- defect-875 (HEAD)
                /                /
    ...--o--o--B                /
                              /
                 o--o--o      /
                            /
                         o--R   <-- upstream/devel
    

    This new commit M exists on your own defect-875 branch.

    Sending the new commits to upstream

    You can now, if you are authorized anyway, use git push to create a defect-875 branch on the Git repository your Git refers-to as upstream, using:

    git push upstream defect-875
    

    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 and M—and then suggest to their Git that they create a branch named defect-875, using commit M 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:

                 L----------------M   <-- defect-875 (HEAD), upstream/defect-875
                /                /
    ...--o--o--B                /
                              /
                 o--o--o      /
                            /
                         o--R   <-- upstream/devel
    

    Your own branch defect-875 is not changed in any way: your name defect-875 still identifies your commit M (by its actual hash ID, whatever that is). You have merely given their Git these two commits and had their Git set their name defect-875 to match yours.

    If you like, you can now set the upstream for your own branch defect-875 to the name upstream/defect-875:

    git branch --set-upstream-to=upstream/defect-875 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 your git push:

    git push -u upstream defect-875
    

    but that’s just a convenience optimization.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search