Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to ls-remote (branches, tags), copy tags #1

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 42 additions & 57 deletions tomono.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# script from bash instead of executing it.

${DEBUGSH:+set -x}
if [[ "$BASH_SOURCE" == "$0" ]]; then
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
is_script=true
set -eu -o pipefail
else
Expand All @@ -21,46 +21,23 @@ fi
# Default name of the mono repository (override with envvar)
: "${MONOREPO_NAME=core}"

# Monorepo directory
monorepo_dir="$PWD/$MONOREPO_NAME"



##### FUNCTIONS

# Silent pushd/popd
pushd () {
command pushd "$@" > /dev/null
}

popd () {
command popd "$@" > /dev/null
}

function read_repositories {
sed -e 's/#.*//' | grep .
}

# Simply list all files, recursively. No directories.
function ls-files-recursive {
find . -type f | sed -e 's!..!!'
}

# List all branches for a given remote
function remote-branches {
# With GNU find, this could have been:
#
# find "$dir/.git/yada/yada" -type f -printf '%P\n'
#
# but it's not a real shell script if it's not compatible with a 14th
# century OS from planet zorploid borploid.

# Get into that git plumbing. Cleanest way to list all branches without
# text editing rigmarole (hard to find a safe escape character, as we've
# noticed. People will put anything in branch names).
pushd "$monorepo_dir/.git/refs/remotes/$1/"
ls-files-recursive
popd
git ls-remote --heads --refs "$1" | sed 's#.*refs/heads/##'
}

# List all tags for a given remote
function remote-tags {
git ls-remote --tags --refs "$1" | sed 's#.*refs/tags/##'
}

# Wrapper for fetching further information about a tag
function tag-info {
git for-each-ref "refs/tags/${1}" --format="%(${2})"
}

# Create a monorepository in a directory "core". Read repositories from STDIN:
Expand All @@ -78,44 +55,55 @@ function create-mono {
pushd "$MONOREPO_NAME"
else
if [[ -d "$MONOREPO_NAME" ]]; then
echo "Target repository directory $MONOREPO_NAME already exists." >&2
echo "Target repository directory ${MONOREPO_NAME} already exists." >&2
return 1
fi
mkdir "$MONOREPO_NAME"
pushd "$MONOREPO_NAME"
git init
fi

# This directory will contain all final tag refs (namespaced)
mkdir -p .git/refs/namespaced-tags

read_repositories | while read repo name folder; do

read_repositories | while read -r repo name folder; do
if [[ -z "$name" ]]; then
echo "pass REPOSITORY NAME pairs on stdin" >&2
return 1
elif [[ "$name" = */* ]]; then
echo "Forward slash '/' not supported in repo names: $name" >&2
echo "Forward slash '/' not supported in repo names: ${name}" >&2
return 1
fi

if [[ -z "$folder" ]]; then
folder="$name"
fi

echo "Merging in $repo.." >&2
echo "Merging in ${repo}..." >&2
git remote add "$name" "$repo"
echo "Fetching $name.." >&2
echo "Fetching ${name}..." >&2
git fetch -q "$name"

# Now we've got all tags in .git/refs/tags: put them away for a sec
if [[ -n "$(ls .git/refs/tags)" ]]; then
mv .git/refs/tags ".git/refs/namespaced-tags/$name"
fi
# Copy tags including their proper content
remote-tags "$name" | while read -r tag; do
echo "Copying tag ${tag} over to ${name}/${tag}..."

(
GIT_COMMITTER_NAME="$(tag-info "$tag" "taggername")"
GIT_COMMITTER_EMAIL="$(tag-info "$tag" "taggeremail")"
GIT_COMMITTER_DATE="$(tag-info "$tag" "taggerdate")"

contents=$(tag-info "$tag" "contents")
if [[ -n "$contents" ]]; then
git tag -a -m "$contents" "${name}/${tag}" "${tag}^{}"
else
git tag "${name}/${tag}" "${tag}^{}"
fi
)

git tag -d "$tag"
done

# Merge every branch from the sub repo into the mono repo, into a
# branch of the same name (create one if it doesn't exist).
remote-branches "$name" | while read branch; do
remote-branches "$name" | while read -r branch; do
if git rev-parse -q --verify "$branch"; then
# Branch already exists, just check it out (and clean up the working dir)
git checkout -q "$branch"
Expand All @@ -128,17 +116,14 @@ function create-mono {
git rm -rfq --ignore-unmatch .
git commit -q --allow-empty -m "Root commit for $branch branch"
fi
git merge -q --no-commit -s ours "$name/$branch" --allow-unrelated-histories
git read-tree --prefix="$folder/" "$name/$branch"
git commit -q --no-verify --allow-empty -m "Merging $name to $branch"

git merge -q --no-commit -s ours "${name}/${branch}" --allow-unrelated-histories
git read-tree --prefix="${folder}/" "${name}/${branch}"
git commit -q --no-verify --allow-empty -m "Merge branch '${name}/${branch}' into '${branch}'"
done
done

# Restore all namespaced tags
rm -rf .git/refs/tags
mv .git/refs/namespaced-tags .git/refs/tags

git checkout -q master
git checkout -q "$(git config --default "main" --get init.defaultBranch)"
git checkout -q .
}

Expand Down