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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits