I have 2 branches (both locally and remotely on github). I will call them BranchA and BranchB. I have BranchA with commits c1, c2 and c3. I have BranchB only with commit c1. Both branches are totally updated with github, and I can check that BranchA has c1,c2 and c3 and BranchB has only c1.
I want to push from local BranchA to remote BranchB. To do this I try:
git push origin refs/heads/BranchA:refs/remotes/origin/BranchB
When I do this, I get a strange output:
Total 0 (delta 0), reused 0 (delta 0)
To https://github.com/<username>/<repo>.git
<hash> BranchA -> origin/BranchB
When I go to github I see that BranchB is not updated and still only has c1. Then, if I run the command again, it returns that everything is up to date.
This was confusing me, but it got worse since when I try to do the command without the full ref, like this:
git push origin BranchA:BranchB
...it worked.
I am very confused by this. I searched for it and I thought that using the full ref (/refs/heads/ for example) would never do any harm.
Why is this happening? Shouldn't it work when I specify the full branch name?
As prahlad venkata commented, the reference on the remote is refs/heads/BranchB
, not refs/heads/remotes/BranchB
.
Remember that when using git push
or its counterpart, git fetch
, there are two Gits, and two repositories, involved in the process. Let's call these yours (matovski's) and GitHub's.
Your Git has a branch named BranchA
whose full name is refs/heads/BranchA
. This name stores a (single) hash ID.
Their Git has a branch named BranchB
whose full name is refs/heads/BranchB
. Their Git probably also has a branch named BranchA
. These names also store hash IDs (one each).
When your Git talks to GitHub's Git, your Git sees their refs/heads/BranchA
and their refs/heads/BranchB
. Your Git would like to remember those two name-and-ID pairs, but if your Git stored them as refs/heads/BranchA
and refs/heads/BranchB
, your Git would overwrite your own branches. So your Git renames their branch names, replacing refs/heads/
with refs/remotes/origin/
.
In other words, refs/remotes/origin/BranchB
is your Git's memory of their Git's refs/heads/BranchB
. But when your Git talks to their Git, your Git has to tell their Git:Please set your refs/heads/BranchB
. If your Git asks their Git to set their refs/heads/origin/BranchB
, that would set their Git's memory of some third Git's refs/heads/BranchB
(which, as you just saw, is sometimes allowed!—it just has no useful effect in this case, since GitHub's Git repos don't use these on their own).
When you use the short-hand syntax git push origin BranchA:BranchB
, your Git sends their Git a request to set BranchB
. Their Git figures out, based on this request, that you probably meant to set their refs/heads/BranchB
, and does what (it thinks) you meant, rather than what you asked. This particular trick works only when they already have a refs/heads/BranchB
since their Git won't attempt this same guess if they don't have one yet.