Last active
September 19, 2023 18:33
-
-
Save leeor/42362866af0a8af69a9e to your computer and use it in GitHub Desktop.
A shell script to merge one git repository into another, including all branches and tags
This file contains 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 | |
set -e | |
# Merge this repo | |
SRCREPO=$1; shift | |
# into this one | |
DSTREPO=$1; shift | |
# at this path | |
BASENAME=$1; shift | |
# and apply this patch when done | |
PATCHFILE="" | |
if [[ $1 != "" ]]; then | |
PATCHFILE=$1; shift | |
fi | |
SRCNAME=$(basename ${SRCREPO} | sed -e 's/\(.*\)\..*/\1/') | |
if [[ $SRCNAME == "" ]]; then | |
# most likely, passed as a path argument | |
_dirname=$(dirname ${SRCREPO}) | |
SRCNAME=${_dirname##*/} | |
fi | |
DSTNAME=$(basename ${DSTREPO} | sed -e 's/\(.*\)\..*/\1/') | |
if [[ $DSTNAME == "" ]]; then | |
# most likely, passed as a path argument | |
_dirname=$(dirname ${DSTREPO}) | |
DSTNAME=${_dirname##*/} | |
fi | |
echo "Cloning source repository ${SRCREPO} into ${SRCNAME}" | |
git clone ${SRCREPO} ${SRCNAME} | |
cd ${SRCNAME} | |
SRCPATH=$(pwd) | |
SRCBRANCHES=$(git branch -r --no-color | ack -v HEAD | sed -e 's/\s*origin\/\(.*\)/\1/') | |
echo "Will merge branches: ${SRCBRANCHES}" | |
# checkout each branch so we have a local copy of it after removing 'origin' | |
for branch in $SRCBRANCHES; do | |
git checkout ${branch} | |
done | |
echo "Removing 'origin'" | |
git remote remove origin | |
echo "Moving all content into ${BASENAME} subfolder" | |
NESTEDAT="" | |
if [[ ${BASENAME##*/} != ${BASENAME%/*} ]]; then | |
# moving into a nested subfolder, need to create the path leading to it | |
# before each move | |
NESTEDAT=${BASENAME%/*} | |
fi | |
git filter-branch -f --prune-empty --tag-name-filter cat --tree-filter "mkdir -p .sub && mv * .sub && if [ -n \"${NESTEDAT}\" ]; then mkdir -p \"${NESTEDAT}\"; fi && mv .sub ${BASENAME}" -- --all | |
for branch in $SRCBRANCHES; do | |
git checkout ${branch} | |
if [[ -f .gitignore ]]; then | |
echo "Moving .gitignore" | |
git mv .gitignore ${BASENAME} | |
git commit -m "moved .gitignore" | |
fi | |
if [[ -f .gitmodules ]]; then | |
echo "Removing submodule directories" | |
for module in $(git submodule status | cut -d ' ' -f 2); do | |
echo "Removing $module" | |
git rm -rf $module | |
done | |
git commit -m "removed submodule paths" | |
fi | |
done | |
SKIPCHECKOUT=0 | |
cd .. | |
if [[ -d ${DSTNAME} ]]; then | |
echo "Destination repo exists at ${DSTNAME}" | |
SKIPCHECKOUT=1 | |
else | |
echo "Cloning destination repository ${DSTREPO} into ${DSTNAME}" | |
git clone ${DSTREPO} ${DSTNAME} | |
fi | |
cd ${DSTNAME} | |
if [[ $SKIPCHECKOUT == 0 ]]; then | |
# try to checkout needed branches (and create those that do not exist) | |
for branch in $SRCBRANCHES; do | |
echo "Checking out branch ${branch}" | |
if ! git checkout ${branch}; then | |
echo "Branch ${branch} does not exist in ${SRCNAME}, creating" | |
git checkout -b ${branch} | |
fi | |
# back to master before the next iteration | |
git checkout master | |
done | |
fi | |
echo "Removing 'origin'" | |
# just to be on the safe side, do this even if the repo was not cloned by us | |
git remote remove origin || true | |
echo "Adding local source repository as a remote" | |
git remote add merge-repo ${SRCPATH}/.git | |
echo "Fetching source repository's objects" | |
git remote update | |
echo "Merging source repository" | |
for branch in $SRCBRANCHES; do | |
# Switch to the branch if it exists, create a new one if it doesn't | |
echo "Merging branch ${branch}" | |
git checkout ${branch} | |
git merge -m "Merged branch ${branch} of ${SRCNAME}" -s recursive --strategy-option=ours merge-repo/${branch} | |
done | |
echo "Removing 'merge-repo' remote" | |
git remote remove merge-repo | |
git checkout master | |
if [[ ${PATCHFILE} != "" ]]; then | |
echo "Patching..." | |
git apply ../${PATCHFILE} || true | |
echo "Check patch results, commit with 'git commit -am \"updated references to ${DSTNAME} location\"'" | |
fi | |
echo "Use 'git remote add origin ${SRCREPO}' to add the 'origin' remote back in ${SRCPATH}" | |
cd .. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For anyone using this couple of points that might assist:
Line 35 you could substitute grep for ack if you don't have ack installed
Line 110 later versions of git don't out of the box support the merging of unrelated histories. To solve add this flag to the merge command --allow-unrelated-histories
Usage is basically:
repo-merge-sh
Good script that works well