Programming with AI
ENRU
E

Git — rescue scenarios

what to type when everything is broken

What it is

Not a git course but a list of situations. Every command was run in a real repository, and the output is real. The basics of git are in article 5.1.

Every day

git status            # what is changed right now
git diff              # what exactly is changed, line by line
git diff --staged     # what will go into the commit
git add .             # take everything changed
git commit -m "..."   # record it
git log --oneline     # the history as a short list

Before asking an AI for a large edit, it is worth making a commit. That is one command, and it turns any failure into "roll back and try differently".

I broke a file and want it back as it was

$ git diff --stat
 app.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git restore app.py      # restore one file
$ cat app.py
third version

To bring everything back to the last commit:

git restore .

I deleted a file

$ rm app.py
$ git restore app.py
$ ls
app.py

As long as the file was in the last commit, it comes back.

I committed the wrong thing

The commit hasn't been pushed — undo the commit, keep the changes:

$ git reset --soft HEAD~1
$ git status --short
M  app.py

The changes are there, the commit isn't. You can fix it and commit again.

The commit has been pushed — don't rewrite history, add an opposite commit instead:

$ git revert --no-edit HEAD
[main 2127961] Revert "third version"
 1 file changed, 1 insertion(+), 1 deletion(-)

$ cat app.py
second version

revert creates a new commit that undoes the old one. The history stays truthful and nothing breaks for anyone.

I urgently need to set my changes aside

$ echo "draft" >> app.py
$ git stash          # hide them and return to a clean state
$ cat app.py
second version

$ git stash pop      # take them back out
$ cat app.py
second version
draft

Handy when an AI has done half the work and you urgently need to check something else.

I committed a secret

$ git rm --cached .env
$ echo ".env" >> .gitignore
$ git add .gitignore && git commit -m "removed .env from the repository"
$ git ls-files
.gitignore
app.py

.env is gone from the working copy and from future commits. But:

$ git log --oneline --all -- .env
c07fe2e removed .env from the repository
1736a45 added .env

The file is still in the history, and the key comes out of it with one command. So the order of operations is: change the key first, and clean the repository afterwards. If the repository is public, treat the key as compromised from the moment it was pushed (article 5.7).

When it broke — and it used to work

git log --oneline              # find the suspicious commit
git show <hash>                # see what changed in it
git restore --source=<hash> <file>   # take the file's version from that commit

If there are many commits and it isn't clear which is to blame, there is git bisect, which halves the history (the technique from article 4.4, only automatic).

Branches

git switch -c trial    # a new branch from where you are
git switch main        # go back
git merge trial        # bring the work in
git branch -d trial    # delete the branch

For working with an AI a branch is useful in one specific case: a large rework that you can't tell in advance will survive to the end. In a branch you can abandon it without clearing up after yourself.

I did reset --hard and lost a commit

A commit that "disappeared" after reset has not been deleted yet. Git remembers where HEAD stood every time:

$ git reset --hard HEAD~1
$ cat app.py
first version
$ git reflog
ee9fbbe HEAD@{0}: reset: moving to HEAD~1
585928f HEAD@{1}: commit: second version
ee9fbbe HEAD@{2}: commit (initial): first version
$ git reset --hard HEAD@{1}
$ cat app.py
second version

reflog is the last line of defence: it keeps entries for several weeks. But it only rescues what was committed. Uncommitted changes after reset --hard cannot be brought back by anything — one more reason to commit before every task you give an AI.

What not to do

git push --force to a shared branch. It rewrites the history for everyone.

git reset --hard without understanding it. It throws away both commits and edits.

An "everything at once" commit once a week. Such a commit can't be partially reverted — and that is exactly what you need when an AI broke one of twenty changes.

Committing without reading the diff. git diff before a commit is thirty seconds that catch a debug print left in by accident and a forgotten key.