269

I'm wondering how I can exclude an entire directory from my Git diff. (In this case /spec). I'm creating a diff for our entire software release using the git diff command. However the changes to the specs are irrelevant for this procedure, and just create headaches. now I know i can do

git diff previous_release..current_release app/

This would create a diff for all the changes in the app directory, but not for instance, in the lib/ directory. Does anyone know how to accomplish this task? Thanks!

Edit: I just want to be clear, I know I can just string the parameters of all of my directories on the end, minus /spec. I was hoping there was a way to truly exclude a single directory in the command.

Mysrt
  • 3,044
  • 2
  • 17
  • 14
  • 4
    I found this amazing question because I was aiming to ignore _submodules_. In this case, if you are as naive as me, [this question](http://stackoverflow.com/questions/3240881/) can be more helpful. – brandizzi Aug 23 '12 at 20:53
  • That's strange that we still have no native Git switch for doing this on Sep 2016 – Michael Z Sep 12 '16 at 15:20

7 Answers7

476

Based on this answer you can also use:

git diff previous_release..current_release -- . ':!spec'

This is a newish git feature which allows excluding certain paths. It should be more reliable than various shell oneliners.

I'm posting this here because this question is still the #1 hit for "git diff exclude path".

Since Git 2.13 (Q2 2017), you can replace ! with ^. The latter doesn't need quotes. So you can write it as:

git diff previous_release..current_release -- . :^spec
Penghe Geng
  • 13,286
  • 5
  • 31
  • 40
cdleonard
  • 6,570
  • 2
  • 20
  • 20
  • 5
    Love this answer. If adding options then do so before the double dash. Eg: `git diff previous_release current_release --name-status -- . ':!spec'` – liamvictor Jan 04 '16 at 15:59
  • Didn't realize I could use branch names for the `current_release` and `previous_release` - made me smile when I figured it out. – trueheart78 Jan 26 '16 at 20:26
  • 3
    What worked for me `git diff --stat dev -- . ':!/Mopy/Docs/*'`. What did _not_ work for me `git diff dev --stat -- . ':!('Mopy/Docs/Wrye Bash General Readme.html'|'Mopy/Docs/Wrye Bash Advanced Readme.html')'` and variations – Mr_and_Mrs_D Mar 12 '16 at 23:07
  • Really helpful. Here is the syntax to see if any files are changed, other than database.yml: `git diff --check -- . ':!config/database.yml'` – Dan Kohn May 03 '16 at 01:43
  • 31
    Can also exclude multiple items like so: `git diff previous_release current_release -- . ':!file_a' ':!file_b'` – PseudoNoise May 18 '16 at 17:49
  • For ```git diff -- . ':!_site'``` it gives ```fatal: Unimplemented pathspec magic '_' in ':!_site'``` – Marius Jul 25 '18 at 12:22
  • This works for me without needing any `.` after the `--`. Just `git diff [..arguments...] -- ':!thing-to-exclude'` – Pistos Feb 17 '21 at 18:18
  • 4
    The manpage this is documented in is `gitglossary`. – user1338062 Mar 26 '21 at 06:55
  • 1
    This follows the normal rules for filespecs in git, so if you specify an entire directory, git includes all folders and subfolders under it. So `:^/Path/To/*` can be the simpler `:^/Path/To`. There's also no need to add the `.` because git assumes you intend all files as the starting point if the only pathspecs are exclusions. Do `git help glossary` to see all the docs for these. Last, this command is sensitive to the current directory. If you are at the root, don't need the initial `/`. – ErikE Nov 16 '21 at 21:03
  • Note this doesn’t work with `--no-index`. – bfontaine Feb 23 '22 at 14:12
  • the "latter version" describes using the carrot as needing no quotes (`:^spec`); keep in mind carrot notation will still need quotes on zsh if you use wildcards, e.g. `":^*.lock"` --for what it's worth :) (I liked the notion of a more succinct command, but the exceptions rob just a little of the joy) – Kay V Apr 09 '22 at 16:07
150

Assuming you use bash, and you've enabled extended globbing (shopt -s extglob), you could handle that from the shell side:

git diff previous_release current_release !(spec)

Saves you having to list all other things.

Or, shell-agnostic:

git diff previous_release current_release --name-only | grep -v '^spec/' \
    | xargs git diff previous_release current_release --

You could wrap that up in a one-liner shell script to save yourself having to retype the arguments.

l0b0
  • 55,365
  • 30
  • 138
  • 223
Cascabel
  • 479,068
  • 72
  • 370
  • 318
  • 2
    P.S. globbing doesn't match hidden files, so if you're obsessive, you might want to tack on `.!(.|)` to match everything starting with a `.` besides `.` and `..`. – Cascabel Dec 07 '10 at 20:43
  • This doesn't seem to work for files that are brand new or deleted across branches. I get errors that halt execution of the script, saying it can't diff against it. – Graham Christensen Apr 25 '11 at 21:16
  • 3
    @Graham: I believe that the `--` I just added should fix that. – Cascabel Apr 25 '11 at 23:01
  • 1
    Awesome answer. I only wanted names to show (not the actual diff) so I had to add `--name-only` at the very end as well. – aug Oct 13 '14 at 18:32
  • 1
    After playing around with it, just wanna add that if you change the name of a file, it will throw an error but all you need to do is add `-- path/of/new/file` and it will figure it out :) – aug Oct 14 '14 at 16:45
  • @Jefromi, Thanks man this work for me, but, How can I exclude more than one folder? !(spec,spec2) or !(spec) ,!(spec2) ? – Aby W Aug 10 '16 at 09:12
  • @AbyW, [using the pipe](https://www.linuxjournal.com/content/bash-extended-globbing) `|`, like `!(spec*|foo|bar)`. – Kamafeather Sep 16 '20 at 19:33
50

If you want to specify more than one path to exclude in a git diff, you just add additional exclusion parameters to the end, e.g. to exclude everything in vendor and bin directories from the stats:-

git diff --stat previous_release..current_release -- . ':!vendor' ':!bin'
David Graham
  • 541
  • 4
  • 4
38

You can try and unset the diff attribute for any files within the lib directory.
.gitattributes:

lib/* -diff

racl101 adds in the comments:

Just to add to this, for those of you who want to limit the git diff output of files of a specific type, within a specific directory and its subdirectories, like all the JavaScript generated by Webpack's bundling of your .js files, for example, you can add this to .gitattributes:

dist/js/**/*.js -diff

Then you don't see all that noise in your git diff output and it just shows as: Binary files ... differ which is more helpful.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 1
    this would remove spec from all of my diffs though. I still want to see my diffs in the spec folder normally, just not when i'm running this 'release' diff – Mysrt Dec 07 '10 at 20:09
  • 2
    @Mystr: You could set that `.gitattributes` file in a special branch made only for this kind of 'release' diff ;) Rebase that special branch on top of `current_release` and do your diff from there. – VonC Dec 07 '10 at 20:10
  • This, too doesn't seem to work for files that are deleted across branches. Files which were removed still show the complete diff. – Graham Christensen Apr 25 '11 at 21:20
  • Doesn't work for me. Are _ paths special? ```_site/* -diff``` – Marius Jul 25 '18 at 12:21
  • @MariusAndreiana No, '_' is not special. Can you check which gitattributes rule apply for a file of that folder with: https://git-scm.com/docs/git-check-attr – VonC Jul 25 '18 at 12:23
  • ```git-check-attr -a _site/index.html``` gives ```_site/index.html: diff: unset```. It actually works, I was confused by the output ```Binary files a/_site/index.html and b/_site/index.html differ``` -- I see that's expected. Thanks! – Marius Jul 26 '18 at 13:07
