Reputation: 3663
I have a branch set up to track a ref in origin. git checkout <branchname>
switches to that branch, and a git status
will show me how far ahead or behind my branch is from origin, but I'm surprised that origin/HEAD
still points at origin/master
, and not origin/<branchname>
So my question is, under what circumstances does origin/HEAD get moved?
EDIT:
I appreciate the answers about how to move origin/HEAD, but I'm interested in what "organically" moves it, outside of me explicitly telling it to do so.
For example, when I switch branches, git makes HEAD point at the branch I'm checking out, so I'm surprised that origin/HEAD doesn't move in the same manner.
Upvotes: 224
Views: 295224
Reputation: 1323963
Robert Siemer mentions in his answer:
What sets origin/HEAD?
git clone
fetches and sets it- it would make sense if
git fetch
updates it like any other reference, but it doesn’t.
Actually… it does now! A git fetch
is enough.
With Git 2.48 (Q1 2025), pre-2.48-rc1, when "git fetch $remote
"(man)" notices that refs/remotes/$remote/HEAD
is missing and discovers what branch the other side points with its HEAD
, refs/remotes/$remote/HEAD
is updated to point to it.
See commit b1b713f, commit 3f763dd, commit 9963746, commit ed2f6f8, commit dfe86fa, commit 4f07c45, commit d842cd1, commit 8102d10, commit 2fd5555, commit 54d820d (22 Nov 2024) by Bence Ferdinandy (ferdinandyb
).
(Merged by Junio C Hamano -- gitster
-- in commit 5f21268, 19 Dec 2024)
fetch
: set remote/HEAD if it does not existSigned-off-by: Bence Ferdinandy
When cloning a repository
remote/HEAD
is created, but when the user creates a repository withgit init
(man), and later adds a remote,remote/HEAD
is only created if the user explicitly runs a variant of "remote set-head
".
Attempt to setremote/HEAD
duringfetch
, if the user does not have it already set.
Silently ignore any errors.
That essentially automates something that previously had to be done explicitly (via git remote set-head
).
It means that if you create a local repository (with git init
), add a remote, then do a fetch, Git will try to set remote/HEAD
to the remote's default branch if remote/HEAD
is not already set.
"git fetch
"(man) from a configured remote learned to update a missing remote-tracking HEAD, but it asked the remote about their HEAD even when it did not need to.
That has been corrected with Git 2.48 (Q1 2025), pre-2.48-rc1.
Incidentally, this also corrects "git fetch --tags $URL
"(man)" which was broken by the new feature in an unspecified way.
See commit 6c915c3, commit e02082c (06 Dec 2024) by Junio C Hamano (gitster
).
(Merged by Junio C Hamano -- gitster
-- in commit ae75cef, 19 Dec 2024)
fetch
: do not ask for HEAD unnecessarilyReported-by: Josh Steadmon Signed-off-by: Junio C Hamano
In 3f763dd ("
fetch
: set remote/HEAD if it does not exist", 2024-11-22, Git v2.49.0 -- merge),git-fetch
(man) learned to opportunistically set$REMOTE/HEAD
when fetching by always asking for remote HEAD, in the hope that it will help settingrefs/remotes/<name>/HEAD
if missing.But it is not needed to always ask for remote HEAD.
When we are fetching from a remote, for which we have remote-tracking branches, we do need to know about HEAD.
But if we are doing one-shot fetch, e.g.,$ git fetch --tags https://github.com/git/git
we do not even know what sub-hierarchy of
refs/remotes/<remote>/
we need to adjust the remote HEAD for.
There is no need to ask for HEAD in such a case.Incidentally, because the unconditional request to list "HEAD" affected the number of
ref-prefixes
requested in thels-remote
request, this affected how the requests for tags are added to the samels-remote
request, breaking "git fetch --tags $URL
"(man)" performed against a URL that is not configured as a remote.
So, instead of always querying the remote's HEAD
(which could lead to unnecessary or broken fetching scenarios, e.g., git fetch --tags URL
for a non-configured remote), Git now only queries it if it actually needs to update remote/HEAD
.
And now, git fetch
has a new setting remote.<remote>.followRemoteHEAD
:
With Git 2.48 (Q1 2025), pre-2.48-rc1, "git fetch
"(man) honors remote.<remote>.followRemoteHEAD
settings to tweak the remote-tracking HEAD in refs/remotes/<remote>/HEAD
".
See commit 012bc56, commit 9e2b700, commit ad739f5 (05 Dec 2024), and commit b7f7d16 (29 Nov 2024) by Bence Ferdinandy (ferdinandyb
).
See commit 761e62a (27 Nov 2024) by Junio C Hamano (gitster
).
(Merged by Junio C Hamano -- gitster
-- in commit a1f34d5, 19 Dec 2024)
fetch
: add configuration forset_head
behaviourSigned-off-by: Bence Ferdinandy
In the current implementation, if
refs/remotes/$remote/HEAD
does not exist, running fetch will create it, but if it does exist it will not do anything, which is a somewhat safe and minimal approach.
Unfortunately, for users who wish to NOT haverefs/remotes/$remote/HEAD
set for any reason (e.g. so thatgit rev-parse origin
(man) doesn't accidentally point them somewhere they do not want to), there is no way to remove this behaviour.
On the other side of the spectrum, users may want fetch to automatically update HEAD or at least give them a warning if something changed on the remote.Introduce a new setting,
remote.$remote.followRemoteHEAD
with four options:
- "
never
": do not ever do anything, not even create- "
create
": the current behaviour, now the default behaviour- "
warn
": print a message if remote and local HEAD is different- "
always
": silently update HEAD on every change
git config
now includes in its man page:
remote.<name>.followRemoteHEAD
How
git fetch
should handle updates toremotes/<name>/HEAD
.
- The default value is "
create
", which will createremotes/<name>/HEAD
if it exists on the remote, but not locally, but will not touch an already existing local reference.- Setting to "
warn
" will print a message if the remote has a different value, than the local one and in case there is no local reference, it behaves like "create
".- Setting to "
always
" will silently update it to the value on the remote.- Finally, setting it to "
never
" will never change or create the local reference.
Upvotes: 0
Reputation: 34677
What moves origin/HEAD "organically"?
git clone
sets it once to the spot where HEAD is on origin
git clone
What does HEAD on origin represent?
git clone
uses it in such a wayWhat sets origin/HEAD?
git clone
fetches and sets itgit fetch
updates it like any other reference, but it doesn’tgit remote set-head origin -a
fetches and sets it
Trivia
origin/HEAD
can also be set to any other value without contacting the remote: git remote set-head origin <branch>
origin/HEAD
is used as short-cut to a branch, when you name a remote onlyUpvotes: 47
Reputation: 9488
It gets set by git clone
and also by git remote add -m master $url
. Other than that you would have to set it manually.
I did sent some patches to be updated with git fetch
as well (depending on some configuration). That would be the most organic way to update it in my opinion. Unfortunately they haven't been merged yet.
Upvotes: 1
Reputation: 66244
Disclaimer: this is an update to Cascabel's answer, which I'm writing to save the curious some time.
I tried in vain to replicate (in Git 2.0.1) the remote HEAD is ambiguous
message that Cascabel mentions in his answer; so I did a bit of digging (by cloning https://github.com/git/git and searching the log). It used to be that
Determining HEAD is ambiguous since it is done by comparing SHA1s. In the case of multiple matches we return refs/heads/master if it matches, else we return the first match we encounter. builtin-remote needs all matches returned to it, so add a flag for it to request such.
(Commit 4229f1fa325870d6b24fe2a4c7d2ed5f14c6f771
, dated Feb 27, 2009, found with git log --reverse --grep="HEAD is ambiguous"
)
However, the ambiguity in question has since been lifted:
One long-standing flaw in the pack transfer protocol used by "git clone" was that there was no way to tell the other end which branch "HEAD" points at, and the receiving end needed to guess. A new capability has been defined in the pack protocol to convey this information so that cloning from a repository with more than one branches pointing at the same commit where the HEAD is at now reliably sets the initial branch in the resulting repository.
(Commit 9196a2f8bd46d36a285bdfa03b4540ed3f01f671
, dated Nov 8, 2013, found with git log --grep="ambiguous" --grep="HEAD" --all-match
)
Edit (thanks to torek):
$ git name-rev --name-only 9196a2f8bd46d36a285bdfa03b4540ed3f01f671
tags/v1.8.4.3~3
This means that, if you're using Git v1.8.4.3 or later, you shouldn't run into any ambiguous-remote-HEAD problem.
Upvotes: 16
Reputation: 2831
Run the following commands from git CLI:
# move to the wanted commit
git reset --hard <commit-hash>
# update remote
git push --force origin <branch-name>
Upvotes: 2
Reputation: 53462
It is your setting as the owner of your local repo. Change it like this:
git remote set-head origin some_branch
And origin/HEAD will point to your branch instead of master. This would then apply to your repo only and not for others. By default, it will point to master, unless something else has been configured on the remote repo.
Manual entry for remote set-head provides some good information on this.
Edit: to emphasize: without you telling it to, the only way it would "move" would be a case like renaming the master branch, which I don't think is considered "organic". So, I would say organically it does not move.
Upvotes: 122
Reputation: 496892
Note first that your question shows a bit of misunderstanding. origin/HEAD represents the default branch on the remote, i.e. the HEAD that's in that remote repository you're calling origin. When you switch branches in your repo, you're not affecting that. The same is true for remote branches; you might have master
and origin/master
in your repo, where origin/master
represents a local copy of the master
branch in the remote repository.
origin's HEAD will only change if you or someone else actually changes it in the remote repository, which should basically never happen - you want the default branch a public repo to stay constant, on the stable branch (probably master). origin/HEAD is a local ref representing a local copy of the HEAD in the remote repository. (Its full name is refs/remotes/origin/HEAD.)
I think the above answers what you actually wanted to know, but to go ahead and answer the question you explicitly asked... origin/HEAD is set automatically when you clone a repository, and that's about it. Bizarrely, that it's not set by commands like git remote update
- I believe the only way it will change is if you manually change it. (By change I mean point to a different branch; obviously the commit it points to changes if that branch changes, which might happen on fetch/pull/remote update.)
Edit: The problem discussed below was corrected in Git 1.8.4.3; see this update.
There is a tiny caveat, though. HEAD is a symbolic ref, pointing to a branch instead of directly to a commit, but the git remote transfer protocols only report commits for refs. So Git knows the SHA1 of the commit pointed to by HEAD and all other refs; it then has to deduce the value of HEAD by finding a branch that points to the same commit. This means that if two branches happen to point there, it's ambiguous. (I believe it picks master if possible, then falls back to first alphabetically.) You'll see this reported in the output of git remote show origin
:
$ git remote show origin
* remote origin
Fetch URL: ...
Push URL: ...
HEAD branch (remote HEAD is ambiguous, may be one of the following):
foo
master
Oddly, although the notion of HEAD printed this way will change if things change on the remote (e.g. if foo is removed), it doesn't actually update refs/remotes/origin/HEAD
. This can lead to really odd situations. Say that in the above example origin/HEAD actually pointed to foo, and origin's foo branch was then removed. We can then do this:
$ git remote show origin
...
HEAD branch: master
$ git symbolic-ref refs/remotes/origin/HEAD
refs/remotes/origin/foo
$ git remote update --prune origin
Fetching origin
x [deleted] (none) -> origin/foo
(refs/remotes/origin/HEAD has become dangling)
So even though remote show knows HEAD is master, it doesn't update anything. The stale foo branch is correctly pruned, and HEAD becomes dangling (pointing to a nonexistent branch), and it still doesn't update it to point to master. If you want to fix this, use git remote set-head origin -a
, which automatically determines origin's HEAD as above, and then actually sets origin/HEAD to point to the appropriate remote branch.
Upvotes: 263
Reputation: 1490
Remember there are two independent git repos we are talking about. Your local repo with your code and the remote running somewhere else.
Your are right, when you change a branch, HEAD points to your current branch. All of this is happening on your local git repo. Not the remote repo, which could be owned by another developer, or siting on a sever in your office, or github, or another directory on the filesystem, or etc...
Your computer (local repo) has no business changing the HEAD pointer on the remote git repo. It could be owned by a different developer for example.
One more thing, what your computer calls origin/XXX is your computer's understanding of the state of the remote at the time of the last fetch.
So what would "organically" update origin/HEAD? It would be activity on the remote git repo. Not your local repo.
People have mentioned
git symbolic-ref HEAD refs/head/my_other_branch
Normally, that is used when there is a shared central git repo on a server for use by the development team. It would be a command executed on the remote computer. You would see this as activity on the remote git repo.
Upvotes: 10