-1

When merging master into feature branches, I have always done

git merge origin master

It has always worked fine. However, I was peripherally aware that the syntax origin/master also exists, to specify which remote you want to use (I think). However this morning, I wanted a clean version of master so I deleted my local copy, and then fetched. Now when I do git merge origin master I receive the message

Did you mean this?
  origin/master

Do I need the forward slash? Why is git now requiring this? What are the differences between using it and not using it?

1252748
  • 14,597
  • 32
  • 109
  • 229
  • 1
    You probably were doing `git pull origin master`. The "pull" command takes a remote and branch. The merge command just takes refspecs (branches, commit Ids, tags, etc). – Greg Burghardt Aug 17 '20 at 18:05
  • @GregBurghardt No I wasn't. – 1252748 Aug 17 '20 at 18:49
  • https://stackoverflow.com/questions/18137175/in-git-what-is-the-difference-between-origin-master-vs-origin-master Found in https://stackoverflow.com/search?q=%5Bgit%5D+difference+%22origin%2Fmaster%22+%22origin+master%22 – phd Aug 17 '20 at 19:13

1 Answers1

2

This stuff is very confusing. Git could be more consistent here, if enough of its commands had been written at the same time. They weren't—these various operations were put together over the course of several years—and backwards compatibility limited the Git authors.

Let's define some terms:

  • A remote is a short name, like origin. The purpose of this short name is several-fold:

    1. It stores a URL, so that you don't have to keep typing it. The URL might be https://github.com/user/project.git or ssh://git@github.com/user/project.git or something much longer, but the first remote name is almost always origin and that's only six letters, hence much easier to type.

    2. It acts as a prefix for remote-tracking names.


    A remote always represents some other Git. That other Git is a full Git repository (although often it's a --bare repository, meaning it has no working tree).

  • A branch name is a name like master or topic or feature/short or whatever. There are some constraints on branch names—for instance, they may not contain two or more adjacent dots, so that hello.world is OK but well...yes is not.

  • A remote-tracking name is a name like origin/master or origin/topic. It's built out of two parts: a remote name, like origin, and a branch name as seen on that other Git.

When your Git calls up another Git, via a remote name like origin, your Git has that Git list out all of its branch names. These branch names correspond to specific commits in that other Git repository. Your Git will get these commits from that Git, if you don't already have them. Then, your Git will remember their branch names by creating or updating your remote-tracking names. So if their master represents commit a123456, your origin/master will now be updated to hold a123456 too.

In general, Git can turn either a branch name like master, or a remote-tracking name like origin/master, into a commit hash ID. Running:

git rev-parse master

will show you what hash ID your Git has for your master right now, for instance.

When you run git merge—which is an early command, predating even the existence of remote names like origin—what you need to give to it is name(s) of one or more commits. So git merge origin/master suffices here, because origin/master names one specific commit. git merge topic also suffices, if you have a branch named topic, because your branch name topic names one specific commit.

If you give git merge the names of two or more commits, Git will (well, sometimes will) perform what Git calls an octopus merge. For instance:

git merge topic1 topic2 topic3

performs a merge that combines three topic branches with whatever your current branch is (perhaps master).

If you've been running:

git checkout master
git merge origin master

you have been asking Git to do one of these octopus merges. The commit you're asking to merge is origin.

Now, origin itself is not a remote-tracking name. But try running:

git rev-parse origin

as well as:

git rev-parse origin/master

You will almost certainly see that both commands produce the same commit hash ID. The reason is that the name origin by itself, when used where Git expects a branch or remote-tracking name, is expanded to read origin/HEAD. Then, origin/HEAD is expanded too, usually to origin/master. So this winds up meaning git merge origin/master master.

Meanwhile, you are already on your own master so git merge master would not do anything anyway. So this particular "octopus" merge collapses into a regular merge of your master with your origin/master, which is your Git's memory of some other Git's master.

In general you should use git merge origin/master here, to be explicit and not depend on origin/HEAD mapping to origin/master. Moreover, you may want to make use of another Git feature, called the upstream setting of a branch name, so that you can just run git merge (without any extra names at all). But it's been working because origin here got expanded to origin/HEAD which meant origin/master, and your octopus merge request got turned into a more ordinary merge in the end.

Note that the git pull command is quite different: it's meant as a convenience. It runs git fetch first, then it runs a second Git command. The default second Git command is git merge. When it does the git fetch step, it needs a remote—a name like origin. So the origin in git pull origin master is for git fetch. Then, when it does the second step, it needs to know the name of the branch you wanted as seen on the other Git. So the master in git pull origin master means master as seen over on origin, which your Git remembers as origin/master for non-pull commands like git merge.

That's where all this confusion comes from: git pull predates remotes, and you used to do things like git pull url master. Here it was clear that the url part was to get to some other Git, and then master meant their master. Remotes—like origin—didn't exist, and therefore remote-tracking names like origin/master didn't exist either. Now they do, and we have the situation we have.

torek
  • 448,244
  • 59
  • 642
  • 775
  • `git rev-parse master` => `fatal: ambiguous argument 'master': unknown revision or path not in the working tree.` However, yes: `rev-parse`ing `origin` and `origin/master` got me the same hash. "Then, origin/HEAD is expanded too, usually to origin/master. So this winds up meaning git merge origin/master master.". This is really, really good information. I have always wondered about that. Thank you so much! – 1252748 Aug 17 '20 at 21:00
  • ¶ It looks to be simply like `master` "lost track" of the remote it was meant to track when I deleted it (no surprise). `git checkout master; git branch -u origin/master`, seems to have resolved the issue. This is a great answer. Lots to think about here. Thanks again. – 1252748 Aug 17 '20 at 21:01
  • If `git rev-parse master` came up with nothing at all, you've somehow deleted the branch name `master`. Deleting the branch name itself is mostly harmless. It does lose something important: the answer to the question *which commit was I using for my branch `master`?* But if the commit you were using is also the commit the *other* Git is using for *its* `master`, that information is enough! – torek Aug 17 '20 at 23:10
  • When you run `git checkout master` and you don't have a branch named `master`, your Git checks through your remote-tracking names. If you have `origin/master` (and nothing else that looks too much like the name `master`), your Git says: *aha, you must want to **create** your `master` now* and so it does that, then checks out that commit, and you're all set. – torek Aug 17 '20 at 23:11