Created
July 14, 2016 12:33
-
-
Save cedricwalter/21b34178d552c077f5d2276a242872bb to your computer and use it in GitHub Desktop.
The script adds remotes for every project and then merges in every branch and tag. These are renamed to have the origin project name as a prefix Usage: mergeGitRepositories.sh <new_project> <my_repo_urls.lst> Where <new_project> is the name of the new project to create <my_repo_urls.lst> is a file contaning the URLs to the respositories which ar…
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 | |
# | |
################################################################################ | |
## Script to merge multiple git repositories into a new repository | |
## - The new repository will contain a folder for every merged repository | |
## - The script adds remotes for every project and then merges in every branch | |
## and tag. These are renamed to have the origin project name as a prefix | |
## | |
## Usage: mergeGitRepositories.sh <new_project> <my_repo_urls.lst> | |
## - where <new_project> is the name of the new project to create | |
## - and <my_repo_urls.lst> is a file contaning the URLs to the respositories | |
## which are to be merged on separate lines. | |
## | |
## Version: 0.2.0 Created: 2015-06-17 Robert von Burg [email protected] | |
## Version: 0.2.1 Created: 2016-07-14 Cédric Walter www.cedricwalter.com | |
## | |
################################################################################ | |
# | |
# disallow using undefined variables | |
shopt -s -o nounset | |
# Script variables | |
declare SCRIPT_NAME="${0##*/}" | |
declare SCRIPT_DIR="$(cd ${0%/*} ; pwd)" | |
declare ROOT_DIR="$PWD" | |
# Detect proper usage | |
if [ "$#" -ne "2" ] ; then | |
echo -e "ERROR: Usage: $0 <new_project> <my_repo_urls.lst>" | |
exit 1 | |
fi | |
# Script functions | |
function failed() { | |
echo -e "ERROR: Merging of projects failed:" | |
echo -e "$1" | |
exit 1 | |
} | |
function commit_merge() { | |
current_branch="$(git symbolic-ref HEAD 2>/dev/null)" | |
CHANGES=$(git status | grep "working directory clean") | |
MERGING=$(git status | grep "merging") | |
if [[ "$CHANGES" != "" ]] && [[ "$MERGING" == "" ]] ; then | |
echo -e "INFO: No commit required." | |
else | |
echo -e "INFO: Committing ${sub_project}..." | |
if ! git commit --quiet -m "[Project] Merged branch '$1' of ${sub_project}" ; then | |
failed "Failed to commit merge of branch '$1' of ${sub_project} into ${current_branch}" | |
fi | |
fi | |
} | |
## Script variables | |
PROJECT_NAME="${1}" | |
PROJECT_PATH="${ROOT_DIR}/${PROJECT_NAME}" | |
REPO_FILE="${2}" | |
REPO_URL_FILE="${ROOT_DIR}/${REPO_FILE}" | |
# Make sure the REPO_URL_FILE exists | |
if [ ! -e "${REPO_URL_FILE}" ] ; then | |
echo -e "ERROR: Repo file ${REPO_URL_FILE} does not exist!" | |
exit 1 | |
fi | |
# Make sure the required directories don't exist | |
if [ -e "${PROJECT_PATH}" ] ; then | |
echo -e "ERROR: Project ${PROJECT_NAME} already exists!" | |
exit 1 | |
fi | |
# create the new project | |
echo -e "INFO: Creating new git repository ${PROJECT_NAME}..." | |
echo -e "====================================================" | |
cd ${ROOT_DIR} | |
mkdir ${PROJECT_NAME} | |
cd ${PROJECT_NAME} | |
git init | |
echo "Initial Commit" > initial_commit | |
# Since this is a new repository we need to have at least one commit | |
# thus were we create temporary file, but we delete it again. | |
# Deleting it guarantees we don't have conflicts later when merging | |
git add initial_commit | |
git commit --quiet -m "[Project] Initial Master Repo Commit" | |
git rm --quiet initial_commit | |
git commit --quiet -m "[Project] Initial Master Repo Commit" | |
echo | |
# Merge all projects into th branches of this project | |
echo -e "INFO: Merging projects into new repository..." | |
echo -e "====================================================" | |
for url in $(cat ${REPO_URL_FILE}) ; do | |
# extract the name of this project | |
export sub_project=${url##*/} | |
export sub_project=${sub_project%.*} | |
echo -e "Work on Project: ${sub_project}" | |
echo -e "Work on URL: ${url}" | |
echo -e "----------------------------------------------------" | |
# Fetch the project | |
echo "INFO: Fetching ${sub_project}" | |
echo "git remote add ${sub_project} ${url}" | |
git remote add ${sub_project} ${url} | |
echo git fetch --tags ${sub_project} | |
if ! git fetch --tags ${sub_project} 2>/dev/null ; then | |
failed "Failed to fetch project ${sub_project}" | |
fi | |
if ! git fetch --all 2>/dev/null ; then | |
failed "Failed to fetch project ${sub_project}" | |
fi | |
# add remote branches | |
echo -e "INFO: Creating local branches for ${sub_project}..." | |
while read branch ; do | |
branch_ref=$(echo $branch | tr " " "\t" | cut -f 1) | |
branch_name=$(echo $branch | tr " " "\t" | cut -f 2 | cut -d / -f 3-) | |
echo -e "INFO: Creating branch ${branch_name}..." | |
# create and checkout new merge branch off of master | |
git checkout --quiet -b "${sub_project}/${branch_name}" master | |
git reset --hard --quiet | |
git clean -d --force --quiet | |
# Merge the project | |
echo -e "INFO: Merging ${sub_project}..." | |
if ! git merge --allow-unrelated-histories --quiet --no-commit "remotes/${sub_project}/${branch_name}" 2>/dev/null ; then | |
failed "Failed to merge branch 'remotes/${sub_project}/${branch_name}' from ${sub_project}" | |
fi | |
# And now see if we need to commit (maybe there was a merge) | |
commit_merge "${sub_project}/${branch_name}" | |
# relocate projects files into own directory | |
if [ "$(ls)" == "${sub_project}" ] ; then | |
echo -e "WARN: Not moving files in branch ${branch_name} of ${sub_project} as already only one root level." | |
else | |
echo -e "INFO: Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..." | |
mkdir ${sub_project} | |
for dir in ./* ; do | |
dir=${dir%*/} | |
echo "list: ${dir}" | |
if [[ "$dir" == "${sub_project}" ]] || | |
[[ "$dir" == "." ]] || | |
[[ "$dir" == ".." ]] ; then | |
continue | |
fi | |
echo -E "git mv -k "$dir" "${sub_project}/"" | |
git mv -k "$dir" "${sub_project}/" | |
done | |
git mv -k .git "${sub_project}/" | |
# commit the moving | |
if ! git commit --quiet -m "[Project] Move ${sub_project} files into sub directory" ; then | |
failed "Failed to commit moving of ${sub_project} files into sub directory" | |
fi | |
fi | |
echo | |
done < <(git ls-remote --heads ${sub_project}) | |
# checkout master of sub probject | |
if ! git checkout "${sub_project}/master" 2>/dev/null ; then | |
failed "sub_project ${sub_project} is missing master branch!" | |
fi | |
# copy remote tags | |
echo -e "INFO: Copying tags for ${sub_project}..." | |
while read tag ; do | |
tag_ref=$(echo $tag | tr " " "\t" | cut -f 1) | |
tag_name=$(echo $tag | tr " " "\t" | cut -f 2 | cut -d / -f 3) | |
# hack for broken tag names where they are like 1.2.0^{} instead of just 1.2.0 | |
tag_name="${tag_name%%^*}" | |
tag_new_name="${sub_project}/${tag_name}" | |
echo -e "INFO: Copying tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}..." | |
if ! git tag "${tag_new_name}" "${tag_ref}" 2>/dev/null ; then | |
echo -e "WARN: Could not copy tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}" | |
fi | |
done < <(git ls-remote --tags ${sub_project}) | |
# Remove the remote to the old project | |
echo -e "INFO: Removing remote ${sub_project}..." | |
git remote rm ${sub_project} | |
echo | |
done | |
# Now merge all project master branches into new master | |
git checkout --quiet master | |
echo -e "INFO: Merging projects master branches into new repository..." | |
echo -e "====================================================" | |
for url in $(cat ${REPO_URL_FILE}) ; do | |
# extract the name of this project | |
export sub_project=${url##*/} | |
sub_project=${sub_project%*.git} | |
echo -e "INFO: Merging ${sub_project}..." | |
if ! git merge --quiet --no-commit "${sub_project}/master" 2>/dev/null ; then | |
failed "Failed to merge branch ${sub_project}/master into master" | |
fi | |
# And now see if we need to commit (maybe there was a merge) | |
commit_merge "${sub_project}/master" | |
echo | |
done | |
# Done | |
cd ${ROOT_DIR} | |
echo -e "INFO: Done." | |
echo | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment