From 4d800e21b4dcb18aeae58aa2bd18802a0aa230e8 Mon Sep 17 00:00:00 2001 From: James Coleman Date: Tue, 16 Jun 2026 11:09:03 -0500 Subject: [PATCH] Add bare git repository support with configurable source, destination, and bare flag --- README.md | 24 +++++++++++++++++++++-- mirror-sync.sh | 52 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 32ca706..97350ec 100644 --- a/README.md +++ b/README.md @@ -111,18 +111,38 @@ Each repo has at bare minimum the following configurations: - dusum - Path to a file to store disk usage summary results of the repository directory. ### git -Synchronizes a git repository via git pull. To use this method, you need to have the git package installed. +Synchronizes a git repository. To use this method, you need to have the git package installed. + +If the destination (`repo`) does not yet contain a git repository, it is cloned from the configured `source`. Otherwise it is updated in place. Bare repositories have no working tree, so they cannot be updated with `git pull`; instead the script clones them with `git clone --mirror` and updates them with `git remote update --prune`, which honors the repository's configured fetch refspec. + +Whether a repository is bare can be set explicitly with the `bare` configuration, otherwise existing repositories are auto-detected. + +#### source +The git URL to clone the repository from. Required when the destination does not already contain a git repository. + +#### bare +Set to `true` to treat the repository as bare. When cloning, this clones with `git clone --mirror`. When updating, this forces use of `git remote update --prune`. If unset, existing repositories are auto-detected. #### options -Extra options appended to `git pull`. +Extra options appended to the git command (`git clone` when cloning, `git pull` when updating a working-tree repository, or `git remote update --prune` when updating a bare repository). #### Example ```bash example_sync_method="git" +example_source="https://github.com/example/example.git" example_repo="/home/mirror/http/example" example_timestamp="/home/mirror/timestamp/example" ``` +#### Bare repository example +```bash +example_sync_method="git" +example_source="https://github.com/example/example.git" +example_repo="/home/mirror/git/example.git" +example_bare="true" +example_timestamp="/home/mirror/timestamp/example" +``` + ### aws Synchronize with an s3 bucket using aws cli. To use this, you need the aws cli package installed. diff --git a/mirror-sync.sh b/mirror-sync.sh index fdcf316..0cc6dae 100644 --- a/mirror-sync.sh +++ b/mirror-sync.sh @@ -5,7 +5,7 @@ PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:$HOME/.local/ # Variables for trace generation. PROGRAM="mirror-sync" -VERSION="20260602" +VERSION="20260616" TRACEHOST=$(hostname -f) mirror_hostname=$(hostname -f) DATE_STARTED=$(LC_ALL=POSIX LANG=POSIX date -u -R) @@ -617,12 +617,58 @@ git_sync() { # Start the module. module_config "$1" - # Do a git pull within the repo folder to sync. + # Read git specific configuration. + eval source="\$${MODULE}_source" + eval bare="\$${MODULE}_bare" + + # Normalize the bare flag to either "true" or empty. + case "${bare,,}" in + 1|true|yes|bare) bare="true" ;; + *) bare="" ;; + esac + + # If the destination does not yet contain a git repository, clone it from + # the configured source. Bare repositories are cloned with --mirror so the + # configured fetch refspec mirrors all refs from upstream. + if [[ ! -e "${repo:?}/.git" ]] && [[ ! -e "${repo:?}/HEAD" ]]; then + if [[ ! $source ]]; then + echo "No git repository at '${repo:?}' and no source defined to clone from." + exit 1 + fi + echo "Cloning '${source}' into '${repo:?}'." + if [[ $bare ]]; then + eval git clone --mirror ${options:+$options} "'${source}'" "'${repo:?}'" + else + eval git clone ${options:+$options} "'${source}'" "'${repo:?}'" + fi + RT=$? + if (( RT == 0 )); then + post_successful_sync + else + post_failed_sync + fi + log_end_header + return + fi + + # Move into the repo folder to sync. if ! cd "${repo:?}"; then echo "Failed to access '${repo:?}' git repository." exit 1 fi - eval git pull ${options:+$options} + + # Determine whether the repository is bare. Honor an explicit configuration + # if set, otherwise auto-detect. Bare repositories have no working tree, so a + # `git pull` is not possible; instead use `git remote update` which honors the + # configured fetch refspec (e.g. a mirror clone created with --mirror). + if [[ ! $bare ]] && [[ $(git rev-parse --is-bare-repository 2>/dev/null) == "true" ]]; then + bare="true" + fi + if [[ $bare ]]; then + eval git remote update --prune ${options:+$options} + else + eval git pull ${options:+$options} + fi RT=$? if (( RT == 0 )); then post_successful_sync