From: Michael Montalbo <mmontalbo@gmail.com>
To: git@vger.kernel.org
Cc: Patrick Steinhardt <pks@pks.im>
Subject: [RFC PATCH 03/14] organize: add status --exit-code
Date: Sun, 23 Aug 2026 10:18:47 -0700 [thread overview]
Message-ID: <20260823171915.2662373-4-mmontalbo@gmail.com> (raw)
In-Reply-To: <20260823171915.2662373-1-mmontalbo@gmail.com>
git organize status reports and always returns zero. A project that keeps
its tree reconciled needs status to fail when the tree drifts, so a check
can gate on the result.
Add --exit-code. status returns 1 when a file is out of place or a recorded
path no longer exists, and 0 otherwise. A standing backlog alone does not
fail it: a backlog file matches no rule, so it is not out of place.
--exit-code changes no output.
Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
---
Documentation/git-organize.adoc | 15 ++++++++++++---
builtin/organize.c | 12 +++++++-----
t/t0096-organize.sh | 20 +++++++++++++++-----
3 files changed, 34 insertions(+), 13 deletions(-)
diff --git a/Documentation/git-organize.adoc b/Documentation/git-organize.adoc
index 8b216146b6..37ada38234 100644
--- a/Documentation/git-organize.adoc
+++ b/Documentation/git-organize.adoc
@@ -9,7 +9,7 @@ git-organize - Reconcile a source tree against a declared layout
SYNOPSIS
--------
[verse]
-'git organize status'
+'git organize status' [--exit-code]
'git organize apply'
'git organize apply' --labels-only [--reseed]
@@ -44,7 +44,9 @@ is not in yet.
`git organize status` reads `[labels]` and reports the out-of-place files,
the backlog, a file in scope that `[labels]` does not record, and a
recorded path that no longer exists. status runs no
-configured command and changes nothing.
+configured command and changes nothing. With `--exit-code` it exits
+non-zero when a file is out of place, a file in scope is unrecorded, or a
+recorded path is missing; a standing backlog alone does not fail it.
`git organize apply` reconciles the tree. It moves each out-of-place file
into its directory. A move that git organize makes on its own is a
@@ -72,7 +74,9 @@ status::
in (the moves), the backlog (recorded files with no matching
rule), a file in scope that `[labels]` does not record, and a recorded
path that no longer exists. Runs no configured
- command and changes nothing.
+ command and changes nothing. With `--exit-code`, exit non-zero when a
+ file is out of place, a file in scope is unrecorded, or a recorded path
+ is missing.
apply::
Move each out-of-place file into its directory as a content-identical
@@ -92,6 +96,11 @@ never do.
OPTIONS
-------
+--exit-code::
+ Exit non-zero from status when a file is out of place, a file in scope
+ is unrecorded, or a recorded path is missing. A standing backlog alone
+ does not fail it. Changes no output.
+
--labels-only::
With apply, run the labeler and record the labels; move no file. A
recorded file keeps its line; the labeler only seeds a file that has no
diff --git a/builtin/organize.c b/builtin/organize.c
index 354ba2151f..35247c5aef 100644
--- a/builtin/organize.c
+++ b/builtin/organize.c
@@ -15,13 +15,13 @@
#include "repository.h"
static const char *const organize_usage[] = {
- "git organize status",
+ "git organize status [--exit-code]",
"git organize apply",
"git organize apply --labels-only [--reseed]",
NULL
};
-static int organize_status(struct repository *repo)
+static int organize_status(struct repository *repo, int exit_code)
{
struct organize_plan plan = ORGANIZE_PLAN_INIT;
int to_move, backlog, unrecorded, orphans;
@@ -67,7 +67,7 @@ static int organize_status(struct repository *repo)
}
organize_plan_release(&plan);
- return 0;
+ return exit_code && (to_move || unrecorded || orphans) ? 1 : 0;
}
static int organize_apply(struct repository *repo)
@@ -106,8 +106,10 @@ int cmd_organize(int argc,
const char *prefix,
struct repository *repo)
{
- int labels_only = 0, reseed = 0;
+ int exit_code = 0, labels_only = 0, reseed = 0;
struct option options[] = {
+ OPT_BOOL(0, "exit-code", &exit_code,
+ N_("exit non-zero from status when a file is out of place")),
OPT_BOOL(0, "labels-only", &labels_only,
N_("with apply, run the labeler and record the labels")),
OPT_BOOL(0, "reseed", &reseed,
@@ -126,7 +128,7 @@ int cmd_organize(int argc,
if (!strcmp(subcmd, "status")) {
if (labels_only)
die(_("git organize: --labels-only is an apply option"));
- ret = organize_status(repo);
+ ret = organize_status(repo, exit_code);
} else if (!strcmp(subcmd, "apply")) {
if (labels_only) {
organize_run_labeler(repo, reseed);
diff --git a/t/t0096-organize.sh b/t/t0096-organize.sh
index c2e6539ef2..d8de3c7e90 100755
--- a/t/t0096-organize.sh
+++ b/t/t0096-organize.sh
@@ -156,6 +156,10 @@ test_expect_success 'status reports the files to move' '
test_grep "2 file(s) would move" actual
'
+test_expect_success 'status --exit-code fails when a file is out of place' '
+ test_expect_code 1 git organize status --exit-code
+'
+
test_expect_success 'apply moves files as content-identical renames and repoints [labels]' '
git organize apply &&
git diff --cached -M --name-status >actual &&
@@ -169,6 +173,7 @@ test_expect_success 'apply moves files as content-identical renames and repoints
git diff --cached --name-only >staged &&
test_grep "^.gitorganize$" staged &&
git commit -m reconciled &&
+ git organize status --exit-code &&
git organize status >actual &&
test_grep "nothing to move" actual &&
test_grep "^odb/blob.c component=odb" .gitorganize &&
@@ -435,7 +440,8 @@ test_expect_success 'a basename shared across directories does not collide' '
test_path_is_file odb/dup.c &&
test_path_is_file sub/dup.c &&
test_path_is_missing dup.c &&
- git commit -m reconciled
+ git commit -m reconciled &&
+ git organize status --exit-code
)
'
@@ -484,17 +490,20 @@ test_expect_success 'a file in scope with no recorded label is unrecorded' '
git commit -m declare &&
git organize apply --labels-only &&
git commit -m labels &&
- # a.c is recorded but matches no rule: backlog
+ # a.c is recorded but matches no rule, so it is backlog; a
+ # standing backlog alone does not fail --exit-code
git organize status >actual &&
test_grep "backlog:" actual &&
test_grep "^ a.c$" actual &&
- # a source in scope that [labels] never recorded is unrecorded
+ git organize status --exit-code &&
+ # a source in scope that [labels] never recorded is unrecorded drift
echo b >b.c &&
git add b.c &&
git commit -m add-b &&
git organize status >actual &&
test_grep "in scope but unrecorded:" actual &&
- test_grep "^ b.c$" actual
+ test_grep "^ b.c$" actual &&
+ test_expect_code 1 git organize status --exit-code
)
'
@@ -519,7 +528,8 @@ test_expect_success 'status reports a recorded path that no longer exists' '
git commit -m drop-b &&
git organize status >actual &&
test_grep "declared but missing" actual &&
- test_grep " b.c" actual
+ test_grep " b.c" actual &&
+ test_expect_code 1 git organize status --exit-code
)
'
--
2.54.0
next prev parent reply other threads:[~2026-08-23 17:19 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-23 17:18 [RFC PATCH 00/14] git organize: record file placement and apply it Michael Montalbo
2026-08-23 17:18 ` [RFC PATCH 01/14] organize: add the git organize builtin Michael Montalbo
2026-08-23 17:18 ` [RFC PATCH 02/14] organize: add the labeler, organizer, and apply --labels-only Michael Montalbo
2026-08-23 17:18 ` Michael Montalbo [this message]
2026-08-23 17:18 ` [RFC PATCH 04/14] organize: add the --label selector Michael Montalbo
2026-08-23 17:18 ` [RFC PATCH 05/14] organize: declare Git's scope and layout Michael Montalbo
2026-08-23 17:18 ` [RFC PATCH 06/14] organize: record a label for every source in scope Michael Montalbo
2026-08-23 17:18 ` [RFC PATCH 07/14] odb: gather the odb sources under odb/ Michael Montalbo
2026-08-24 14:08 ` Oswald Buddenhagen
2026-08-24 14:43 ` Junio C Hamano
2026-08-24 20:03 ` Oswald Buddenhagen
2026-08-23 17:18 ` [RFC PATCH 08/14] refs: gather the refs sources under refs/ Michael Montalbo
2026-08-23 17:18 ` [RFC PATCH 09/14] pack: gather the pack sources under pack/ Michael Montalbo
2026-08-23 17:18 ` [RFC PATCH 10/14] diff: gather the diff sources under diff/ Michael Montalbo
2026-08-23 17:18 ` [RFC PATCH 11/14] revision: gather the revision sources under revision/ Michael Montalbo
2026-08-23 17:18 ` [RFC PATCH 12/14] index: gather the index sources under index/ Michael Montalbo
2026-08-23 17:18 ` [RFC PATCH 13/14] setup: gather the setup sources under setup/ Michael Montalbo
2026-08-23 17:18 ` [RFC PATCH 14/14] transport: gather the transport sources under transport/ Michael Montalbo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260823171915.2662373-4-mmontalbo@gmail.com \
--to=mmontalbo@gmail.com \
--cc=git@vger.kernel.org \
--cc=pks@pks.im \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox