git reset hard is the command everyone eventually googles mid-panic, and the honest answer is that "git reset" isn't one operation — it's a small family of commands that all move a branch pointer, but disagree wildly on what else they touch. Some modes are completely reversible. One mode can permanently delete uncommitted work in a fraction of a second, with no confirmation prompt. This guide is a scenario-driven reference for the whole git reset family: unstaging a file, resetting to an arbitrary commit, hard resetting to match GitHub, and recovering when you got a mode wrong. If you specifically want to undo your most recent commit and are deciding between --soft, --mixed, and --hard for that one case, the git undo last commit guide covers that scenario end to end. This one covers everything git reset does beyond that.

Quick Answer: Every git reset Command at a Glance

If you just want to know how to reset hard, unstage a file, or reset to a commit right now, here are the six commands that cover nearly every real-world situation. Full explanations, including exactly what each one is safe to run against, follow in the sections below.

Situation Command What it touches
Unstage a file, keep your edits git restore --staged file Index only
Undo the last commit, keep changes staged git reset --soft HEAD~1 Branch pointer only
Undo the last commit, keep changes unstaged git reset HEAD~1 (mixed, the default) Branch pointer + index
Discard the last commit and its changes entirely git reset --hard HEAD~1 Branch pointer + index + working tree
Reset to a specific older commit git reset --hard commit-id Branch pointer + index + working tree
Hard reset github: match a remote exactly git fetch origin && git reset --hard origin/main Branch pointer + index + working tree

Notice the pattern: every mode moves the branch pointer, some also touch the index, and only --hard touches your working tree files. That single axis — how far down the stack a mode reaches — is the entire mental model behind git reset, and it's covered properly in the next two sections. For the full official flag list, see the git-reset documentation.

What Does git reset Do? Moving a Branch Pointer

At its core, git reset does one thing: it moves where your current branch points to. A branch in git is nothing more than a named pointer to a single commit — when you commit, git creates a new commit object and moves the pointer forward to it. git reset moves that pointer to a commit you specify instead, which can mean going backward (undoing commits) or, less commonly, forward or sideways to a commit reachable some other way.

Everything else git reset does is a side effect of which mode you choose, layered on top of that pointer move. Run git reset with no commit argument at all and it defaults to git reset --mixed HEAD — it doesn't move the pointer anywhere (HEAD is already where you are), it just unstages everything currently staged. That's the answer to "what does git reset do" when people run it bare and get confused that nothing about their commit history changed: nothing was supposed to. Add a target — a commit ID, a branch name, or a relative reference like HEAD~1 — and the pointer actually moves, which is what undoes commits.

# Bare git reset: unstages everything, moves nothing
git reset

# Same thing, explicit
git reset --mixed HEAD

# With a target, the branch pointer actually moves
git reset HEAD~1

The command's power and its danger come from the same source: it directly rewrites what your branch points to and, in --hard mode, what your files contain, without asking for confirmation. There's no undo dialog. That's not a design flaw — git assumes you know what you're targeting — but it's exactly why the reflog recovery workflow later in this guide is worth reading before you need it, not after.

The Three Trees: HEAD, Index, Working Tree

Git tracks three separate representations of your project at once, and every reset mode is defined by exactly which of these three it updates. Once this model clicks, you stop needing to memorize which flag does what — you can derive it.

  • HEAD — technically a pointer to your current branch, which in turn points to a commit. This is your last committed snapshot.
  • The index (also called the staging area) — the snapshot that will become your next commit if you run git commit right now. This is what git add populates.
  • The working tree — the actual files sitting on your disk, the ones your editor has open.
Same three trees, different reset reach HEAD current commit pointer Index (Staging) what git add populated Working Tree files on disk, editor sees these --soft pointer moves, nothing else --mixed (default) + index resets too --hard — HEAD + Index + Working Tree, all overwritten Bar length shows how far each mode rewinds the pipeline.
Work flows left to right through HEAD, the index, and the working tree — a reset mode is defined by how far back through that pipeline it rewinds.

Normal work flows left to right through these three trees: you edit files in the working tree, git add copies the change into the index, and git commit copies the index into a new commit, moving HEAD forward. git reset mode selection is just choosing how far back through that pipeline you want to rewind: --soft only rewinds HEAD, --mixed rewinds HEAD and the index, and --hard rewinds all three. Git's own documentation calls this exact mental model "Reset Demystified," and it's worth reading once in full — see the Git Tools: Reset Demystified chapter for the canonical explanation with its own diagrams.

