The premise of your question is wrong:
A simple git checkout <argument>
is impossible because it would not check out remote branch the way git checkout origin/<argument>
would.
It's important to realize several interlocking things here about Git:
- There is always—well, almost always—a current commit, which you can use the word
HEAD
to find.
- There is not always a current branch, but if there is, it's a branch name, i.e., a reference whose full name has the form
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.
- A remote-tracking name, such as
origin/master
, is not a branch name. Its full form starts with refs/remotes/
rather than refs/heads/
.
- If you tell
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 way git 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.
Side notes
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:
- Is
HEAD
attached to a branch? If so, which branch?
- What is the hash ID of the current commit?
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.)