Rikki Gibson
Rikki Gibson

Reputation: 4337

Number of modified files in git index

How can I find out the number of modified files in the index? That is to say, files that show as "modified" when running git status.

I know lots of visual git tools that do this, as well as shell add-ons that show the count next to the prompt, but I just don't see how to do it on the command line in a reasonable way.

Upvotes: 7

Views: 6516

Answers (3)

KyleMit
KyleMit

Reputation: 29899

Since this questions is about finding the count of changes, there are a couple git commands that do this natively, without having to add | wc -l to your command

You can use git diff with the following args:

  • --stat - generate a diffstat
  • --numstat - shows number of added and deleted lines in decimal notation
  • --shortstat - only the last line of the --stat
$ git diff --stat

src/models/models.ts | 5 +++++
src/utils/git.ts     | 9 ++++++---
2 files changed, 11 insertions(+), 3 deletions(-)
$ git diff --numstat

5       0       src/models/models.ts
6       3       src/utils/git.ts
$ git diff --shortstat

2 files changed, 11 insertions(+), 3 deletions(-)

Upvotes: 3

torek
torek

Reputation: 487983

TL;DR

You probably want either HEAD vs work-tree or HEAD vs index, or perhaps HEAD vs index plus index vs work-tree. If you want to keep the two comparisons separate, you cannot do it in one step.

Long description

There are several possible answers depending on precisely what question you meant. Note that for all of the diff commands below, you can use --name-only if you're not interested in the particular change(s) in the file(s). The status letter (Added, Deleted, Modified, etc.) is useful if you decide to add --diff-filter to choose to examine only specific kinds of changes. For instance, if you want to know how many files are strictly modified, not counting files added or deleted, you could filter on M.

  1. git diff --cached --name-status HEAD | wc -l
  2. git diff --name-status HEAD | wc -l
  3. git diff --name-status | wc -l
  4. git status -s -uno | wc -l (as in mpromonet's comment).

Each produces a different result:

  1. Compares HEAD to the index: what would be different in the commit you would make now if you ran git commit right now, compared to what's in the commit that is HEAD right now? That is, you have a current commit now, and if you make a new commit, the current commit will be the new commit's parent. What will be different?

    (Remember, the index represents the next commit to make. It starts out with copies of the exact same files as in HEAD. Each time you git add a file, you copy the file back from the work-tree, into the index. This is therefore comparing what you will commit now if you commit, to what you have in your HEAD commit. As a special case here, you can omit the name HEAD; --cached will assume HEAD.)

    In the long git status output, these come out in the first section: Changes to be committed.

  2. Compares HEAD to the work-tree. These are files that are modified in the work-tree, but are not staged for commit. In the longer git status output, these generally come out in the second section, Changes not staged for commit—but see the very next bit.

  3. Compares the index to the work-tree. These, too, are files that are modified in the work-tree—but this time they're changed with respect to the files already copied back into the index. To the extent that the index matches the HEAD commit, #2 and #3 produce the same list of files. But suppose README is in HEAD as Version H (for Head). You modify README and git add README, copying version W (for Work-tree) into version I (for Index). Then you modify version W again. Now all three are different! Using git status --short or git status -s, you will see MM README in the output: it's modified twice.

    The output from this section is really what shows up in the second half of the git status output, the Changes not staged for commit part.

  4. Compares HEAD vs index, then compares index vs work-tree, then prints—in a summary, combined format—the name and status of each file, using two letters for each file.

To illustrate this clearly, consider this repository with a single initial commit, with one file in it. The file is named README and the initial commit version contains the text Read me.

$ git init
Initialized empty Git repository in ...
$ echo 'Read me.' > README
$ git add README
$ git commit -m initial
[master (root-commit) a9362c4] initial
 1 file changed, 1 insertion(+)
 create mode 100644 README
$ echo 'Please read me.' > README
$ git add README
$ echo 'Read me, for I am very readable.' > README

There are now three active versions of file README, and:

$ git status -s
MM README

The long version of git status shows us that README is both ready to commit, and has changes not staged for commit:

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   README

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:   README

Look, though, at what happens if we put the initial text back, then compare HEAD to the work-tree:

$ echo 'Read me.' > README
$ git diff --name-status HEAD
$

The current commit matches the work-tree, so there is no difference here. If you git add README, the short status goes from MM README to empty:

$ git status -s
MM README
$ git add README
$ git status -s
$

So, if you are wondering "what do I get if I add and commit everything", you will want to compare HEAD with the work-tree. If you are wondering "what do I get if I commit now", you want to compare HEAD to index.

Upvotes: 3

mpromonet
mpromonet

Reputation: 11942

You can get the number of modified file using :

git status -s -uno | wc -l

From the git status documentation, -s give output in short format, and -uno does not show untrack files.

Upvotes: 18

Related Questions