Skip to content

Instantly share code, notes, and snippets.

@thehans
Last active August 22, 2022 21:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thehans/5e5976c49054b6ce4f8959d231708d6b to your computer and use it in GitHub Desktop.
Save thehans/5e5976c49054b6ce4f8959d231708d6b to your computer and use it in GitHub Desktop.
#!/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