git reset --soft vs --mixed vs --hard

With the three-trees model in place, the three main modes are almost self-explanatory:

# --soft: moves HEAD only. Index and working tree untouched.
# Changes from the "undone" commit land back as staged edits.
git reset --soft HEAD~1

# --mixed (the default if you omit the flag): moves HEAD and resets
# the index to match. Working tree untouched — edits become unstaged.
git reset HEAD~1
git reset --mixed HEAD~1

# --hard: moves HEAD, resets the index, AND overwrites the working
# tree to match. This is the only mode that can destroy uncommitted work.
git reset --hard HEAD~1

git reset soft head is the safest of the three to experiment with, because it never touches a file on disk — worst case, you re-run git commit and you're back where you started. The mixed reset sits in the middle: it unstages cleanly but leaves your edits intact in the working tree, which is why plain git reset HEAD~1 is the default behavior when you omit a mode flag entirely.

--hard is different in kind, not just degree. Because it overwrites the working tree, running git reset --hard against a commit that's behind your current state deletes any uncommitted edits sitting on top of it — instantly, silently, and with no recovery path if those edits were never committed anywhere. That's the entire reason "git reset soft vs hard" is such a common comparison search: the gap in risk between the two isn't small, it's categorical.

A quick note on naming, since "git reset force" shows up in search fairly often: there is no --force flag on git reset. People searching that phrase usually mean --hard, which does force an overwrite of your working tree without asking — or they mean git push --force, an unrelated command that forces a remote branch to accept history it would otherwise reject. The two are easy to conflate because both mean "stop checking and just do it," but they operate on completely different things: one rewrites your local files, the other rewrites a remote branch.

Comparison Table: What Each Mode Touches

Mode HEAD / branch pointer Index (staged) Working tree (files) Can lose uncommitted work?
--soft Moved Unchanged Unchanged No
--mixed (default) Moved Reset to match target Unchanged No
--hard Moved Reset to match target Overwritten to match target Yes
--keep Moved Reset to match target Refuses if local changes conflict with the move No — it aborts instead
Four modes, three zones — what gets touched HEAD Index Working Tree --soft Moved unchanged unchanged --mixed Moved Reset unchanged --hard Moved Reset Overwritten --keep Moved Reset checked aborts on conflict
Soft only moves HEAD. Mixed also resets the index. Hard overwrites all three — the only mode that can destroy uncommitted work. Keep resets HEAD and the index but refuses to overwrite conflicting files.

--keep is worth knowing even though it's rarely the first choice: it behaves like --hard for files that don't conflict with the reset, but refuses to touch files where your working-tree changes would collide with the target commit — an escape hatch when you want most of a hard reset's behavior without the blind-overwrite risk. It's a narrow tool, but it exists precisely because --hard's all-or-nothing overwrite is sometimes more than you want.

git reset HEAD: Unstaging Files Without Losing Work

A large share of people who search "git reset head" aren't trying to undo a commit at all — they staged a file with git add by mistake and want it back out of the index without touching their edits. This is the single most common everyday use of git reset, and it deserves to be treated as its own operation rather than a footnote to commit-undo.

# Classic form: unstage one file, leave the edits in place
git reset HEAD path/to/file.js

# Unstage everything staged, leave all edits in place
git reset HEAD

# Modern equivalent (Git 2.23+), same result, clearer name
git restore --staged path/to/file.js

Both commands do the same thing here: they reset the index entry for that file back to how it looked at HEAD, without touching the working-tree copy at all. Your edits are never at risk in either form — this operation never reaches the working tree, which is exactly why it's safe to run liberally. git reset HEAD~1 and git reset HEAD file look similar but do fundamentally different things: the first moves the branch pointer and undoes a commit; the second doesn't move HEAD at all, it just changes what's staged.

Unstaging only moves the index entry Working Tree your edited file never touched here Index (Staged) git add populates this snapshot HEAD (Committed) last commit not involved here git add git reset HEAD file / git restore --staged file The working tree copy is identical before and after — only where the file is tracked changes.
git add copies a file into the index; git reset HEAD file (or the newer git restore --staged file) copies it back out. The working tree file never changes in either direction.

git restore is the newer, purpose-built command for this — it was split out of git checkout and git reset in Git 2.23 specifically so that "unstage a file" and "discard a file's edits" would each have one obvious, narrowly-named command instead of overloading two commands that also do several other, unrelated things. git restore --staged file unstages without touching the working tree — the same outcome as git reset HEAD file. Drop --staged and git restore file does something different and more destructive: it discards working-tree edits, overwriting the file on disk to match the index. Confusing the two is an easy mistake with a real cost, so it's worth reading twice before you run either against a file you haven't committed.

git reset to a Commit ID or HEAD~n

Resetting isn't limited to the single commit right before your current one. You can reset HEAD to a commit any distance back, by relative offset or by exact commit ID, once you know which commit you're targeting.

# Find the commit you want to reset to
git log --oneline -10

# Reset three commits back, keeping your working tree files unchanged
git reset HEAD~3

# Reset to a specific commit by its SHA (a full or short hash both work)
git reset --hard a1b2c3d

# git reset to commit id, mixed mode — unstages but keeps the files as-is
git reset a1b2c3d

HEAD~n counts commits backward along the first-parent line from your current position — HEAD~3 means "three commits before where I am now," regardless of what those commits contain. A commit ID targets one exact commit directly, which matters once your history has merges, since HEAD~n counting gets ambiguous across merge points while a SHA never does. git log --oneline is the fastest way to read off the short hash you need before running the reset — always confirm the target commit is the one you think it is before adding --hard to the command, since that's the point where a wrong commit ID becomes a wrong, destructive reset.

Hard Reset to Match GitHub or Another Remote

This is the single most common reason developers land on "hard reset github" as a search: a local branch has drifted — through a bad rebase, a botched merge, or just an afternoon of experiments you don't want to untangle — and the fastest fix is to throw the local state away entirely and match whatever's on the server.

# 1. Fetch the remote's current state without touching your working tree
git fetch origin

# 2. Reset your local branch to match it exactly — pointer, index, and files
git reset --hard origin/main

# One-liner version of the same two steps
git fetch origin && git reset --hard origin/main

git fetch updates your local copy of the remote's history (origin/main) without changing your current branch or working tree at all — that step is always safe to run. If the fetch fails or pulls from the wrong server, fix the address before resetting: changing the origin remote URL covers repointing after an org rename or host migration. The reset that follows is where the destructive part happens: git reset --hard origin/main moves your local main pointer, index, and working-tree files to match origin/main exactly, discarding every local commit, staged change, and uncommitted edit that isn't already reflected on the remote.

This is also the answer to "how to reset github" when what someone actually means is resetting their local copy of a GitHub repo, not anything on GitHub's servers themselves — a hard reset run locally never touches the remote at all, it only overwrites your machine. If you instead want the remote branch itself to match a local commit — the opposite direction — that's a force push, not a reset, and it's a much riskier operation because it rewrites history other people may already have.

Hard reset snaps your local branch onto the remote Before: diverged After: fetch + reset --hard common ancestor local main (HEAD) messy local history origin/main HEAD = origin/main discarded git fetch origin && git reset --hard origin/main Only commits that also exist on origin/main survive — local-only commits are discarded.
git fetch updates your local copy of the remote's history; git reset --hard origin/main then moves the pointer, index, and working tree to match it exactly, discarding every local-only commit.

Before running this against a branch with local commits you haven't pushed, check what you're about to lose:

# Commits that exist locally but not on the remote — these get discarded
git log origin/main..HEAD

# Uncommitted changes — these also get discarded by --hard
git status

If git log origin/main..HEAD comes back empty and git status is clean, the hard reset changes nothing you'll miss. If either shows something, that's your cue to stash, branch off, or copy the relevant file content out before you proceed — recovery after the fact depends entirely on whether that work was ever committed, which the next section on reflog recovery covers in detail.

Discarding All Uncommitted Changes Safely

Sometimes there's no remote involved at all — you just want to throw away every uncommitted edit in the working tree and get back to your last commit. The git reset hard command for that is simply:

# Discard every uncommitted change, staged or not, back to the last commit
git reset --hard HEAD

# Also remove untracked files git isn't tracking at all (new files, build output)
git clean -fd

# Preview what git clean would delete, without deleting anything
git clean -nd

git reset --hard HEAD targets your current commit rather than an earlier one, so it doesn't undo any commits — it only discards uncommitted edits and staged changes, snapping the working tree and index back to exactly what's already committed. It's the cleanest way to bail out of a messy in-progress edit that isn't worth salvaging.

git reset --hard never touches files git isn't tracking — new files you created but never git added are invisible to it entirely, which surprises people who expect a hard reset to be a full "clean slate" command. If you also want those gone, git clean -fd is the separate command for it (-f forces the delete, -d includes directories) — and it's worth running the -nd dry-run variant first, since git clean has no reflog safety net at all: files it deletes were never committed, so there's no commit object for git to recover them from later.

This is also a reasonable moment to reach for a plain visual diff tool rather than trusting memory. Diff Checker, a free Chrome extension, opens a full-tab, Monaco-based editor with a side-by-side or unified view — paste the file content you're about to discard into one pane before you run --hard, and you have a snapshot to compare against afterward if you second-guess the decision. It has no git awareness of its own; it's just a fast way to keep a visual copy of text you're about to overwrite.

git reset vs git restore vs git revert vs git checkout

Git has accumulated four commands that all sound like they might do the same thing, largely because git checkout historically did far too much and git reset still does more than its name suggests. Here's the decision matrix that actually resolves which one you want:

You want to... Command Moves branch pointer? Rewrites history?
Unstage a file git restore --staged file No No
Discard working-tree edits to a file git restore file No No
Undo commits on a private, unpushed branch git reset (soft/mixed/hard) Yes Yes — commits disappear
Undo a commit already pushed / shared git revert commit-id Yes — forward, adds a new commit No — adds history, doesn't remove it
Switch to a different branch git switch branch Yes No

The generational shift matters here: git switch and git restore were both introduced in Git 2.23 (2019) specifically to split git checkout's overloaded behavior — switching branches and restoring files — into two narrower, harder to misuse commands. git reset predates both and still hasn't been split up, which is exactly why it retains the widest blast radius of the four: it's the only one of these commands that can both move your branch pointer and overwrite your working tree in the same call.

Which undo command do you want? What do you want to undo? git restore unstage a file / discard edits git reset undo local commits, not pushed yet git revert undo a commit already pushed git switch move to a different branch reset: pointer moves back HEAD orphaned revert: adds a new commit HEAD kept revert commit reset removes commits from history; revert adds a new one that undoes them.
Reset moves the branch pointer backward and can orphan commits — safe only on history nobody else has pulled. Revert moves forward, adding a new commit that undoes an earlier one without touching what's already there.

git revert vs git reset is the comparison worth internalizing above the others: revert adds a brand-new commit that undoes the effect of an earlier one, leaving the original commit and the branch's public history intact — safe on shared branches because nobody else's history gets rewritten out from under them. reset moves the branch pointer and can make earlier commits unreachable from that branch entirely, which is only safe on branches nobody else has already pulled. The full mechanics of git revert, including reverting a merge commit and reverting a revert, are covered in the git revert commit guide.

Two naming traps worth flagging explicitly, the way "git rm branch" trips people up elsewhere in git: there is no git revert --hard--hard is a reset-only flag, and revert has no equivalent because it never touches your working tree or index in the first place, it just makes a normal new commit. Likewise there is no git restore --hard — restore's whole design point is being narrower than reset, and "hard" specifically means "also overwrite the working tree," which for a command whose entire job is already the working tree would be redundant. If you see either phrase in a tutorial, it's describing git reset --hard, not restore or revert.

Recovering From a Bad Reset with git reflog

Here's the fact that should calm you down immediately after a reset you regret: git reflog records every position HEAD has occupied recently, including the one right before your reset ran. Undoing a reset is, in most cases, just running another reset back to that recorded position.

# Show recent HEAD positions with SHAs and timestamps
git reflog

# Example output — look for the entry right before "reset:"
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# f4e5d6c HEAD@{1}: commit: fix payment retry logic

# Move HEAD back to the commit that existed before the reset
git reset --hard f4e5d6c

Undo git reset soft is the easiest recovery case of all: because --soft never touched your working tree, reflog recovery is exact — you get back precisely the staged state you had a moment ago, no data was ever at risk to begin with. A mixed reset recovery is also complete, since it only rearranged where changes sat between the index and working tree, never deleting content from either.

