370

Is it possible to ask git diff to include untracked files in its diff output, or is my best bet to use git add on the newly created files and the existing files I have edited, then use:

git diff --cached

?

Scott Anderson
  • 631
  • 5
  • 26
Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338

12 Answers12

351

With recent git versions you can git add -N the file (or --intent-to-add), which adds a zero-length blob to the index at that location. The upshot is that your "untracked" file now becomes a modification to add all the content to this zero-length file, and that shows up in the "git diff" output.

git diff

echo "this is a new file" > new.txt
git diff

git add -N new.txt
git diff
diff --git a/new.txt b/new.txt
index e69de29..3b2aed8 100644
--- a/new.txt
+++ b/new.txt
@@ -0,0 +1 @@
+this is a new file

Sadly, as pointed out, you can't git stash while you have an --intent-to-add file pending like this. Although if you need to stash, you just add the new files and then stash them. Or you can use the emulation workaround:

git update-index --add --cacheinfo \
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 new.txt

(setting up an alias is your friend here).

Or A.
  • 1,220
  • 1
  • 15
  • 28
araqnid
  • 127,052
  • 24
  • 157
  • 134
  • 1
    It turns out that my copy of Git isn't recent enough to have add -N, but this answers my question. – Andrew Grimm May 19 '09 at 02:30
  • 1
    You can emulate "git add -N new.txt" with "git update-index --add --cacheinfo 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 new.txt" (how did I manage to put this on the wrong answer?) – araqnid May 19 '09 at 14:58
  • Feature added in 1.6.1: https://github.com/git/git/blob/master/Documentation/RelNotes/1.6.1.txt – MarcH May 31 '13 at 18:23
  • Git 2.5 will fix that: the "file to be added later" will no longer be displayed at "to be committed": see http://stackoverflow.com/a/30341632/6309 – VonC May 20 '15 at 06:31
  • 2
    what if you have a lot of new files, is there an easy way to add them all and then diff? – Vic Jul 08 '16 at 14:14
  • 2
    @Vic `git add -N .` – Nathan Nov 15 '17 at 15:03
  • @araqnid , so with --intent-to-add, you still need to manually create the 'empty file'? It errors when I did git add --intent-to-add file if the file doesn't exist. – ajfbiw.s May 08 '18 at 22:37
  • @ajfbiw.s I'm having trouble imagining the use case, but you can do `touch mynewfile && git add -N mynewfile` – henry Jun 28 '18 at 02:10
  • I keep getting something as follows `deleted file mode 100755 index e69de29..0000000` which does not allow for me to obtain `git diff` result @araqnid – alper Mar 16 '20 at 11:33
  • 1
    You also can't `git restore` that files. This command just truncates all the "intent-to-add"-ed files! – Anthony Apr 23 '20 at 15:41
  • 1
    Add all untracked files without adding deleted files: `git add -N --no-all .` – cambunctious Sep 11 '20 at 21:29
  • If you want to undo `git add -N` run `git reset --mixed` [based on this](https://stackoverflow.com/a/62444729/771768). – Carl Walsh Dec 22 '22 at 18:12
136

I believe you can diff against files in your index and untracked files by simply supplying the path to both files.

git diff --no-index tracked_file untracked_file
CharlesB
  • 86,532
  • 28
  • 194
  • 218
Harold
  • 5,147
  • 1
  • 19
  • 15
  • 4
    Does that work if you've got more than one untracked file that you've created since the last commit? – Andrew Grimm May 13 '09 at 07:46
  • 18
    Yes, perfect answer! I can then use `git diff --no-index untracked_file_1 untracked_file_2` to get `git diff` syntax coloring etc. on diffs ... beautiful. – Colin D Bennett Sep 30 '13 at 21:56
  • 61
    I don't understand why you're comparing a tracked file with ***an unrelated untracked file.*** If you just wanted to get diff output for the untracked file, you can just use [`/dev/null`](https://en.wikipedia.org/wiki/Null_device) instead: `git diff --no-index -- /dev/null `. –  May 02 '14 at 21:36
  • 7
    Or just `cat untracked_file_1`, or perhaps `printf '\e[1;32m%s\e[0m\n' "$(cat untracked_file_1)"` if you really need green output. :) (Although on a more serious note, please note that command substitution will remove the trailing newlines from your file.) – Wildcard Mar 17 '16 at 09:34
  • 13
    This should be the accepted answer -- it doesn't require changing git's index; which as the original author says, has its downside – Byte Lab Apr 20 '16 at 22:36
  • 3
    This does not actually answer the question. As @user456814 noted, there's is _no_ second file to diff against - nothing to replace `tracked_file` with. – einpoklum Jan 24 '17 at 10:20
  • 1
    This answer should be modified to include @user456814's suggestion, and then be the accepted answer. – hlongmore Jul 08 '17 at 00:03
  • 1
    There is no reason to compare a file with `/dev/null` or anything else. As @Wildcard pointed out, you can just `cat` it if you really want to see **one file** and you know it's name... The whole point of the question as I understand it is to be able to see all changes (**multiple** files) by one `diff` command, even if part of these changes are changes of previously existing files (so, "diff" has perfect sense) and other part are new files (and you still want to see their contents as "diff" alongside with changes of existing files - just to see the whole picture). – Ruslan Stelmachenko May 28 '21 at 18:35
  • @user456814 That does not work on Git for Windows. `$ git diff --no-index -- /dev/null .gitignore` results in `error: Could not access 'rx/72nbuild/nul'` – AJM May 04 '22 at 18:48
  • While this answer show how to solve the problem in the OP, it have some drawbacks in that it is not possible to diff all files at once (or use git difftool on all files for that matter). Today there are other tools that can do a diff with colors, on a single file, eg. colordiff, or simply using `diff --color`. – patrik Sep 07 '22 at 14:03
  • Update to my previous comment in this thread - it *is* now working for me in Git Bash. I have honestly no idea why it didn't before. But it's very useful now that it does! – AJM Oct 11 '22 at 13:02
51

Not 100% to the point, but if for some reason you don't want to add your files to the index as suggested by the accepted answer, here is another option:

If the files are untracked, obviously the diff is the whole file, so you can just view them with less:

less $(git ls-files --others --exclude-standard)

Navigate between them with :n and :p for next and previous..

Update from the comments: If you need a patch format you can also combine it with git diff:

git ls-files --others --exclude-standard -z | xargs -0 -n 1 git --no-pager diff /dev/null | less

You can also redirect the output to a file or use an other diff command in this case.

ErikE
  • 48,881
  • 23
  • 151
  • 196
user1587520
  • 3,777
  • 1
  • 21
  • 20
  • 7
    you can also run `git diff /dev/null ` and get the patch in patch format rather than "just" a file – SimSimY Nov 08 '17 at 21:11
  • 3
    this answer combined with git diff is perfect solution. – iwind Apr 12 '19 at 09:48
  • 1
    The second command line here works so well. I like to keep color too, so I put `--color=always` after the word `diff`, and use `-R` with less. So all together: `git ls-files --others --exclude-standard | xargs -n 1 git --no-pager diff --color=always /dev/null | diff-so-fancy | less -R`. – Tyler Collier Mar 18 '21 at 05:29
  • 2
    @TylerCollier need to add `-z` to the git command and `-0` to `xargs`. ALWAYS use null-termination when possible, to avoid bugs (and security vulnerabilities!) with things like file names containing carriage returns or other weird characters. – ErikE Sep 02 '21 at 14:54
44

For my interactive day-to-day gitting (where I diff the working tree against the HEAD all the time, and would like to have untracked files included in the diff), add -N/--intent-to-add is unusable, because it breaks git stash.

So here's my git diff replacement. It's not a particularly clean solution, but since I really only use it interactively, I'm OK with a hack:

d() {
    if test "$#" = 0; then
        (
            git diff --color
            git ls-files --others --exclude-standard |
                while read -r i; do git diff --color -- /dev/null "$i"; done
        ) | `git config --get core.pager`
    else
        git diff "$@"
    fi
}

Typing just d will include untracked files in the diff (which is what I care about in my workflow), and d args... will behave like regular git diff.

Notes:

  • We're using the fact here that git diff is really just individual diffs concatenated, so it's not possible to tell the d output from a "real diff" -- except for the fact that all untracked files get sorted last.
  • The only problem with this function is that the output is colorized even when redirected; but I can't be bothered to add logic for that.
  • I couldn't find any way to get untracked files included by just assembling a slick argument list for git diff. If someone figures out how to do this, or if maybe a feature gets added to git at some point in the future, please leave a note here!
Jo Liss
  • 30,333
  • 19
  • 121
  • 170
  • 4
    Ironically, my `git update-index --add --cacheinfo 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 new.txt` workaround I suggested for older gits *does* work with `git stash`, assuming you've already got e69de29bb in your db, e.g. by trying to use `add -N` previously. So apparently it's not exactly equivalent to `git add -N` in some way: tbh I'm not sure how. – araqnid Feb 04 '11 at 13:41
  • 2
    You're performing string comparison rather than numeric equality check with your `test` command, by the way. Shouldn't affect anything, but `test "$#" -eq 0` is more precisely what's intended. – Wildcard Mar 17 '16 at 09:36
  • 2
    Yeah, still looks like you need to do it pairwise... but can fake it into one `less` so you don't have to press `q` for each file and it feels exactly like `git diff`, by removing per-file pagination (`-P`), adding it back afterwards (`| less`), preserving colour (`--color=always`) and interpreting it as colour (`less -r` or `less -R`). So altogether it's: `do git -P diff --color=always -- /dev/null "$i"; done | less -r` – hyperpallium Aug 16 '19 at 06:47
  • In case you wish to be bothered in the future, `test -t 1` (so e.g. `if [ -t 1 ]; then color_arg=--color; fi` or something) is a way for the shell to check if its output is a terminal, which is a useful way to decide on the coloring. And `xargs` might give a way to get rid of the while loop. You'll still need `-n 1` on that, so it'll still launch git a bunch of times, and still need to be pairwise that way, but... it gets rid of `while` and `read`, so maybe that's better?!? I leave that to the reader. – lindes Feb 12 '20 at 20:11
37
git add -A
git diff HEAD

Generate patch if required, and then:

git reset HEAD
Jerska
  • 11,722
  • 4
  • 35
  • 54
Amol Pujari
  • 2,280
  • 20
  • 42
  • 5
    This has the potential to lose prior (pre-)work, by adding everything. Especially the case if one makes use of `git add -p` very often (which I generally recommend, by the way)... This does give a way of doing the basic thing, it just... should be noted that it has the potential for unwanted side effects. – lindes Feb 12 '20 at 20:14
22

For one file:

git diff --no-index /dev/null new_file

For all new files:

for next in $( git ls-files --others --exclude-standard ) ; do git --no-pager diff --no-index /dev/null $next; done;

As alias:

alias gdnew="for next in \$( git ls-files --others --exclude-standard ) ; do git --no-pager diff --no-index /dev/null \$next; done;"

For all modified and new files combined as one command:

{ git --no-pager diff; gdnew }
radzimir
  • 1,178
  • 13
  • 10
  • 3
    This is a FANTASTIC answer! Much better than the accepted one! – Jeremy Moritz Jan 17 '21 at 17:34
  • 1
    I also added this alias for the last command: `alias gdall="git --no-pager diff; gdnew"` – Jeremy Moritz Jan 17 '21 at 17:41
  • The first example given does not work in Git for Windows: `$ git diff --no-index -- /dev/null .gitignore` resulted, in my case, in: `error: Could not access 'device_support/oem/renesas/rx72n-envision/e2studio-CCRX/R5F572NN/nul'` – AJM May 04 '22 at 18:50
16

this works for me:

git add my_file.txt
git diff --cached my_file.txt
git reset my_file.txt

Last step is optional, it will leave the file in the previous state (untracked)

useful if you are creating a patch too:

  git diff --cached my_file.txt > my_file-patch.patch
Alejandro Moreno
  • 5,578
  • 2
  • 31
  • 29
  • The specificity of this add/reset pair is a nice contrast to the shotgun approach of https://stackoverflow.com/a/50486906/313756 ... thanks for that. – lindes Feb 12 '20 at 20:16
6

Update: My answer is for staged and unstaged changes. Not tracked and untracked. See the accepted answer for the tracked/untracked information. Leaving here for posterity.

The following will give you only unstaged changes:

$ git diff

If you want both staged and unstaged changes, add HEAD to the command:

$ git diff HEAD
alairock
  • 1,826
  • 1
  • 21
  • 28
  • 44
    `HEAD` is the [default value](https://git-scm.com/docs/git-diff#git-diff-emgitdiffem--options--cachedltcommitgt--ltpathgt82308203), so this is the same as `git diff` which doesn't solve the problem. – Iulian Onofrei Dec 08 '17 at 11:18
  • 7
    This requires `git add` from every not tracked file – SilvioQ Jan 04 '19 at 11:21
  • If you edit this answer to include the `git add` it is the simplest _if_ your use case is to check what you just added/want to add – KCD Oct 21 '19 at 03:35
  • @lulian Incorrect, `git diff` will only show unstaged changes, `git diff HEAD` will show unstaged and staged ones. – ruohola Oct 26 '20 at 18:22
  • 3
    This doesn't answer the question, which is about "untracked", not "unstaged" – Nick Nov 05 '21 at 23:01
  • Agreed. I'll make an update. Not to my answer, as I think my post existing could still help others, but to clarify and point them to the proper answer for the original question. – alairock Jan 12 '22 at 04:51
3

Using the idea that you can stage the new file and you can diff the staged files, you can combine these two to see the diff. I find it simple to use.

  1. Add the files you want to see the diff.In your case, add only untracked files. You can optionally choose to add only those files you want to see the diff for.

    git stash && git add . && git stash pop 
    
  2. Diff the staged

    git diff --staged
    
  3. Reset the staged files if needed

    git reset
    

Combining all the above,

   git stash && git add . && git stash pop && git diff --staged && git reset 
Arun Gowda
  • 2,721
  • 5
  • 29
  • 50
2

Usually when I work with remote location teams it is important for me that I have prior knowledge of what changes are done by other teams in the same file, before I follow git stages untrack-->staged-->commit for that I wrote a bash script which helps me to avoid unnecessary resolve merge conflict with remote team or make new local branch and compare and merge on main branch

#set -x 
branchname=`git branch | grep -F '*' |  awk '{print $2}'`
echo $branchname
git fetch origin ${branchname}
for file in `git status | grep "modified" | awk "{print $2}" `
do
echo "PLEASE CHECK OUT GIT DIFF FOR "$file 
git difftool FETCH_HEAD $file ;
done

In the above script I fetch remote main branch (not necessary it's master branch) to FETCH_HEAD them make a list of my modified files only and compare modified files to git difftool

Here many difftools are supported by git. I configure 'Meld Diff Viewer' for good GUI comparison.

AJM
  • 1,317
  • 2
  • 15
  • 30
adg
  • 99
  • 6
-1

Hack using git stash:

# Stash unstaged changes
git stash --keep-index --include-untracked --message="pre-commit auto-stash"
git stash show --only-untracked stash@{0}
git stash pop

I needed this in the context of scripts that use git stash (i.e. a pre-commit git hook). Here's my full working example: (written/tested on git v2.34.1 on macOS Big Sur)

# Stash unstaged changes
# NOTE: we always create a stash - possibly even a totally empty one.
git stash --keep-index --include-untracked --message="pre-commit auto-stash"
diffTracked=$(git diff --stat --staged stash@{0})
diffUntracked=$(git stash show --only-untracked stash@{0})
[[ $diffTracked || $diffUntracked ]] && {
  echo "Stashed diff:"
  # Ensure diffs have standard coloring:
  git diff --stat --staged stash@{0}
  git stash show --only-untracked stash@{0}
}
Devin Rhode
  • 23,026
  • 8
  • 58
  • 72
-13

Assuming you do not have local commits,

git diff origin/master
Pradhan
  • 421
  • 5
  • 17
  • 9
    The question asks for a `git diff` command that includes untracked files. This command does not include them. Also, whether or not local commits exists has absolutely nothing to do with the question. – toon81 Feb 25 '16 at 09:44
  • JFTR, I `git merge --squash mybranch`, and `git diff master` showed me the changes in untracked files. – muammar Nov 07 '16 at 22:16
  • 2
    That's impossible. You seem to be confused about what "untracked" means. Untracked doesn't mean that a file is tracked in one branch and not another, it means that it is not "in Git" anywhere in any way. Whether you squash or not makes no difference. `git diff` doesn't show differences in untracked files: because they are untracked there are never any differences to show, by definition. It's just the way Git works. :) – toon81 Jan 17 '17 at 11:58