← Back to Blog

Mastering Git: Advanced Workflows

By Sabbir AI

Let me tell you about the day I accidentally force-pushed to main and deleted 3 days of work from 4 different developers. Everyone was very calm and understanding. Just kidding—they wanted to murder me. That's when I realized I needed to actually learn Git beyond "git add, git commit, git push."

Most developers use like 5% of Git's capabilities. We know the basics: commit, push, pull, maybe branch if we're feeling fancy. But Git is insanely powerful once you dig deeper. The problem? The documentation reads like it was written by robots for robots.

I'm going to show you the Git workflows I use daily that make me look way smarter than I actually am. These are the commands that'll make your coworkers think you're a Git wizard, even though you're just someone who read this blog post.

Interactive Rebase (Your Commit History's Best Friend)

Here's a scenario: you've been working on a feature branch all day. You've made 47 commits with messages like "fix", "actually fix", "why doesn't this work", "FINALLY", and "removed console.log". Now you need to merge to main and you realize your commit history looks like a crime scene.

This is where interactive rebase saves your reputation.

git rebase -i HEAD~10  # Last 10 commits

This opens an editor showing your commits. Now you can:

  • pick - Keep the commit as-is
  • squash - Combine with previous commit
  • reword - Change the commit message
  • edit - Amend the commit content
  • drop - Delete the commit entirely

So you can turn this mess:

pick abc123 Add user authentication
pick def456 fix
pick ghi789 fix again
pick jkl012 actually works now
pick mno345 removed console.log

Into this beauty:

pick abc123 Add user authentication
squash def456 fix
squash ghi789 fix again
squash jkl012 actually works now
squash mno345 removed console.log

Result? One clean commit: "Add user authentication" with all those fixes squashed into it. Your commit history looks professional instead of like a panicked stream of consciousness.

Pro tip: Only rebase commits that haven't been pushed to a shared branch. If others are working on the same branch, rebasing will create conflicts and people will hate you (trust me on this one).

Git Bisect (The Bug Hunting Time Machine)

Picture this: your app worked fine last week. Now there's a bug. You've made 50 commits since then. Which one broke it? You could manually test each commit like a caveman, or you could use git bisect and feel like a time-traveling detective.

Git bisect does a binary search through your commit history to find exactly when a bug was introduced. Here's how it works:

git bisect start
git bisect bad  # Current commit has the bug
git bisect good abc123  # This old commit was fine

Git checks out a commit halfway between good and bad. You test it:

# If the bug exists:
git bisect bad

# If it works fine:
git bisect good

Git keeps narrowing it down, checking out different commits. You keep marking them good or bad. After 5-10 steps (depending on your commit count), it tells you EXACTLY which commit introduced the bug.

I used this last month to track down a bug that was introduced 200 commits ago. Found it in 8 steps. Would've taken hours manually.

Want to automate it? If you can write a script that tests for the bug (exits with 0 if good, 1 if bad), you can automate the whole thing:

git bisect start HEAD abc123
git bisect run npm test

Git will automatically run your tests on each commit and find the culprit. It's like having a robot detective working for you.

Git Worktrees (Work on Multiple Branches Without Going Insane)

You're deep in feature development. Suddenly: "URGENT BUG IN PRODUCTION!" You need to fix it immediately, but your current branch is a disaster of uncommitted changes and you don't want to lose context.

Most people do this horrible dance:

git stash
git checkout main
# fix bug
git checkout feature-branch
git stash pop

There's a better way: git worktrees. It lets you have multiple working directories for the same repo, each on different branches.

# Create a new worktree for the hotfix
git worktree add ../myproject-hotfix main

# Now you have two directories:
# myproject/ - on feature-branch
# myproject-hotfix/ - on main

Work on the hotfix in the new directory. Meanwhile, your original directory stays exactly as it was. No stashing, no losing context, no mental overhead.

When you're done with the hotfix:

cd ../myproject-hotfix
git commit
git push
cd ../myproject
git worktree remove ../myproject-hotfix

I use this constantly. I have a dedicated worktree for quick experiments. One for reviewing PRs. One for my main work. No more branch-switching anxiety.

Git Reflog (Your Safety Net)

Remember when I said I force-pushed to main and deleted everyone's work? Git reflog is what saved me from being fired that day.

Reflog is Git's secret history. It records EVERYTHING—every commit, every reset, every rebase, every branch switch. Even commits you deleted.

git reflog

This shows you everything you've done recently:

abc123 HEAD@{0}: commit: Add feature
def456 HEAD@{1}: rebase: Fix bug
ghi789 HEAD@{2}: reset: moving to HEAD~1
jkl012 HEAD@{3}: commit: Oops deleted this

See that commit you deleted with git reset? It's still there. You can recover it:

git checkout jkl012

This is how I recovered the deleted work. I found the commit hash in the reflog from before the force-push and restored it. Crisis averted. Reputation slightly less damaged.

Pro tip: Reflog entries expire after 90 days. So you have a 3-month safety net for any Git disasters.

Cherry-Pick (Stealing Commits Like a Pro)

You made a commit on the wrong branch. Or you want to take just ONE commit from a feature branch and apply it elsewhere. Cherry-pick is your friend.

git checkout main
git cherry-pick abc123  # Apply commit abc123 to current branch

That commit is now on main, without bringing any other commits from the feature branch. Super useful for:

  • Hotfixing production with a commit from a feature branch
  • Moving a commit that was made on the wrong branch
  • Creating a release branch with only specific features

You can cherry-pick multiple commits:

git cherry-pick abc123 def456 ghi789

Or a range of commits:

git cherry-pick abc123..ghi789

Git Stash (But Make It Useful)

Everyone knows git stash. But most people just do git stash and git stash pop and wonder why their stash list is a graveyard of forgotten changes.

Here's how to actually use stash effectively:

# Name your stashes
git stash save "Work in progress on auth feature"

# List all stashes
git stash list

# Apply a specific stash without removing it
git stash apply stash@{2}

# View what's in a stash
git stash show -p stash@{0}

# Delete a specific stash
git stash drop stash@{1}

# Clear all stashes
git stash clear

I also use stash for quick experiments:

# Stash current work
git stash

# Try something risky
# ... make experimental changes ...

# If it worked:
git stash drop

# If it failed:
git reset --hard HEAD
git stash pop  # Back to safety

Git Blame (Without the Shame)

Git blame gets a bad rap because it sounds accusatory. But it's incredibly useful for understanding WHY code exists.

git blame filename.js

Shows you who changed each line and when. But here's the better way to use it:

git blame -L 10,20 filename.js  # Only show lines 10-20
git blame -w filename.js  # Ignore whitespace changes
git blame -C filename.js  # Detect lines moved/copied from other files

The real power move? Combine it with show to see the full context of why a change was made:

git blame filename.js | grep "weird function"
# Find the commit hash
git show abc123

Now you can see the commit message, the PR discussion, and understand the business context. Suddenly that "weird" code makes sense—it was a hotfix for a critical bug at 2 AM.

The Git Workflow That Actually Works

Here's the workflow I use on every project. It's not complicated, but it prevents 90% of Git disasters:

1. Feature branches for everything

git checkout -b feature/user-authentication

Never commit directly to main. Ever. Features get branches. Bugs get branches. Everything gets a branch.

2. Commit early and often (on your branch)

git add .
git commit -m "WIP: halfway through auth logic"

Don't worry about perfect commits yet. Just save your progress frequently. You'll clean it up with interactive rebase later.

3. Before merging, clean up your history

git rebase -i main  # Rebase on latest main
# Squash/reword commits to make them clean
git push --force-with-lease  # Safely update remote branch

Now your feature branch has a clean history ready for review.

4. Use pull requests for code review

Even if you're the only developer, create PRs. It forces you to review your own changes and write a description. You'll catch bugs.

5. After merge, delete the branch

git branch -d feature/user-authentication
git push origin --delete feature/user-authentication

Keep your branch list clean. Old branches are just clutter.

Git Aliases (Because Life's Too Short)

I'm lazy. I don't want to type git status 50 times a day. So I set up aliases:

git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.cm commit
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'
git config --global alias.visual 'log --oneline --graph --all'

Now git st shows status. git visual shows a beautiful commit graph. git unstage filename unstages a file.

My favorite alias:

git config --global alias.oops 'commit --amend --no-edit'

Forgot to add a file to your last commit? git add filename && git oops. Done.

When to Use Merge vs Rebase

This causes more arguments than tabs vs spaces. Here's my take:

Use merge when:

  • Bringing main into your feature branch (preserves history)
  • Merging pull requests (creates a clear merge commit)
  • You've already pushed commits and others might be using them

Use rebase when:

  • Cleaning up your feature branch before PR (interactive rebase)
  • Keeping your branch up to date with main (rebase on main)
  • You haven't pushed yet or you're the only one on the branch

The golden rule: Never rebase commits that exist on a shared branch. You'll create duplicate commits and confuse everyone.

Recovering From Disasters

Because you WILL mess up eventually. We all do.

Accidentally committed to main instead of a branch?

git branch feature-branch  # Create branch at current commit
git reset --hard HEAD~1  # Move main back one commit
git checkout feature-branch  # Your work is safe here

Committed secrets/sensitive data?

# Remove file from history (nuclear option)
git filter-branch --force --index-filter   "git rm --cached --ignore-unmatch secrets.env"   --prune-empty --tag-name-filter cat -- --all

# Then change those secrets immediately!

Merged the wrong branch?

git revert -m 1 abc123  # Revert merge commit

Final Thoughts

Git is one of those tools where investing time to learn it properly pays dividends forever. You'll use it every single day for the rest of your career. Might as well get good at it.

Start with interactive rebase and git reflog. Those two alone will make you more confident working with Git. Then gradually add the other commands as you need them.

And remember: Git is really hard to permanently break. Almost everything is recoverable with reflog. So experiment, try things, and don't be afraid to mess up. That's how you learn.

Now go forth and rebase with confidence. And for the love of all that's holy, don't force-push to main.