fuzzyworld

fuzzyworld-tools / files / lib / repoint.sh

repoint.sh

Blame: the commit that last changed each line · file · history

b75ceb4e 2026-10-27 Pavel Safronov1#!/bin/bash
2# fw repoint: move working copies from GitLab to DreamHost. Changes only remotes and tracking.
3#
4# fw repoint [DIR] [--https] [--dry-run]
5#
6# For every git repo directly inside DIR (default: current directory) whose `origin` is
7# gitlab.com/PavelSafronov/<repo> (or the old gitlab.com/fuzzy-rp/<repo>):
8# origin -> renamed `gitlab`; new origin -> dh:git/<repo>.git (needs the `dh` ssh alias)
9# with --https: new origin -> https://code.fuzzyworld.net/<repo>.git (read-only; public repos only)
10# then fetches, and points each branch that tracked a GitLab branch at the same branch on origin.
11# Uncommitted work, HEAD and stashes are fingerprinted before and after; a difference is reported.
12set -u
13DIR=.; HTTPS=false; DRY=false
14for a in "$@"; do
15 case "$a" in
16 --https) HTTPS=true ;;
17 --dry-run) DRY=true ;;
18 -*) echo "fw repoint: unknown option $a" >&2; exit 2 ;;
19 *) DIR=$a ;;
20 esac
21done
22
23fp() { { git -C "$1" status --porcelain=v1 -uall; git -C "$1" rev-parse HEAD; git -C "$1" symbolic-ref -q HEAD; git -C "$1" stash list; } 2>/dev/null | shasum | cut -c1-12; }
24
25ok=0; skipped=0; failed=0
26for d in "$DIR"/*/; do
27 d=${d%/}
28 [ -d "$d/.git" ] || continue
29 url=$(git -C "$d" remote get-url origin 2>/dev/null) || continue
30 repo=$(printf '%s' "$url" | sed -nE 's#^.*gitlab\.com[:/](PavelSafronov|fuzzy-rp)/([^/]+)/?$#\2#p' | sed 's/\.git$//')
31 [ -n "$repo" ] || continue
32 name=$(basename "$d")
33 if $HTTPS; then
34 new="https://code.fuzzyworld.net/$repo.git"
35 if ! git ls-remote --exit-code "$new" HEAD >/dev/null 2>&1; then
36 echo "skip $name: $repo is not public on code.fuzzyworld.net"; skipped=$((skipped+1)); continue
37 fi
38 else
39 new="dh:git/$repo.git"
40 fi
41 if git -C "$d" remote get-url gitlab >/dev/null 2>&1; then
42 echo "skip $name: already has a remote named gitlab"; skipped=$((skipped+1)); continue
43 fi
44 if $DRY; then echo "would $name: origin $url -> $new"; ok=$((ok+1)); continue; fi
45
46 before=$(fp "$d")
47 git -C "$d" remote rename origin gitlab
48 git -C "$d" remote add origin "$new"
49 if ! git -C "$d" fetch --quiet origin; then
50 echo "FAIL $name: fetch from $new failed (remotes changed; run 'git -C $d fetch origin' to retry)"; failed=$((failed+1)); continue
51 fi
52 moved=""
53 for b in $(git -C "$d" for-each-ref --format='%(refname:short)' refs/heads); do
54 [ "$(git -C "$d" config --get "branch.$b.remote")" = gitlab ] || continue
55 m=$(git -C "$d" config --get "branch.$b.merge" | sed 's#^refs/heads/##')
56 git -C "$d" rev-parse -q --verify "refs/remotes/origin/$m" >/dev/null &&
57 git -C "$d" branch --quiet --set-upstream-to="origin/$m" "$b" && moved="$moved $b"
58 done
59 after=$(fp "$d")
60 if [ "$before" = "$after" ]; then
61 echo "ok $name -> $new (tracking:${moved:- none})"; ok=$((ok+1))
62 else
63 echo "WARN $name: working copy fingerprint changed; check it"; failed=$((failed+1))
64 fi
65done
66echo "repointed $ok, skipped $skipped, problems $failed$($DRY && echo ' (dry run: nothing changed)')"