Skip to content

Instantly share code, notes, and snippets.

@davidonlaptop
Last active March 6, 2018 19:34
Show Gist options
  • Save davidonlaptop/68bcabb20e384207a58a885fddd086e5 to your computer and use it in GitHub Desktop.
Save davidonlaptop/68bcabb20e384207a58a885fddd086e5 to your computer and use it in GitHub Desktop.
Merges multiple git repositories into a single one
#!/bin/bash
# See: https://stackoverflow.com/questions/1683531/how-to-import-existing-git-repository-into-another
# https://help.github.com/articles/about-git-subtree-merges/
# https://github.com/git/git/blob/master/contrib/subtree/git-subtree.txt
# https://git-scm.com/book/en/v1/Git-Tools-Subtree-Merging
# git filter-branch
# git read-tree
# https://unix.stackexchange.com/questions/280217/how-to-replay-git-repository-history-into-subdirectory
###########################
# SYNOPSIS
###########################
USAGE="./merge-git-repo.sh <target_repo_url> <child_repo_1> [, ...]"
#
# CAVEATS:
# - Assumes an empty target directory
# - Potential path collision in non-master branches
#
# TODO: how to handle source branches without collisions.
# Start with empty destination repo
# For each source repo
# Clone repo
# Rewrite entire history with path prefix
# Commit, but not push
#
# Clone destination repo
# For each source repo
# Add source remote into destination repo
# For each source branch
# Create local branch (if necessary)
# Replay source branch history into destination
#
# For each source repo branches
# Create a branch matching
function merge_child_repo {
SOURCE_URL="$1"
SOURCE_BASENAME=`basename "$1" ".git"`
SOURCE_REMOTE="${SOURCE_BASENAME}-remote"
SOURCE_BRANCH="${SOURCE_BASENAME}-branch"
echo -e "\n\t*** Fetching ${SOURCE_URL} as remote ${SOURCE_REMOTE}"
git remote add ${SOURCE_REMOTE} ${SOURCE_URL}
git fetch ${SOURCE_REMOTE}
git checkout -b ${SOURCE_BRANCH} ${SOURCE_REMOTE}/master
echo -e "\n\t*** Move all git files to subfolder ${SOURCE_BASENAME}/"
mkdir -p ${SOURCE_BASENAME}/
FILES=`git ls-tree ${SOURCE_REMOTE}/master --name-only`
for FILE in $FILES
do
echo -ne "\t${FILE}";
git mv $FILE ${SOURCE_BASENAME}/
done
echo -e "\n\t*** Done.\n"
git commit -m "Moved child repository ${SOURCE_URL} to subdirectory ${SOURCE_BASENAME}"
echo -e "\n\t*** Merging branch back into master"
git checkout master
git merge ${SOURCE_BRANCH} --allow-unrelated-histories # should add ZZZ/ to master
git commit
git remote rm ${SOURCE_REMOTE}
git branch -d ${SOURCE_BRANCH} # to get rid of the extra branch before pushing
#git push # if you have a remote, that is
}
##########################
# CONFIGURATION
##########################
if (( $# < 2 )); then
echo $USAGE
exit 1
fi
##########################
# MAIN
##########################
DEST_URL="$1"
echo -e "\n\t*** Fetching ${DEST_URL} to dest/"
rm -rf dest/
git clone ${DEST_URL} dest/
cd dest/
# Loop through source URLs
shift
for SOURCE_URL in "$@" ; do
#echo "$SOURCE_URL"
merge_child_repo ${SOURCE_URL}
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment