· ai and git · 12 min read
Git is the new code
AI is writing more code than ever before (if not all of it). But our most important job as developers hasn’t gone away—it’s simply changed. We spend less time typing code and more time reading, reviewing, and making sure everything works as it should. Here are some quick guidelines and git commands to help you
Neciu Dan
Hi there, it's Dan, a technical co-founder of an ed-tech startup, host of Señors at Scale - a podcast for Senior Engineers, Organizer of ReactJS Barcelona meetup, international speaker and Staff Software Engineer, I'm here to share insights on combining
technology and education to solve real problems.
I write about startup challenges, tech innovations, and the Frontend Development.
Subscribe to join me on this journey of transforming education through technology. Want to discuss
Tech, Frontend or Startup life? Let's connect.
What a time to be a developer.
A few weeks back, Spotify co-CEO Gustav Söderström made a surprising announcement during their Q4 earnings call. He said their most experienced engineers “have not written a single line of code since December.” Instead, they’re using an internal system called Honk, built on top of Claude Code, to handle everything.
They shipped over 50 new features in 2025 this way.
Before you assume this is only happening at Spotify, it’s not. Satya Nadella said AI now writes 20-30% of Microsoft’s code. Sundar Pichai confirmed that over 25% of Google’s new code is AI-assisted. Mark Zuckerberg expects AI to write most of Meta’s code within the next year. These aren’t small startups with just a few developers.
These are the biggest engineering orgs on the planet.
Addy Osmani, who works on developer experience at Google, has been tracking this shift obsessively. He wrote about what he calls “the 80% problem”: by late 2025, early adopters reported that AI was generating about 80% of their code. Sounds amazing, right? More code, faster, everybody goes home early.
Except that’s not what happened.
Faros AI and Google’s DORA report show that teams using a lot of AI merged nearly twice as many pull requests, but the time spent reviewing them also increased by about 91%.
That’s a huge jump.
Atlassian’s 2025 survey says 99% of devs using AI save over 10 hours a week, but most of them don’t feel their daily workload has gone down. The hours we’re not spending writing code? We’re now pouring into reviewing it.
And the numbers aren’t getting any prettier: PRs are about 18% bigger, incidents per PR are up 24%, and change failure rates have climbed 30%. Nearly half the code written by AI — about 45% — has security issues.
So, this is where we are now: AI generates the code, and we’re the ones left reviewing, testing, and shipping it. When things go wrong in the middle of the night, it’s still our job to fix the problems.
Which brings us to Git.
How does a British developer start version control for his side project?
git init.
Git is the new programming language. Not because you write apps in it, but because this is where you’ll spend most of your time. When AI writes the code, your job is to understand what changed, why it changed, and whether it’s safe to ship. The more you know Git — its commands, workflows, and shortcuts — the better you can review what the AI produced and catch mistakes before they reach production. The next sections cover the Git skills you need for this work.
Undoing Commits
Mistakes happen! Maybe we put the wrong message, or we forgot to add a specific file, and instead of committing again, we can simply undo our last commit.
git reset --soft HEAD~1
This undoes the last commit but keeps all changes staged exactly as they were. Working directory untouched. Fix what you need to fix, restage if necessary, recommit.
Do you need to go back further than one commit? HEAD~2, HEAD~3, etc, they do the same thing.
The key thing to remember is that --soft keeps everything, while --hard deletes everything.
Use --soft for local fixes before pushing, and be very careful with --hard, as it can permanently remove your work.
Another thing to note is that if the commit has already been pushed and your teammates have pulled it, git reset will mess up their git history.
Instead, try to use git revert, which creates a new commit that undoes the changes without altering the history, making it safe for you and your colleagues.
The Reflog
Here’s something that changed how I think about Git: it almost never truly deletes anything.
Even after a bad rebase, an accidental branch deletion, or a reset --hard, which you regret right away, the data is still there, hidden in the reflog.
git reflog
The reflog records every position HEAD has ever pointed to. Unlike git log, which shows the commit history of a branch, the reflog shows your personal movement through the repo: every checkout, commit, reset, and rebase. It’s your private timeline.
A typical reflog looks like this:
a1b2c3d HEAD@{0}: reset: moving to HEAD~2
e4f5g6h HEAD@{1}: commit: Adding a payment processing system
i7j8k9l HEAD@{2}: commit: Refactoring the auth middleware
m0n1o2p HEAD@{3}: checkout: moving from feature/auth to main
If you accidentally reset too far and have lost a commit with some important code?
You can always find it in the reflog:
git checkout e4f5g6h # inspect the lost commit
git checkout -b recovered # save it to a new branch
Entries stay for 30 to 90 days, depending on your git config.
Reading Diffs
git diff main..feature/new-auth
This command shows every change between two branches. But if you have two big AI-generated PRs, the raw diff would be overwhelming.
You can and should break it down:
git diff main..feature/new-auth -- src/auth/ # scope to a directory
git diff main..feature/new-auth --stat # summary: files changed, lines added/removed
git diff main..feature/new-auth --name-only # just the filenames
I always start with --stat. It tells you the shape of the PR before you get deep into the code.
Reviewing commit by commit
AI-generated PRs often appear as a single large commit. Which is a big NO-NO!
Luckily, developers have started the best practice of committing small and often, and now it’s much better to review the PRs one commit at a time:
git log --oneline main..feature/new-auth # list all commits in the branch
git show <commit-hash> # inspect a single commit
git log -p main..feature/new-auth # full patch for every commit
Who changed what and when
When reviewing unfamiliar code or trying to understand why something exists:
git blame src/auth/middleware.js # line-by-line authorship
git log --follow -p -- src/auth/middleware.js # full history of a single file
git blame is now one of the most important commands because it shows whether a line was written by a human, added through an AI workflow, or is part of older code that the AI may have changed.
Actually checking out the branch.
I can’t stress this enough. Every PR now contains a lot of code, and about 75% of it will be AI-generated. It’s your responsibility to pull it locally, run it, test it, and explore the changes.
If the PR added an auth flow, try logging in with incorrect credentials. If it added a payment form, see what happens if the network fails.
Best practice: the PR author should write Given/When/Then acceptance criteria with a checkbox for each scenario. The reviewer can then pull the branch and tick each one off as they walk through the actual flow.
Cherry-Picking
Most of us know git cherry-pick — grab a commit from one branch, apply it to another.
The trick most people don’t know is the --no-commit flag:
git cherry-pick -n <commit-hash>
This command brings the changes into your working directory and staging area without committing them. It’s very useful if you want to review changes before committing, combine several cherry-picks into one clean commit, or check that the changes work with your current code before making them permanent.
I always use this command when on a remote server or codespace, and I need to bring a specific commit to test it.
Stash on Steroids
Everyone knows stash exists. Almost nobody uses it properly. The typical workflow is:
git stash # throw everything in
git stash pop # get it back
That’s fine for simple cases. But the stash is actually a full stack with names, indexes, and options for selective stashing.
You can name your stashes.
git stash push -m "WIP: auth flow refactor"
git stash push -m "experimental: new caching strategy"
Now, when you list the stashes with git stash list, you can see everything you stashed:
stash@{0}: On feature/auth: experimental: new caching strategy
stash@{1}: On feature/auth: WIP: auth flow refactor
You can target specific stashes.
git stash pop stash@{1} # apply and remove a specific stash
git stash apply stash@{0} # apply and keep it around
git stash drop stash@{2} # remove without applying
pop removes the stash after applying. apply keeps it. The number in the brackets is the index on the stash. Think of it as a stack or an array.
Use apply when you want to test the same changes on multiple branches.
You can stash only specific files.
git stash push -m "just the config changes" -- config/ .env.local
Or go interactive and pick individual hunks:
git stash push -p -m "partial stash"
This command walks through each change and asks if you want to stash it, similar to how git add -p works. It’s really helpful when your working directory has changes from different tasks.
Finding Bugs with Bisect
I’ve talked about Bisect before and how useful it is.
Something is broken in production, but it worked two weeks ago. Since then, there have been 200 commits from a dozen developers and several AI agents. Checking each one by hand would take forever.
git bisect uses binary search to find the exact commit that broke things.
git bisect start
git bisect bad # current commit has the bug
git bisect good v2.3.0 # this tag was working
Git checks out a commit halfway between the two. Then you test it locally and tell Git if it’s good or bad (good meaning the defect is not here, let’s keep going):
git bisect good # this one's fine
# or
git bisect bad # this one's broken
It keeps halving until it finds the first bad commit. When you’re done:
git bisect reset
You can automate it
If you have a test that catches the bug:
git bisect start HEAD v2.3.0
git bisect run npm test
Git automatically runs the test at each step and stops when it fails.
Working on Multiple Branches with Worktrees
Context switching kills productivity. You’re deep in a code review, and someone pings you about a hotfix. The old way: stash, switch branches, do the work, switch back, pop — is tedious and error-prone.
The command git worktree lets you check out multiple branches into separate directories at the same time:
git worktree add ../hotfix main
Now you have two working directories sharing the same Git history.
~/projects/my-app/ # reviewing feature/new-dashboard
git worktree add ../my-app-hotfix hotfix/login-bug
cd ../my-app-hotfix
# fix the bug, commit, push
cd ../my-app
git worktree remove ../my-app-hotfix
Keep things tidy:
git worktree list # see all active worktrees
git worktree remove ../hotfix # clean up
git worktree prune # remove stale metadata
Rewriting History
Before pushing a feature branch, the commit history usually looks something like this:
fix typo
WIP
actually fix the bug
add feature X
WIP part 2
fix tests
This is normal, and it’s even more common when AI generates code in steps. But it doesn’t have to be part of the permanent record.
git rebase -i HEAD~6
This opens an editor showing the last 6 commits; here you can reorder, squash, reword, or drop your commits. Here is how:
- squash (s): merge into the commit above, combine messages
- fixup (f): same, but discard the message
- reword (r): keep changes, edit the message
- drop (d): remove entirely
Although you should never rebase commits that have already been pushed to a shared branch.
Seeing Who Knows What with Shortlog
When working on a big codebase, especially one you didn’t build, it helps to know who has context on what:
git shortlog -sne
-s for summary, -n for numerical sort, -e for email, and you can scope it to a directory:
git shortlog -sne -- src/auth/
This will give you the names and emails of the most active contributors in the auth folder. Invaluable when you need to track down someone who actually understands the legacy code you’re reviewing.
Pro Tip: In your CI/CD pipeline, you can automate code reviewers based on shortlog so the person with the most context is assigned as a reviewer. Although if that person is a bot, you might be in trouble.
Cheat Sheet
Some extra commands specifically useful during code review:
# compare a file across multiple branches
git diff main:src/app.js feature:src/app.js
# search commits for a string (the "pickaxe")
git log -S "deprecated_function" --oneline
# history of a specific function
git log -L :functionName:src/file.js
# graphical branch history
git log --oneline --graph --all
# files changed in the last N commits
git diff --name-only HEAD~10
We are all reviewers now.
Here’s the uncomfortable part.
Our most important job now isn’t writing code — it’s understanding it. The gap between these two skills is growing every day. Osmani himself described crossing a line he didn’t expect: an AI agent implemented a feature he’d been putting off for days, the tests passed, he skimmed it, nodded, and merged.
Three days later, he couldn’t explain how it worked.
He and others call this “comprehension debt.” You can still review code long after you’ve lost the ability to write it from scratch.
So what does responsible reviewing actually look like?
Check out the code and run it.
Read the code until you can explain it — not just until the tests pass, but until you could explain it to a teammate or debug it at 2 AM if something goes wrong.
If you can’t do that, the review isn’t finished.
Always question the architecture. AI is good at writing functions that work, but it’s not good at understanding how those functions fit into the bigger system.
Is it duplicating logic? Adding strange dependencies? These are the issues AI often misses.
And finally, take responsibility for the outcome.
This is important: when code reaches production, it doesn’t matter if a human wrote it or if it was generated by AI. If you reviewed and approved it, you are responsible for it, along with whoever or whatever wrote it.
The reviewer’s signature on a pull request is not a formality. It’s a statement: this code is correct, it’s secure, and it’s ready for production. That is true now, regardless of whether the author was a junior dev, a senior engineer, or an LLM.
The AI writes the code. You make sure it’s worth shipping.
Let’s fucking do this. 🚀