Lets assume I have a branch called 'my-local-changes', which is a local branch, that was created from a branch called 'some-remote-branch', which is a remote branch.
Let's also assume there is a third branch called 'develop', which is the remote branch where code is pulled into from multiple branches ('some-remote-branch' is one of them, I also have a local branch called 'develop' that tracks the remote develop branch)
My question is how can i set up 'my-local-changes' to track the 'develop' branch, but push to the branch 'some-remote-branch'?
For those curious, as to why I want to do this, I would like to be able to run git status and see if I am behind 'develop' while not having to switch to that branch, and easily still be able to push to 'some-remote-branch'
My current process is as follows (I'd love any suggestions for improving this as well)
git checkout -b my-local-branch some-remote-branch
(make some changes and add them)
git fetch origin
git checkout develop
git status
(do this to see if any changes on develop that I need to merge, if not run)
git push origin my-local-branch some-remote-branch
(Not sure why the question is downvoted,1 and you have almost answered it yourself anyway...)
You are nearly there: simply configure push.default
to simple
or nothing
, so that you must specify the push target for this case, and then for your final command, use:
git push origin my-local-branch:some-remote-branch
The way this works is that git push
takes, after origin
(the name of the remote), a series of refspecs. A refspec is not very complicated: it's just a pair of names, like master:master
, or develop:develop
, or develop:foo
, optionally with a leading plus sign +
, and optionally omitting either the first or second name: :foo
or develop
.
Refspecs get slightly complicated in that they work differently in git fetch
and git push
, when you leave out one of the two names. If you use both names each time, they stay simple: the name on the left is the name on the source repository, and the name on the right is the name on the destination. For fetch
, the source is the remote and the destination is your own repository. For push
, the source is your repository and the destination is the remote.
Leaving out the source name only works with push
, and in this case, it means delete. Hence git push origin :foo
means delete foo
on remote origin
. (That's not what you want, so avoid that.)
Leaving out the destination name works with both fetch
and push
but means different things to them. Let's just ignore fetch
here. With push
, it means use the same name on both local and remote. Since you don't want that here, don't use it here. (Or, for those cases where you do want it, go ahead and use it.)
The leading +
sign, if present, means the same as --force
. (In fact --force
just adds the +
on everything.)
If you run git push origin
, or git push
(without even a remote
argument), Git looks up push.default
to see what to push. Setting it to nothing
means just fail / error-out, so that I have to type in a refspec. Setting it to simple
means push just one branch, the current branch, to the current branch's upstream, but also require that the upstream name match. Since my-local-branch
and some-remote-branch
don't match, this will fail for your case of local branch foo
is tracking remote-tracking branch origin/develop
: the names foo
and develop
do not match.
Either way, Git will force you to enter a refspec for this push.
With the nothing
configuration, Git will force you to enter a refspec for every push. (I have used this and it works, but it's not terribly convenient.) With the simple
configuration, Git will allow you to push your develop
to the upstream develop
easily, and will allow you to push your foo
to an upstream develop
(or an upstream jazzy
, or any other name but foo
) explicitly, but won't push foo
to foo
since that's not its defined upstream. (I have used this and it works better.)
As of Git version 2.0, simple
is the default configuration. So if you have Git version 2.0 or later, you are already good to go. If not, see if you can upgrade your Git version, but push.default
is configurable as far back as Git version 1.6. (I'm not sure what values it could take on, then. The current set-of-5 values goes back to 1.7.11 if not earlier, I think.)
For completeness, the other three possible values are: current
, upstream
, and matching
. The current
value means use the current branch's name: git push origin $branch:$branch
where $branch
is the current branch. The upstream
value means use the current branch's upstream name: git push origin $branch:$merge
, where $merge
is from git config --get branch.$branch.merge
.
The matching
value is the hardest to describe, and was the default before Git version 2.0: it means get a list of every branch on the remote, and match up their names and our local branch names, and then do an en-masse push of all of our local branches to the branch of the same name on the remote, wherever the two names match up. This is not a very safe setting, although as long as you do not use --force
, it actually works pretty well in practice, which is why it was usable for so many years.
Git's terminology is kind of a mess.
A local branch (or just "a branch" or "a branch name"), like master
, is a name in the refs/heads/
name-space. It points to a commit ID. When you make a new commit while on that branch, Git reads the commit ID, makes the new commit with that ID as the new commit's parent, and then writes the new commit's ID into the branch-name, so that the branch now points to the new commit (which in turns points to the previous branch-tip, and so on).
You can git checkout
a local branch, which puts you on "on the branch", so that git status
says On branch master
for instance. This sets things up so that new commits advance the branch, as I just noted above.
A remote-tracking branch like origin/master
is a name in the refs/remotes/
name-space. After refs/remotes/
we find the name of the remote itself, origin
, then another slash, and finally the name of the branch as seen on that remote (sans refs/heads/
of course). These names are stored locally, in your own repository: they're not actually remote at all. They are just automatically updated when your Git contacts the remote, through git fetch
and (to a more limited extent) through git push
.
You can git checkout
a remote-tracking branch, but if you do, git checkout
puts you in "detached HEAD" mode, so that git status
and git branch
claim you're not on any branch (or are on "no branch" or similar, sometimes using the "detached HEAD" phrasing). When this happens you are actually on the (single, special) anonymous branch. When you get back on a normal branch, any work you did on the anonymous branch will eventually fade away. (So if you want to keep the work, set up a name, so that it's not anonymous anymore—or use git checkout
to get back on a regular branch, before you do that work.)
A local branch can track another branch (local or remote). When local branch B is tracking another branch U, Git calls this the "upstream" of the local branch. This upstream is composed of two parts: the name of the remote, like origin
, and the name of the branch as seen on the remote, like master
. To track a local branch as your upstream for branch B, Git just sets the remote to .
instead.
Hence local branch B is tracking upstream U if it has these two items configured. U is itself normally a remote-tracking branch, which is a local entity (one of those refs/remotes/
name-space names). You must git fetch
or git push
to get the remote-tracking branch(es) updated, after which git status
and git branch -vv
will report local branches that are tracking the remote-tracking branches, as being ahead and/or behind their counterparts.
Local branch B can instead track another local branch as its upstream. In this case, since everything is locally-updated, git status
and git branch -vv
will be up-to-date without any git fetch
needed.
A local branch B need not track anything, of course. The commands git branch --set-upstream-to upstream
and git branch --unset-upstream
will set or unset the upstream of the current branch. You can also set, change, and examine the two individual parts of the upstream (the branch.$branch.remote
and the branch.$branch.merge
parts) using git config
, although using git branch
is usually nicer and more convenient.
1Perhaps the downvote is because of the Git 2.0 default push.default
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With