Subject: [PATCH] commit -v: add --word-diff opt to commit template

From: "Kraktus via GitGitGadget" <[email-masked]>
Date: Mon, 20 Apr 2026 20:13:58 +0000

From: kraktus <[email-masked]>

`--word-diff` was available for the `diff` command, but not for
the `commit` one, whereas it is very useful when updating column-based
data-format like CSV for example.

Only support the PORCELAIN mode for now, but if deemed necessary can
add plain too, I don't think COLOR makes sense when outputting a file.

Signed-off-by: kraktus <[email-masked]>
---
    commit -v: add --word-diff opt to commit template

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2276%2Fkraktus%2Fgdiff_word_commit-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2276/kraktus/gdiff_word_commit-v1
Pull-Request: https://github.com/git/git/pull/2276

 Documentation/git-commit.adoc |  4 ++++
 builtin/commit.c              |  4 ++++
 t/t7507-commit-verbose.sh     | 45 +++++++++++++++++++++++++++++++++++
 wt-status.c                   |  2 ++
 wt-status.h                   |  1 +
 5 files changed, 56 insertions(+)

diff --git a/Documentation/git-commit.adoc b/Documentation/git-commit.adoc
index 8329c1034b..6cb5359b52 100644
--- a/Documentation/git-commit.adoc
+++ b/Documentation/git-commit.adoc
@@ -371,6 +371,10 @@ If specified twice, show in addition the unified diff between
 what would be committed and the worktree files, i.e. the unstaged
 changes to tracked files.
 
+`--word-diff`::
+	Show a word diff instead of a line diff in the verbose commit
+	template (requires `-v`).
+
 `-q`::
 `--quiet`::
 	Suppress commit summary message.
diff --git a/builtin/commit.c b/builtin/commit.c
index a3e52ac9ca..b24f086fd2 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -126,6 +126,7 @@ static int all, also, interactive, patch_interactive, only, amend, signoff;
 static struct interactive_options interactive_opts = INTERACTIVE_OPTIONS_INIT;
 static int edit_flag = -1; /* unspecified */
 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
+static int commit_word_diff;
 static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static const char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
@@ -573,6 +574,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
 		s->reference = "HEAD^1";
 	}
 	s->verbose = verbose;
+	s->word_diff = commit_word_diff;
 	s->index_file = index_file;
 	s->fp = fp;
 	s->nowarn = nowarn;
@@ -1705,6 +1707,8 @@ int cmd_commit(int argc,
 	static struct option builtin_commit_options[] = {
 		OPT__QUIET(&quiet, N_("suppress summary after successful commit")),
 		OPT__VERBOSE(&verbose, N_("show diff in commit message template")),
+		OPT_BOOL(0, "word-diff", &commit_word_diff,
+			N_("show word diff in verbose commit template")),
 
 		OPT_GROUP(N_("Commit message options")),
 		OPT_FILENAME('F', "file", &logfile, N_("read message from file")),
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index b53d71c086..98693d22f2 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -166,4 +166,49 @@ test_expect_success "status ignores commit.verbose=true" '
 	! grep "^diff --git actual"
 '
 
+write_script "check-for-word-diff" <<\EOF
+cp "$1" template_out
+exit 0
+EOF
+test_set_editor "$PWD/check-for-word-diff"
+
+test_expect_success 'setup for word-diff tests' '
+	echo "the quick brown fox" >wordfile &&
+	git add wordfile &&
+	git commit -F message &&
+	echo "the slow brown fox" >wordfile &&
+	git add wordfile &&
+	git commit -F message
+'
+
+test_expect_success '--word-diff with -v shows word diff' '
+	git commit --amend -v --word-diff &&
+	grep "^-quick$" template_out &&
+	grep "^+slow$" template_out
+'
+
+test_expect_success '--word-diff without -v is a no-op' '
+	git commit --amend --word-diff &&
+	! grep "^~$" template_out
+'
+
+test_expect_success '-v -v --word-diff shows word diff in both sections' '
+	echo "the fast brown fox" >wordfile &&
+	git commit --amend -v -v --word-diff &&
+	grep "^-quick$" template_out &&
+	grep "^+slow$" template_out &&
+	grep "^-slow$" template_out &&
+	grep "^+fast$" template_out
+'
+
+test_expect_success 'word-diff markers stripped from saved commit message' '
+	git commit --amend -v --word-diff &&
+	check_message message
+'
+
+test_expect_success 'no --word-diff produces line diff without markers' '
+	git commit --amend -v &&
+	! grep "^~$" template_out
+'
+
 test_done
diff --git a/wt-status.c b/wt-status.c
index 479ccc3304..086275bae3 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1177,6 +1177,8 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
 	rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;
 	rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit;
 	rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
+	if (s->word_diff)
+		rev.diffopt.word_diff = DIFF_WORDS_PORCELAIN;
 	rev.diffopt.file = s->fp;
 	rev.diffopt.close_file = 0;
 	/*
diff --git a/wt-status.h b/wt-status.h
index e9fe32e98c..90883d2c28 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -130,6 +130,7 @@ struct wt_status {
 	int detect_rename;
 	int rename_score;
 	int rename_limit;
+	int word_diff;
 	enum wt_status_format status_format;
 	unsigned char added_cut_line; /* boolean */
 	struct wt_status_state state;

base-commit: e8955061076952cc5eab0300424fc48b601fe12d
-- 
gitgitgadget