Last active
August 22, 2022 21:03
-
-
Save thehans/5e5976c49054b6ce4f8959d231708d6b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# This script is meant to help with updating old PRs. | |
# Mainly any OpenSCAD branch which has not been updated since 2022-02-06 | |
# It is intended to be run on a git repo in which a merge has already been started (merging master into the current, old branch), | |
# and has conflicts which show as "deleted by them:", or "(modified/deleted)". | |
# | |
# Due to how git DOES NOT track file moves/renames, in addition to how it determines file "similarity" (by # of exactly matched lines in a file), | |
# it makes it very difficult to merge master into branches created before 2 significant events in the OpenSCAD repo: | |
# 1) A large code style reformatting (PR #4095 on 2022-02-06), and | |
# 2) A large restructuring of the source tree (PRs #4159 on 2022-03-06, #4161 on 2022-03-12) | |
# | |
# When attempting to merge master into such PR branches, the files are wrongly detected by Git as (modified/deleted). | |
# That is, modified locally (fine), but deleted in the remote master (WRONG). | |
#### THE PROBLEM #### | |
# Git doesn't track the renaming of files. | |
# Instead it attempts to detect them after the fact, through countless semi-functional hacks. | |
# This detection fails in our particular situation, making a proper 3-way merge extremely difficult. | |
# | |
#### THE SOLUTION #### | |
# This script goes through the list of conflicting files, determines which ones were presumed "deleted by them", | |
# properly follows the renames to determine the final destination path, and then rewrites the git index | |
# in such a way that "git mergetool" will detect them as "both modified". | |
# | |
#### CAVEATS and SHORTCOMINGS #### | |
# - This script doesn't work the other way around, for files renamed/moved in the current branch. | |
# - Only designed for merge, not sure about rebase | |
# - There is not currently any error checking or backup mechanism built into the script, so please USE WITH CAUTION | |
if [ "$#" -ne 2 ]; then | |
echo "Usage: git_merge_fix_false_deletions.sh <remote> <branch>" | |
echo "e.g. git_merge_fix_false_deletions.sh origin master" | |
echo "Please read over comments in this script before running." | |
exit 1 | |
fi | |
REMOTE=$1 | |
BRANCH=$2 | |
DIFFBASE="${REMOTE}/${BRANCH}" | |
ANCESTOR=$(git merge-base HEAD "$DIFFBASE") | |
echo -n "Using common ancestor:" | |
git -P show -s --pretty='format:%C(auto)%H (%an, %as) %s%n %b%n' $ANCESTOR | |
FILES=$(git diff --name-only --diff-filter=U) # get a clean list of just the files which are (U)nmerged, (because they have conflicts) | |
echo | |
echo "Files modified between current branch and $DIFFBASE:" | |
while IFS= read -r FILE; do | |
SRC="${FILE}" | |
echo -n " ${SRC}" | |
DELETED_IN=$(git -P log $DIFFBASE -M --follow --diff-filter=D --pretty=format:"%H" -- $SRC) | |
while [[ $DELETED_IN ]]; do | |
#echo -n "deleted in " | |
#git -P show -s --pretty='format:%C(auto)%H (%an, %as) %s%n' $DELETED_IN | |
STAT_LINE=$(git -P show --name-status $DELETED_IN | grep -F $SRC) | |
REGEX="^[[:space:]]*R[0-9]+[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]+)[[:space:]]*$" | |
if [[ $STAT_LINE =~ $REGEX ]]; then | |
SRC="${BASH_REMATCH[1]}" | |
DST="${BASH_REMATCH[2]}" | |
echo -n " ==> ${DST}" | |
else | |
echo -n " ==> (actually deleted?)" | |
DST="" | |
break | |
fi | |
SRC="${DST}" | |
DELETED_IN=$(git -P log $DIFFBASE -M --follow --diff-filter=D --pretty=format:"%H" -- $SRC) | |
done | |
echo | |
if [[ $DST ]]; then | |
mkdir -p $(dirname $DST) | |
# have to clear index entry before updating, with 0 file mode, and all 0's SHA1 | |
echo "0 0000000000000000000000000000000000000000 ${DST}" > .index-info.tmp | |
git ls-files --stage ${FILE} | sed "s^${FILE}^${DST}^g" >> .index-info.tmp | |
git ls-files --stage ${DST} | sed "s^ 0\t^ 3\t^g" >> .index-info.tmp | |
git update-index --index-info < .index-info.tmp # Repair the conflict | |
git rm ${FILE} # remove local old-named file | |
DST="" # clear DST for next file | |
fi | |
done <<< "$FILES" | |
rm .index-info.tmp | |
echo |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment