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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.