258

I have just run a git diff, and I am getting the following output for all of my approx 10 submodules

diff --git a/.vim/bundle/bufexplorer b/.vim/bundle/bufexplorer
--- a/.vim/bundle/bufexplorer
+++ b/.vim/bundle/bufexplorer
@@ -1 +1 @@
-Subproject commit 8c75e65b647238febd0257658b150f717a136359
+Subproject commit 8c75e65b647238febd0257658b150f717a136359-dirty

What does this mean? How do I fix it?

Adexe Rivera
  • 396
  • 6
  • 12
mrwooster
  • 22,109
  • 11
  • 34
  • 44

10 Answers10

295

Update Jan. 2021, ten years later:

"git diff"(man) showed a submodule working tree with untracked cruft as Submodule commit <objectname>-dirty, but a natural expectation is that the "-dirty" indicator would align with "git describe --dirty"(man), which does not consider having untracked files in the working tree as source of dirtiness.
The inconsistency has been fixed with Git 2.31 (Q1 2021).

See commit 8ef9312 (10 Nov 2020) by Sangeeta Jain (sangu09).
(Merged by Junio C Hamano -- gitster -- in commit 0806279, 25 Jan 2021)

diff: do not show submodule with untracked files as "-dirty"

Signed-off-by: Sangeeta Jain

Git diff reports a submodule directory as -dirty even when there are only untracked files in the submodule directory.
This is inconsistent with what git describe --dirty(man) says when run in the submodule directory in that state.

Make --ignore-submodules=untracked the default for git diff(man) when there is no configuration variable or command line option, so that the command would not give '-dirty' suffix to a submodule whose working tree has untracked files, to make it consistent with git describe --dirty that is run in the submodule working tree.

And also make --ignore-submodules=none the default for git status(man) so that the user doesn't end up deleting a submodule that has uncommitted (untracked) files.

git config now includes in its man page:

By default this is set to untracked so that any untracked submodules are ignored.


Original answer (2011)

As mentioned in Mark Longair's blog post Git Submodules Explained,

Versions 1.7.0 and later of git contain an annoying change in the behavior of git submodule.
Submodules are now regarded as dirty if they have any modified files or untracked files, whereas previously it would only be the case if HEAD in the submodule pointed to the wrong commit.

The meaning of the plus sign (+) in the output of git submodule has changed, and the first time that you come across this it takes a little while to figure out what’s going wrong, for example by looking through changelogs or using git bisect on git.git to find the change. It would have been much kinder to users to introduce a different symbol for “at the specified version, but dirty”.

You can fix it by:

  • either committing or undoing the changes/evolutions within each of your submodules, before going back to the parent repo (where the diff shouldn't report "dirty" files anymore). To undo all changes to your submodule just cd into the root directory of your submodule and do git checkout .

dotnetCarpenter comments that you can do a: git submodule foreach --recursive git checkout .

  • or add --ignore-submodules to your git diff, to temporarily ignore those "dirty" submodules.

New in Git version 1.7.2

As Noam comments below, this question mentions that, since git version 1.7.2, you can ignore the dirty submodules with:

git status --ignore-submodules=dirty
VonC
  • 1,042,979
  • 435
  • 3,649
  • 4,283
  • 2
    Also a good thing to know: you can still exec `git commit -a` without having to worry adding these changes. Although they're marked with `M` in the front, they won't end up in your commit. – gitaarik Oct 15 '14 at 21:51
  • 2
    For me, I had to go into each dirty submodule and run `git clean -id`. – GDP2 Nov 13 '17 at 15:45
  • 1
    @GDP2 Which you can don in one line, with `git submodule foreach --recursive git clean -id` (to be tested in a backup repo first ;) ) – VonC Nov 13 '17 at 19:21
  • 1
    The case I kept seeing this inexplicably, what was happening was that I had untracked files that weren't in the submodule's `.gitignore`. Adding them there or to my global ignore list fixed things. – Ben Sep 04 '19 at 20:02
27

To ignore all untracked files in any submodule use the following command to ignore those changes.

git config --global diff.ignoreSubmodules dirty

It will add the following configuration option to your local git config:

[diff]
  ignoreSubmodules = dirty

Further information can be found here

Devpool
  • 561
  • 6
  • 5
  • The politically most correct recommendation would probably say "why don't you just keep your sub-modules clean?" Anyhow is this solution still showing modified files? Form which git version on can this procedure be done? – grenix May 19 '21 at 17:30
26

Also removing the submodule and then running git submodule init and git submodule update will obviously do the trick, but may not always be appropriate or possible.

alex
  • 438,662
  • 188
  • 837
  • 957
user1178907
  • 359
  • 1
  • 4
  • 6
  • 2
    This worked for me when I converted some existing folders to submodules and then pulled onto another machine which still had the old folders. – Roger Lipscombe Nov 01 '13 at 13:00
19

