Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/edda

crates/storage-git-gix/parked-gix/.nornir/bench/transfer-setup.sh

#!/usr/bin/env bash
# Build the transfer-shape corpus: 19 real workspace_nordisk repositories pushed
# into a gunnar store BY STOCK GIT, with a client that is genuinely N commits
# behind, so a fetch is a real negotiation and not a replay.
#
# The corpus is built by PUSHING into a bare store. `git clone --bare` silently
# repacks and would make every arm measure git's repacker instead of gunnar's.
#
# Two snapshots come out of this and nothing else in the lane rebuilds them:
#   $X/pristine/data       the gunnar data root right after the pushes
#   $X/pristine/client-$D  one client per depth, holding exactly the old state
set -u -o pipefail

X=${XFER_DIR:-/home/rickard/scratch/xfer}
G=${GUNNAR_BIN:?set GUNNAR_BIN to the binary under test}
PORT=${PORT:-12931}
DEPTHS=${DEPTHS:-"50 200"}
REPOS=${REPOS:-"nornir modgunn funnel-core holger facett edda skade znippy znippy-zoomies knut korp korp-collectors ordning Skidbladnir tunnr draupnir dwarves bifrost workspace_nordisk"}
SRC=${SRC:-/home/rickard/git}

die() { echo "FATAL: $*" >&2; exit 1; }

# The subject binary, named on stdout before anything runs. A bench lane
# measured a ten-hour-old binary because nothing on the row said which one it
# was; path, size and mtime are what actually distinguish two builds.
[ -x "$G" ] || die "GUNNAR_BIN=$G is not executable"
echo "subject: $G"
stat -c 'subject: size=%s mtime=%y' "$G"

mkdir -p "$X"
rm -rf "$X/pristine" "$X/data" "$X/hashes.tsv"
mkdir -p "$X/pristine" "$X/keys"
[ -f "$X/keys/id_ed25519" ] || ssh-keygen -t ed25519 -N '' -C xfer -f "$X/keys/id_ed25519" >/dev/null
chmod 600 "$X/keys/id_ed25519"
SSH="ssh -i $X/keys/id_ed25519 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR"
export GIT_SSH_COMMAND="$SSH"

stop_server() {
  local pid
  pid=$(ps -eo pid,args | awk -v p="--ssh-bind 127.0.0.1:$PORT" 'index($0,p)&&!/awk/{print $1}')
  [ -n "$pid" ] && kill $pid 2>/dev/null
  for _ in $(seq 1 40); do
    ps -p ${pid:-0} >/dev/null 2>&1 || return 0
    sleep 0.25
  done
  return 0
}

start_server() { # data-root logfile
  GUNNAR_DATA_ROOT=$1 nohup "$G" serve --ssh-bind 127.0.0.1:$PORT --no-grpc \
    --admin-key "@$X/keys/id_ed25519.pub" > "$2" 2>&1 &
  for _ in $(seq 1 60); do
    sleep 0.5
    if timeout 3 bash -c "exec 3<>/dev/tcp/127.0.0.1/$PORT && head -c 20 <&3" 2>/dev/null | grep -q 'SSH-2.0-gunnar'; then
      return 0
    fi
  done
  die "gunnar did not answer on $PORT: $(tail -5 "$2")"
}

# ── create the repositories, which must happen with the server DOWN (S-019) ──
stop_server
mkdir -p "$X/data"
GUNNAR_DATA_ROOT=$X/data "$G" namespace create bench >/dev/null || die "namespace create"
# The hash comes from the SOURCE repository, not from a literal. `tunnr` is a
# sha256 repository and creating it as sha1 fails at the wire with "the
# receiving end does not support this repository's hash algorithm" -- which is
# also the only reason the corpus covers both hashes at all.
for r in $REPOS; do
  h=$(git -C "$SRC/$r" rev-parse --show-object-format 2>/dev/null) || h=sha1
  GUNNAR_DATA_ROOT=$X/data "$G" repo create "bench/$r" --hash "$h" --public >/dev/null \
    || die "repo create bench/$r ($h)"
  echo "$r $h" >> "$X/hashes.tsv"
done

start_server "$X/data" "$X/serve-setup.log"

url() { echo "ssh://git@127.0.0.1:$PORT/bench/$1.git"; }

# ── push the OLD state, clone the clients off it, then push the rest ─────────
# The client's history is real: it was cloned when the server held only the
# first (n-D) commits, so its `have`s are the ones a client that has been away
# actually sends. Resetting a full clone would leave every "missing" object in
# the client's own object store and the fetch would send nothing.
for D in $DEPTHS; do mkdir -p "$X/pristine/client-$D"; done

for r in $REPOS; do
  head=$(git -C "$SRC/$r" rev-parse HEAD) || die "$r: no HEAD"
  # Push the old state at the DEEPEST depth first so every shallower client can
  # be cut from the same server state; then push each successively newer cut.
  # Sorting keeps the pushes strictly forward and avoids a non-fast-forward.
  for D in $(for d in $DEPTHS; do echo "$d"; done | sort -rn); do
    old=$(git -C "$SRC/$r" rev-parse "HEAD~$D" 2>/dev/null) || old=""
    if [ -z "$old" ]; then echo "skip $r at depth $D (history shorter)"; continue; fi
    git -C "$SRC/$r" -c lfs.allowincompletepush=true push --no-verify -q "$(url "$r")" "+$old:refs/heads/main" || die "$r push old@$D"
    git -c filter.lfs.smudge=cat -c filter.lfs.process= -c filter.lfs.required=false clone -q --no-checkout "$(url "$r")" "$X/pristine/client-$D/$r" || die "$r clone client@$D"
  done
  git -C "$SRC/$r" -c lfs.allowincompletepush=true push --no-verify -q "$(url "$r")" "+$head:refs/heads/main" || die "$r push head"
  echo "pushed $r"
done

stop_server
cp -a "$X/data" "$X/pristine/data"
echo "corpus ready: $(du -sh "$X/pristine/data" | cut -f1) in $X/pristine"