Skip to content

Instantly share code, notes, and snippets.

@leeor
Last active September 19, 2023 18:33
Show Gist options
  • Save leeor/42362866af0a8af69a9e to your computer and use it in GitHub Desktop.
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
#!/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 ..
@marchc
Copy link

marchc commented Mar 5, 2018

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

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