Skip to content

Instantly share code, notes, and snippets.

@sunsided
Created October 31, 2025 23:13
Show Gist options
  • Select an option

  • Save sunsided/3411ae2cb6c6a57ed1d57382dd75ab25 to your computer and use it in GitHub Desktop.

Select an option

Save sunsided/3411ae2cb6c6a57ed1d57382dd75ab25 to your computer and use it in GitHub Desktop.
Convert bazaar repos from lp directory into git repos in gh directory
#!/usr/bin/env bash
set -Eeuo pipefail
# Migrates Bazaar branches in ./lp/<name> to bare Git repos in ./gh/<name>.git
# If ./lp/<name> is missing (or not a bzr branch), it will "brz branch lp:<name> ./lp/<name>".
#
# Optional env:
# DEFAULT_BRANCH=main # rename 'master' to 'main' and set HEAD accordingly
# ONLY=<comma,separated,names> # process only these names (matching subdirs or Launchpad names)
require() { command -v "$1" >/dev/null 2>&1 || { echo "Missing: $1" >&2; exit 1; }; }
require brz
require git
ROOT="${PWD}"
LP_DIR="${ROOT}/lp"
GH_DIR="${ROOT}/gh"
CACHE_DIR="${ROOT}/.cache-bzr2git"
mkdir -p "$LP_DIR" "$GH_DIR" "$CACHE_DIR"
# Expand list of projects
declare -a names=()
if [[ -n "${ONLY:-}" ]]; then
IFS=',' read -r -a names <<<"$ONLY"
else
while IFS= read -r -d '' path; do
base="$(basename "$path")"
names+=("$base")
done < <(find "$LP_DIR" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z)
fi
if [[ "${#names[@]}" -eq 0 ]]; then
echo "No projects found under $LP_DIR and no ONLY=… given." >&2
exit 1
fi
for name in "${names[@]}"; do
echo "────────────────────────────────────────────────────────────"
echo "Project: $name"
BZR_DIR="${LP_DIR}/${name}"
GIT_DIR="${GH_DIR}/${name}.git"
BZR_MARKS="${CACHE_DIR}/${name}.bzr.marks"
GIT_MARKS="${CACHE_DIR}/${name}.git.marks"
# 1) Ensure we have a local Bazaar branch; if not, try Launchpad.
if [[ ! -d "${BZR_DIR}/.bzr" ]]; then
echo "No .bzr in ${BZR_DIR}; attempting: brz branch lp:${name} ${BZR_DIR}"
mkdir -p "$LP_DIR"
brz branch "lp:${name}" "${BZR_DIR}"
fi
# Quick sanity info
echo "Bazaar info:"
(cd "${BZR_DIR}" && brz info | sed 's/^/ /')
# 2) Prepare bare git target
if [[ ! -d "${GIT_DIR}" ]]; then
echo "Initializing bare git repo: ${GIT_DIR}"
git init --bare "${GIT_DIR}" >/dev/null
fi
# 3) Stream Bazaar → Git with per-project marks (idempotent & re-runnable)
echo "Importing history (this may take a moment)…"
# Create marks files if missing to let fast-import update them
: > "${BZR_MARKS}" || true
: > "${GIT_MARKS}" || true
# Export + import in one pipe
brz fast-export --export-marks="${BZR_MARKS}" "${BZR_DIR}" \
| git --git-dir="${GIT_DIR}" fast-import --export-marks="${GIT_MARKS}"
# 4) Optional: rename default branch (e.g., master → main) and set HEAD
if [[ -n "${DEFAULT_BRANCH:-}" ]]; then
echo "Setting default branch to '${DEFAULT_BRANCH}' (renaming if needed)…"
# Try to rename master → DEFAULT_BRANCH if master exists
if git --git-dir="${GIT_DIR}" show-ref --verify --quiet refs/heads/master; then
if [[ "${DEFAULT_BRANCH}" != "master" ]]; then
git --git-dir="${GIT_DIR}" branch -m master "${DEFAULT_BRANCH}" || true
fi
fi
# Ensure HEAD points at DEFAULT_BRANCH (create it if importer used a different name)
if git --git-dir="${GIT_DIR}" show-ref --verify --quiet "refs/heads/${DEFAULT_BRANCH}"; then
git --git-dir="${GIT_DIR}" symbolic-ref HEAD "refs/heads/${DEFAULT_BRANCH}"
fi
fi
# 5) Report refs
echo "Heads:"
git --git-dir="${GIT_DIR}" for-each-ref --format=' %(refname:short)' refs/heads/ | sort || true
echo "Tags:"
git --git-dir="${GIT_DIR}" for-each-ref --format=' %(refname:short)' refs/tags/ | sort || true
echo "Done: ${GIT_DIR}"
done
echo "All done. Bare Git repos are in: ${GH_DIR}"
echo "Tip: to inspect one repo, run: git clone ./gh/<name>.git"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment