AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Awesome Git History Reset

skill-khasky-awesome-agent-skills-awesome-git-history-reset · by khasky

Erases a repository's entire git history and replaces it with a single fresh commit, then force-pushes — safely: access checks, a verified mirror backup, a secret scan, and an explicit confirmation gate before anything irreversible. Use when asked to 'wipe git history', 'squash all commits into one', 'reset history to a single Initial commit', 'start the history fresh', 'clean/erase the commit lo…

No reviews yet
0 installs
11 views
0.0% view→install

Install

$ agentstack add skill-khasky-awesome-agent-skills-awesome-git-history-reset

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-khasky-awesome-agent-skills-awesome-git-history-reset)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
23d ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Awesome Git History Reset? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Git History Reset

Collapse a repository's entire history into one fresh Initial commit and force-push it, so the published commit log starts clean. The file tree is preserved byte-for-byte; only the history leading to it is discarded.

Why the ceremony: this is an irreversible, outward-facing rewrite of a shared remote. Once you force-push, every old commit on the default branch is gone from the tip, open pull requests break, and anyone who cloned or forked keeps the old history anyway. The steps below are not bureaucracy — each one closes a specific way this goes wrong: pushing without write access, discovering too late that the branch was protected, losing content in the squash, or force-pushing over a teammate's commit you never saw.

Core principle

NOTHING IRREVERSIBLE UNTIL FOUR THINGS HOLD: write access is confirmed, a mirror backup exists and is verified, the history is scanned for secrets, and the user has explicitly confirmed the wipe. If any one is missing, stop at that gate.

Three invariants hold throughout:

  • Never operate on the user's existing checkout. Always work in a fresh clone in a scratch directory. If the push fails or the result is wrong, the scratch clone is disposable and the user's own working copy was never touched.
  • Never assume the default branch is main. Detect it from the remote. Rewriting the wrong branch, or one that isn't the default, silently leaves the real history in place.
  • Never delete the backup, and never delete a remote ref, without asking. The backup is the only rollback path. Extra branches and tags may be the user's, not stale.

Invocation

/awesome-git-history-reset  [branch] [--message "Initial commit"]
  • ` — required. HTTPS or SSH (https://github.com/owner/repo.git or git@github.com:owner/repo.git`).
  • [branch] — optional. Defaults to the remote's detected default branch. Only pass this to target a non-default branch.
  • --message — optional. The single commit's message. If omitted, Phase 0 asks for it at preflight (default Initial commit).

If the user invokes the skill without a URL, ask for one before doing anything else.

Tooling check (run first)

  • git --version — required. Everything destructive is plain git; the host tooling below only powers the preflight gates.
  • A host CLI — optional but strongly preferred: it verifies write/admin permission, branch protection, open pull/merge requests, and fork count before the destructive step. Without one, write access can't be confirmed until the push itself, and the protection/PR/fork warnings are unavailable — say so explicitly and proceed only after the user accepts that blind spot.
  • gitleaks version — optional: scans history for secrets before the rewrite. Without it, note that history was not scanned.

Confirm each is on PATH (exit 0) before relying on it. Never assume a host CLI or gitleaks is installed.

Which host CLI. Detect the host from the remote URL, then use its tool. The gates in Phase 0 give the command for each:

| Host | CLI | Check it's present | Generic escape hatch | |---|---|---|---| | GitHub | gh | gh --version | gh api | | GitLab (SaaS or self-managed) | glab | glab --version | glab api (--hostname for self-managed) | | Bitbucket, Gitea/Forgejo, Azure DevOps, plain SSH remote | none assumed | — | the host's REST API over curl with a token, if the user supplies one |

For a host with no CLI and no token, treat every gate below that needs one as unavailable, not as passed: list which checks you could not run, and get the user's explicit acceptance before Phase 1. An unrunnable gate is a blind spot to disclose, never a gate to skip silently.


Phase 0 — Preflight and access verification (stop gates)

Do every check that your available tools allow. Each failure is a hard stop, not a warning to push past.

  1. Parse the URL into /; keep the URL verbatim for git, derive owner/repo for gh.
  1. Read access + existence — the cheapest real check:

`` git ls-remote `` Non-zero exit or auth prompt → stop. The URL is wrong, the repo is private and you're unauthenticated, or the network is down.

  1. Hard rule — the remote's owner must match your git identity. Rewriting history on a repo you don't own is almost always a mistake (wrong clone URL, a colleague's repo, an upstream you meant to fork). Compare the repo owner against the identity that will push:

``` git config user.name git config user.email

gh api user -q .login # GitHub — the account that will actually push glab api user # GitLab — read username from the JSON `` glab api has no field-selection flag: it prints JSON, so read the field yourself rather than piping through a jq you haven't confirmed is installed. The repo owner is (from the URL, or gh repo view / --json owner / the namespace.full_path field of glab api projects/). With a host CLI present, the authoritative comparison is vs the authenticated login, case-insensitive; for an org- or group-owned repo the logins won't match by name — fall back to the write-permission check in step 6 as proof of ownership. With no host CLI, match against git config user.name or the local-part of git config user.email`. Mismatch → stop and report it: name the repo owner and your local git identity side by side, and do not proceed until the user explicitly confirms they intend to rewrite a repo owned by a different account. Hard stop, not a warning to skip.

  1. Detect the default branch (unless a branch was passed):

`` git ls-remote --symref HEAD ` Read the ref: line — that ref (e.g. refs/heads/main) is the default branch. Use its short name as . Do not hardcode main`.

  1. Hard rule — more than one branch means stop and ask. Extra branches keep the old history reachable (so the "clean history" is incomplete), and usually mean the repo holds work you're about to strand.

`` git ls-remote --heads ` More than one branch → **stop, list every branch, and offer the user the choice explicitly: continue anyway (only ` is rewritten; the other branches keep their full history) or abort.** Do not decide this yourself. Exactly one branch → continue.

  1. Write / admin permission (needs a host CLI):

`` gh repo view / --json viewerPermission,isFork,parent,forkCount glab api projects/ # read permissions, forked_from_project, forks_count ` GitHub: viewerPermission must be WRITE, MAINTAIN, or ADMIN; READ / null → stop, you cannot push. GitLab: the effective accesslevel under permissions.projectaccess (or permissions.groupaccess) must be ≥ 40 (Maintainer) — 30 (Developer) cannot force-push a protected branch and usually cannot push to the default one. Note whether the repo is a fork (isFork / forkedfrom_project`) — rewriting a fork's history is fine but doesn't touch the upstream; make sure that's what the user wants.

URL-encode the GitLab project path: group/sub/repogroup%2Fsub%2Frepo. Inside a checkout of that project, glab api projects/:fullpath substitutes it for you.

  1. Branch protection (needs a host CLI) — force-push to a protected default branch will be rejected at push time, after the backup and squash are already done:

`` gh api repos///branches//protection glab api projects//protected_branches/ ` A 200 with force-push disallowed, or required reviews / linear-history / status checks → stop and tell the user to lift protection or grant a bypass first (GitHub: Settings → Branches; GitLab: Settings → Repository → Protected branches, where allowforcepush is the field that matters). A 404` means the branch is unprotected — good.

  1. Open pull / merge requests (needs a host CLI) — they reference old commits and break on a wholesale rewrite:

`` gh pr list --repo / --state open glab mr list --repo / # defaults to open MRs; --all would add closed and merged `` Any open PR/MR → surface the list. The user should close or merge them first; proceeding will orphan their base commits.

  1. Forks (from step 6 — forkCount on GitHub, forks_count on GitLab) — a rewrite cannot reach a fork; every forker keeps a full copy of the old history. If the count is above zero, say so plainly: this is not a way to make the old history unrecoverable.
  1. Commit message. Ask the user what to name the single commit the whole history collapses into — unless --message was already passed on invocation. Offer Initial commit as the default so they can accept it in one word. Record the answer as ``; Phase 3 commits with it verbatim, and the confirmation gate below quotes it back.
  1. Confirmation gate. State exactly what will happen, then get an explicit yes:

> This will permanently erase all history on ` of / and replace it with a single commit (`), then force-push. Old commits will be unrecoverable from the remote tip (a verified backup is kept locally). Open PRs will break; existing forks keep the old history. Proceed?

No explicit confirmation → stop here. Everything up to this point was read-only.


Phase 1 — Backup (mandatory, verified)

A mirror clone is the rollback path. Make it before touching anything.

git clone --mirror  -backup-.git

Then verify it — an unverified backup is not a backup:

cd -backup-.git
git rev-list --all --count            # must be > 0
git log --oneline -1          # record this as OLD_SHA
git fsck --full                       # no missing/broken objects

Record OLD_SHA (the pre-rewrite tip) — later phases prove no content was lost against it, and it's the exact ref to restore from if the user ever wants to roll back:

# rollback, if ever needed:
git push --force  OLD_SHA:refs/heads/

Report the backup's absolute path. Never delete it as part of this skill.


Phase 2 — Secret scan of history (before the rewrite)

A force-push does not remove a leaked secret: it survives in forks, in the host's dangling-commit cache, and in anyone's existing clone. Finding one now changes the plan from "rewrite" to "rewrite and rotate."

Clone a normal (non-mirror) working copy — you'll reuse it for the squash:

git clone  
cd 
git checkout 

If gitleaks is available:

gitleaks git . --no-banner
  • gitleaks git scans commit history — that is the command this step needs. gitleaks directory . scans the working tree instead and would miss committed-then-removed keys, which are exactly what a history rewrite is usually about. On gitleaks older than 8.19 the spelling is gitleaks detect --source . --no-banner; 8.19 renamed detectgit and detect --no-gitdirectory, keeping the old names working but hidden from --help.
  • Findings → stop and tell the user to rotate the exposed credentials. The rewrite can still proceed afterward, but rotation is the part that actually protects them; the force-push is cosmetic for an exposed secret.
  • Clean → continue — for the tracked history only. Neither command sees .gitignored paths, so a secret in secrets/ or *.local is unscanned either way; say so rather than reporting a blanket clean.
  • No gitleaks → state that history was not scanned and recommend installing it if secrets in old commits are a concern: winget install gitleaks (Windows), brew install gitleaks (macOS/Linuxbrew), the distro package on Linux (apt install gitleaks, pacman -S gitleaks, dnf install gitleaks), or a release binary from the project's GitHub releases where the distro has none.

Phase 3 — Squash to a single commit (orphan method)

In the working clone, on the target branch:

git checkout --orphan fresh
git add -A
git commit -m ""          # default: "Initial commit"
git branch -D 
git branch -m fresh 

Verify no content was lost — the whole point is a clean history over an identical tree:

git log --oneline                  # exactly one commit
git diff --stat  HEAD      # MUST be empty — same tree as the old tip

If git diff HEAD shows anything, stop: the squash changed the file tree (usually an untracked or ignored file that git add -A did or didn't pick up). Do not push a tree that differs from what was there.


Phase 4 — Force-push (the irreversible step)

Use --force-with-lease, not a bare --force: it aborts if someone pushed to `` after your clone, so you never silently overwrite a commit you never saw.

git push --force-with-lease=: origin 
  • Rejected as stale info → a new commit landed after your backup. Stop, re-run from Phase 1 against the new tip; do not switch to --force to steamroll it.
  • Rejected as protected branch → Phase 0 step 7 was skipped or protection was added since; lift it and retry.

Verify the remote tip is now the new commit:

git ls-remote origin        # sha matches your new HEAD, not OLD_SHA

Phase 5 — Prune local history

Drop the old objects from the working clone so it reflects the clean state:

git reflog expire --expire=now --all
git gc --prune=now --aggressive

Verify:

git rev-list --all --count          # 1
git fsck --unreachable              # old commits gone (empty or only expected)

Phase 6 — Remote cleanup (only with per-item confirmation)

The rewrite only touched ``. Any other branch or tag still points into the old history, which keeps those objects alive and the "clean history" incomplete.

git ls-remote --heads --tags origin
  • Only `` remains → nothing to do.
  • Other refs exist → list them for the user and ask per ref. These may be release tags or teammates' branches, not stale debris. Delete only the ones the user names, one at a time:

`` git push origin --delete ` **Deleting a tag orphans the release built on it** — on GitHub and GitLab alike the release object and its notes/assets survive, but its tag link goes dead. Warn the user per tag before deleting, and note the tradeoff: keeping the tag leaves the old history reachable through it (the reset stays cosmetic for `); deleting it completes the wipe but breaks the release.

  • Tidy local remote-tracking refs:

`` git remote prune origin ``

Never batch-delete refs. Never assume a tag is disposable.


Phase 7 — Report

Close with what happened, the evidence, and what the user must still do by hand:

Repository:   /  (branch )
Rewrite:       →   (N commits → 1)
Content:      git diff .. empty — tree identical, no files lost
Backup:         (verified: N commits, fsck clean)
Secret scan:  clean | FINDINGS (rotate now) | not scanned (no gitleaks)
Verified:     ls-remote tip = ; local rev-list = 1; fsck clean

Manual residuals (cannot be done by command):

  • Open PRs — close or recreate; they reference commits that no longer exist.
  • Forks — keep a full copy of the old history; a rewrite can't reach them.
  • Full erasure guarantee — a force-push leaves the old commits dangling and cache-reachable for a while on every major host. The only reliable way to drop them is to delete the repository and recreate it (GitHub: Settings → Delete repository; GitLab: Settings → General → Advanced → Delete project, which is a delayed deletion on some plans). On a self-managed host, ask the administrator what its garbage-collection schedule actually is rather than assuming.
  • Leaked secret — if the scan (or the user) found one, rotate it. The rewrite does not make it unrecoverable.
  • Backup — tell the user where it is and that they can delete it once they've confirmed the remote is good. The skill never deletes it.

Guardrails

  • The user's own checkout is off-limits. All work happens in a fresh scratch clone and a mirror backup.
  • Read-only until the Phase 0 confirmation gate. Everything before i

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.