Skip to content

Instantly share code, notes, and snippets.

@Arakade
Created April 22, 2017 18:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Arakade/237079d9290e0d5c208e17481a7ac0bd to your computer and use it in GitHub Desktop.
Save Arakade/237079d9290e0d5c208e17481a7ac0bd to your computer and use it in GitHub Desktop.
BASH script to port a Git commit from one Git repo to another unrelated Git repo (can run with BASH on Windows)
$ GitPortPatch.sh 22b7f80c22ac1861e945ddf30e30671c46b53d6e
Getting commit message
Getting patch
Getting affected filenames
Getting current status of those files...
Checking them...
Ensuring affected files are ready for patching...
Preparing Assets/TerraVolPack/TerraVol/Scripts/Data/MeshData.cs
Preparing Assets/TerraVolPack/TerraVol/Scripts/Map/Chunk.cs
Apply patch? y/n : y
Patching...
patching file Assets/TerraVolPack/TerraVol/Scripts/Data/MeshData.cs
patching file Assets/TerraVolPack/TerraVol/Scripts/Map/Chunk.cs
Adding affected files to staged group...
Assets/TerraVolPack/TerraVol/Scripts/Data/MeshData.cs
Assets/TerraVolPack/TerraVol/Scripts/Map/Chunk.cs
Commit patch? y/n : y
Committing...
[voxels-generations 2842944] MeshData: validation working (maybe doing extraneous calculations?) (from 22b7f80c22ac1861e945ddf30e30671c46b53d6e)
2 files changed, 80 insertions(+), 25 deletions(-)
Done.
#!/bin/bash
# Process a Git commit from one Git repository into another (unrelated) repository.
# (there are definitely better ways if they are related, e.g. cherry-pick)
# Currently leaves tmp files in /tmp (since it's sometimes handy to check).
# v1.0 2017/04/22 Rupert Key / @Arakade
showHelp() {
echo -e "\n${CmdName} [-h -? -g -a -G -A -1 <DirFrom> -2 <DirTo> ] <SHA1>"
echo -e "\t-h -? : this help"
echo -e "\t-g and -G : -g get patch (default), -G do not get patch"
echo -e "\t-a and -A : -a apply patch (default), -A do not apply patch"
echo -e "\t-1 : set 'from' repository"
echo -e "\t-2 : set 'to' repository"
echo -e "\tSHA1 : commit to port\n"
echo -e "\tYou will be asked before anything is changed."
echo -e "\tIf you wish to tweak a patch before applying,"
echo -e "\t- answer 'n' to not apply,"
echo -e "\t- tweak the files in /tmp/<SHA1>* as needed,"
echo -e "\t- resume without redoing the 'get' with $CmdName -G <SHA1>"
echo -e "\n"
}
checkRepo() {
if [ "XX" == "X${1}X" ] ; then
showHelp
echo -e "ERROR: No Git repository specified as $2\n"
exit 1
fi
if [ ! -d "$1" ] ; then
showHelp
echo -e "ERROR: \"$1\" does not exist (or is not a directory)\n"
exit 1
fi
if [ ! -d "$1/.git" ] ; then
showHelp
echo -e "ERROR: \"$1\" is not a Git repository\n"
exit 1
fi
}
GetPatch() {
cd "$D1"
# Get log for committing at end
echo "Getting commit message"
git log --pretty=%B "${commit}^..${commit}" | awk "NR==1 {printf(\"%s (from %s)\n\", \$0, \"${commit}\")} NR>1" > "$LogFile"
if [ 0 -ne $? ] ; then
echo "Failed to get log"
exit 1
fi
# Get the actual patch
echo "Getting patch"
git diff "${commit}^" "${commit}" > "$PatchFile"
if [ 0 -ne $? ] ; then
echo "Failed to get diff"
exit 1
fi
# Process filenames affected from the patch
echo "Getting affected filenames"
# --- a/Assets/TerraVolPack/TerraVol/Scripts/Data/ActionData.cs
sed -n 's%^--- a/\(.*\)$%\1%p' "$PatchFile" > "$FilenamesFile"
cd "$D2"
# See whether any already modified
echo "Getting current status of those files..."
git status > "$StatusFileBefore"
echo "Checking them..."
AlreadyModified=""
cat "$FilenamesFile" | while read f ; do
if grep -q "$f" "$StatusFileBefore" ; then
# Really changed (not just whitespace)
linesDiff=$(git diff -w "$f" | wc -l)
if [ $linesDiff -ne 0 ] ; then
AlreadyModified="\t$f\n${AlreadyModified}"
fi
fi
done
if [ "XX" != "X${AlreadyModified}X" ] ; then
echo -e "Some affected files already modified. They'll need commiting first:\n$AlreadyModified"
exit 1
fi
# Start actually changing things
echo "Ensuring affected files are ready for patching..."
cat "$FilenamesFile" | while read f ; do
if [ -f "$f" ] ; then
echo -e "\tPreparing $f"
dos2unix -q "$f"
fi
done
}
ApplyPatch() {
read -n 1 -p "Apply patch? y/n : " response
if [ "X${response}X" != "XyX" ] ; then
echo ""
exit 0
fi
cd "$D2"
# Actually apply the patch
echo -e "\nPatching..."
patch -i "$PatchFile" -p 1 -F 10 -N
echo "Adding affected files to staged group..."
cat "$FilenamesFile" | while read f ; do
if [ -f "$f" ] ; then
echo -e "\t$f"
git add "$f"
fi
done
read -n 1 -p "Commit patch? y/n : " response
if [ "X${response}X" != "XyX" ] ; then
echo ". OK, not committing."
echo "Commit with git commit --file \"$LogFile\""
exit 0
fi
echo -e "\nCommitting..."
git commit --file "$LogFile"
echo "Done."
}
##
## MAIN
##
CmdName="$(basename $0)"
D1=""
D2=""
doGetPatch=1
doApplyPatch=1
commit=""
OPTIND=1
while getopts "h?-gaGA1:2:" opt ; do
case "$opt" in
h|\?|-)
showHelp
exit 0
;;
g)
doGetPatch=1
;;
G)
doGetPatch=0
;;
a)
doApplyPatch=1
;;
A)
doApplyPatch=0
;;
1)
D1="$(readlink -m ${OPTARG})"
;;
2)
D2="$(readlink -m ${OPTARG})"
;;
esac
done
shift $((OPTIND-1))
[ "X${1}X" == "X--X" ] && shift
commit="$1"
if [ "X${commit}X" == "XX" ] ; then
showHelp
echo -e "ERROR: No commit supplied\n"
exit 1
fi
checkRepo "$D1" "source"
checkRepo "$D2" "destination"
PatchFile="/tmp/${commit}.patch"
LogFile="/tmp/${commit}.log"
FilenamesFile="/tmp/${commit}.filenames"
StatusFileBefore="/tmp/${commit}-before-status.txt"
[ $doGetPatch -eq 1 ] && GetPatch
[ $doApplyPatch -eq 1 ] && ApplyPatch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment