1

I understand that when I checkout or reset --hard to a specific commit/branch then I get the relevant contents in my working directory and the index file.

But how does Git internally re-build the index and working directory contents upon checkout or reset --hard.

Is the index restoration done by reading the tree pointed to by the the commit we have checked-out/reset to ?

Is the working directory also restored the same way ?

Does that mean that after reset --hard or checkout <some_branch> the index and working directory will always match the tree of that commit because they were rebuilt from it ?


Editing: What I'm basically asking: is the restoration of the index/WD content done using the tree pointed by the commit we have arrived at ? Because as I see it there is no other way for git to fetch that content rather than from the commit history

caffein
  • 303
  • 1
  • 10
  • For a (very long) discussion of how the index and work-tree manipulation go in various corner cases, see https://stackoverflow.com/questions/22053757/checkout-another-branch-when-there-are-uncommitted-changes-on-the-current-branch – torek Apr 10 '20 at 00:14

2 Answers2

0

Does that mean that after reset --hard or checkout the index and working directory will always match the tree of that commit because they were rebuilt from it ?

Short answer: YES

Long answer:

git checkout:

Updates files in the working tree to match the version in the index or the specified tree.

https://git-scm.com/docs/git-checkout

git-reset --hard:

Resets the index and working tree. Any changes to tracked files in the working tree since are discarded.

https://git-scm.com/docs/git-reset

Both of these commands move HEAD, which then inspects whatever commit its been moved to, and alters the index and/or WD accordingly. If it doesn't alter one or both, it still acts as the keeper of the state of your files at the time of that commit and allows you to run commands to compare your WD and index to the state of your files at that moment in time.

I hope that is helpful, I was unsure as to exactly what you are looking for with your question.

symlink
  • 11,984
  • 7
  • 29
  • 50
  • What I mean - is the restoration of the index/WD content done using the tree pointed by the commit we have arrived at ? Because as I see it there is no other way for git to fetch that content rather than from the commit history, right ? – caffein Apr 09 '20 at 15:30
  • @caffein Yes, each commit comprises a FULL history of the state of the (commited) files at a point in time, and HEAD is where git is currently looking, so since git checkout [commit or branch] and git reset move HEAD to an older commit, git then compares the state of your files at that point in time to your Index and WD. If you wanted to "undo" the checkout or reset, you would have to check out or reset back to the commit you were on before (unless you git checkout a single file, which is another story). Does that answer your question? – symlink Apr 09 '20 at 15:37
  • You said that after e.g. checkout "git then compares the state of your files at that point in time to your index and WD". Did you mean that it **updates** the index/WD according to files' state (and not compares) ? because if we e.g. reset hard then they might not be equal but git will still have to update the index/WD according to the files in the target commit, correct ? Or did you mean it compares anyway because if we use `reset --soft` then git must know which files and which diff will remain in WD/index ? Is that what you mean ? :) – caffein Apr 09 '20 at 15:47
  • @caffein It compares (silently + instantly), then updates if necessary. As you know, depending on the command and flag used, these commands update either nothing, the WD, or the WD and index. The reason I say "compares" is because a lot of the time, the dev uses git reset and checkout to move to a spot in time and FIRST compare their current files (in their WD or index) to the state of the files at the time of the commit, THEN choose which files from that commit they want to checkout (checking out a single file doesn't move HEAD). This gives them finer grained control to get what they need. – symlink Apr 09 '20 at 16:18
-1

Not exactly. At least, not for the working directory.

Honestly it took me a few minutes to figure out what your question could mean. You're asking if the working directory will end up matcing the checked-out commit after running a command that you say you know sets the working directory to match the checked-out commit, so the question is... a little murky.

But there ARE situations where the working directory will end up different from what's in the checked-out commit. For example, ignored (or otherwise untracked) files will generally be undisturbed. In some cases that's not possible; maybe I have an untracked file named foo, but the target tree has a file also named foo at the same path. Different commands handle that differently. (And here it's important to note, checkout commit is a VERY different command from checkout commit -- path.) Some commands will clobber your local untracked data (which will then be unrecoverable, at least as far as any git mechanism is concerned), while others will abort and warn you that local changs would be overwritten.

So, it is not strictly correct to say the working tree is "rebuilt" from the target tree - in that the directory is not entirely rm'd and then recreated from the tree data. Rather, files are added, deleted, or replaced as necessary (which is a more efficient procedure anyway).

For the index: it's harder to show any "symptom" of the index not being fully rebuilt, because there is no analogous concept to untracked files in the index. I would expect git to take advantage of the hashes it stores for everything, so that it again would just change what needs changing instead of fully rebuilding; but I'm not 100% sure in this case and it's an implementation detail that doesn't really matter. If you tell git to set the index to match a commit, the index will match the commit.

Mark Adelsberger
  • 42,148
  • 4
  • 35
  • 52