3

Does git have a built-in way* to check whether files in the working directory magically** changed***?

If so: How does one make git check the working directory for integrity?

* Figuring out which files in the working directory are tracked, hashing only them, deleting them, checking them out, again, hashing them, again, and comparing the hashes doesn't quite count as "built-in".

** Files' contents changed because of a cosmic ray hitting the HDD, a unicorn galloping over the plates, or other means which doesn't let the FS know that a change happened (like editing the character device).

*** In name, contents, or other tracked properties (like having magically become executable).


Note that this question is a weakened version of that other question. Answering this question (the one you're currently reading) is easier. I will link to this question in the other question if this question gets solved and point out the parts of the other question which haven't been solved by the answers below this question.

It has been asked what the difference between git status and what I'm asking for is. git status only notices changes if the FS tells it that changes happened. If files got corrupted magically, the FS doesn't know, therefore, git status won't indicate a change. git diff, git add --all, etc. won't either, of course.


In response to the proposal of using git fsck: It doesn't work.

What I did:

christoph@christoph-laptop-16-04-2:/t$ dd if=/dev/zero bs=1M count=30 of=/tmp/con
30+0 records in
30+0 records out
31457280 bytes (31 MB, 30 MiB) copied, 0.0143967 s, 2.2 GB/s
christoph@christoph-laptop-16-04-2:/t$ mkfs.ext4 /tmp/con
mke2fs 1.42.13 (17-May-2015)
Discarding device blocks: done                            
Creating filesystem with 30720 1k blocks and 7680 inodes
Filesystem UUID: 9865efe8-fb30-42ab-ace7-a8f88330bdfd
Superblock backups stored on blocks: 
    8193, 24577

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (1024 blocks): done
Writing superblocks and filesystem accounting information: done

christoph@christoph-laptop-16-04-2:/t$ sudo mount -t ext4 -o loop /tmp/con /mnt
[sudo] password for christoph: 
christoph@christoph-laptop-16-04-2:/t$ cd /mnt
christoph@christoph-laptop-16-04-2:/mnt$ sudo chown christoph .
christoph@christoph-laptop-16-04-2:/mnt$ git init
Initialized empty Git repository in /mnt/.git/
christoph@christoph-laptop-16-04-2:/mnt$ echo "some contents" > file
christoph@christoph-laptop-16-04-2:/mnt$ git add file 
christoph@christoph-laptop-16-04-2:/mnt$ git commit -m "a"
[master (root-commit) d29fbd5] a
 1 file changed, 1 insertion(+)
 create mode 100644 file
christoph@christoph-laptop-16-04-2:/mnt$ git status
On branch master
nothing to commit, working directory clean
christoph@christoph-laptop-16-04-2:/mnt$ cd ~
christoph@christoph-laptop-16-04-2:~$ sudo umount /mnt

At this point, I opened /tmp/con in my favorite hex editor, searched for the word "contents", and swapped the "s" in the end out for a "z".

christoph@christoph-laptop-16-04-2:~$ sudo mount -t ext4 -o loop /tmp/con /mnt
christoph@christoph-laptop-16-04-2:~$ cd /mnt
christoph@christoph-laptop-16-04-2:/mnt$ git status
On branch master
nothing to commit, working directory clean
christoph@christoph-laptop-16-04-2:/mnt$ cat file 
some contentz
christoph@christoph-laptop-16-04-2:/mnt$ git fsck
Checking object directories: 100% (256/256), done.
christoph@christoph-laptop-16-04-2:/mnt$ git status
On branch master
nothing to commit, working directory clean
christoph@christoph-laptop-16-04-2:/mnt$ cat file
some contentz

I has been proposed to touch all files in the working directory. Changing their accessed date wouldn't be much of a problem but that doesn't make git recheck the file. Changing their modified date actually does make git check those files again, however, it also causes other trouble: Backup applications back the file up, again. With big repositories, this can be a problem.

  • 1
    Is this not what git status does? What more are you looking for? – heavyd Feb 21 '17 at 23:08
  • @heavyd I updated the question to point out even more distinctly what I'm looking for. – UTF-8 Feb 21 '17 at 23:16
  • Have you looked at git fsck? – heavyd Feb 21 '17 at 23:54
  • @heavyd It only checks the objects, not the contents of the working directory. I added details regarding this to the question. – UTF-8 Feb 22 '17 at 12:13
  • sounds like a git-verify-working-dir extension would be needed. – quixotic Mar 13 '17 at 22:43
0

I was reading an answer to another question, about how to make git acknowledge your new line-ending preferences on github, and it included this gem:

  1. Remove the index and force Git to rescan the working directory.

    rm .git/index

  2. Rewrite the Git index to pick up all the new line endings.

    git reset

  3. Show the rewritten, normalized files.

    git status

It suggests saving your current state in case you have anything else you care about, which might be relevant.

Especially since the git reset step will rewrite the file.

  • Thank you. It seems to work. :) What do you mean by "It suggests saving your current state in case you have anything else you care about, which might be relevant."? Are you referring to anything but "Save your current files in Git, so that none of your work is lost."? I don't see why it'd be important to have a clean working directory for my usecase. In fact, it seems counter-productive to me. – UTF-8 Apr 9 '18 at 22:05
  • git reset is going to replace files in your working copy with fresh ones, so if you have uncommitted changes you need to keep, you might want to commit those. Or, if in the OP's question, those behind-the-scene changes are ones you want to keep, you'd need to save those before reset. – davenpcj Jun 12 '18 at 15:38
  • That doesn't seem to be true to me: pastebin.com/ANLAsKCR – UTF-8 Jun 13 '18 at 11:01
0

This answer on SO says that:

Git tries hard to get convinced from the lstat() value alone that the worktree matches the index, because falling back on file contents is very expensive.

In other words, comparing file byte-by-byte would take quite a long time, so git attempts to optimize it by comparing results of lstat function first. That includes file size and modification time. If these are unchanged, git assumes that the file wasn't modified. As you have proven, it's not a foolproof method, but it works for cases that git was designed to handle.

Changing any of the values returned by lstat will make git re-check files for modification:

$ touch file
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   file

no changes added to commit (use "git add" and/or "git commit -a")
  • Unfortunately, this doesn't leave the working directory unchanged. For someone who doesn't care about modification times, this might be a good solution as all files in the repo can be touched via the exec switch of find. I do care about modification times but not about access times. However, I already tried this way before posting my answer and remember than changing the access time – unfortunately – doesn't have this effect. – UTF-8 Feb 22 '17 at 22:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.