Skip to content

Instantly share code, notes, and snippets.

@coderxin
Created December 22, 2023 13:34
Show Gist options
  • Save coderxin/c5b341cd41e1d0ab52e5365ee270ad05 to your computer and use it in GitHub Desktop.
Save coderxin/c5b341cd41e1d0ab52e5365ee270ad05 to your computer and use it in GitHub Desktop.
git-split: Script to duplicate a file while preserving git line history
#!/bin/sh
# Script to duplicate a file while preserving git line history
#
# This could be useful if you want two copies of a file,
# say, one where you are doing a bunch of disruptive work,
# and another that remains largely unchanged. The project continues to use the old,
# stable version, but there’s a feature flag to switch to the new one.
# Eventually, you’ll make the new one the default version.
#
# NOTE: Based on https://devblogs.microsoft.com/oldnewthing/20190919-00/?p=102904
set -euo pipefail
usage() {
echo "Usage: git-split <originalFile> <copyName> [customCommitMessage]"
echo "Duplicate a file while preserving git line history"
echo "Optionally, provide custom commit message as explanation."
exit 1
}
if [[ "$#" -lt 2 ]] || [[ "$#" -gt 3 ]]; then
usage
fi
originalFile=$1
copyName=$2
defaultCommitMsg="to preserve original file history (see: script/git-split)."
explanationCommitMsg=${3:-$defaultCommitMsg}
branchName=copy-with-history-$1
# Method: Create a branch where the desired new file appears
# to have been created via a rename of the original file.
# And then restore the original file.
commit_message() {
if [ "$1" ]; then
echo "${1} $2"
else
echo "$2"
fi
}
# Extract XX-##### tag for commit messages
BRANCH_TAG=$(git rev-parse --abbrev-ref HEAD | grep -Eo '[A-Z]+-[0-9]+' || true)
# Create and switch to a branch
git checkout -b $branchName
# Make the duplicate of the original file
git mv $originalFile $copyName
summaryCommitMsg=$(commit_message "$BRANCH_TAG" "Duplicate $originalFile to $copyName")
git commit -m "$summaryCommitMsg" -m "$explanationCommitMsg"
# Bring back the original file
git checkout HEAD~ $originalFile
summaryCommitMsg=$(commit_message "$BRANCH_TAG" "Restore duplicated $originalFile")
git commit -m "$summaryCommitMsg" -m "$explanationCommitMsg"
# Switch back to the previous branch
# `-` (dash) is a special symbol that Git interprets
# as a reference to the previous branch you were working on.
git checkout -
# Merge created branch back into source
summaryCommitMsg=$(commit_message "$BRANCH_TAG" "Merge branch $branchName")
git merge --no-ff $branchName \
-m "$summaryCommitMsg" -m "$explanationCommitMsg"
# Delete the branch
git branch -D $branchName
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment