Skip to content

[DRAFT][clang-format] helper script for resolving downstream reformat… #80462

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions llvm/utils/git/clang-format-merge-resolver.sh
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This script should have mode 0755 (executable) instead of 0644 to be able to invoke it with the documented command-line.

Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env bash

# Usage:
# If clang-format is not on PATH:
# export CLANG_FORMAT_PATH=/path/to/clang-format
# Run this script to resolve the formatting conflicts and leave only
# the non-formatting conflicts for you to resolve manually.

# Find the .git directory.
GIT_DIR=$(git rev-parse --git-dir)

# Are we in the midst of a merge? If not, this is the wrong tool.
if [ ! -e $GIT_DIR/MERGE_HEAD ]; then
echo Not doing a merge?
exit -1
fi

# Find the "current" "other" and "base" commits.
# The commit we are merging into.
read CURRENT < $GIT_DIR/ORIG_HEAD
# The commit being merged into CURRENT.
read OTHER < $GIT_DIR/MERGE_HEAD
# Where it all started.
BASE=$(git merge-base $CURRENT $OTHER)

# Set up a place to keep temp files.
MYTEMP=$(mktemp -d)
trap 'rm -rf $MYTEMP' EXIT

# Find clang-format the same way code-format-helper.py does.
if [ -x "$CLANG_FORMAT_PATH" ]
then
CLANG_FORMAT=$CLANG_FORMAT_PATH
else
CLANG_FORMAT=$(which clang-format)
if [ ! $? ]
then
echo clang-format not found on PATH
exit -1
fi
fi

# resolve_one_file will perform formatting-conflict resolution on one file.
# If any conflicts remain, informs the user, otherwise will git-add the
# resolved file.
resolve_one_file() {
file=$1
echo Resolving "$file"...

# Get formatted copies of the base, current, and other files.
git show "$BASE:$file" | $CLANG_FORMAT --style=file --assume-filename="$file" > "$MYTEMP/base"
git show "$OTHER:$file" | $CLANG_FORMAT --style=file --assume-filename="$file" > "$MYTEMP/other"
git show "$CURRENT:$file" | $CLANG_FORMAT --style=file --assume-filename="$file" > "$MYTEMP/current"
Comment on lines +51 to +53
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The technique of figuring out $BASE/$OTHER/$CURRENT commits and using them here is fragile (because likely incomplete) and also unneeded because git already has these 3 versions of the file in its index for an unresolved conflict.

To use the versions from the git index, I can suggest either this (syntax explained here):

git show ":1:$file" | $CLANG_FORMAT ... >"$MYTEMP/base"
git show ":2:$file" | $CLANG_FORMAT ... >"$MYTEMP/current"
git show ":3:$file" | $CLANG_FORMAT ... >"$MYTEMP/other"

Or this (explained here):

read -r basetmp currenttmp othertmp rem < <(git checkout-index --stage=all "$file")
$CLANG_FORMAT ... -i "$basetmp"
$CLANG_FORMAT ... -i "$currenttmp"
$CLANG_FORMAT ... -i "$othertmp"

In both versions, all the logic for figuring out $BASE/$OTHER/$CURRENT can be dropped. In the second version, git creates suitable temp files, so you don't need $MYTEMP (but you would still be responsible to cleanup the temp files).


# Merge the formatted files and report failures.
git merge-file -Lcurrent -Lbase -Lother "$MYTEMP/current" "$MYTEMP/base" "$MYTEMP/other"
STATUS=$?
if [ $STATUS -lt 0 ]
then
echo git merge-file failed for "$file"
else
mv "$MYTEMP/current" "$file"
if [ $STATUS -eq 0 ]
then
git add "$file"
else
echo Conflicts remain in $file
fi
fi
}

# Find all the conflicted files, and operate on each one.
# `git status --porcelain` identifies conflicted files with 'UU ' prefix.
# No other prefix is relevant here.
git status --porcelain | grep '^UU ' | cut -c 4- | \
while read -r file
do
resolve_one_file "$file"
done
Comment on lines +75 to +79
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will only work if you invoke the script from the top-level directory of the working copy? I think you can in any case get a valid filename by prefixing it with the output git rev-parse --show-cdup. But be sure to check if that is sufficient for the other commands inside resolve_one_file. (If too difficult, the script could error out when not in the top-level.)