Schrödinger's Commit: Superposition-Based Version Control with Git Tags

TL;DR Use git tag + git reset --hard to jump between experimental states without branch overhead. All local versions stay in superposition until you lock one in with git push. Perfect for rapid prototyping, testing or agentic coding workflows.

The general version control workflow

One of the big advantages of version control is that you can always undo changes and restore the prior state of a repo. So you can think of commits as tactical checkpoints saving your work before you go onto the next fix/feature/experiment. Usually you commit to a branch you created that exists locally and might or might not exist on the remote version of the repo. The process is:

  • You create a branch with git checkout -b branch-name
  • You create a chain of commits implementing your changes
  • You push the branch to the remote repo using git push -u origin branch-name
  • You create a PR (pull request) alerting collaborators to review the changes
  • Your branch gets merged: Changes implemented!

Sometimes you might want to experiment more freely before committing to a particular direction. Maybe you're exploring two different approaches to solving a problem and want to easily switch between them. Or perhaps you just want quick, lightweight checkpoints that you can navigate between without cluttering your branch structure.

For these scenarios, the standard branch-and-merge workflow can feel like overkill. What you really want is a way to mark specific points in your commit history, jump back to them at will, and continue working from there. All without affecting what eventually gets pushed to the remote repo.

Unlock the magic with git tag

This is where git tags combined with git reset --hard come in. Think of tags as personal bookmarks in your commit history. Unlike branches, they don't move when you make new commits. They stay fixed to a specific commit, making them perfect for marking "safe points" you might want to return to.

Have a look at this commit history (explained in this blog, top row = last commit):

Note the two yellow tags in there. Tags are created by running git tag tag-name and will automatically attach to the last commit. Think of them like a (local) bookmark. Update a tag to point to the most recent commit again after some changes with git tag -f tag-name.

Jump back in time

Let's create a timeline to show what happened here.

So, because you have created the tag before-wild-experiment earlier, you can undo all commits that happened since then with one command. If you jump back this way, then push, the remote branch will end at d412711 cleanly, as if nothing ever happened past that point:

git reset --hard before-wild-experiment

Jump forward again from the past into the future

Now this is the cool part. Although resetting like this enables you to push the past state with a clean branch, as if nothing ever happened, you can still restore the 'forgotten' future state (locally) easily, because you have created the tag after-wild-experiment. So, say you have jumped back, haven't pushed to remote yet. You can still jump forward to revisit your experimental changes, jump back again, and repeat as needed - freely moving between states until you're ready to choose one to 'materialize' by pushing it to the remote:

Create multiple possible timelines - materialize one only by pushing

Struggling to find a better analogy, is it possible to create separate, possible 'timelines' like that, and choose one by resetting to a tag, then pushing to remote? Absolutely! You can tag multiple experimental endpoints, jump between them to compare, then reset to your chosen winner and push. Although you might just pivot back to branches at that point. 🪾 If you're maintaining multiple parallel timelines: Congrats, you've just reinvented branches with extra steps... But for quick, short-lived experiments where you want minimal overhead, tags work great.

Why would something like that work?

  • Tags are just pointers. They point to a specific commit and never move, preserving access to them.
  • Commits aren't deleted immediately. When you reset, old commits still exist as long as a tag points to them.
  • Your branch is just another pointer.
    git reset --hard tag-name simply moves your branch pointer to that commit.
  • Pushing only pushes the current state. Git sends whatever your branch currently points to. Commits reachable only via tags are ignored.

Do the tags actually change anything? Why not just reference the hashes?

You could, but tags serve two purposes:

  • They're human-readable bookmarks before-wild-experiment is a lot easier to remember and type than d412711.
  • Tags keep commits reachable. Without a tag or branch pointing to a commit, Git considers it orphaned and will eventually garbage-collect it (typically in ~2W)
  • So if you reset back and don't have a tag on your experimental commits, they'll quietly disappear from your repo forever at some point... 😢

Some pitfalls to keep in mind

  • The workflow will only work smoothly if the experimental commits aren't pushed in between tags
  • Tags are local by default. Unlike branches, tags don't get pushed automatically. Your 'bookmarks' stay private unless you run git push origin tag-name
  • git reset --hard destroys uncommitted work. Any staged or unstaged changes are gone instantly, no recovery. Commit or stash before jumping

Author:
Matthias Albert
Powered by The Information Lab
1st Floor, 25 Watling Street, London, EC4M 9BR
Subscribe
to our Newsletter
Get the lastest news about The Data School and application tips
Subscribe now
© 2025 The Information Lab