1

Does git checkout --detach have to do anything? Sometimes when I run it on our large codebase, it runs for 5 seconds and sometimes it runs for over a minute. I'm not sure what affects the time difference. But, what does it actually do that takes time?

Scott Stafford
  • 43,764
  • 28
  • 129
  • 177

1 Answers1

4

As the git checkout documentation says, somewhat obliquely:

You could omit <branch>, in which case the command degenerates to "check out the current branch", which is a glorified no-op with a rather expensive side-effects to show only the tracking information, if exists, for the current branch.

In your particular case, because of the --detach, it's not a no-op after all, but it still winds up doing the "rather expensive side-effects" part.

Internally, Git is walking through both the current commit and the new commit (it assumes these differ; this assumption is harmlessly wrong in this case). Wherever the trees for these commits differ, it must make a change to the index and work-tree files. This change is disallowed in some cases, which will abort the git checkout and leave everything untouched. See Checkout another branch when there are uncommitted changes on the current branch for details, but note that since the trees match (because the commits match), the "where the trees differ" case is always false: they always match.

Even though the trees match, though, it still compares the index and work-tree files so as to report git status-like output. So you get two delays. One is to read through the two sets of trees and compare them to the index contents (this is probably pretty fast). The second is to run git status. This is probably the slow part, especially if your git status will sometimes update cache information on files, and/or if you are using Git-LFS. If you have CRLF translation enabled, or when using Git-LFS, there are some cases where Git (or LFS) must do a lot of data transformation and checking just to discover that everything is up to date anyway.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Fascinating. In my case, there may well be working copy changes. I'm doing the `--detach` in order to 'update' the branch to the working copy's status, via https://stackoverflow.com/a/15993574/237091. Is there a faster way to do this? – Scott Stafford Jan 18 '18 at 19:27
  • 1
    Yes: see the accepted answer to the same question. Instead of using `git checkout --detach` to disconnect from the current branch without changing anything else, you can update `HEAD` directly. Ultimately you want `HEAD` to be a symbolic ref to a different branch (not detached at all) so `git symbolic-ref HEAD refs/heads/` will do that in one step. You don't want the index and work-tree touched, and this won't touch or even look at them. Leave out the `git reset` (which touches the index) from the accepted answer, and you're ready to `git commit` on the branch that `HEAD` now names. – torek Jan 18 '18 at 19:54
  • It's worth nothing that this is a very peculiar work-flow, not suited to usage by human beings. It makes sense for certain automated build processes though. – torek Jan 18 '18 at 19:54