[clang] 3f801e0 - [clang-format] git-clang-format --staged should format the index

via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 10 19:30:49 PDT 2022


Author: Gergely Meszaros
Date: 2022-08-10T19:30:41-07:00
New Revision: 3f801e07fa82055de98278d3ebeaade68fbacee9

URL: https://github.com/llvm/llvm-project/commit/3f801e07fa82055de98278d3ebeaade68fbacee9
DIFF: https://github.com/llvm/llvm-project/commit/3f801e07fa82055de98278d3ebeaade68fbacee9.diff

LOG: [clang-format] git-clang-format --staged should format the index

When --staged (or --cached) use the index for formatting as well, not just
for the line numbers to format. Without this change git-clang-format gets
the changed line numbers based on the index, but then formats these lines on
the working tree version of the file.

This is a problem when the working tree and index differ. One common case
would be (and is the motivation behind this patch) when applying the
suggested changes git-clang-format --staged, then forgetting to add the
applied changes. When git-clang-format --staged --diff is used in a
pre-commit hook in this scenario, then the hook would allow committing the
improperly formatted changes, as the file is correctly formatted in the work
tree.

Fixes #56797.

Differential Revision: https://reviews.llvm.org/D130108

Added: 
    

Modified: 
    clang/tools/clang-format/git-clang-format

Removed: 
    


################################################################################
diff  --git a/clang/tools/clang-format/git-clang-format b/clang/tools/clang-format/git-clang-format
index 6a1172e0bf17b..7ce6b60a8e655 100755
--- a/clang/tools/clang-format/git-clang-format
+++ b/clang/tools/clang-format/git-clang-format
@@ -179,15 +179,17 @@ def main():
 
   if len(commits) > 1:
     old_tree = commits[1]
-    new_tree = run_clang_format_and_save_to_tree(changed_lines,
-                                                 revision=commits[1],
-                                                 binary=opts.binary,
-                                                 style=opts.style)
+    revision = old_tree
+  elif opts.staged:
+    old_tree = create_tree_from_index(changed_lines)
+    revision = ''
   else:
     old_tree = create_tree_from_workdir(changed_lines)
-    new_tree = run_clang_format_and_save_to_tree(changed_lines,
-                                                 binary=opts.binary,
-                                                 style=opts.style)
+    revision = None
+  new_tree = run_clang_format_and_save_to_tree(changed_lines,
+                                               revision,
+                                               binary=opts.binary,
+                                               style=opts.style)
   if opts.verbose >= 1:
     print('old tree: %s' % old_tree)
     print('new tree: %s' % new_tree)
@@ -394,11 +396,29 @@ def create_tree_from_workdir(filenames):
   return create_tree(filenames, '--stdin')
 
 
+def create_tree_from_index(filenames):
+  # Copy the environment, because the files have to be read from the original
+  # index.
+  env = os.environ.copy()
+  def index_contents_generator():
+    for filename in filenames:
+      git_ls_files_cmd = ['git', 'ls-files', '--stage', '-z', '--', filename]
+      git_ls_files = subprocess.Popen(git_ls_files_cmd, env=env,
+                                      stdin=subprocess.PIPE,
+                                      stdout=subprocess.PIPE)
+      stdout = git_ls_files.communicate()[0]
+      yield convert_string(stdout.split(b'\0')[0])
+  return create_tree(index_contents_generator(), '--index-info')
+
+
 def run_clang_format_and_save_to_tree(changed_lines, revision=None,
                                       binary='clang-format', style=None):
   """Run clang-format on each file and save the result to a git tree.
 
   Returns the object ID (SHA-1) of the created tree."""
+  # Copy the environment when formatting the files in the index, because the
+  # files have to be read from the original index.
+  env = os.environ.copy() if revision == '' else None
   def iteritems(container):
       try:
           return container.iteritems() # Python 2
@@ -406,11 +426,15 @@ def run_clang_format_and_save_to_tree(changed_lines, revision=None,
           return container.items() # Python 3
   def index_info_generator():
     for filename, line_ranges in iteritems(changed_lines):
-      if revision:
-        git_metadata_cmd = ['git', 'ls-tree',
-                            '%s:%s' % (revision, os.path.dirname(filename)),
-                            os.path.basename(filename)]
-        git_metadata = subprocess.Popen(git_metadata_cmd, stdin=subprocess.PIPE,
+      if revision is not None:
+        if len(revision) > 0:
+          git_metadata_cmd = ['git', 'ls-tree',
+                              '%s:%s' % (revision, os.path.dirname(filename)),
+                              os.path.basename(filename)]
+        else:
+          git_metadata_cmd = ['git', 'ls-files', '--stage', '--', filename]
+        git_metadata = subprocess.Popen(git_metadata_cmd, env=env,
+                                        stdin=subprocess.PIPE,
                                         stdout=subprocess.PIPE)
         stdout = git_metadata.communicate()[0]
         mode = oct(int(stdout.split()[0], 8))
@@ -422,7 +446,8 @@ def run_clang_format_and_save_to_tree(changed_lines, revision=None,
       blob_id = clang_format_to_blob(filename, line_ranges,
                                      revision=revision,
                                      binary=binary,
-                                     style=style)
+                                     style=style,
+                                     env=env)
       yield '%s %s\t%s' % (mode, blob_id, filename)
   return create_tree(index_info_generator(), '--index-info')
 
@@ -448,11 +473,12 @@ def create_tree(input_lines, mode):
 
 
 def clang_format_to_blob(filename, line_ranges, revision=None,
-                         binary='clang-format', style=None):
+                         binary='clang-format', style=None, env=None):
   """Run clang-format on the given file and save the result to a git blob.
 
   Runs on the file in `revision` if not None, or on the file in the working
-  directory if `revision` is None.
+  directory if `revision` is None. Revision can be set to an empty string to run
+  clang-format on the file in the index.
 
   Returns the object ID (SHA-1) of the created blob."""
   clang_format_cmd = [binary]
@@ -461,10 +487,10 @@ def clang_format_to_blob(filename, line_ranges, revision=None,
   clang_format_cmd.extend([
       '-lines=%s:%s' % (start_line, start_line+line_count-1)
       for start_line, line_count in line_ranges])
-  if revision:
+  if revision is not None:
     clang_format_cmd.extend(['-assume-filename='+filename])
     git_show_cmd = ['git', 'cat-file', 'blob', '%s:%s' % (revision, filename)]
-    git_show = subprocess.Popen(git_show_cmd, stdin=subprocess.PIPE,
+    git_show = subprocess.Popen(git_show_cmd, env=env, stdin=subprocess.PIPE,
                                 stdout=subprocess.PIPE)
     git_show.stdin.close()
     clang_format_stdin = git_show.stdout


        


More information about the cfe-commits mailing list