Skip to content

Instantly share code, notes, and snippets.

@jwarkentin
Created March 8, 2024 09:43
Show Gist options
  • Save jwarkentin/0cd53beda345afde53cf1464237f7f32 to your computer and use it in GitHub Desktop.
Save jwarkentin/0cd53beda345afde53cf1464237f7f32 to your computer and use it in GitHub Desktop.
Migrate stashes and branches from an old git repo to a new clone
#!/usr/bin/env bash
TMP_SRC_REMOTE="git-stash-copy-source"
TMP_TARGET_REMOTE="git-stash-copy-target"
FROMDIR="$( cd "$1" && pwd )"
TODIR="$( cd "$2" && pwd )"
if ! cd "$FROMDIR" && git rev-parse --git-dir 2> /dev/null ; then
echo "Error: \"$FROMDIR\" is not a git repository"
exit 1
fi
if ! cd "$TODIR" && git rev-parse --git-dir 2> /dev/null ; then
echo "Error: \"$TODIR\" is not a git repository"
exit 1
fi
UNTRACKED_FILES="$(git -C "$TODIR" ls-files --others --exclude-standard | wc -l)"
UNSTAGED_FILES="$(git -C "$TODIR" diff --name-only | wc -l)"
STAGED_FILES="$(git -C "$TODIR" diff --cached --name-only | wc -l)"
if [ $UNTRACKED_FILES -gt 0 ] || [ $UNSTAGED_FILES -gt 0 ] || [ $STAGED_FILES -gt 0 ]; then
echo "Error: \"$TODIR\" has uncommitted changes"
exit 1
fi
printf "From Dir: $FROMDIR\nTo Dir: $TODIR\n"
read -p "Continue? [Y/n] " -n 1 -r
printf "\n"
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
printf "\n"
echo "Adding old repository as temporary remote \"$TMP_SRC_REMOTE\""
git -C "$TODIR" remote add $TMP_SRC_REMOTE "$FROMDIR" > /dev/null 2>&1
CURRENT_BRANCH="$(git -C "$TODIR" branch --show-current)"
CURRENT_HASH="$(git -C "$TODIR" rev-parse HEAD)"
CURRENT_REF=$([[ -z "$CURRENT_BRANCH" ]] && echo "$CURRENT_HASH" || echo "$CURRENT_BRANCH")
NUM_STASHES="$(git -C "$FROMDIR" stash list | wc -l)"
for i in $(seq $(($NUM_STASHES - 1)) 0); do
printf "\n"
echo "Copying stash@{$i}"
STASH_MSG="$(git -C "$FROMDIR" log --format=%B -n 1 stash@{$i})"
PARENT_COMMIT="$(git -C "$FROMDIR" rev-parse stash@{$i}^)"
PATCH="$(git -C "$FROMDIR" stash show -p stash@{$i})"
# git -C "$TODIR" checkout "$PARENT_COMMIT" > /dev/null 2>&1
if ! git -C "$TODIR" checkout "$PARENT_COMMIT" > /dev/null 2>&1 ; then
echo "Could not find parent commit in local copy. Fetching from remote."
if ! git -C "$TODIR" -c uploadpack.allowReachableSHA1InWant fetch --depth=1 $TMP_SRC_REMOTE "$PARENT_COMMIT" ; then
echo "Error: Could not fetch from remote. Skipping stash."
else
git -C "$TODIR" checkout "$PARENT_COMMIT" > /dev/null 2>&1 || exit 1
fi
fi
echo "$PATCH" | git -C "$TODIR" apply
git -C "$TODIR" add .
git -C "$TODIR" stash save -m "$STASH_MSG"
done
git -C "$TODIR" checkout "$CURRENT_REF" > /dev/null 2>&1
printf "\n"
echo "Deleting temporary remote \"$TMP_SRC_REMOTE\""
git -C "$TODIR" remote remove $TMP_SRC_REMOTE
printf "\n"
read -p "Would you like to copy your branches? [Y/n] " -n 1 -r
printf "\n"
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Adding new repository as temporary remote \"$TMP_TARGET_REMOTE\""
git -C "$FROMDIR" remote add $TMP_TARGET_REMOTE "$TODIR" > /dev/null 2>&1
BRANCHES="$(git -C "$FROMDIR" for-each-ref --format='%(refname:short)' refs/heads/)"
echo "$BRANCHES"
for BRANCH in $BRANCHES; do
if [[ $BRANCH != "main" && $BRANCH != "master" ]]; then
printf "\n"
echo "Copying branch \"$BRANCH\""
git -C "$FROMDIR" push $TMP_TARGET_REMOTE "$BRANCH"
fi
done
echo "Deleting temporary remote \"$TMP_TARGET_REMOTE\""
git -C "$FROMDIR" remote remove $TMP_TARGET_REMOTE
fi
printf "\n"
echo "Done."
@jwarkentin
Copy link
Author

After downloading the file, usage is simple:

chmod u+x ./migrate-git-repo.sh
./migrate-git-repo.sh <path-to-old-repo> <path-to-new-repo>

Relative paths are fine. It will prompt to confirm before starting and ask whether you want to copy branches or just copy stashes.

@paul-eeoc
Copy link

I tried this and it didn't work. ah well.

@paul-eeoc
Copy link

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment