본문으로 건너뛰기 Git Undo & History | reset, revert, and rebase explained

Git Undo & History | reset, revert, and rebase explained

Git Undo & History | reset, revert, and rebase explained

이 글의 핵심

reset vs revert vs restore, interactive rebase (squash, fixup, reword), conflict resolution, force push and reflog recovery, merge vs rebase team rules—practical Git workflow.

Git in practice #4: Undo, rebase, and cleanup

Previous: Remote repos and collaboration (#3) covered push, pull, and PRs. When you need to undo commits or tidy history, the usual tools are reset (move HEAD and optionally index/worktree), revert (create a new commit that undoes another), and rebase (replay commits on top of another tip). This article explains what each does and when to be careful. After reading you will:

  • Understand git reset --soft, --mixed, --hard, and how they differ from git restore / git switch
  • Know when to pick git revert vs git reset, including after a push
  • Use git rebase, interactive rebase (squash, fixup, reword, edit), and resolve rebase conflicts
  • Recognize risks of git reset --hard and git push --force, recover with reflog, and align with merge vs rebase team rules

Table of contents

  1. Overview
  2. git reset
  3. git revert
  4. git rebase
  5. Practical cautions
  6. FAQ
  7. Command comparison
  8. Rebase deep dive
  9. Scenarios
  10. Dangerous commands and recovery
  11. History and team policy

1. Overview

GoalCommandBehavior
Undo the last commit but keep changes stagedgit reset --soft HEAD~1Moves HEAD; index and working tree unchanged
Undo commit + unstagegit reset HEAD~1 (or --mixed)Commit and staging undone; files stay modified
Hard reset to a commit (destructive)git reset --hard HEAD~1Commit, index, and working tree match that commit
Undo a pushed change safelygit revert <commit>Adds a new commit that reverses the target
Linear history on top of maingit rebase mainReplays your commits (rewrites hashes)
If a commit is already on the remote, avoid “deleting” it with reset --hard + force push—use revert to add an explicit undo commit. Use rebase mainly on local branches not yet pushed (or per team rules).

2. git reset

reset —soft

Only removes the commit; staging and working tree stay:

git reset --soft HEAD~1

Typical uses:

# Fix the last commit message
git commit -m "Fix bug"
git reset --soft HEAD~1
git commit -m "Fix authentication bug"
# Add a forgotten file to the last commit
git commit -m "Add feature"
git reset --soft HEAD~1
git add forgotten_file.js
git commit -m "Add feature"
# Squash last 3 commits locally
git reset --soft HEAD~3
git commit -m "Implement user authentication"

HEAD shortcuts: HEAD~1, HEAD^, HEAD~3, or a specific hash.

reset —mixed (default)

Undoes the commit and staging; changes remain unstaged.

git reset HEAD~1
# or
git reset --mixed HEAD~1

Then git add selectively and commit again.

reset —hard

Moves commit, index, and working tree to the target—uncommitted work is lost. Stash or branch first if you might need it.

git reset --hard HEAD~1

Avoid on shared branches; combined with force push it rewrites teammates’ bases.

3. git revert

revert applies the inverse of a commit as a new commit—history stays linear and auditable.

git revert HEAD
git revert abc1234
git revert -m 1 <merge_commit_sha>   # pick parent for merge commits

reset vs revert (already pushed):

# reset: removes C from history (bad if others pulled C)
# revert: adds C' that undoes C (safe for collaboration)

Conflicts during revert: fix files → git addgit revert --continue, or git revert --abort.

4. git rebase

Rebase replays your branch’s commits on top of another tip (often main). Commit hashes change—do not rebase commits others have already pulled unless coordinated.

git switch feature/login
git rebase main

On conflicts: edit → git addgit rebase --continue, or git rebase --abort to cancel. Rules of thumb:

  • Prefer rebase on local-only feature branches.
  • Do not rebase shared integration branches like main casually.

5. Practical cautions

  • Before push: reset/rebase locally as you like.
  • After push: prefer revert for bad releases; force push only with team agreement.
  • merge vs rebase: many teams merge into main and rebase feature branches only—pick one policy and stick to it.

6. FAQ

Can I recover after reset --hard?

Often yes, immediately via git reflog to find the old HEAD and reset to it. Older entries may be pruned—do not rely on it as backup.

revert vs reset?

reset moves HEAD and can drop commits from the recorded history. revert adds a new commit; safer when others share the branch.

Conflicts during rebase?

Edit conflict markers, git add the files, git rebase --continue. Use git rebase --abort to go back to pre-rebase state.

7. Command comparison

A quick lookup table for “how do I undo X” across the tools this article covers, plus the two you’ll reach for less often (restore, switch) but that avoid ambiguity in scripts and muscle-memory habits.

SituationCommandAffects history?Safe after push?
Unstage a file, keep editsgit restore --staged <file>No
Discard uncommitted edits to a filegit restore <file>No✅ (but destroys local edits)
Undo last commit, keep changes stagedgit reset --soft HEAD~1Yes (local)⚠️ only if unpushed
Undo last commit, unstage changesgit reset HEAD~1Yes (local)⚠️ only if unpushed
Undo last commit, discard changesgit reset --hard HEAD~1Yes (local)❌ never on pushed/shared
Undo an already-pushed commitgit revert <sha>No (adds new commit)
Move current branch onto another tipgit rebase <branch>Yes (rewrites hashes)⚠️ only if unpushed or coordinated
Switch branchesgit switch <branch>No
Rule of thumb: if the commit has been pushed and anyone else might have pulled it, prefer commands in the right column marked ✅ — they add history instead of rewriting it.

8. Rebase deep dive

Interactive rebase: squash, fixup, reword

git rebase -i opens an editable list of commits, letting you reshape recent history before it’s shared.

git rebase -i HEAD~5
pick a1b2c3d Add login form
squash e4f5g6h Fix typo in login form
fixup h7i8j9k Remove debug print
reword k1l2m3n Add password validation
pick n4o5p6q Add remember-me checkbox
  • pick: keep the commit as-is
  • squash: merge into the previous commit, keeping both messages (editable)
  • fixup: merge into the previous commit, discarding this commit’s message
  • reword: keep the commit’s changes, edit only its message
  • edit: pause here to amend the commit’s content

--onto: replaying onto a different base

--onto moves a range of commits to a new base without carrying commits from the branch’s old base — useful when a feature branch was accidentally built on top of another unmerged feature branch.

# Move commits unique to feature (since it diverged from old-base)
# onto new-base instead
git rebase --onto new-base old-base feature

Autosquash

Combine git commit --fixup=<sha> with git rebase -i --autosquash to automatically place fixup commits next to their target without manually reordering the todo list.

git commit --fixup=a1b2c3d
git rebase -i --autosquash HEAD~5   # fixup line is pre-sorted into place

9. Scenarios

“I committed to the wrong branch"

git branch correct-branch      # create a branch at the current (wrong) commit
git reset --hard HEAD~1        # remove the commit from the wrong branch
git switch correct-branch      # the commit now lives here instead

"I need to split one commit into two"

git rebase -i HEAD~3   # mark the commit as `edit`
git reset HEAD~1       # commit is undone, changes are staged
git add -p             # stage half the changes
git commit -m "First half"
git add .
git commit -m "Second half"
git rebase --continue

"I want to drop a commit entirely, not just its message"

git rebase -i HEAD~5   # delete the line for that commit, or mark it `drop`

"My feature branch is 40 commits behind main and has conflicts everywhere”

Rebasing all 40 commits at once often means resolving the same conflict repeatedly. git rebase --rebase-merges combined with a merge of main first (creating one conflict-resolution point) is usually less painful than a raw rebase across a long-diverged branch — check team policy before choosing either.

10. Dangerous commands and recovery

The danger list

CommandWhy it’s dangerousRecovery path
git reset --hardDiscards uncommitted work permanentlygit reflog finds the pre-reset HEAD, but uncommitted (never-staged) work is unrecoverable
git push --forceOverwrites remote history; can erase others’ pushed commitsThe remote’s reflog (if available) or the collaborator’s local copy
git clean -fdDeletes untracked files and directories with no undoNone — there is no reflog for untracked files
git branch -DForce-deletes a branch even with unmerged commitsgit reflog still shows the commit’s SHA if you find it before GC

Recovering with reflog

git reflog records every place HEAD has pointed, including commits that no longer belong to any branch — this is the safety net for most “I think I lost work” situations, as long as garbage collection hasn’t run yet.

git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# e4f5g6h HEAD@{1}: commit: Add feature X   ← the "lost" commit
git reset --hard e4f5g6h   # restore it
# or, to keep current work and just recover the commit as a branch:
git branch recovered-work e4f5g6h

push --force vs push --force-with-lease

--force-with-lease refuses to push if the remote has commits you haven’t fetched yet — it protects against silently overwriting a teammate’s work that landed after your last fetch, which plain --force does not check.

git push --force-with-lease   # ✅ fails safely if remote moved since your last fetch
git push --force               # ⚠️ overwrites unconditionally

11. History and team policy

Merge vs rebase workflows

PolicyHow feature branches land on mainHistory shape
Merge commitsgit merge feature (no --ff-only)Preserves branch topology, every merge visible
Rebase + fast-forwardgit rebase main then git merge --ff-only featureLinear history, no merge commits
Squash mergegit merge --squash featureOne commit per feature, internal commits discarded

Choosing a policy

  • Merge commits: best when you want to preserve exactly how a feature was built, or need to trace which merge introduced a bug (git log --merges)
  • Rebase + fast-forward: best for small teams that want a clean, linear git log and are comfortable with local rebasing before push
  • Squash merge: best when a feature branch has messy work-in-progress commits and only the final combined result matters to main’s history

The one rule that matters most

Whatever the team picks, the non-negotiable part is: never rewrite history on a branch other people have already pulled from, unless the whole team is explicitly coordinating the rewrite (e.g. a scheduled history cleanup). Rebase and reset --hard are safe experimentation tools on your own unpushed or exclusively-owned branches; they are a liability the moment a commit is shared.

Closing

  • reset --soft / --mixed: undo commits (and maybe staging) while keeping work—great locally.
  • reset —hard: destructive; avoid on pushed shared branches.
  • revert: add an undo commit—preferred for shared history.
  • rebase: linearize history—best on unpushed feature branches. One line: Use reset/rebase locally; use revert on shared branches. Continue from the Git series index. Previous: #3 Remote collaboration

Reset touches three layers (summary)

flowchart LR
  subgraph layers [Per commit]
    H[HEAD / commit]
    I[Staging index]
    W[Working tree]
  end
  H --> I --> W

--soft moves HEAD only; --mixed also matches the index; --hard aligns all three to the target commit.

More on this site


Practical tips

Debugging

  • Read compiler and linter messages first
  • Reproduce with a minimal example

Performance

  • Measure before optimizing
  • Define what “fast enough” means

Code review

  • Match team conventions
  • Check edge cases reviewers care about

Checklist

Before you rewrite history

  • Is this the smallest safe change?
  • Will teammates understand the result?
  • Any performance or release constraints?

While resolving conflicts

  • Are merges intentional and tested?
  • Edge cases covered?
  • Error handling appropriate?

During review

  • Intent clear?
  • Tests updated?
  • Docs updated if behavior changed?

Git, undo, reset, revert, rebase, reflog, restore, interactive rebase, force push, commit history


Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. reset vs revert vs restore, interactive rebase (squash, fixup, reword), conflict resolution, force push and reflog.

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers Git, Undo, reset, revert, rebase, git reset, git revert, reflog, restore, interactive rebase.