What reflog can and can't undo after --hard 1 Before reset commit intact clean or has uncommitted edits 2 reset --hard runs files overwritten committed work still safe in .git/objects 3 reflog + reset --hard SHA restores pointer history intact, back where you were Committed work: recoverable via reflog, about 30-90 days NOT recoverable Never-staged edits are gone the instant --hard runs. Staged blobs may survive in .git/objects.
--soft and --mixed never touch the working tree, so reflog recovery is exact. --hard overwrites files instantly — committed work is recoverable via reflog for a while, staged work may survive as a dangling blob, and never-staged edits are gone the moment it runs.

Here is the honest limit, and it's the one detail most guides gloss over: reflog recovery only restores committed work — commit objects that git had already saved to .git/objects before the reset ran. The reflog only tracks where HEAD and branches pointed, never the contents of an uncommitted working tree, so for uncommitted edits there is simply no reflog entry to return to.

But "uncommitted" splits into two very different cases, and confusing them is why people give up on recoverable work:

  • Staged but never committed — often recoverable. The moment you run git add, git writes the file's contents into .git/objects as a blob. A hard reset moves pointers and overwrites files, but it does not immediately delete that blob — it just leaves it unreferenced, or dangling. Until garbage collection prunes it, you can still fish it out.
  • Never staged, never committed — genuinely gone. If you edited a file and ran git reset --hard without ever running git add, git never had a copy of those bytes anywhere. No command recovers them. This is the one case where the data is truly unrecoverable.

To recover staged work after a hard reset, search the object database for dangling blobs rather than the reflog. git fsck lists unreferenced objects, and git show prints the contents of any blob you find:

# List unreferenced objects — staged-but-lost content shows up as "dangling blob"
git fsck --lost-found

# Inspect a candidate blob's contents (use the SHA from the output above)
git show a1b2c3d

# Write it back to a file once you've identified the right one
git show a1b2c3d > recovered-file.js

On a busy repository git fsck can list many dangling blobs with no filenames attached — git stores blob contents, not paths, so identification is manual. Dumping the candidates and diffing them against the current file is usually the fastest way to find the right one, and it is a legitimate use for a side-by-side comparison tool: paste the recovered blob into one pane and the current file into the other, and the correct match is obvious in seconds. This route only exists for content that was staged at least once, which is a practical argument for running git add early and often.

The one thing that would have saved them is git stash, run before the reset — stashing creates real commit objects for your uncommitted changes, which is precisely why stashed work survives a hard reset that would otherwise destroy it. That's the practical takeaway: if you're not certain a hard reset is safe, git stash costs three seconds and turns an unrecoverable mistake into a recoverable one.

# Before an uncertain hard reset, stash first — now the changes are a commit object
git stash

# Run the reset with confidence
git reset --hard origin/main

# If you needed the stashed changes after all, they're still there
git stash pop

For committed work, the recovery window is git's garbage collection schedule, not indefinite — typically 30 days for entirely unreachable commits and 90 for commits still reachable through some other ref, both configurable and both shorter if someone has run git gc --prune=now on the repository. Recover sooner rather than later.

Why You Should Not Reset a Shared Branch

Every warning in this guide about --hard being safe to undo via reflog comes with an unstated assumption: it's safe for you, on your machine. Reset a branch that other people have already pulled, and their local history now disagrees with yours — the commits your reset made unreachable are still sitting in their clones, and the next time they push, git rejects it or, worse, someone force-pushes over your reset and the confusion compounds.

This is the actual reason git push --force shows up so often paired with git reset --hard in tutorials: after resetting a local branch backward, a normal push fails, because the remote has commits your local branch no longer does. Forcing the push is how the reset propagates outward — and force-pushing a shared branch is exactly the moment a private, locally-recoverable reset becomes a team-wide incident, since everyone else's unpushed work built on top of the discarded commits is now orphaned on their end too.

If what you actually need is to rewrite commit history — squashing, reordering, or editing older commits — rather than simply discarding the tip, git rebase is usually the better-fitting tool. There's no such thing as "git rebase hard" — rebase has no --hard flag, because rebase already rewrites history by design; the caution that applies to reset --hard on shared branches applies to any rebase on a shared branch for the identical reason. The safe default for both: keep destructive history rewrites, reset or rebase, confined to branches only you have pushed to, or that you've explicitly coordinated a rewrite on with everyone who has a clone.

Once a branch's purpose is genuinely finished — merged and no longer needed locally or on the remote — deleting it outright is usually cleaner than resetting it back to some earlier state. The git delete local branch guide covers the full local-vs-remote deletion mechanics, including how to bulk-clean a pile of merged branches safely.

Verify What You're About to Destroy

Every destructive command in this guide has the same shape: it's instant, it's silent, and it gives you no preview by default. Building a two-second verification habit before any --hard command costs almost nothing and catches the mistakes that reflog can't fix — the uncommitted-edit case from the recovery section above.

# Always run before --hard: what's staged, what's modified, what's untracked
git status

# What local commits would a hard reset to origin/main discard
git log origin/main..HEAD --oneline

# The actual content of a commit before you reset past it
git show HEAD --stat

For anything more than a glance — a file with a lot of uncommitted edits, or a commit you want to confirm the exact line-level content of before it disappears — reading raw git diff or git show output line by line is slower and more error-prone than a real diff view. Pasting the two versions of a file into a proper side-by-side comparison makes it obvious at a glance whether the content you're about to discard is truly disposable, and Diff Checker's local history (stored in IndexedDB, never leaves the browser) is a reasonable place to stash a copy of a file's current state before you run a reset you're not fully sure about — a lightweight safety net that costs nothing and sits entirely outside git's own recovery mechanisms.

If you're comparing raw command-line output rather than files — two git log dumps, or a before-and-after git status — the same unified diff format git itself uses under the hood is worth understanding, since it's what git diff prints by default and what most patch-based tooling expects.

Frequently Asked Questions

What does git reset do?

git reset moves the current branch pointer to a different commit and, depending on the mode you pick, updates the staging area (index) and working tree to match. Run it with no arguments and no path and it defaults to git reset --mixed HEAD, which unstages every staged file without moving the branch pointer or touching a single line in your working tree — it only changes what's staged for the next commit. Add a commit reference like HEAD~1 or a SHA and it also moves the branch pointer, which is how git reset undoes commits. Add --hard and it also overwrites your working tree files to match, which is the only mode that can destroy uncommitted work.

How do I unstage a file without losing my changes?

Run git restore --staged <file> (Git 2.23+) or the older equivalent git reset HEAD <file>. Both remove the file from the staging area and move it back to being an unstaged modification — your edits stay exactly as they were in the working tree, nothing is deleted. This is different from undoing a commit: you're not moving the branch pointer at all, just changing what's queued for the next commit. If you actually want to discard the edits themselves rather than just unstage them, that's git restore <file> (no --staged flag) or git checkout -- <file>, and that one does overwrite your changes.

How do I reset my local branch to match GitHub exactly?

Run git fetch origin followed by git reset --hard origin/main (swap in your actual branch name if it isn't main). The fetch updates your local copy of the remote's history without touching your working tree; the hard reset then moves your local branch pointer, index, and working tree to match that remote history exactly, discarding any local commits, staged changes, or uncommitted edits that aren't on the remote. This is the standard fix for a local branch that's diverged, conflicted, or just accumulated a mess you don't want to untangle by hand — but it is destructive, so check git status and git log origin/main..HEAD first to see exactly what you're about to throw away.

Can I undo a git reset --soft?

Yes, and it's the easiest reset to undo because --soft never touches your working tree or staging area — it only moves the branch pointer, so your files never changed. Run git reflog to find the SHA the branch pointed to before the reset (it will show as the entry right before the "reset: moving to" line), then run git reset --soft <SHA> or plain git reset <SHA> to move the pointer back. Because nothing but the pointer moved in either direction, this recovery is exact — you get back precisely the commit and staging state you had before.

What is the difference between git reset and git restore?

git restore, added in Git 2.23, is the modern, narrower command for restoring files — it only ever touches the working tree and, with --staged, the index; it never moves the branch pointer and it has no --hard mode. git reset does everything restore does for unstaging files, but it can also move HEAD to a different commit, which restore cannot do. In practice: use git restore --staged <file> to unstage, use git restore <file> to discard working-tree edits, and reach for git reset only when you actually need to move the branch pointer, such as undoing a commit or resetting to match a remote.