Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

git diff with respect to working tree including untracked files

Tags:

git

diff

I have an old commit A. I am now at commit B=HEAD!=A, and want to compare the state of my working directory, INCLUDING untracked files, with A.

The reason is that the currently untracked files were part of commit A, so they wouldn't show up in, and thus wouldn't clutter, the diff I desire.

As a workaround, I could manually git add <all the untracked files> before the diff and git reset <all the untracked files> after. However, there are many untracked files, and I would like to do this in a maximally atomic and robust way, as I am doing all of this in a script.

EDIT: This question also concerns diff against untracked files. However, the answers there require manual adding and later removing of untracked files, and I preferred a solution that does this more automatically and in an atomic way that is error proof against interruptions in a scripted context. The accepted answer does exactly this, and indeed is different from the answers to the linked question.

like image 949
Bananach Avatar asked Sep 13 '17 16:09

Bananach


2 Answers

As others have suggested, you can use git add -N to add dummy entries to the index. Running:

git diff <hash>

will then compare the given to the current work-tree, using the existing index (which now has entries for the untracked files) to decide which work-tree versions to compare to the given <hash> (<hash> may be a commit ID, or a tree ID, or a branch name, or anything that Git can resolve to a tree). But this does mess with the index, and as you said, you:

would like to do this in a maximally atomic and robust way

The trick is to use, instead, a temporary index.

The initial commit here has files bar and foo; the second commit has file foo removed. The current work-tree has foo resurrected and a new file, as shown in git status --short.

$ git log --all --decorate --oneline --graph
* 11b241c (HEAD -> master) remove foo
* 8527327 initial
$ git status --short
A  diffscript
A  foo

Note that the initial commit is 8527327, which is what we pass to diffscript as an argument:

$ ./diffscript 8527327
diff --git a/diffscript b/diffscript
new file mode 100755
index 0000000..8d5c978
--- /dev/null
+++ b/diffscript
@@ -0,0 +1,6 @@
+#! /bin/sh
+export GIT_INDEX_FILE=$(mktemp) || exit
+rm -f $GIT_INDEX_FILE
+trap "rm -f $GIT_INDEX_FILE" 0 1 2 3 15
+git add -A
+git diff ${1:-HEAD}
index 3ec370c..d319048 100644
--- a/foo
+++ b/foo
@@ -1 +1 @@
-I am a foo
+I was foo, am now resurrected

This diffscript defaults to diffing against HEAD. Hence, if we run it with no arguments, we would compare commmit 11b241c to the index/work-tree (because we git add -A it mostly does not matter whether we compare to index or work-tree, at this point: they are essentially the same, modulo .gitignore directives).

The trap ... line makes sure we remove the temporary index, whether the script is terminated by ^C (signal 2, SIGINT), or a network disconnect (signal 1, SIGHUP) or a QUIT (signal 3, SIGQUIT) or TERMINATE (signal 15, SIGTERM), or just exits normally (0, not a signal, just normal termination).

Annoyingly, Git insists that the file either not exist at all, or have an index file signature—an empty file is not allowed—so we remove the temp file mktemp makes, so that the git add step can create it with the right signature.

like image 149
torek Avatar answered Nov 15 '22 00:11

torek


You can take a look at:

git add --intent-to-add (same as git add -N)

This will only append an empty entry to the index, but not the files themselves. The difference will be seen when using the diff command.

GIT documentation:

--intent-to-add

This is useful for, among other things, showing the unstaged content of such files with git diff and committing them with git commit -a.> Record only the fact that the path will be added later. An entry for the path is placed in the index with no content.

like image 38
Maciej Jureczko Avatar answered Nov 15 '22 00:11

Maciej Jureczko