33

Relative to the git root directory

git diff accepts an optional exclude

git diff -- ":(exclude)thingToExclude"

You might want to add some wild cards

git diff -- ":(exclude)*/thingToExclude/*"

Target specific file types

git diff -- ":(exclude)*/$1/*.png"

Some syntactical sugar applied

git diff -- ":!/\$1/"

Or drop a little script for your dotfiles

Such as .bash_profile or .zshrc

gde() {
    : '
        git diff exclude files or folders
        usage:
        gde fileOrFolderNameToExclude
    '
    git diff -- ":!/\$1/"
}
jasonleonhard
  • 12,047
  • 89
  • 66
28

Git diff now accepts a custom exclusion format: git diff -- ':(exclude)lib/*'

Be careful if you have a lot of repetitive folder names ('/app', '/lib', etc.), as this will exclude files relative to the current working directory AND the git root directory.

MaxPRafferty
  • 4,819
  • 4
  • 32
  • 39
  • 1
    are you missing a dot, shouldn't it rather be `git diff -- . ':(exclude)lib/*'`? – Nicolas Dermine Dec 17 '17 at 06:55
  • 2
    @NicolasDermine No, though what you wrote is equivalent. The "includes" portion of the path is also optional. That's why you can just do `git diff` for everything relative to your current path, rather than requiring `git diff -- .` – MaxPRafferty Jul 30 '18 at 19:16
  • 1
    Note: on windows machines, use double quotes, like `git diff -- ":(exclude)lib/*"` – cnlevy Nov 12 '19 at 15:11
  • No idea why, but for me it works only with the dot, as suggested by @NicolasDermine . I use Cmder on win10. – Olivvv Jan 27 '20 at 12:31
  • 1
    When the folder to exclude was not a direct subfolder of the root folder of the diff I had to precede it with */ also. Ex. `git diff -- . ":!*/lib/*"` Using `!` is shorthand for `(exclude)` – jk7 Sep 23 '20 at 22:55
-8
git diff app lib
Jed Schneider
  • 14,085
  • 4
  • 35
  • 46