EDIT: This answer (and most of the others) are obsolete; see Devpool's answer instead.


Originally, there were no config options to make "git diff --ignore-submodules" and "git status --ignore-submodules" the global default (but see also Setting git default flags on commands). An alternative is to set a default ignore config option on each individual submodule you want to ignore (for both git diff and git status), either in the .git/config file (local only) or .gitmodules (will be versioned by git). For example:

[submodule "foobar"]
    url = git@bitbucket.org:foo/bar.git
    ignore = untracked

ignore = untracked to ignore just untracked files, ignore = dirty to also ignore modified files, and ignore = all to ignore also commits. There's apparently no way to wildcard it for all submodules.

BuZZ-dEE
  • 3,875
  • 7
  • 48
  • 76
  • As for > Git 2.31 even the recommended answer linked in this edited post seems to be obsolete. Anyway the most context information about git versions in in the accepted answer. Still confused what to do! (I have Git 2.17 in my ubuntu LTS) – grenix May 19 '21 at 17:09
  • 1
    I checked modifying .gitmodules like in this solution with Git 2.17 and it does what expected. Maybe I'm wrong but I like this solution the most since .gitmodules will be versioned by git. (+1) If doing it like in the recomended answer one should do `git config --global diff.ignoreSubmodules untracked` not `git config --global diff.ignoreSubmodules dirty` – grenix May 19 '21 at 19:48
13

This is the case because the pointer you have for the submodule isn’t what is actually in the submodule directory. To fix this, you must run git submodule update again:

Robin Ren
  • 131
  • 1
  • 3
12
git submodule foreach --recursive git checkout .

This didn't do the trick for me but it gave me a list of files (in my case only one) that had been changed in the submodule (without me doing anything there).

So I could head over to the submodule and git status showed me that my HEAD was detached -> git checkout master, git status to see the modified file once again, git checkout >filename<, git pull and everything fine again.

alex
  • 438,662
  • 188
  • 837
  • 957
JustABit
  • 365
  • 3
  • 11
12

I ended up removing the submodule directory and initializing it once again

cd my-submodule
git push
cd ../
rm -rf my-submodule
git submodule init
git submodule update
Szymon Wygnański
  • 9,426
  • 6
  • 29
  • 44
  • 4
    I'd rather understand what happened, but this was also the only thing that worked for me... – smilebomb Sep 06 '17 at 14:32
  • Sometimes the axe is the best tool to use :) I would feel better using 'git status' after the push to see if everything is clean. (just on paranoid days I would move my-submodule to recycle bin) – grenix May 19 '21 at 18:40
7

A submodule may be marked as dirty if filemode settings is enabled and you changed file permissions in submodule subtree.

To disable filemode in a submodule, you can edit /.git/modules/path/to/your/submodule/config and add

[core]
  filemode = false

If you want to ignore all dirty states, you can either set ignore = dirty property in /.gitmodules file, but I think it's better to only disable filemode.

alex
  • 438,662
  • 188
  • 837
  • 957
dryobs
  • 1,122
  • 11
  • 18
  • Thanks for the hint that file mode changes can provoke the behavior also, but is filemode generally not desirable in submodules? – grenix May 19 '21 at 18:16
1

In my case I wasn't sure what had caused this to happen, but I knew I just wanted the submodules to be reset to their latest remote commit and be done with it. This involved combining answers from a couple of different questions on here:

git submodule update --recursive --remote --init

Sources:

How do I revert my changes to a git submodule?

Easy way to pull latest of all git submodules

Adam Westbrook
  • 780
  • 6
  • 13
  • Just wondering if this solution does the same than here: https://stackoverflow.com/a/43324275/2623045 ? Would this also remove untracked files? – grenix May 19 '21 at 18:50
1

Do you have adequate permissions to your repo'?

My solution was unrelated to git, however I was seeing the same error messages, and dirty status of submodules.

The root-cause was some files in the .git folder were owned by root, so git did not have write access, therefore git could not change the dirty state of submodules when run as my user.

Do you have the same problem?

From your repository's root folder, use find to list files owned by root [optional]

find .git -user root

Solution [Linux]

Change all files in the .git folder to have you as the owner

sudo chown -R $USER:$USER .git

# alternatively, only the files listed in the above command...
sudo find .git -user root -exec chown $USER:$USER {} +

How did this happen?

In my case I built libraries in sub-modules from a docker container, the docker daemon traditionally runs as root, so files created fall into root:root ownership.

My user has root privileges by proxy through that service, so even though I didn't sudo anything, my git repository still had changes owned by root.

I hope this helps someone, git outa here.

marc_s
  • 675,133
  • 158
  • 1,253
  • 1,388
FraggaMuffin
  • 3,016
  • 2
  • 17
  • 24
  • Thank you for sharing, hopefully I remember this when I should fall in this situation. Would be interesting to know if all versions of git have such unspecific error messages for this use case. – grenix May 19 '21 at 18:58