Skip to content

regit: put a lost .git back, without touching the files

The problem

A folder gets copied without its .git: a sync that skips dot-folders, a flattened project tree, a zip, a file-by-file restore. The files survive; the history and the remote do not. The copy may also hold edits that exist nowhere else. Cloning fresh and copying over mixes in deleted files; reset --hard destroys the edits; committing everything and merging buries what differs.

The method

Load upstream into the index, never the working tree. read-tree makes git believe the folder is a checkout of upstream's tip while not one byte on disk moves. git add -A then stages exactly the difference.

  • Nothing differs: point upstream's branch at the tip and switch to it. A clean checkout, tracking set, files untouched.
  • Something differs: secrets scan, then one commit on a new branch whose parent is upstream's tip. Upstream's branch is never moved; nothing is pushed.
  • Ask the server for the default branch (ls-remote --symref): a fresh fetch never sets origin/HEAD, so reading it guesses main and fails on master or trunk.
  • Name the git dir on every call, so a folder inside another repo is not judged, or staged, by the parent's .git.

Sample

git init -q "$dir"
git -C "$dir" remote add origin "$url"
git -C "$dir" fetch -q origin "+refs/heads/$branch:refs/remotes/origin/$branch"
sha=$(git -C "$dir" rev-parse "origin/$branch")
env GIT_INDEX_FILE="$idx" git -C "$dir" read-tree "$sha"   # index only
env GIT_INDEX_FILE="$idx" git -C "$dir" add -A              # stage the difference
env GIT_INDEX_FILE="$idx" git -C "$dir" diff-index --cached --quiet "$sha" \
  && echo identical || echo "commit on local-$(date +%F)"

env rather than GIT_INDEX_FILE=... myfunc: in POSIX sh an assignment in front of a shell function may outlive the call, and the later commit reads a missing index and records an empty tree.

Use it:

regit --dry-run myproject https://example.com/me/myproject.git   # writes nothing
regit myproject https://example.com/me/myproject.git

Limits

  • Only for folders with no history: a .git with commits is refused.
  • An existing remote with another URL is refused, not overwritten.
  • Ignored files are neither compared nor committed.

Code

methods/regit/ in the methods repository: POSIX sh and git, a README and a 50-check test (bash's sh mode and dash). Tried live on a copy of a public repo with no .git: 27 local differences became one commit on local-2026-11-01 whose parent is the published tip; the published branch was not touched.