2

I saw in a tutorial that a person used git push origin master but the remote branch was named o/master. There was no remote named origin, just o. Was that a mistake he made or does origin have a special meaning?

This is the tutorial. (Click the red button if you get a popup). Type in level, go to the Remote tab, and on the second row, click the 4th button that says push arguments. Keep going until you get to the first visual demonstration.

user2030677
  • 3,448
  • 4
  • 23
  • 36
  • 1
    Might help to include a link to said tutorial, so we could see if the author simply made a mistake, or was talking about how to name remotes, or was talking about the `fetch =` lines in the git config, or whatever... – torek Aug 24 '14 at 16:17
  • @torek [Here you go](http://pcottle.github.io/learnGitBranching/) (Click the red button if you get a popup). Type in `level`, go to the `Remote` tab, and on the second row, click the 4th button that says `push arguments`. Keep going until you get to the first visual demonstration. – user2030677 Aug 24 '14 at 16:22
  • I'm having all kinds of trouble with the tutorial itself, but based on the answer already added below, the author is in fact setting up his `fetch` line to use "o/branch" instead of "origin/branch". – torek Aug 24 '14 at 16:47

2 Answers2

4

This is not exactly an answer to the question asked. If I go to the page using the link in the comment, and allow enough stuff through NoScript to let the scripts run, etc., it appears to start off with a demo of a moderately complicated git rebase operation, and I'm rapidly lost in advanced items instead of starting at some beginning. However, if the question is reframed, we get...

Remotes and "remote branches"

A "remote" is simply a name, like origin or upstream or bob, that goes into a .gitconfig or .git/config file as a "remote" section:

[remote "bob"]
    url = git://name.in.some.domain/path/to/bob.git
    fetch = +refs/heads/*:refs/remotes/bob/*

The remote-name is the thing in double quotes. The url line gives the URL for fetch and push operations. The fetch = line is pretty important too; we'll come back to it almost immediately.

When you first run git clone, the clone command sets up a "remote" section. The name it uses is origin by default, although you can choose some other name with -o or --origin.

Remote branches (aka remote-tracking branches)

All of git's references—branches, tags, and "remote branches"—are actually local, in the sense that they're stored locally in your own .git directory. Branches and tags are things you are probably already familiar with, and "remote branches" seem pretty straightforward too. But there's a bit of a twist or two here.

When you first clone some repository, the source repository has a bunch of branches. Well, it has at least one branch, probably. Some repositories only have a master branch for instance. (You can even have a repository with no branches at all; git clone gives a warning for this, but allows it.)

For discussion let's just assume that there are two branches in the original source, called master and develop. In any case, git copies all the remote's branches. But it does not simply make them ordinary local branches, even though it has to store them locally. It makes them "remote branches".

Let's also say that you're not using -o to change any names, so that your clone has a remote named origin. What git clone will do in this case is name the "remote branches" origin/master and origin/develop.

How did it get those names? That's where the fetch = line comes in.

refs and refspecs

The contents of the fetch = line is a "refspec". A refspec is, at its second-most-simplest, just a pair of ref-names separated by a colon, such as master:master or develop:develop. But hang on, what exactly is a ref-name in the first place?

A ref-name is just a human-readable name, like master or origin/master; but each one comes in several forms. There's a "full name" form, generally starting with refs/, and a "short" form like master. The short name master is just a convenient way of writing the full name, refs/heads/master. The full name carries not just the name itself, but also the "name-space" of that name, which really tells you what kind of name it is. The refs/heads/ name-space is where all your regular branches live. The refs/tags/ name-space carries all your tags, and refs/remotes/ carries all your remote-tracking branches.

That's really almost all there is to it: A "local" branch is a ref-name in refs/heads/, and a "remote" branch is one in refs/remotes/. The purpose of the remote branch, which git stores locally in your own repository, is to keep track of "where the branch was on the remote, the last time I (git) got a chance to look on the remote and see."

The fetch = line contains a refspec

Again, a simple form of a refspec is just ref:ref—a left-hand-side refname and a right-hand-side refname. The fetch = line uses this, but adds a couple of asterisks and a plus sign: +refs/heads/*:refs/remotes/origin/*.

The plus sign has a relatively simple meaning: it's the "force" flag. We'll get back to this.

The asterisks do a pretty obvious thing too. We said above that when we cloned the repository, the original had master and develop. These, being local branches, are actually refs/heads/master and refs/heads/develop. The asterisk matches everything it can, so the left side, refs/heads/*, winds up matching those two branches. On the right side, the asterisk means "put in whatever was matched on the left".

In other words, this matches refs/heads/master (left side) and produces refs/remotes/origin/master (right side). It also matches refs/heads/develop and produces refs/remotes/origin/develop.

In front, the plus sign means "force", or "do a forced update". This tells git to update the reference even if the update is not a "fast forward" (and I'll just conveniently ignore fast-forwarding in this answer :-) ). What all of this does, then, is make git fetch always update the remote-tracking branches, whenever you run git fetch. That maintains the property we want: git connected to the remote repository, talked with its gitty counterpart on that server, and found out where all the branches there are, so now it should update our "remote branch" copy.

(Wait a minute, how did we go from git clone to git fetch? Well, clone is really just a first-time fetch: it initializes a repository, adds its first "remote", and does a fetch, all as one thing. So it obeys the same rules as git fetch, here.)

When you check out a (new, local) branch with the same name as a remote-tracking branch, git makes your new local branch start at the same commit as the remote-tracking branch, and also sets up your local branch to refer to the remote branch, so that git status can print things like 1 ahead, 3 behind. Confusingly, some git documentation likes to say that this local branch "tracks" the remote branch, while other git documentation calls the "remote branch" a "remote-tracking branch" and notes that this branch "tracks" the remote. (Really, it just gets updated whenever we get our local git to contact the remote.)

At first, the reason for this renaming is a bit obscure. Once you've used git for a while, though, it becomes obvious. Let's say you clone that repository, and then make some commits yourself, on branch master or develop or both. Then you do something that contacts the remote, and the remote branches get updated. If git were to change your own (local) master and/or develop, this would lose the work you already did. So it only updates the remote-tracking branches.

Remote-tracking branches don't have to match the remote, name-wise

In my example above, I had [remote "bob"] and then fetch = +refs/heads/*:refs/remotes/bob/*. But the fetch = line does not have to use the same string, on the right hand side of its refspec, as the remote. If the fetch line reads +refs/heads/*:refs/remotes/robert/*, all of my remote-branches for remote bob wind up being named robert/branch.

[That (apparently) is the answer to the original puzzle here: for whatever reason (apparently, width of a window), the tutorial author has set up origin's fetch = line to contain refs/remotes/o/*. But let's finish off this larger answer with something else.]

What are these arguments to git push anyway?

A classic example push command is git push origin develop or git push origin master. If you check the git push documentation, though, you'll see that the two arguments are shown as "repository" and "refspec". How is origin a repository, when it's the name of a remote, and how can master be a refspec when it has no colon and hence no left and right hand sides?

The answer to the first part is that git push has evolved over time. The remote-as-repository argument origin probably was not allowed in some ancient version of git. Instead, you had to write the actual URL, the one now stored in the url = line in the remote's definition. You can still do this today, so the documentation can't limit that argument to just a "remote" name. So in git push origin, the origin part is the name of the remote, which is a short way to write the full blown URL. (For that matter, you can actually have a separate "pushurl" listed in the config file, so that fetch goes to one URL and push goes to another.)

For the second part, well, when I said a refspec was a pair of ref-names separated by a colon, I also said this was the second-simplest form. The simplest form is just a single ref-name! When written this way, git push treats it as if it were the ref-name repeated with a colon in the middle. So here, master just means master:master. (For various reasons, git fetch handles no-colon refspecs differently.)

If you've finally managed to "get" why git renames references on git fetch, this just raises another question.

Why do we push master:master and develop:develop?

With fetch, we update remote-tracking branches, origin/master (or maybe o/master), and so on. Why not update "push-tracking branches" on the server? I could push my work to refs/me/master, for instance.

Actually, we can do that; there are systems that work with this sort of thing (some kinds of pull requests and some kinds of automated testing systems use pushes to refs/for/... and the like). But git "out of the box" doesn't do it. We just push directly to the original branch on the remote.

This means that the last argument to git push—the master in git push origin master—pushes our master directly to their master. That push needs to be a "fast forward", otherwise it gets "rejected" (or we have to use the force flag, with --force or—you should recognize this from the fetch line—a plus sign, git push origin +master:master).

[This also leads to some minor flaws—recently fixed, actually—in git, for the case of "safely retracting" work, including "rewinding" published branches in ways that people can handle. The force flag is all-or-nothing, but in some cases, it's nice to be able to tell a remote server: "I think your ref <ref> is at <raw commit ID>; if so, change it to <new ID>." Essentially, this is compare-and-swap for ref-names: it enables a form of atomic update that is otherwise impossible. But that's another topic, and this answer has already taken way too long to write. :-) ]

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775
  • Wow! I'm going to have to stop what I'm doing to read this! And from what I've read so far you've answered my question and I understand now. Thanks. – user2030677 Aug 24 '14 at 23:15
2

The remote name does not have to be present anywhere in the refname.

A perfectly valid section in .git/config could look like

[remote "origin"]
    url = https://github.com/pcottle/learnGitBranching.git
    fetch = +refs/heads/*:refs/remotes/o/*

which does show the remote branches as o/master etc. in git log --decorate, but still uses origin as the remote name.

In other words, what you see as o isn't the remote name.

  • He did say that he was using `o` because `origin` couldn't fit in the box that showed the name of the branch. So `origin` is referring to `o` in `o/master`? – user2030677 Aug 24 '14 at 16:44
  • @user2030677 Not quite. During `git push`/`git pull` operations, `origin` refers to that section of the configuration file that defines the behaviour for that specific remote. That in turn may refer to local refs `refs/remotes/o/*`, or, depending on the specific arguments, may not. In `git push origin master`, what `refs/remotes/o/master` currently points to is unused. `refs/heads/master` is used to update `origin`, where `origin`'s section in the config file tells Git its URL, and after that, the remote `refs/heads/master` is matched to the local `refs/remotes/o/master` to update the latter. –  Aug 24 '14 at 16:51
  • "git treats it as if it were the ref-name repeated with a colon in the middle" - if this isn't the case for you check your git config push.default. This has burnt me in the past. – russau Jul 15 '15 at 22:40