32

I have a bunch(10-15) of local git repositories somewhere on my filesystem, but all in the folder /data/

I want to find all/any folder that has uncommitted changes. How can I do that? Kind of like a recursive global git status variant.

All the answers got it wrong I think. Any git command only works within the folder that's under git control. I need something to search for such folders.

So instead I wrote this script that does this:

#!/usr/bin/env ruby
require 'find'
require 'fileutils'

#supply directory to search in as argument

@pat = ARGV[0]
(puts "directory argument required"; exit) unless @pat
Dir.chdir(@pat)
Find.find(@pat) do |path|
  if FileTest.directory?(path)
    Dir.chdir(path)
    resp = `git status 2>&1`
    unless resp =~ /fatal|nothing to commit \(working directory clean\)/i
      puts "#{'#'*10}\n#{Dir.pwd}#{'#'*10}\n#{resp}"
      Find.prune
    end

    Dir.chdir(@pat)
  end
end
Cœur
  • 37,241
  • 25
  • 195
  • 267
ulver
  • 1,521
  • 16
  • 29

13 Answers13

30

Adapted from this gist on how to print git status of all repositories under the current folder:

find . -type d -name '.git' | while read dir ; do sh -c "cd $dir/../ && echo -e \"\nGIT STATUS IN ${dir//\.git/}\" && git status -s" ; done

The find command is your friend, along with some shell magic.

KyleMit
  • 30,350
  • 66
  • 462
  • 664
  • 1
    Digging up the past a little here, but could you add an explanation about how this works? – GPPK Jan 21 '20 at 10:03
  • 2
    @GPPK Find all `.git` directories below current folder, pipe to while loop to get a named variable, `cd` to each `$dir` and echo its `git status` while also showing that directory – OneCricketeer May 21 '21 at 19:48
  • This is nice, but I'd add a `-maxdepth 2`, it's much faster. Final optimized command: `find . -type d -name '.git' -maxdepth 2 | while read dir ; do sh -c "cd $dir/../ && echo -e \"\nGIT STATUS IN ${dir//\.git/}\" && git status -s" ; done` – Massimiliano Arione May 17 '23 at 06:55
8

There is also a shell script called multi-git-status that does this.

Simply use the mgitstatus command (with an optional directory name argument):

slhck
  • 36,575
  • 28
  • 148
  • 201
7

I don't think git has this build in, thus I (also) created a script to do this: https://github.com/mnagel/clustergit

The problem with the snippets posted here is that they break as the output format of git status changes. My script has the same problem (as it basically works the same way), but at least you always get the latest version. enter image description here

reducing activity
  • 1,985
  • 2
  • 36
  • 64
mnagel
  • 6,729
  • 4
  • 31
  • 66
6

Something along these lines?

$ for i in /data/*/; do (cd $i && (echo $i; git status)); done
$ for i in /data/*/; do (cd $i \
> && (git status | grep -qx 'nothing to commit (working directory clean)' \
> || (echo $i && git status))); done
ephemient
  • 198,619
  • 38
  • 280
  • 391
4

Using only find

There's really no need for fancy bash tomfoolery, just use find.

find . -type d -name .git -print -execdir git status \;
  • find . -type d -name .git recursively find all .git repositories
  • -print print the path of the .git directory
  • -execdir git status \; run git status in that diectory (the directory containing the git directory)
CervEd
  • 3,306
  • 28
  • 25
1

I think this will do the job for each repo

git status -uall

https://git-scm.com/docs/git-status

arccoder
  • 57
  • 5
1
for rp in /srv/*/
do
  printf '\ec'
  cd "$rp"
  git status
  echo "${rp%/}"
  read
done

Example

Zombo
  • 1
  • 62
  • 391
  • 407
0

It's not very fancy for formatting but how about something like

find . -iname ".git" -type d | sed -rne "s|(.+)/\.git|echo \1;cd \1;git status;cd /data|p" | bash
David Hunt
  • 31
  • 4
0

I realise this is a pretty old thread now but in Windows 10 I couldn't get the WHILE/DO loop from @Tasos solution to work so I converted it to PowerShell.

This version also prints out the status of each uncommitted change as well as a bit of formatting for readability to group by folder names.

cd C:\Projects

$msgs = @()
gci ".git" -Directory -Recurse -Hidden | % {
    $dir = $_
    cd $dir\..

    $gitMsgs = (git status -s)
    if ( $gitMsgs ) {
        $msgs += "$dir ****************************"
        $msgs += $gitMsgs
        $msgs += ""
    } 
}
$msgs | % { Write-Host $_ }

Richard Hauer
  • 1,298
  • 9
  • 22
0

cargo install fd-find https://github.com/sharkdp/fd#installation

find_nonclean_git () {
    fd -E Trash -H '^\.git$' ~ -x echo '{//}' | while read dir; do
        cd $dir
        echo $dir >/dev/null
        #git status -s | grep -c ^
        lnnum=$(git status -s | grep -c ^)
        #if [[ "$lnnum" != "0" ]]; then
        if (( $lnnum > 0 )); then
            echo $dir $lnnum
        fi
    done
}
walkman
  • 1,743
  • 2
  • 10
  • 10
0


Save this as a .dart file at the root of where you store all of your repositories.

Then, dart run git.dart

import 'dart:io';

void main(List<String> args)
{
    print("");
    Run(".");
}

void Run(String path)
{
    for (var entity in Directory(path).listSync(recursive: false, followLinks: false))
    {
        if (entity.statSync().type != FileSystemEntityType.directory)
        {
            continue;
        }

        if (!Directory(entity.path + Platform.pathSeparator + ".git").existsSync())
        {
            Run(entity.path);
            continue;
        }

        print("Looking At Repo: ${entity.path}");

        {
            var gitRemoteUpdateResult = Process.runSync('git', ['remote', 'update'], workingDirectory: entity.absolute.path);
            if (gitRemoteUpdateResult.exitCode != 0)
            {
                stderr.write(gitRemoteUpdateResult.stderr);
                return;
            }
        }

        var gitStatusResult = Process.runSync('git', ['status'], workingDirectory: entity.absolute.path);
        if (gitStatusResult.exitCode != 0)
        {
            stderr.write(gitStatusResult);
            return;
        }

        var gitStatusResultOut = gitStatusResult.stdout as String;

        if (
            !gitStatusResultOut.contains("nothing to commit, working tree clean") ||
            !gitStatusResultOut.contains("Your branch is up to date with"))
        {
            print("DIRTY");
        }

        print("");
    }
}
olfek
  • 3,210
  • 4
  • 33
  • 49
0

I have written a simple program in C# that lists all the changed (but uncommitted) repos starting from D:\DEV

class Program
{
    static void Main(string[] args)
    {
        var directories = Directory.GetDirectories("D:\\DEV", ".git", SearchOption.AllDirectories);
        foreach (string gitdir in directories)
        {
            DirectoryInfo di = new DirectoryInfo(gitdir);
            DirectoryInfo dir = di.Parent;
            string resp = CommandOutput("git update-index --refresh", dir.FullName);
            if (!string.IsNullOrEmpty(resp.Replace("\n","").Trim()))
            {
                Console.WriteLine(dir.FullName+" : ");
                Console.WriteLine(resp);
            }
        }

        Console.WriteLine("Press any key to exit");
        Console.ReadKey();
    }

public static string CommandOutput(string command, string workingDirectory )
{
    try
    {
        ProcessStartInfo procStartInfo = new ProcessStartInfo("cmd", "/c " + command);
        procStartInfo.RedirectStandardError = procStartInfo.RedirectStandardInput = procStartInfo.RedirectStandardOutput = true;
        procStartInfo.UseShellExecute = false;
        procStartInfo.CreateNoWindow = true;
        if (null != workingDirectory)
        {
            procStartInfo.WorkingDirectory = workingDirectory;
        }

        Process proc = new Process();
        proc.StartInfo = procStartInfo;

        StringBuilder sb = new StringBuilder();
        proc.OutputDataReceived += delegate (object sender, DataReceivedEventArgs e)
        {
            sb.AppendLine(e.Data);
        };
        proc.ErrorDataReceived += delegate (object sender, DataReceivedEventArgs e)
        {
            sb.AppendLine(e.Data);
        };

        proc.Start();
        proc.BeginOutputReadLine();
        proc.BeginErrorReadLine();
        proc.WaitForExit();
        return sb.ToString();
    }
    catch (Exception objException)
    {
        return string.Format("Error in command: {0}, {1}", command, objException.Message);
    }
}
}
Sergey
  • 835
  • 7
  • 6
0

You can use "git ls-files --modified --deleted --exclude-standard" to list all modified and deleted files (--exclude-standard is not probably needed there, but just in case you want to list all unknown files that are not ignored with --other...). You can then check if the output of this command is empty.

Or you can check the exit status of "git diff --quiet HEAD" if you want to check if "git commit -a" would pick up anything, or "git diff --cached --quiet HEAD" if you want to check if "git commit" would pick anything (or one of its plumbing relatives: git-diff-files or git-diff-index).

Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230