Last active
March 6, 2018 19:34
-
-
Save davidonlaptop/68bcabb20e384207a58a885fddd086e5 to your computer and use it in GitHub Desktop.
Merges multiple git repositories into a single one
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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