Maetveis created this revision.
Maetveis added a project: clang-format.
Herald added a subscriber: arphaman.
Herald added a project: All.
Maetveis requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

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.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D130108

Files:
  clang/tools/clang-format/git-clang-format

Index: clang/tools/clang-format/git-clang-format
===================================================================
--- clang/tools/clang-format/git-clang-format
+++ clang/tools/clang-format/git-clang-format
@@ -184,8 +184,12 @@
                                                  binary=opts.binary,
                                                  style=opts.style)
   else:
-    old_tree = create_tree_from_workdir(changed_lines)
+    if opts.staged:
+      old_tree = create_tree_from_index(changed_lines)
+    else:
+      old_tree = create_tree_from_workdir(changed_lines)
     new_tree = run_clang_format_and_save_to_tree(changed_lines,
+                                                 revision="" if opts.staged else None,
                                                  binary=opts.binary,
                                                  style=opts.style)
   if opts.verbose >= 1:
@@ -393,12 +397,27 @@
   Returns the object ID (SHA-1) of the created tree."""
   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 +425,14 @@
           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 +444,8 @@
       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 +471,12 @@
 
 
 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 +485,10 @@
   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
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to