I believe I found the reason for this. Commit f56f31af0301
to Git changed the implementation of sparse-checkout
so that, when you have an uninitialized working tree (as you would right after running git clone --no-checkout
), running git sparse-checkout init
will not check out any files into your working tree. In previous versions, the command would actually check out files, which could have unexpected effects given that you wouldn’t have an active branch at that point.
The relevant commit, f56f31af0301
was included in Git 2.27, but not in 2.25. That accounts for why the behavior you see is not the behavior shown on the web page you’re trying to follow. Basically, the behavior on the web page was a bug that nobody realized was a bug at the time, but with Git 2.27, it has been fixed.
This is explained very well, I think, in the message for commit b5bfc08a972d
:
So…that brings us to the special case: a git clone performed with
--no-checkout
. As per the meaning of the flag,--no-checkout
does not
check out any branch, with the implication that you aren’t on one and
need to switch to one after the clone. Implementationally,HEAD
is
still set (so in some sense you are partially on a branch), but
- the index is “unborn” (non-existent)
- there are no files in the working tree (other than
.git/
)- the next time
git switch
(orgit checkout
) is run it will run
unpack_trees withinitial_checkout
flag set to true.It is not until you run, e.g.
git switch <somebranch>
that the index
will be written and files in the working tree populated.With this special
--no-checkout
case, the traditionalread-tree -mu HEAD
behavior would have done the equivalent of acting likecheckout
—
switch to the default branch (HEAD
), write out an index that matches
HEAD
, and update the working tree to match. This special case slipped
through the avoid-making-changes checks in the originalsparse-checkout
command and thus continued there.After
update_sparsity()
was introduced and used (see commitf56f31a
(“sparse-checkout: use new update_sparsity() function
“, 2020-03-27)),
the behavior for the--no-checkout
case changed: Due to git’s
auto-vivification of an empty in-memory index (seedo_read_index()
and
note thatmust_exist
is false), and due tosparse-checkout
‘s
update_working_directory()
code to always write out the index after it
was done, we got a new bug. That made it so thatsparse-checkout
would
switch the repository from a clone with an “unborn” index (i.e. still
needing aninitial_checkout
), to one that had a recorded index with no
entries. Thus, instead of all the files appearing deleted ingit status
being known to git as a special artifact of not yet being on a
branch, our recording of an empty index made it suddenly look to git as
though it was definitely on a branch with ALL files staged for deletion!
A subsequent checkout or switch then had to contend with the fact that
it wasn’t on aninitial_checkout
but had a bunch of staged deletions.