There are some more advanced git commands that don’t come up in every work session. When the need does come up though, knowing they exist is helpful. This article covers those more advanced git commands and when to use them.

If you haven’t read Git Commands Worth Learning First, start there. That article covers the commands you’ll use in almost every work session. This one picks up where it left off.

Stash

Git won’t let you switch branches if you have uncommitted changes. It’s a safety guard. Switching with open changes risks mixing work from two different contexts.

But sometimes you’re mid-feature and something urgent comes up on a different branch. You’re not ready to commit what you have, and you don’t want to lose it. Stash is the solution. It takes all your uncommitted changes, saves them to a temporary holding area, and reverts your working directory back to the last clean commit. Your branch is clean. You can switch freely. When you come back, you restore everything exactly where you left off.

It’s the clean alternative to making a throwaway commit just to clear your working directory. Throwaway commits pollute your history. Stash doesn’t.

git stash and git stash pop

Save your current changes to the stash:

git stash

Handle whatever needed attention on the other branch. When you’re back and ready to continue, pop the stash to restore everything:

git stash pop

pop applies the most recent stash and removes it from the holding area. Your working directory is back to where you left off.

If you stash more than once without popping, the stashes stack. There’s a git stash list command for managing that. But in my experience, it rarely comes up. If you find yourself juggling multiple stashes, it’s worth looking into, but most of the time a single stash and pop is all you need.

Fixing Commits

This group covers three tools: amend, reset, and revert. They all undo or correct committed work, but they behave very differently.

Amend and reset rewrite history. The original commit is replaced or removed. That’s only safe on commits that haven’t been pushed to a remote yet. Once a commit is on GitHub and potentially on someone else’s machine, rewriting it causes problems.

Revert doesn’t touch history at all. It creates a new commit that reverses the changes. The original commit stays exactly where it is. That makes it the right tool for anything already pushed.

As a rule of thumb: local commits that haven’t been pushed are fair game for amend and reset. Pushed commits get reverted.

Amend: Editing the Last Commit

After you commit, you realize the message has a typo. Or you forgot to stage one file. Or you committed with the wrong Git username configured. Amend lets you fix the most recent commit without creating a new one.

To fix the commit message:

git commit --amend -m "feat: add SaveManager with slot support"

To add a file you forgot without changing the message, stage it first:

git add forgotten-file.cs
git commit --amend --no-edit

--no-edit keeps the existing message exactly as it was. Only the staged content changes.

One firm rule: only amend commits that haven’t been pushed to a remote yet. Amend rewrites history. The commit gets a new hash. If that commit already exists on GitHub, your local version and the remote version will now disagree, and pushing will fail or cause problems for anyone else on the branch.

Amend is safe on local-only commits. Once something is pushed, use revert instead.

Soft Reset: Undoing a Commit, Keeping the Changes

A soft reset moves your branch pointer back by one or more commits, but keeps all the changes from those commits staged and ready to work with. Nothing is lost. You’re just unwinding the commit itself.

git reset --soft HEAD~1

HEAD refers to the most recent commit on your current branch. HEAD~1 means “one commit back.” HEAD~3 would go back three commits.

After a soft reset, your files are unchanged. Your changes are still staged. The commit is just gone.

Use this when you committed too early, want to rewrite a message on something older than the last commit, or need to split one large commit into several smaller ones. It gives you the work back and lets you re-commit it however you want.

Hard Reset: Undoing a Commit and Discarding the Changes

A hard reset moves the branch pointer back and throws everything away. The commits are gone. The file changes are gone. There is no undo.

git reset --hard HEAD~1

Use this when you want to completely discard recent work. It’s the right call when you’ve gone down a wrong path and want to start clean from a known good state. But be certain before you run it. There’s no recovering from a hard reset.

Like amend, don’t hard reset commits that have already been pushed. Rewriting history on a shared branch causes real problems for anyone else working from it.

Revert: The Safe Undo for Pushed Commits

Revert is the answer to a specific problem: you need to undo a commit that’s already been pushed to a remote branch.

Amend and reset rewrite history. That’s fine locally, but it breaks things on shared branches. Revert takes a different approach. Instead of rewriting anything, it creates a brand new commit that reverses the changes from the target commit. The original commit stays in the history. The revert commit gets added on top.

git revert HEAD

That reverts the most recent commit. You can also target a specific commit by hash:

git revert a3f82bc

