Is there any way to list all git repositories in terminal?
Asked Answered
T

6

20

I have several git repositories on my system and I want to list them all in terminal.

What I'm looking for is something like this:

/path/to/my/git/repo/1/ REPONAME1  
/path/to/my/git/repo/2/ REPONAME2  
/path/to/my/git/repo/3/ REPONAME3

If you can come up with how to show branch name and repo's status eg. [master] c:0 u:1 d:0, that would be great.

Thermo answered 24/2, 2011 at 7:6 Comment(1)
This is a bit similar to #2765753 where you can also find useful ideas.Roos
P
12

To list all the git repositories you have on your system you can run the following command in a bash shell terminal (at the command line) and find them.

find / -name .git -type d -exec dirname {} \;
Paraffin answered 24/2, 2011 at 9:59 Comment(2)
This is a reasonable first approximation, but misses a lot. Bare repositories will be missed, as will any repos that use a regular file named .git to specify a git directory.Valerievalerio
C:\xampp\htdocs\rimedit> find / -name .git -type d -exec dirname {} ` "C:/Users/Jakub/AppData/Local/GitHub/PortableGit_fed20eba68b3e238e49a47cdfed0a45 783d93651/bin/find.exe":` missing argument to -exec'`Concertize
P
6

I found this more accurate than the find command and much faster. I'm sure it can be improved but it's a good start. Obviously your file database will need to be up-to-date before running this command. On linux systems you can do this by running sudo updatedb.

locate -br '\.git$' | rev | cut -c 6- | rev
Pangaro answered 12/11, 2012 at 6:29 Comment(0)
R
5

If you can come up with how to show branch name and repo's status

The idea is to:

First, chose any config key name you want.
For example: "list.repos"

Reinitialize the list, using git config --unset-all (in the global config file):

git config --global --unset-all list.repos

Then use either:

Redirect the result in a file 'list-repos.txt', then register those repositories, reading each line of the file:

while IFS="" read -r repo || [ -n "${repo}" ]
do
  git config --global --add list.repos ${repo}
done < list-repos.txt

Once your repositories are registered (as global git config values), it is trivial to loop over them using the new command git for-each-repo, executing any command you need.

With Git 2.30 (Q4 2020), a new command is introduced, initially to manage parts of "git maintenance"(man) and to ease writing crontab entries (and other scheduling system configuration) for it.

See commit 0016b61, commit 61f7a38, commit a4cb1a2 (15 Oct 2020), commit 2fec604, commit 0c18b70, commit 4950b2a, commit b08ff1f (11 Sep 2020), and commit 1942d48 (28 Aug 2020) by Derrick Stolee (derrickstolee).
(Merged by Junio C Hamano -- gitster -- in commit 7660da1, 18 Nov 2020)

for-each-repo: run subcommands on configured repos

Signed-off-by: Derrick Stolee

It can be helpful to store a list of repositories in global or system config and then iterate Git commands on that list.
Create a new builtin that makes this process simple for experts.
We will use this builtin to run scheduled maintenance on all configured repositories in a future change.

The test is very simple, but does highlight that the "--" argument is optional.

git for-each-repo now includes in its man page:

git-for-each-repo(1)

NAME

git-for-each-repo - Run a Git command on a list of repositories

SYNOPSIS

[verse]
'git for-each-repo' --config=<config> [--] <arguments>

DESCRIPTION

Run a Git command on a list of repositories. The arguments after the known options or -- indicator are used as the arguments for the Git subprocess.

THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.

For example, we could run maintenance on each of a list of repositories stored in a maintenance.repo config variable using


git for-each-repo --config=maintenance.repo maintenance run

This will run git -C <repo> maintenance run for each value <repo> in the multi-valued config variable maintenance.repo.

OPTIONS

--config=<config>

Use the given config variable as a multi-valued list storing absolute path names. Iterate on that list of paths to run the given arguments.

These config values are loaded from system, global, and local Git config, as available. If git for-each-repo is run in a directory that is not a Git repository, then only the system and global config is used.

SUBPROCESS BEHAVIOR

If any git -C <repo> <arguments> subprocess returns a non-zero exit code, then the git for-each-repo process returns that exit code without running more subprocesses.

Each git -C <repo> <arguments> subprocess inherits the standard file descriptors stdin, stdout, and stderr.


With Git 2.30.1 (Q1 2021), "git for-each-repo --config=<var> <cmd>"(man) should not run <cmd> for any repository when the configuration variable <var> is not defined even once.

See commit 6c62f01 (08 Jan 2021) by Derrick Stolee (derrickstolee).
(Merged by Junio C Hamano -- gitster -- in commit aa08688, 15 Jan 2021)

for-each-repo: do nothing on empty config

Reported-by: Andreas Bühmann
Helped-by: Eric Sunshine
Helped-by: Junio C Hamano
Signed-off-by: Derrick Stolee

'git for-each-repo --config=X'(man) should return success without calling any subcommands when the config key 'X' has no value.
The current implementation instead segfaults.

A user could run into this issue if they used 'git maintenance start'(man) to initialize their cron schedule using 'git for-each-repo --config=maintenance.repo ...'(man) but then using 'git maintenance unregister'(man) to remove the config option.
(Note: 'git maintenance stop'(man) would remove the config and remove the cron schedule.)

Add a simple test to ensure this works.
Use 'git help --no-such-option'(man) as the potential subcommand to ensure that we will hit a failure if the subcommand is ever run.


Note: with Git 2.39 (Q4 2022), 'git for-each-repo'(man) is taught to expand tilde characters in paths.

See commit 03744bb (15 Nov 2022) by Taylor Blau (ttaylorr).
See commit be0fd57 (15 Nov 2022) by Ævar Arnfjörð Bjarmason (avar).
See commit 1f80129, commit 13d5bbd (09 Nov 2022) by Ronan Pigott (RPigott).
(Merged by Junio C Hamano -- gitster -- in commit 56a64fc, 23 Nov 2022)

for-each-repo: interpolate repo path arguments

Signed-off-by: Ronan Pigott
Signed-off-by: Taylor Blau

This is a quality of life change for git-maintenance, so repos can be recorded with the tilde syntax.
The register subcommand will not record repos in this format by default.


With Git 2.41 (Q2 2023), git for-each-repo is more robust:

See commit 3611f74, commit 9e2d884, commit 1c7e239, commit f7b2ff9, commit a428619, commit f6f348a, commit b83efce, commit e7587a8, commit 258902c (28 Mar 2023) by Ævar Arnfjörð Bjarmason (avar).
(Merged by Junio C Hamano -- gitster -- in commit 87daf40, 06 Apr 2023)

for-each-repo: with bad config, don't conflate and

Signed-off-by: Ævar Arnfjörð Bjarmason

Fix a logic error in 4950b2a ("for-each-repo: run subcommands on configured repos", 2020-09-11, Git v2.30.0-rc0 -- merge listed in batch #6).
Due to assuming that elements returned from the repo_config_get_value_multi() call wouldn't be "NULL" we'd conflate the <path> and <command> part of the argument list when running commands.

As noted in the preceding commit the fix is to move to a safer "*_string_multi()" version of the *_multi() API.
This change is separated from the rest because those all segfaulted.
In this change we ended up with different behavior.

When using the "--config=<config>" form we take each element of the list as a path to a repository.
E.g.
with a configuration like:

[repo] list = /some/repo

We would, with this command:

git for-each-repo --config=repo.list status builtin

Run a "git status"(man) in /some/repo, as:

git -C /some/repo status builtin

I.e.
ask "status" to report on the "builtin" directory.
But since a configuration such as this would result in a "struct string_list *" with one element, whose "string" member is "NULL":

[repo] list

We would, when constructing our command-line in "builtin/for-each-repo.c"...

strvec_pushl(&child.args, "-C", path, NULL);
for (i = 0; i < argc; i++)
  strvec_push(&child.args, argv[i]);

...have that "path" be "NULL", and as strvec_pushl() stops when it sees NULL we'd end with the first "argv" element as the argument to the "-C" option, e.g.:

git -C status builtin

I.e.
we'd run the command "builtin" in the "status" directory.

In another context this might be an interesting security vulnerability, but I think that this amounts to a nothingburger on that front.

A hypothetical attacker would need to be able to write config for the victim to run, if they're able to do that there's more interesting attack vectors.
See the "safe.directory" facility added in 8d1a744 ("setup.c: create safe.bareRepository", 2022-07-14, Git v2.38.0-rc0 -- merge listed in batch #6).

An even more unlikely possibility would be an attacker able to generate the config used for "for-each-repo --config=<key>", but nothing else (e.g. an automated system producing that list).

Even in that case the attack vector is limited to the user running commands whose name matches a directory that's interesting to the attacker (e.g. a "log" directory in a repository).
The second argument (if any) of the command is likely to make git die without doing anything interesting (e.g. "-p" to "log", there being no "-p" built-in command to run).

Roos answered 17/1, 2021 at 21:46 Comment(2)
the perl and python repo-finders are kinda heavy for a oneliner, and at least the perl one gets it a bit wrong, it will miss valid repos. find -name HEAD -execdir test -d refs -a -d objects \; -printf %h\\n will do it, on Macs you need the brew find I think. You can use (IFS=$'\n'; find `locate */HEAD`…) etc to speed up locating repos that weren't cloned or inited since the last updatedb.Riviera
@Riviera Agree. This answer is not about a one-liner approach. It is to illustrate the new command.Roos
S
4

lsgit is a script that does this. It's essentially a wrapper around locate -br '^HEAD$'.

If there are multiple clones of the same repo on the local machine, it will indicate that those repos are related.

Susi answered 2/5, 2012 at 15:53 Comment(0)
Z
3

With Powershell you can add all repositories to a global git variable using a script like this:

$dirName = Split-Path -Path ./ -Leaf
$repoProp = "$dirName.repos"

$repos = Get-ChildItem . `
    -Attributes Directory+Hidden `
    -ErrorAction SilentlyContinue `
    -Filter ".git" `
    -Recurse

git config --global --unset-all $repoProp
$repos | ForEach-Object { git config --global --add $repoProp $_.Parent.FullName }

Copy the script in the root folder and then run it. Then you can use git for-each-repo command as explained by VonC.

Zhao answered 17/11, 2021 at 17:36 Comment(0)
J
1

A wrapper around:
locate -br '^HEAD$' as done by Dee Newcum https://github.com/DeeNewcum/dotfiles/blob/master/bin/lsgit or else find . -name 'HEAD' as modified in https://gist.github.com/nrbray/a0ae8ec59d1fd1ae03e2947368096d2e

gives an alternative to searching for gits by the folder or filename alone.

[Not enough reputation to comment on Dee's answer, but I used his solution and it worked for me]

Jobie answered 4/9, 2019 at 10:52 Comment(1)
Please earn reputation and then comment adding comment as answer mislead.Conoid

© 2022 - 2024 — McMap. All rights reserved.