Skip to content

Instantly share code, notes, and snippets.

@creasty
Last active October 5, 2023 03:11
Show Gist options
  • Save creasty/cb1b42d21fc85edc0bd41fb335af4b62 to your computer and use it in GitHub Desktop.
Save creasty/cb1b42d21fc85edc0bd41fb335af4b62 to your computer and use it in GitHub Desktop.
Merge two git repositories while perfectly preserving all commit histories
#!/usr/bin/env bash
set -euo pipefail
# shellcheck disable=1091
source "$(dirname "$0")/shared.sh"
# Funtions
#-----------------------------------------------
install_git_filter_repo() {
local bin_dir="$TMP_DIR/bin"
export PATH="$bin_dir:$PATH"
if command -v 'git-filter-repo' > /dev/null 2>&1; then
echo "Skipping (already installed)"
return 0
fi
[ -d "$bin_dir" ] || mkdir -p "$bin_dir"
curl -L https://raw.githubusercontent.com/newren/git-filter-repo/main/git-filter-repo -o "$bin_dir/git-filter-repo"
chmod +x "$bin_dir/git-filter-repo"
}
remove_submodule() {
local module="$1"
subsection "$module"
local module_dir="$ROOT_DIR/$module"
if ! [ -d "$module_dir" ]; then
echo "Skipping (not found)"
return 0
fi
git -C "$ROOT_DIR" rm --cached "$module_dir"
git -C "$ROOT_DIR" config -f "$ROOT_DIR/.git/config" --remove-section "submodule.$module"
git -C "$ROOT_DIR" config -f "$ROOT_DIR/.gitmodules" --remove-section "submodule.$module"
rm -rf "$module_dir"
rm -rf "$ROOT_DIR/.git/modules/$module"
git -C "$ROOT_DIR" add -A
git -C "$ROOT_DIR" commit -m "Remove submodule: $module"
}
cleanup_root() {
git filter-repo --force --invert-paths \
--path 'general-api' --path 'receipt-api' --path '.gitmodules'
}
clone_module() {
local module="$1"
local repo_url="$2"
subsection "$module"
local module_dir="$TMP_DIR/$module"
if [ -d "$module_dir" ]; then
echo "Skipping (already cloned)"
return 0
fi
git clone -b "$WORK_BRANCH" "$repo_url" "$module_dir"
}
hoist_module() {
local module="$1"
local module_dir="$TMP_DIR/$module"
subsection "$module: Rewriting histories"
if [ -d "$module_dir/$module" ]; then
echo "Skipping (already rewritten)"
else
git -C "$module_dir" filter-repo --path-rename ":$module/" --tag-rename ":$module-"
fi
subsection "$module: Updating remote"
git -C "$ROOT_DIR" remote remove "$module" || echo "Ignored"
git -C "$ROOT_DIR" remote add -f "$module" "$module_dir"
subsection "$module: Merging"
git -C "$ROOT_DIR" merge --allow-unrelated-histories "$module/$WORK_BRANCH"
}
# Main
#-----------------------------------------------
main() {
mkdir -p "$TMP_DIR"
section 'Install git-filter-repo'
install_git_filter_repo
section 'Remove submodules'
for_each_repo remove_submodule
section 'Clean up root'
cleanup_root
section 'Clone modules'
for_each_repo clone_module
section 'Hoist modules'
for_each_repo hoist_module
}
main
#!/usr/bin/env bash
set -euo pipefail
# shellcheck disable=1091
source "$(dirname "$0")/shared.sh"
update_submodule() {
local module="$1"
local module_dir="$ROOT_DIR/$module"
subsection "$module"
git -C "$module_dir" checkout "$WORK_BRANCH"
git -C "$module_dir" pull --ff origin "$WORK_BRANCH"
}
create_commit() {
git -C "$ROOT_DIR" add -A
git -C "$ROOT_DIR" commit -m 'Update submodules'
}
main() {
section 'Update submodule'
for_each_repo update_submodule
section 'Create commit'
create_commit
}
main
#!/usr/bin/env bash
# shellcheck disable=2034
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
TOOLS_DIR="$ROOT_DIR/poc-tools"
TMP_DIR="$TOOLS_DIR/tmp"
WORK_BRANCH=develop-monorepo
# Helpers
#-----------------------------------------------
section() {
printf "\e[33m==> %s\e[0m\n" "$*"
}
subsection() {
printf "\e[34m--> %s\e[0m\n" "$*"
}
# Functions
#-----------------------------------------------
for_each_repo() {
local cmd="$1"
for module in general-api receipt-api; do
$cmd "$module" "https://github.com/bw-company/henry-$module.git"
done
}
@creasty
Copy link
Author

creasty commented Mar 24, 2023

Screen Shot 2023-03-24 at 22 52 48

pull

$ ./poc-tools/pull

全ての submodule を最新の monorepo branch でチェックアウトします。

submodule の ref 更新用のコミットも自動で作成します。

finalize

$ ./poc-tools/finalize

Warning 不可逆操作なので注意して実行してください
ディレクトリ構成はそのまま、submodule になっている general-api, receipt-api を monorepo-poc のリポジトリにそれぞれの履歴を保持した状態で完全にマージします。

  • monorepo-poc 上の commit history からも submodule の存在が消されます
  • general-api, receipt-api 上の branch, tag 名にはそれぞれのモジュール名が prefix として付きます

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