The history ends up with both the original commit and the revert sitting next to each other. It’s a little noisier, but it’s safe. No history is rewritten. No one else’s branch breaks. For anything that’s already been pushed, revert is the right call.

Cherry-Pick

Cherry-pick copies a single commit from one branch and applies it to another, without merging the entire branch.

Here’s the situation it solves. You’re working on feature/save-system. Partway through, you fix a bug in the player controller. That bug fix is one commit buried inside an unfinished feature branch. You need the fix on development now, but the feature isn’t ready to merge. You can’t merge the whole branch. You just want that one commit.

Cherry-pick lets you grab exactly that commit. The proper move is to cherry-pick it into a dedicated bugfix/ branch and merge that into development through a PR, keeping with the branching workflow. In practice, a lot of developers skip that step for a single commit fix. But the branch approach is the more consistent call in my opinion.

git cherry-pick

First, find the commit hash. Use git log on the source branch, or find it in the GitHub commit history.

git log --oneline feature/save-system

Switch to the branch you want to apply it to, then run:

git cherry-pick a3f82bc

Git applies that commit to your current branch as a new commit. The original commit on the feature branch is unchanged.

The bug fix scenario above is the most common case. It also works when you accidentally committed something to the wrong branch and need to move it. Keep it to targeted, specific situations. It’s a precise tool, not a general-purpose one.

Rebase

To understand rebase, you need to understand what a branch actually is in Git.

When you create a feature branch, it splits off from development at a specific commit. Think of that split point as the branch’s starting point. Every commit you make on the feature branch builds from there.

The problem is that development doesn’t stand still. While you’re working on your feature, other branches finish and merge into development. By the time your feature is ready, development has moved several commits ahead of where your branch started. Your branch is now out of date.

When you merge an out-of-date branch, Git has to record that the two lines of history converged. It does that with a merge commit: a special commit that says “these two histories came together here.” The history looks like two separate tracks joining at a point.

Rebase moves your branch’s starting point. Instead of forking off an old commit on development, it re-attaches your branch to the current tip of development. Then it replays your commits one by one on top of that new starting point. Your work is the same. The starting point is just newer.

The result is a branch that looks like it was created today, from the latest version of the project. When it merges, there’s no divergence to record. No merge commit. Clean, linear history.

git rebase

To rebase your current feature branch onto development:

git rebase development

After this, your branch contains all the latest changes from development, plus your work on top. Opening a PR from here produces a clean, linear diff.

Interactive Rebase: Cleaning Up Before a PR

Interactive rebase goes further. It lets you edit, reorder, squash, or drop individual commits before they land in a review.

git rebase -i HEAD~3

That opens a text editor listing the last three commits. Each line has a command prefix. The default is pick, which keeps the commit as-is. Change it to squash to fold a commit into the one above it. Change it to reword to edit the message. Change it to drop to remove the commit entirely.

It’s useful when your feature branch has a lot of “wip” or “fix” commits from working things out. Before the PR, you can squash those down into clean, intentional commits. The reviewer sees the polished version, not the process.

The Rule: Never Rebase a Shared Branch

Rebase rewrites history. Every commit gets a new hash. That’s fine on a branch only you are working on.

But if another developer has pulled your branch and is working from it, their local version and the rebased remote version will have completely different histories. When they try to push or pull, Git won’t know how to reconcile them. It’s a painful situation to untangle.

Keep rebase to your own feature branches before they’ve been shared. Once a branch is out there and others are working from it, use a standard merge or revert instead. The rule is simple: if in doubt, don’t rebase.

If Rebase Doesn’t Make Sense

Rebase was one of the concepts I struggled with the most when learning Git. It’s also one feature I use the least. I’ve used it professionally a few times. As a solo developer working on my own projects, I’m honestly not sure I’ve ever had a real reason to use it. If it doesn’t fully click yet, that’s fine. Know that it exists and roughly what it’s for. When you actually need it, then you can go deeper.

Wrapping Up

Stash handles the “not ready to commit but need to switch” problem. Amend, soft reset, and hard reset all correct commits in different ways, with escalating consequences. Revert is the safe undo for anything already pushed. Cherry-pick is for moving a single commit between branches. Rebase produces clean, linear history before a merge.

All of these commands have real uses, and I use them when I need them. But combined, they probably account for 10% of my actual Git usage (and that’s being generous). The value isn’t in memorizing them. It’s in knowing they exist. When you hit the situation each one solves, you’ll know what to look up.

Categories: Tutorials

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *