From: "Nakul V S via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Patrick Steinhardt <ps@pks.im>,
Junio C Hamano <gitster@pobox.com>, Nakul V S <vsnakul@gmail.com>,
Nakul V S <vsnakul@gmail.com>
Subject: [PATCH] sequencer: refuse to revert shallow boundary commits by default
Date: Wed, 21 Oct 2026 10:09:41 +0000 [thread overview]
Message-ID: <pull.2273.git.1792577381142.gitgitgadget@gmail.com> (raw)
From: Nakul V S <vsnakul@gmail.com>
When a repository is cloned with --depth=1, Git records the boundary
commit in .git/shallow and rewrites its parent links via the graft
mechanism, making it appear as a root commit. Reverting such a commit
will therefore delete all tracked files, which is almost always
unintended by the user.
Add a check in do_pick_commit() that detects when the target commit is
a shallow boundary (nr_parent < 0 in the commit graft) and refuses the
operation by default. A new --allow-shallow-boundary flag is provided
for the rare cases where the user genuinely intends this behavior.
Signed-off-by: Nakul V S <vsnakul@gmail.com>
---
sequencer: refuse to revert shallow boundary commits by default
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2273%2Fvsnakul%2Fsequencer-shallow-boundary-check-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2273/vsnakul/sequencer-shallow-boundary-check-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2273
Documentation/git-revert.adoc | 12 +++++++++++-
builtin/revert.c | 4 +++-
sequencer.c | 14 ++++++++++++++
sequencer.h | 1 +
t/t3501-revert-cherry-pick.sh | 25 +++++++++++++++++++++++++
5 files changed, 54 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-revert.adoc b/Documentation/git-revert.adoc
index ffba365e63..4395df62e2 100644
--- a/Documentation/git-revert.adoc
+++ b/Documentation/git-revert.adoc
@@ -8,7 +8,7 @@ git-revert - Revert some existing commits
SYNOPSIS
--------
[verse]
-'git revert' [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] <commit>...
+'git revert' [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] [--allow-shallow-boundary] <commit>...
'git revert' (--continue | --skip | --abort | --quit)
DESCRIPTION
@@ -122,6 +122,16 @@ include::rerere-options.adoc[]
configuration variable can be used to enable this option by
default.
+--allow-shallow-boundary::
+ By default, 'git revert' refuses to revert a commit that is a
+ shallow boundary (i.e. a commit listed in `.git/shallow`),
+ because its true parent history is absent from the clone and
+ reverting it would remove all tracked files. This option
+ overrides that safeguard and allows the revert to proceed.
++
+To restore full history before reverting, use
+`git fetch --unshallow` instead.
+
SEQUENCER SUBCOMMANDS
---------------------
diff --git a/builtin/revert.c b/builtin/revert.c
index 52100a20cb..f27397fdec 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -24,7 +24,7 @@
*/
static const char * const revert_usage[] = {
- N_("git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] <commit>..."),
+ N_("git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] [--allow-shallow-boundary] <commit>..."),
N_("git revert (--continue | --skip | --abort | --quit)"),
NULL
};
@@ -128,6 +128,8 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")),
OPT_NOOP_NOARG('r', NULL),
OPT_BOOL('s', "signoff", &opts->signoff, N_("add a Signed-off-by trailer")),
+ OPT_BOOL(0, "allow-shallow-boundary", &opts->allow_shallow_boundary,
+ N_("allow reverting a shallow boundary commit")),
OPT_CALLBACK('m', "mainline", opts, N_("parent-number"),
N_("select mainline parent"), option_parse_m),
OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
diff --git a/sequencer.c b/sequencer.c
index efbfd1315b..6de59bf6aa 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -49,6 +49,7 @@
#include "rebase-interactive.h"
#include "reset.h"
#include "branch.h"
+#include "shallow.h"
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
@@ -2366,6 +2367,19 @@ static enum pick_result do_pick_commit(struct repository *r,
}
discard_index(r->index);
+ if (is_repository_shallow(r)) {
+ struct commit_graft *graft = lookup_commit_graft(r, &commit->object.oid);
+ if (graft && graft->nr_parent < 0 && !opts->allow_shallow_boundary) {
+ return error(_("%s: commit %s is a shallow boundary commit.\n"
+ "Reverting it will remove all tracked files because its\n"
+ "true parent history is not present in this clone.\n"
+ "Use --allow-shallow-boundary to override, or\n"
+ "'git fetch --unshallow' to restore full history first."),
+ command_to_string(command),
+ oid_to_hex(&commit->object.oid));
+ }
+ }
+
if (!commit->parents)
parent = NULL;
else if (commit->parents->next) {
diff --git a/sequencer.h b/sequencer.h
index a5d12a1070..f334bc5357 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -57,6 +57,7 @@ struct replay_opts {
int committer_date_is_author_date;
int ignore_date;
int commit_use_reference;
+ int allow_shallow_boundary;
struct strvec trailer_args;
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index 939e7a16a6..508464004f 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -256,4 +256,29 @@ test_expect_success 'cherry-pick is unaware of --reference (for now)' '
test_grep "^usage: git cherry-pick" actual
'
+# Tests for shallow boundary safeguard
+
+test_expect_success 'setup shallow boundary tests' '
+ git init src &&
+ test_commit -C src A &&
+ test_commit -C src B &&
+ git clone --no-local --depth=1 src shallow-repo
+'
+
+test_expect_success 'revert of shallow boundary commit is refused by default' '
+ test_must_fail git -C shallow-repo revert --no-edit HEAD 2>err &&
+ test_grep "shallow boundary commit" err
+'
+
+test_expect_success 'revert of shallow boundary succeeds with --allow-shallow-boundary' '
+ git -C shallow-repo revert --no-edit --allow-shallow-boundary HEAD
+'
+
+test_expect_success 'revert in non-shallow repo is unaffected' '
+ git init non-shallow &&
+ test_commit -C non-shallow A &&
+ test_commit -C non-shallow B &&
+ git -C non-shallow revert --no-edit HEAD
+'
+
test_done
base-commit: 47fdf2208c5b94c8cc01c106b8f7c85efb91c926
--
gitgitgadget
next reply other threads:[~2026-10-21 10:09 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-21 10:09 Nakul V S via GitGitGadget [this message]
2026-10-21 15:20 ` [PATCH] sequencer: refuse to revert shallow boundary commits by default Phillip Wood
2026-10-21 19:11 ` Junio C Hamano
2026-10-26 10:58 ` Phillip Wood
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=pull.2273.git.1792577381142.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=ps@pks.im \
--cc=vsnakul@gmail.com \
/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.