From: Shane Jaroch Date: Sat, 17 Jan 2026 04:03:33 +0000 (-0500) Subject: repack on init option X-Git-Url: https://git.nutra.tk/v1?a=commitdiff_plain;h=87627aa2d59c9d50591caa0edfb40d5dd2e74997;p=gamesguru%2Fgit-remote-gcrypt.git repack on init option --- diff --git a/git-remote-gcrypt b/git-remote-gcrypt index 74c2a58..3e8bb9f 100755 --- a/git-remote-gcrypt +++ b/git-remote-gcrypt @@ -89,6 +89,7 @@ while [ $# -gt 0 ]; do case "$1" in --force) FORCE_CLEAN=yes ;; --hard) HARD_FORCE=yes ;; + --repack) DO_REPACK=yes ;; --init) FORCE_INIT=yes ;; -*) echo "Unknown option: $1" >&2; exit 1 ;; *) @@ -1128,6 +1129,77 @@ do_fetch() } # do_push PUSHARGS (multiple lines like +src:dst, with both + and src opt.) +# Perform repack, manifest generation, and upload +# Requires: r_revlist, Tempdir, Packkey_bytes, Hashtype, Packlist, Keeplist, Recipients, Refslist, Repoid, Extnlist, URL, NAME, VERSION +perform_repack() +{ + local tmp_encrypted tmp_objlist tmp_manifest pack_id key_ r_pack_delete="" + + tmp_encrypted="$Tempdir/packP" + tmp_objlist="$Tempdir/objlP" + + { + xfeed "$r_revlist" git rev-list --objects --stdin -- + repack_if_needed @r_pack_delete + } > "$tmp_objlist" + + # Only send pack if we have any objects to send + if [ -s "$tmp_objlist" ] + then + key_=$(genkey "$Packkey_bytes") + pack_id=$(export GIT_ALTERNATE_OBJECT_DIRECTORIES="$Tempdir"; + pipefail git pack-objects --stdout < "$tmp_objlist" | + pipefail ENCRYPT "$key_" | + tee "$tmp_encrypted" | gpg_hash "$Hashtype") + + append_to @Packlist "pack :${Hashtype}:$pack_id $key_" + if isnonnull "$r_pack_delete" + then + append_to @Keeplist "keep :${Hashtype}:$pack_id 1" + fi + fi + + # Generate manifest + # Update the gcrypt version in extensions (remove old, add current) + filter_to ! @Extnlist "extn gcrypt-version *" "$Extnlist" + append_to @Extnlist "extn gcrypt-version $VERSION" + + echo_info "Encrypting to: $Recipients" + echo_info "Requesting manifest signature" + + tmp_manifest="$Tempdir/maniP" + PRIVENCRYPT "$Recipients" > "$tmp_manifest" < "$tmp_objlist" - - # Only send pack if we have any objects to send - if [ -s "$tmp_objlist" ] - then - key_=$(genkey "$Packkey_bytes") - pack_id=$(export GIT_ALTERNATE_OBJECT_DIRECTORIES="$Tempdir"; - pipefail git pack-objects --stdout < "$tmp_objlist" | - pipefail ENCRYPT "$key_" | - tee "$tmp_encrypted" | gpg_hash "$Hashtype") - - append_to @Packlist "pack :${Hashtype}:$pack_id $key_" - if isnonnull "$r_pack_delete" - then - append_to @Keeplist "keep :${Hashtype}:$pack_id 1" - fi - fi - - # Generate manifest - # Update the gcrypt version in extensions (remove old, add current) - filter_to ! @Extnlist "extn gcrypt-version *" "$Extnlist" - append_to @Extnlist "extn gcrypt-version $VERSION" - - echo_info "Encrypting to: $Recipients" - echo_info "Requesting manifest signature" - - tmp_manifest="$Tempdir/maniP" - PRIVENCRYPT "$Recipients" > "$tmp_manifest" </dev/null || true exit 0 @@ -1570,6 +1591,12 @@ cmd_clean() else echo_info " git-remote-gcrypt clean --force $URL" fi + + # If user requested repack but found bad files and no force, abort (safety first) + if [ "${DO_REPACK:-}" = "yes" ]; then + echo_info "NOTE: Repack requested but pending file deletions require --force." + fi + CLEAN_FINAL "$URL" git remote remove "$NAME" 2>/dev/null || true exit 0 @@ -1577,6 +1604,20 @@ cmd_clean() echo_info "Removing files..." REMOVE "$URL" "$bad_files" + + if [ "${DO_REPACK:-}" = "yes" ]; then + echo_info "Repacking remote..." + # Prepare r_revlist from all current refs for full repack + r_revlist="" + if isnonnull "$Refslist"; then + r_revlist=$(xecho "$Refslist" | cut -d' ' -f1) + fi + + # Set flag to force repack_if_needed to act + GCRYPT_FULL_REPACK=1 + perform_repack + fi + PUT_FINAL "$URL" CLEAN_FINAL "$URL" git remote remove "$NAME" 2>/dev/null || true diff --git a/tests/system-test-clean-repack.sh b/tests/system-test-clean-repack.sh new file mode 100755 index 0000000..29df415 --- /dev/null +++ b/tests/system-test-clean-repack.sh @@ -0,0 +1,152 @@ +#!/bin/sh +set -e + +# Setup test environment +echo "Setting up repack test environment..." +PROJECT_ROOT="$(pwd)" +mkdir -p .tmp +TEST_DIR="$PROJECT_ROOT/.tmp/repack_test" +rm -rf "$TEST_DIR" +mkdir -p "$TEST_DIR" + +# Repo paths +REPO_DIR="$TEST_DIR/repo" +REMOTE_DIR="$TEST_DIR/remote" + +mkdir -p "$REPO_DIR" +mkdir -p "$REMOTE_DIR" + +# Tools +GCRYPT_BIN="$PROJECT_ROOT/git-remote-gcrypt" +if [ ! -x "$GCRYPT_BIN" ]; then + echo "Error: git-remote-gcrypt binary not found at $GCRYPT_BIN" + exit 1 +fi + +# GPG Setup +export GNUPGHOME="$TEST_DIR/gpg" +mkdir -p "$GNUPGHOME" +chmod 700 "$GNUPGHOME" + +cat <"${GNUPGHOME}/gpg" +#!/usr/bin/env bash +export GNUPGHOME="$GNUPGHOME" +set -efuC -o pipefail; shopt -s inherit_errexit +args=( "\${@}" ) +for ((i = 0; i < \${#}; ++i)); do + if [[ \${args[\${i}]} = "--secret-keyring" ]]; then + unset "args[\${i}]" "args[\$(( i + 1 ))]" + break + fi +done +exec gpg "\${args[@]}" +EOF +chmod +x "${GNUPGHOME}/gpg" + +echo "Generating GPG key..." +gpg --batch --passphrase "" --quick-generate-key "Test " + +# Initialize repo +cd "$REPO_DIR" +git init +git config user.email "test@test.com" +git config user.name "Test User" +git config --global advice.defaultBranchName false + +# Initialize local remote +git init --bare "$REMOTE_DIR" +git remote add origin "gcrypt::$REMOTE_DIR" +git config remote.origin.gcrypt-participants "test@test.com" +git config remote.origin.gcrypt-signingkey "test@test.com" +git config gpg.program "${GNUPGHOME}/gpg" +git config user.signingkey "test@test.com" + +export PATH="$PROJECT_ROOT:$PATH" + +# Create fragmentation by pushing multiple times +echo "Push 1" +echo "data 1" >file1.txt +git add file1.txt +git commit -m "Commit 1" --no-gpg-sign +# Initial push needs force to initialize remote gcrypt repo +git push origin +master + +echo "Push 2" +echo "data 2" >file2.txt +git add file2.txt +git commit -m "Commit 2" --no-gpg-sign +git push origin master + +echo "Push 3" +echo "data 3" >file3.txt +git add file3.txt +git commit -m "Commit 3" --no-gpg-sign +git push origin master + +# Verify we have multiple pack files in remote +# Note: gcrypt stores packs in 'pack' directory if using rsync-like backend? +# For git backend (gitception), they are objects in the git repo. +# We are using local file backend? No, gcrypt::$REMOTE_DIR where REMOTE_DIR is bare git repo. +# This makes it a Git Backend (gitception). +# The packs are stored as blobs in the backend repo. +# But 'do_push' logic downloads packs using 'git rev-list'. +# The 'Packlist' manifest file lists the active packs. +# We can check the Manifest to count packs. + +# Clone the raw backend to inspect manifest +cd "$TEST_DIR" +git clone "$REMOTE_DIR" raw_backend +cd raw_backend +git checkout master +# The manifest is a file with randomized name, but we can find it encrypt/decrypt? +# No, easier: use git-remote-gcrypt to list packs via debug or inference. +# Or just trust that multiple pushes created multiple packs (as gcrypt doesn't auto-repack on push unless configured). + +# Let's count lines in Packlist from the helper's debug output? +# Or we can verify the backend git repo has multiple commits (one per push). +HEAD_SHA=$(git rev-parse HEAD) +echo "Backend SHA: $HEAD_SHA" +# Start should have 3 commits (init, push1, push2, push3) -> wait, init is implicit. +# Each push updates the manifested repo. + +# Run clean --repack +cd "$REPO_DIR" +echo "Running clean --repack..." +git-remote-gcrypt clean --repack origin + +# Verify result +# Clone backend again (pull) and check structure +cd "$TEST_DIR/raw_backend" +git pull origin master + +# Count commits? Repack might add a commit? +# Repack reads all objects, creates 1 new pack, updates manifest. +# This results in a NEW commit on the backend that has the new manifest. +# The OLD packs are removed (deleted from backend). +# So we should see a new commit. +# Check if commit SHA changed. Repack force-pushes a new manifest state. +NEW_HEAD=$(git rev-parse HEAD) +echo "Old HEAD: $HEAD_SHA" +echo "New HEAD: $NEW_HEAD" + +if [ "$NEW_HEAD" != "$HEAD_SHA" ]; then + echo "Repack successful (HEAD changed)." +else + echo "Repack failed (HEAD did not change)." + exit 1 +fi + +# Verify data integrity +cd "$REPO_DIR" +# Force fresh clone to verified data +cd "$TEST_DIR" +git clone "gcrypt::$REMOTE_DIR" verified_repo +cd verified_repo +if [ -f file1.txt ] && [ -f file2.txt ] && [ -f file3.txt ]; then + echo "Data integrity verified." +else + echo "Data integrity failed!" + exit 1 +fi + +echo "Test passed."