OK, I don't have a good answer for you as to why repo.getRef("FETCH_HEAD") works but repo.getAllRefs().containsKey("FETCH_HEAD") should return false.

I can try to justify myself by saying that FETCH_HEAD isn't really a ref, and thus isn't returned by getAllRefs().  And getRef() just takes its input relative to $GIT_DIR and tries to treat the thing you named as a ref... if it smells enough like a ref to be read as a ref, it is, even if it isn't really a ref.  My justification for this getRef() behavior is that it is what is needed to be compatible with C Git, in case someone does something like:

  echo somesha1 >.git/MY_NAME
  jgit log MY_NAME

I can also try to justify my position by saying that C Git's implementation of `git for-each-ref` does not report ORIG_HEAD, FETCH_HEAD or MERGE_HEAD.  JGit's getAllRefs() method is our internal API equivalent to `git for-each-ref`.  If we want to behave the same way under the same repository state, we shouldn't report those names.

But it sounds like you disagree with me.  The current behavior is all rooted around mirroring C Git behavior around the same concepts.  I'm not going to try and say C Git is correct, though I'm sure that if you ask this sort of stuff over on the C Git list you will get a whole bunch of people trying to justify the way it its, and the real reason is really "it just is, that's just how it evolved over the years, we didn't design this stuff, it just got patched and patched and patched and uh... it is the way it it is".

As far as adding ORIG_HEAD goes, same deal as above.  `git for-each-ref` doesn't recognize ORIG_HEAD, and so getAllRefs() doesn't either.  But at least ORIG_HEAD is more like a proper ref... its not like FETCH_HEAD or MERGE_HEAD, because it doesn't carry additional data in it.  (Though it has been proposed to add more data onto the end of the file for use during rebase and bisect.)