SuperAPI AI Directory

linux-repo-sync

Writing & research

View repository

Updated 10-16 03:40

About this skill

Repo Sync — safe git updates on a server

Distro support

Fully portable. This skill is git-only and identical on both families (Debian/Ubuntu and the RHEL family: Fedora, RHEL, CentOS Stream, Rocky, Alma, Oracle). The doctrine below — pull --rebase --autostash, porcelain dirty-checks, never reset --hard/clean -fd — is distro-neutral. The only difference is installing git itself.

Concept Debian/Ubuntu RHEL family
Install git apt install git dnf install git
Everything else (git pull --rebase --autostash, status, conflict recovery) identical identical

In sk-* scripts use pkg_install git from common.sh if you must ensure git is present. See docs/multi-distro/plan.md.

Use when

  • Writing, reviewing, or running any script that pulls git repos on a server (a repo-update menu, a deploy hook, a cron sync).
  • Updating one or many repos on a managed host as part of deployment or routine maintenance.
  • Deciding how an automated update should treat local, uncommitted, or untracked changes in a server-side working tree.

Do not use when

  • The task is a one-off git pull a developer runs by hand in their own checkout and watches the output of.
  • The task is authoring the generic script scaffold rather than the git behaviour; for the script contract load linux-bash-scripting first, then apply this doctrine to the git steps.

Required Inputs

Artefact Source Required? If absent
Repository path(s) and intended branch Operator or approved registry yes Skip the target; never search and update arbitrary repos
Interactive or unattended mode Scheduler/operator yes Default to report-only planning
Remote/authentication and post-pull command Repository owner for update/build Do not infer credentials or run a build
Deployment/change authority Service owner for integration Fetch/status only; do not integrate commits

Capability Contract

Read/search status and fetch inspection may be read-only. Pull/rebase changes the working tree and needs explicit authority. Never delete, reset, auto-resolve, force-push, expose credentials, or run an unapproved post-pull command.

Degraded Mode

If network, credentials, Git, upstream tracking, or a clean conflict state is unavailable, preserve the tree and report branch, status, and the failed step. Do not call a fetched or conflicted repository updated.

Decision Rules

State Action Failure avoided
Dirty tracked work Warn and use --rebase --autostash Lost local edits
Untracked files present Preserve them and report collision risk Deleted uploads/config
Rebase or stash conflict Stop with recovery commands Destructive auto-resolution
Detached HEAD or missing upstream Stop and request intended branch Updating the wrong history

Workflow

  1. Confirm the path is a git working tree (<path>/.git exists).
  2. Check the working tree state with git status --porcelain before touching it.
  3. Pull with git pull --rebase --autostash so local work is stashed, the rebase runs, and the work is re-applied on top.
  4. On conflict, stop and tell the operator how to recover — never discard.
  5. Leave untracked files in place, always.
  6. Run any post-pull build only after a clean, successful update.

Quality standards

  • An update must never destroy uncommitted or untracked work.
  • A dirty working tree is reported to the operator, never silently wiped.
  • A failed rebase or stash re-apply leaves the operator a clear recovery path.
  • The same script is safe to run twice (idempotent) and safe to run on a repo someone edited five minutes ago.

Anti-patterns

  • git reset --hard HEAD in automation. Fix: preserve local work with rebase/autostash and stop on conflict.
  • git clean -fd or -fdx. Fix: leave untracked files untouched and report collisions.
  • Resetting local changes as a routine pre-pull step. Fix: inventory and preserve the dirty state.
  • Auto-resolving or auto-aborting a rebase/stash conflict. Fix: leave state intact for the owner.
  • Removing uploads, .env, or generated config to avoid conflicts. Fix: stop and report the conflicting path.

Outputs

Artefact Consumer Acceptance condition
Per-repo update result Operator Records branch, before/after commit, dirty state, and outcome
Conflict handoff Repository owner Gives exact status plus safe continue/abort/stash recovery
Post-pull result Service owner Runs only after successful integration and records exit status

Evidence Produced

Artefact Acceptance
Repository update evidence Includes porcelain status, branch/upstream, before/after IDs, pull/autostash state, build exit, skipped repos, and redacted credentials

Capability Recovery

On conflict, leave Git's state intact and show git status, git rebase --continue, git rebase --abort, and git stash list. Never choose the recovery path for the owner.

Worked Example

A production checkout has a modified tracked config and an untracked upload. Report both, run the authorised git pull --rebase --autostash, leave the upload untouched, and record commit IDs. If the autostash conflicts, stop before the build and hand the exact recovery state to the owner.

References


The doctrine (binding on every repo-update script)

This is a STANDARD, not a suggestion. It exists because a repo-update menu script once ran git reset --hard HEAD + git clean -fd before pulling and silently wiped a developer's uncommitted edits. That must never be possible again on any server we manage.

  1. NEVER use git reset --hard in an automated or menu repo-update script. It destroys uncommitted changes to tracked files with no undo.
  2. NEVER use git clean -fd (or -fdx) in an automated or menu repo-update script. It deletes untracked files — uploads, .env, generated config — that git is not tracking precisely because they must survive.
  3. Prefer git pull --rebase --autostash. --autostash stashes any local changes before the rebase and re-applies them afterwards, so local work is preserved through the update. This is the default pull command for every server-side update.
  4. Detect a dirty working tree before pulling with git status --porcelain. If it is non-empty, warn the operator that local changes exist (and will be stashed and re-applied, not discarded). Never discard them.
  5. On a rebase or stash-reapply conflict, STOP. Do not auto-resolve, do not auto-abort. Report the conflict and the recovery options to the operator:
    • git rebase --continue after resolving the conflicting files, or
    • git rebase --abort to return to the pre-pull state, and
    • git stash list — the autostash is preserved here; recover it with git stash pop once the tree is clean.
  6. Untracked files are left in place, always. An update never removes a file git is not tracking.

The safe pattern (copy-paste)

update_repo_safely() {
    local path="$1"

    [[ -d "$path/.git" ]] || { echo "not a git repo: $path" >&2; return 1; }
    cd "$path" || return 1

    # 1. Detect a dirty working tree — warn, never wipe.
    if [[ -n "$(git status --porcelain)" ]]; then
        echo "WARNING: $path has local changes."
        echo "         They will be stashed and re-applied, not discarded."
    fi

    # 2. Pull with rebase + autostash: local work is preserved.
    if git pull --rebase --autostash; then
        echo "updated: $(git rev-parse --abbrev-ref HEAD) -> $(git log -1 --oneline)"
    else
        # 3. Conflict — stop and hand the operator a recovery path.
        echo "ERROR: pull/rebase hit a conflict in $path." >&2
        echo "  Resolve the files, then:  git rebase --continue" >&2
        echo "  Or roll back the pull:     git rebase --abort"   >&2
        echo "  Your stashed local work:   git stash list  (recover with: git stash pop)" >&2
        return 1
    fi
}

What this never does: no git reset --hard, no git clean, no removal of untracked files, no automatic conflict resolution.

Canonical script on this server

/usr/local/bin/update-all-repos is the menu-driven repo-update tool that must exist on every managed server (see notes/update-all-repos-setup.md). It must follow this doctrine: a porcelain dirty-check plus git pull --rebase --autostash, never git reset --hard + git clean -fd. The sk-update-all-repos script (linux-site-deployment) is the engine version of the same tool and is held to the same standard.

If you find any repo-update script on a server still doing git reset --hard or git clean -fd, that is a bug to fix, not a pattern to copy.

Scripts

This skill installs the following scripts to /usr/local/bin/. To install:

sudo install-skills-bin linux-repo-sync
Script Source Core? Purpose
sk-update-all-repos scripts/sk-update-all-repos.sh no Pull every registered git repo on this server using git pull --rebase --autostash and a porcelain dirty-check; interactive menu plus --all/--repo flags. This is the engine implementation of the safe-update doctrine above (shared with linux-site-deployment, which installs it as a core tool).

Verify

# A repo-update script is safe iff this returns nothing:
grep -nE 'git[[:space:]]+reset[[:space:]]+--hard|git[[:space:]]+clean[[:space:]]+-fd' /usr/local/bin/update-all-repos

# And iff it pulls with autostash:
grep -n 'pull --rebase --autostash' /usr/local/bin/update-all-repos