You should just use git show origin/master:somefile
here, without the two leading dots. The git show
command probably should complain about ..origin/master:somefile
: print an error message to stderr, and no output at all to stdout.
With the versions of Git I have handy, git show ..<rev>:path
produces no output at all (and exits successfully!), even though the given path exists in both commits. So if I were to use your:
git show ..origin/master:somefile > ../external_folder/somefile
I would always end up with an empty ../external_folder/somefile
file. I conclude that your Git version must predate Git 1.7.11.3.
Long-ish
The <rev1>..<rev2>
syntax mostly1 means select a range of commits, with the range being determined by invoking git rev-list
, more or less as if you had run:
git log ^$(git rev-parse rev1) $(git rev-parse rev2)
You can see this effect more directly if you just run git rev-parse
yourself:
$ git rev-parse master~2..master
b994622632154fc3b17fb40a38819ad954a5fb88
^41eae3eaa81c5f2b61b2610ed74de15c4291b318
The name master
, in this particular Git repository, selects commit b994622632154fc3b17fb40a38819ad954a5fb88
. The relative expression master~2
selects commit 41eae3eaa81c5f2b61b2610ed74de15c4291b318
.
Omitting one of the two names means pretend I wrote HEAD
so since HEAD
is currently master
, we also get:
git rev-parse master~2..
b994622632154fc3b17fb40a38819ad954a5fb88
^41eae3eaa81c5f2b61b2610ed74de15c4291b318
Commands like git log
or git cherry-pick
can take a range of commits like this. The set of commits selected by this range expression is:
- all commits reachable from the non-negated (no
^
prefix) commit, excluding
- all commits reachable from the negated (
^
-prefixed) commit.
So if we have:
...--E--F--G--H <-- master (HEAD)
then master~2..master
, or—shorter but identical in meaning—HEAD~2..
selects commits G
and H
, because master
(or HEAD
) means commit H
while master~2
(or HEAD~2
) means commit F
. We select all commits up through and including commit H
, but then knock out all commits up through and including commit F
, leaving just G
and H
here.
The git show
command, however, normally shows just one thing: one commit or one file, for instance. So the notion of giving it a range expression makes no sense (but see below). The command can, however, show individual files, and it does that when the revision expression you supply names a blob object:
$ git rev-parse master:Makefile
3d3a39fc192d5544a411d4cedb3785473b6f1148
$ git cat-file -t 3d3a39fc192d5544a411d4cedb3785473b6f1148
blob
So when git show master:Makefile
is given a revision expression that resolves to a single blob object like this, git show
shows the contents of that blob object.
Now consider:
$ git rev-parse master~2..master:Makefile
3d3a39fc192d5544a411d4cedb3785473b6f1148
^41eae3eaa81c5f2b61b2610ed74de15c4291b318
While hash IDs are big, ugly, and hard to remember or get right, we can actually see here that what we're giving git show
is:
- the non-negated hash ID of the blob object we'd like to see, plus
- the negated hash ID of the commit
master~2
.
As it turns out, commit c5941f1aac071addc1c9b0781c323b588c542420
, which first went public in Git 1.7.11.3, modified the git show
command to act very much like git log
when given a commit range:
git show master~2..master
works much like running:
git rev-list master~2..master | while read commit; do git show $commit; done
There are a few differences. In particular, git log
by default does not show a patch for merge commits, but git show
by default shows a patch as if you had used git log --cc
. (The other difference is less important here: the shell variant, with the while read commit; do ...; done
sequence, has to spawn multiple git show
commands.)
Note that invoking git rev-list
this way produces no output:
$ git rev-list master~2..master:Makefile
$
That's because the second (non-negated) hash ID is that of a blob object, and blob objects do not participate in revision walking. So this amounts to select no commits, while excluding all commits reachable from master~2
.
The fact that all of this runs and exits quietly, without complaint, is almost certainly a bug. Mixing commit hashes and tag-or-blob hashes in a ..
or ...
expression should just draw an error from the revision parsing code, I think.
1The reason for the adverb mostly here is that a few commands have special handling. In particular, both git diff
and git rebase
play special tricks with the two- and/or three-dot syntax: they don't just let git rev-list
do the work.