https://github.com/llvmbot created https://github.com/llvm/llvm-project/pull/219855
Backport e89830da7f4e6656b9ff54ea7695c31435a53ee4 fe0143b1a97484e10bc11b012ceb2c1ecd8bc38c Requested by: @MaskRay >From f3e7c3b9b15225c6407272730491693a6d681d4e Mon Sep 17 00:00:00 2001 From: eoineoineoin <[email protected]> Date: Sat, 1 Aug 2026 06:22:14 +0100 Subject: [PATCH 1/2] Add option to format a whole file using git-clang-format (#204336) Today, git-clang-format will only format lines which have been modified. However, in some cases, that's not sufficient to get a "clean" file which would be unmodified by running `clang-format` manually. I've got a minimal repro using the default clang-format rules. Setup a new git repository and create a commit with an empty file: ``` mkdir /tmp/bla cd /tmp/bla git init touch t.cpp git add t.cpp git commit -m "V1" ``` Add a line to that file containing a comment: ``` echo "int x = 0; // Comment describing x" > t.cpp git add t.cpp git clang-format --staged # Reports "clang-format did not modify any files" git commit -m "V2" ``` There's nothing wrong with this file yet, and it's working as expected. Problem arises when we add a new line with a comment which is not aligned with the comment we've already added: ``` echo "int longerThanX = 0; // Should cause above to become aligned" >> t.cpp git add t.cpp git clang-format --staged # Reports "clang-format did not modify any files" ``` git-clang-format didn't change the staged file here as it's only inspecting the modified lines, but according to the default formatting rules, this new line actually has an effect on the preceding line. i.e., if you run `clang-format t.cpp`, you'll see that whitespace is added before the "Comment describing x" to align with the "Should cause..." It's not limited to _just_ the preceding line (e.g. in the case where we have multiple lines like the first with comments which become unaligned,) so can't just add more lines of context to the diff. This PR just adds an option to format the whole file, instead of just the modified sections. (cherry picked from commit e89830da7f4e6656b9ff54ea7695c31435a53ee4) --- clang/tools/clang-format/git-clang-format | 45 +++++++++++++++-------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/clang/tools/clang-format/git-clang-format b/clang/tools/clang-format/git-clang-format index d79b57e7f6e10..c9319c55213a3 100755 --- a/clang/tools/clang-format/git-clang-format +++ b/clang/tools/clang-format/git-clang-format @@ -202,6 +202,11 @@ def main(): default=0, help="print extra information", ) + p.add_argument( + "--whole-file", + action="store_true", + help="format whole file instead of only modified lines", + ) p.add_argument( "--diff_from_common_commit", action="store_true", @@ -231,8 +236,8 @@ def main(): # When no commits are given explicitly and the pre-commit CI framework's # environment variables are set, use them to define the diff range. if not opts.args and not dash_dash: - from_ref = os.environ.get('PRE_COMMIT_FROM_REF') - to_ref = os.environ.get('PRE_COMMIT_TO_REF') + from_ref = os.environ.get("PRE_COMMIT_FROM_REF") + to_ref = os.environ.get("PRE_COMMIT_TO_REF") if from_ref and to_ref: opts.args = [from_ref, to_ref] if not opts.diff and not opts.diffstat: @@ -256,7 +261,7 @@ def main(): opts.binary = os.path.abspath(opts.binary) changed_lines = compute_diff_and_extract_lines( - commits, files, opts.staged, opts.diff_from_common_commit + commits, files, opts.staged, opts.whole_file, opts.diff_from_common_commit ) if opts.verbose >= 1: ignored_files = set(changed_lines) @@ -410,10 +415,12 @@ def get_object_type(value): return convert_string(stdout.strip()) -def compute_diff_and_extract_lines(commits, files, staged, diff_common_commit): +def compute_diff_and_extract_lines( + commits, files, staged, whole_file, diff_common_commit +): """Calls compute_diff() followed by extract_lines().""" diff_process = compute_diff(commits, files, staged, diff_common_commit) - changed_lines = extract_lines(diff_process.stdout) + changed_lines = extract_lines(diff_process.stdout, whole_file) diff_process.stdout.close() diff_process.wait() if diff_process.returncode != 0: @@ -446,7 +453,7 @@ def compute_diff(commits, files, staged, diff_common_commit): return p -def extract_lines(patch_file): +def extract_lines(patch_file, whole_file): """Extract the changed lines in `patch_file`. The return value is a dictionary mapping filename to a list of (start_line, @@ -461,17 +468,23 @@ def extract_lines(patch_file): match = re.search(r"^\+\+\+\ [^/]+/(.*)", line) if match: filename = match.group(1).rstrip("\r\n\t") - match = re.search(r"^@@ -[0-9,]+ \+(\d+)(,(\d+))?", line) - if match: - start_line = int(match.group(1)) - line_count = 1 - if match.group(3): - line_count = int(match.group(3)) - if line_count == 0: - line_count = 1 - if start_line == 0: + if whole_file: + # Initialize the key for filename but ensure ranges is an empty + # list. This will prevent any filtering in clang_format_to_blob(). + matches[filename] = [] continue - matches.setdefault(filename, []).append(Range(start_line, line_count)) + if not whole_file: + match = re.search(r"^@@ -[0-9,]+ \+(\d+)(,(\d+))?", line) + if match: + start_line = int(match.group(1)) + line_count = 1 + if match.group(3): + line_count = int(match.group(3)) + if line_count == 0: + line_count = 1 + if start_line == 0: + continue + matches.setdefault(filename, []).append(Range(start_line, line_count)) return matches >From 1c8bd1c39ffe061b03b0e4df29ccca6e412feacd Mon Sep 17 00:00:00 2001 From: Fangrui Song <[email protected]> Date: Sun, 23 Aug 2026 01:08:43 -0700 Subject: [PATCH 2/2] [git-clang-format] Don't format the line preceding a deletion (#215946) `git diff -U0` renders a pure deletion as `@@ -3,3 +2,0 @@`: no new lines, anchored at the preceding line. extract_lines coerces that zero count to one, so clang-format reformats a line the deletion never touched. Skip such hunks, matching clang-format-diff.py. start_line is 0 only for deletions at the start of a file, so that check goes away as well. Aided by Claude Opus 5 (cherry picked from commit fe0143b1a97484e10bc11b012ceb2c1ecd8bc38c) --- clang/tools/clang-format/git-clang-format | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clang/tools/clang-format/git-clang-format b/clang/tools/clang-format/git-clang-format index c9319c55213a3..d9b2d501c00e3 100755 --- a/clang/tools/clang-format/git-clang-format +++ b/clang/tools/clang-format/git-clang-format @@ -480,9 +480,10 @@ def extract_lines(patch_file, whole_file): line_count = 1 if match.group(3): line_count = int(match.group(3)) + # A hunk that adds no lines is a pure deletion. start_line + # refers to the preceding line (0 when the deletion is at the + # start of a file), which the deletion left alone. if line_count == 0: - line_count = 1 - if start_line == 0: continue matches.setdefault(filename, []).append(Range(start_line, line_count)) return matches _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
