This new option can be used to prevent the application of patches in
commit messages that are part of git show output, for example. Such
text will be indented by for spaces.  Without the --no-dedent option,
the patch tool incorrectly recognizes those diffs.

* patch.man: Mention --no-dedent.
* src/common.h (no_dedent): Declare.
* src/patch.c (no_dedent): Define.
(longopts): Add --no-dedent.
(get_some_switches): Handle --no-dedent.
* src/pch.c (intuit_diff_type): Do not skip indentation if no_dedent.
* tests/Makefile.am (TESTS): Add no-dedent.
* tests/nodedent: New test.

---
 patch.man         |  6 ++++++
 src/common.h      |  3 +++
 src/patch.c       |  6 ++++++
 src/pch.c         | 14 +++++++------
 tests/Makefile.am |  1 +
 tests/no-dedent   | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++

This submission is covered by Red Hat's copyright assignment.

diff --git a/patch.man b/patch.man
index 576b4d645378f2c9..17dda588654f9cd3 100644
--- a/patch.man
+++ b/patch.man
@@ -71,6 +71,9 @@ After removing indenting or encapsulation,
 lines beginning with
 .B #
 are ignored, as they are considered to be comments.
+The processing of indentation can be disabled by the
+.B \-\-no\-dedent
+option.
 .PP
 With context diffs, and to a lesser extent with normal diffs,
 .B patch
@@ -308,6 +311,9 @@ and patches should be generated by
 .B "diff\ \*=binary"
 when line endings are significant.)
 .TP
+.B "\*=no\-dedent"
+Do not remove leading indentation from patch lines.
+.TP
 \fB\-c\fP  or  \fB\*=context\fP
 Interpret the patch file as a ordinary context diff.
 .TP
diff --git a/src/common.h b/src/common.h
index f33346cdc254c19b..c6b98eecf2e9d441 100644
--- a/src/common.h
+++ b/src/common.h
@@ -141,6 +141,9 @@ _Noreturn void fatal_exit (void);
 /* Disable the CR stripping heuristic?  */
 extern bool no_strip_trailing_cr;
 
+/* Disable the indentation removal heuristic?  */
+extern bool no_dedent;
+
 #ifndef NULL_DEVICE
 #define NULL_DEVICE "/dev/null"
 #endif
diff --git a/src/patch.c b/src/patch.c
index e4d05245f9ba0e10..1195d191b56c0563 100644
--- a/src/patch.c
+++ b/src/patch.c
@@ -48,6 +48,7 @@ bool dry_run;
 bool follow_symlinks;
 bool force;
 bool no_strip_trailing_cr;
+bool no_dedent;
 bool noreverse_flag;
 bool posixly_correct;
 bool reverse_flag;
@@ -800,6 +801,7 @@ static struct option const longopts[] =
   {"reject-format", required_argument, nullptr, CHAR_MAX + 9},
   {"read-only", required_argument, nullptr, CHAR_MAX + 10},
   {"follow-symlinks", no_argument, nullptr, CHAR_MAX + 11},
+  {"no-dedent", no_argument, nullptr, CHAR_MAX + 12},
   {nullptr, no_argument, nullptr, 0}
 };
 
@@ -865,6 +867,7 @@ static char const *const option_help[] =
 "  -d DIR  --directory=DIR  Change the working directory to DIR first.",
 "  --reject-format=FORMAT  Create 'context' or 'unified' rejects.",
 "  --binary  Read and write data in binary mode.",
+"  --no-dedent  Do not strip leading indentation from patch lines.",
 "  --read-only=BEHAVIOR  How to handle read-only input files: 'ignore' that 
they",
 "                        are read-only, 'warn' (default), or 'fail'.",
 "",
@@ -1093,6 +1096,9 @@ get_some_switches (int argc, char **argv)
            case CHAR_MAX + 11:
                follow_symlinks = true;
                break;
+           case CHAR_MAX + 12:
+               no_dedent = true;
+               break;
            default:
                usage (stderr, EXIT_TROUBLE);
        }
diff --git a/src/pch.c b/src/pch.c
index d9f5c61be24d465d..423c415acad20060 100644
--- a/src/pch.c
+++ b/src/pch.c
@@ -539,12 +539,14 @@ intuit_diff_type (bool need_header, mode_t *p_file_type)
        }
        strip_trailing_cr
          = 2 <= chars_read && patchbuf[chars_read - 2] == '\r';
-       for (s = patchbuf; c_isblank (*s) || *s == 'X'; s++) {
-           if (*s == '\t')
-               indent = (indent + 8) & ~7;
-           else
-               indent++;
-       }
+       s = patchbuf;
+       if (!no_dedent)
+           for (; c_isblank (*s) || *s == 'X'; s++) {
+               if (*s == '\t')
+                   indent = (indent + 8) & ~7;
+               else
+                   indent++;
+           }
        if (c_isdigit (*s))
          {
            for (t = s + 1; c_isdigit (*t) || *t == ',';  t++)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 8f1a248a17a1dc5e..116d73a40fd57358 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -51,6 +51,7 @@ TESTS = \
        munged-context-format \
        need-filename \
        no-backup \
+       no-dedent \
        no-mode-change-git-diff \
        no-newline-triggers-assert \
        preserve-c-function-names \
diff --git a/tests/no-dedent b/tests/no-dedent
new file mode 100644
index 0000000000000000..09dea961d691f606
--- /dev/null
+++ b/tests/no-dedent
@@ -0,0 +1,62 @@
+# Copyright 2026 Free Software Foundation, Inc.
+#
+# Copying and distribution of this file, with or without modification,
+# in any medium, are permitted without royalty provided the copyright
+# notice and this notice are preserved.
+
+. $srcdir/test-lib.sh
+
+require cat
+use_local_patch
+use_tmpdir
+
+cat > file1 <<EOF
+a
+b
+c
+EOF
+
+cat > file2 <<EOF
+A
+B
+C
+EOF
+
+# The indented part is supposed to be ignored due to --no-dedent.
+cat > p.diff <<EOF
+    --- file1.orig     2026-02-06 14:39:12.141634870 +0100
+    +++ file1  2026-02-06 14:39:15.638546556 +0100
+    @@ -1,3 +1,5 @@
+     a
+    +1
+     b
+    +2
+     c
+
+--- file2.orig 2026-02-06 14:39:12.141634870 +0100
++++ file2      2026-02-06 14:39:15.638546556 +0100
+@@ -1,3 +1,5 @@
+ A
++1
+ B
++2
+ C
+EOF
+
+check 'patch --no-dedent < p.diff || echo "Status: $?"' <<EOF
+patching file file2
+EOF
+
+check 'cat file1' <<EOF
+a
+b
+c
+EOF
+
+check 'cat file2' <<EOF
+A
+1
+B
+2
+C
+EOF

base-commit: 1c021293ee2aa623bc90d3c6139a66ee587fec09


Reply via email to