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).