Reputation: 103
Is there any way to fetch & check out supplied argument, without caring if it's a branch name or a commit hash?
git fetch
git checkout origin/<branch> or origin/<commit>
how? git checkout origin/<commit>
says there's no such branch. A simple git checkout <argument>
is impossible because it would not check out remote branch the way git checkout origin/<argument>
would.
Upvotes: 1
Views: 10237
Reputation: 103
This neat trick seems to do just what I want:
git show-ref --head --sha | grep -q ^argument
Its exit code will be 0 if the argument is a commit hash, 1 otherwise. Taken from here https://stackoverflow.com/a/29707340/3116571
Upvotes: 0
Reputation: 489848
The premise of your question is wrong:
A simple
git checkout <argument>
is impossible because it would not check out remote branch the waygit checkout origin/<argument>
would.
It's important to realize several interlocking things here about Git:
HEAD
to find.refs/heads/name
. The same word—HEAD
, in all capital letters, finds that branch name. If there isn't a current branch name, Git calls this a detached HEAD.origin/master
, is not a branch name. Its full form starts with refs/remotes/
rather than refs/heads/
.git checkout
to check out a commit, but identify it by something other than a branch name, Git will—if the checkout succeeds, that is—produce the detached HEAD state described in point 2. (You can also produce this same state with a branch name, using git checkout --detach
.)The consequence of point 4 above is that git checkout origin/name
results in a detached HEAD, the same way that git checkout hash-ID
would.
This means your script can just use git checkout <argument>
, as it will do the same thing—produce a detached HEAD—if the argument is a hash ID or if it is a remote-tracking name like origin/develop
.
Note, however, that if we change this statement to read:
A simple
git checkout <argument>
is unsuitable because it would not first create, then check out, a local branch based on an existing remote-tracking name, the waygit checkout <argument minus the leading origin/ part>
would.
we get a true statement: git checkout develop
will create a new (local) branch named develop
using the name origin/develop
(provided, of course, that local develop
does not exist yet). However, there's no obvious issue with just allowing <argument>
here and having the user provide develop
as the name:
#! /bin/sh
git fetch && git checkout "$@"
for instance.
There is an interesting consequence of points 1 and 2 here, which is that asking what's the value of HEAD
at the moment is really asking one of two different questions:
HEAD
attached to a branch? If so, which branch?The git symbolic-ref HEAD
command answers only the first question; git rev-parse HEAD
mostly answers the second, but can be told to answer the first too / instead.
In point 1 above, the almost is there for a particular reason. Imagine you have just created a new, totally-empty repository. There are no commits in this repository, so which commit is the current commit?
This situation is problematic for Git. You're on a branch, namely master
, that doesn't exist. Git calls this an orphan branch or a branch yet to be created (depending on which part of Git is doing the calling). The way Git handles this is to store the branch's name into .git/HEAD
, without actually creating the branch itself in the reference database. When you make a new commit, that creates the branch itself, and now the problem is resolved: you're on the branch, which identifies the one new commit just made, which is the current commit, so HEAD
names both the current commit and the current branch.
(Git can re-create this slightly distressed situation on demand, using git checkout --orphan
, which writes a new branch's name into HEAD
without actually creating the new branch.)
Upvotes: 1
Reputation: 1044
You can to first fetch all remote branches (automatically including the one that has your desired commit) to your local:
git fetch -a
and then simply checkout the commit by its hash:
git checkout <commit-hash>
The fetch
is needed first because otherwise your local may or may not be aware of the commit-hash on the remote. Without the fetch
, if you were to directly execute the checkout
it would complain that the commit-hash is invalid.
Upvotes: 2