Change local reference for remote branch
git
Solution
Before I answer, I want to change the terms here, as the way you are using some words do not match the words and ways used in most git documentation.
First, let's talk about the graph generated by commits in a git repository.
Any time you create a commit in a repository, you must specify the following items. Many of them are implicit; in fact, `git commit` and `git merge` (which are the two principle commit-creators) will look all of them up for you:
- a parent commit, or a set of parent commits
- an author and the "author date" (usually same as committer)
- a committer and the "commit date" (usually same as author, above)
- a "tree" (normally taken from the index/staging area), and
- a commit message (via `-m` argument, or `git commit` fires up your editor, etc)
Git combines all of these and makes a new unique SHA-1 value—those unwieldy strings of 40 characters like `9c4ea50db79d3ce6fe3abccf20f1af27abae45b2`—that is the commit (it stores this commit-object in the repo, by its SHA-1 ID). If the commit is an ordinary commit, it has one parent, which `git commit` finds via `HEAD` (which git stores in the `.git` directory) shortly before `git commit` goes to create the new commit. If it is a "root commit" like the first commit in a new repo, it has no parents. If it is a "merge commit" (created by `git merge`, for instance), it has at least two parent IDs (these come from files git stores in the `.git` directory as well). The parents stored in any given commit are the raw SHA-1 IDs.
Thus, given any one commit SHA-1 ID, you can read the commit out of the repository and get its parent commits. Reading those gets you their parents. Continue until you reach a root commit and you have something you can draw, with possible branch-points and merge-points in it:
o-o-o-o-o-o-o
\ /
o-o-o
This is a commit graph with ten commit "nodes" (the `o`s) that has some of them branching off and then merging back in. I've drawn lines between the nodes with `-` `\` and `/` characters.
When we use git we often call these lines "branches", and that's a reasonable name, but there's another meaning for "branch".
Next, let's define a "branch" (or a "local branch") this way, which is how git really makes it work: A (local) branch is a name, like `master` or `develop`, that stores an SHA-1 ID and that has one other special property. The special property is that the stored ID changes when we make a new commit, so that the branch-name always identifies the "tip" of the branch.
Remember that each commit has its parent IDs, but not any child IDs. When you make a new commit, the new commit "points back" (via these stored IDs) to the old one(s), but the old ones are never changed. It's difficult to find child IDs given only parent IDs, but it's easy to find parent IDs given only child IDs. So as long as the branch-name "points to" the tip of the branch, we can find the "rest of the branch" easily. So git simply guarantees that the local branch name always identifies the tip of the branch.
(It's a bit unfortunate that "the branch" means both "the local name holding the ID of the tip of the branch" and "the chain of commits formed by starting at the tip and working backwards". It's almost always obvious which one is to be used when, but only "almost".)
Now let's define "remote": A remote is a name you make up and put in your local git repository that will be used to remember things about some other git repository, often on another machine and belonging to someone else. A remote has a `url` (or sometimes several: `remote.origin.url` and optionally `remote.origin.pushurl` for instance), which is how git will fetch and push from and to the other repo.
Of course, that other repo has (local) branches—branch names pointing to tip commits—and it turns out that we want, very often, to know what the other repo's local branches—names and tip commits—are—or more precisely, what they were the last time we checked. (They get out of date, so we refresh them from time to time.) These are what become "remote branches". So now we can define "remote branch" a bit loosely by example: The remote branch `remotes/origin/master` is a copy of what the local branch `master` was, on remote `origin`, the last time we got a chance to connect to `origin` and ask it "what's in your `master`?"
More precisely: Each remote branch `remotes/R/B` is a copy of the local branch `B` on remote `R` the last time we checked and saved it.
Now we just need one more definition, for a "tracking branch". A tracking branch is a local branch where we have told git that there is a corresponding remote, and on that remote, a corresponding branch name. The low-level way we tell git these two things is to configure two strings with `git config`. For instance, let's say we want the local branch `greyhound` to track the branch on remote `racetrack` that is named `hare`. To do this at the lowest level, we do this:
git config branch.greyhound.remote racetrack
git config branch.greyhound.merge hare
(To complicate things—this seems to be a historical accident—the actual tracking is mapped through the `fetch` configuration for the remote. But `remote.racetrack.fetch` is almost always set up to read `+refs/heads/*:refs/remotes/racetrack/*`. For the most part we can just assume the `refs/remotes/` part is constant.) So, again, at the bottom level, when git wants to see if `greyhound` has caught up with `hare`, what it really does is look at `remotes/racetrack/hare`. The `hare` part comes from the `branch.greyhound.merge` line, and the `racetrack` part comes from `branch.greyhound.remote`.
Git will supply the `remotes/` part if you leave it out. Thus, instead of `remotes/origin/master` you can normally just write `origin/master`. (There are some exceptions if you create ambiguities, e.g., if you create a local branch named `origin` it gets tricky. But as long as you don't do that, you can leave out the `remotes/`, and people—and git—often do leave it out.)
One last side note here, since I have all the other definitions above. The special reference `HEAD` is most often a "symbolic reference". A symbolic ref contains the name of another ref, usually a local branch name. This is how git knows to adjust the local branch to the new branch-tip: If you're "on branch master", `HEAD` contains `ref: refs/heads/master`, so `git commit` adds a new commit and also updates `master`. If `HEAD` contains instead a raw SHA-1 value, `git commit` adds the new commit as usual, then changes `HEAD` to have the new SHA-1, but no local branch name points to the new commit. This is the "detached HEAD" condition.
Whew! OK, now back to the question at hand. You're about to stumble over an annoyance that is currently being fixed in git.
You want or need:
- on the remote named `origin`, there must be a git repo with a `master` branch
- on the remote named `develop`, there must be a git repo with a `master` branch
- on your local repo, you want `master` to be a tracking branch for `origin/master`
- on your local repo, you want `trunk` to be a tracking branch for `develop/master`
- when you `git push origin` you'd like to have your `master` get pushed to origin's `master`, and probably not have `trunk` pushed at all
- when you `git push develop` you'd like to have your `trunk` get pushed to develop's `master`, and probably not have `master` pushed at all
- when you `git push` (with no additional arguments), you want ... well, I'm not really sure what you want, but I'll take a guess below.
Steps 1 through 4 need only be done once. If branch `master` is not yet created on one or both, you can do:
git push -u origin master:master
git push -u develop trunk:master
(if your git push is new enough to have `-u`) to do everything all at once. Or, you can use `--set-upstream` if you have that, or do the tracking completely manually with `git config` if you like. But one way or another, create the branches on the remote repos if needed, then pick them up (as "remote branches" in your local repo) and set up your local branches to "track" the remote branches.
That leaves the remaining steps. These get a bit confusing.
In recent versions of git (at least 1.7 onward), there is a configuration setting, `push.default`, that affects how `git push` works when you omit a refspec, i.e., the `push` commands in items 5 through 7. You need this to be set to `upstream`. This is never the default setting, so you must run:
git config push.default upstream
to set it.
If you run a bare `git push` (as in #7), git uses the same tracking branch info to choose the remote. So here, now that you have `push.default` changed, if you're on branch `master` and run `git push`, you will in effect be running `git push origin master:master`, and if you're on branch `trunk` and run `git push`, you will in effect be running `git push develop trunk:master`.
If you just let the `push.default upstream` setting do its thing, though, and run `git push develop` while you are on `master`, well, you didn't tell it to push your local branch named `trunk`. So, to make 5 and 6 work, you need two different git configuration items.
If you run `git push remote` (with no refspec argument), `git push` looks up `remote.remote.push`, and uses that if it's set. Thus, you can set `remote.origin.push master:master` and `remote.develop.push trunk:master`. Now `git push develop` has a setting and does not need to use `push.default`.
Problem
I've got two repositories binded to my working directory. ``` git remote show origin * remote origin Fetch URL: ssh://project.git/ Push URL: ssh://project.git/ HEAD branch: master Remote branch: master tracked Local branch configured for 'git pull': master merges with remote master Local ref configured for 'git push': master pushes to master (up to date) git remote show develop * remote develop Fetch URL: ssh://projecttest.git/ Push URL: ssh://projecttest.git/ HEAD branch: master Remote branch: master new (next fetch will store in remotes/develop) Local ref configured for 'git push': master pushes to master (local out of date) ``` As far as I know local branches are referenced to remote branches in that way: - remote `origin/master` points to local branch `master` - remote `develop/master` points to local branch `master` What I want to achieve is to change this reference for `develop/master` remote branch. I want it to point to `trunk` local branch - remote `develop/master` should point to local branch `trunk` How can I do it?