DevToolBoxGRATIS
Blog

Git cherry-pick, revert e reset spiegati

10 min di letturadi DevToolBox

Understanding git cherry-pick, git revert, and git reset is essential for any developer who needs to move commits between branches, undo mistakes, or rewrite history. This comprehensive guide covers each command in depth with practical examples, conflict resolution strategies, and real-world workflows. Whether you need to backport a hotfix, undo a bad deploy, or clean up messy history, you will find the right approach here.

Generate Git commands interactively with our Git Command Generator →

Cherry-Pick Basics: Applying Individual Commits

git cherry-pick takes one or more existing commits and applies them as new commits on the current branch. Unlike merge or rebase, cherry-pick lets you select specific commits rather than integrating an entire branch. The original commit remains untouched — cherry-pick creates a brand new commit with a different hash but identical changes.

# Apply a single commit to the current branch
git cherry-pick abc1234

# Apply multiple commits at once
git cherry-pick abc1234 def5678 ghi9012

# Cherry-pick the tip of another branch
git cherry-pick feature-branch

When to use cherry-pick:

  • Backporting a bug fix from a development branch to a release branch
  • Pulling a single feature commit without merging unrelated changes
  • Recovering a commit from a deleted or abandoned branch
  • Applying a fix to multiple maintenance branches
# Example: Backport a fix from develop to release
git checkout release/1.0
git cherry-pick abc1234      # the fix commit on develop
git push origin release/1.0

Cherry-Pick Options and Flags

git cherry-pick supports several useful flags that control how the commit is applied. These options give you fine-grained control over the cherry-pick process.

--no-commit (-n)

--no-commit (-n): Apply changes to the working directory and staging area without creating a commit. This lets you combine multiple cherry-picks into a single commit or modify the changes before committing.

# Apply changes without committing
git cherry-pick --no-commit abc1234

# Combine multiple cherry-picks into one commit
git cherry-pick --no-commit abc1234
git cherry-pick --no-commit def5678
git commit -m "Backport: fix login and auth bugs"

# Check what changed before committing
git cherry-pick -n abc1234
git diff --staged

-x (append source reference)

-x: Append a line "(cherry picked from commit ...)" to the commit message. This is extremely useful for tracking the origin of cherry-picked commits, especially across release branches.

# Cherry-pick with source tracking
git cherry-pick -x abc1234

# Resulting commit message:
# Fix: prevent null pointer in auth module
#
# (cherry picked from commit abc1234567890abcdef)

--edit (-e)

--edit (-e): Open the commit message editor before creating the commit. Use this when you need to adjust the commit message for context in the target branch.

# Cherry-pick and edit the commit message
git cherry-pick --edit abc1234

A..B range

A..B range: Cherry-pick a range of commits from A (exclusive) to B (inclusive). Note that A is NOT included — it defines the starting point.

# Cherry-pick commits from abc1234 (exclusive) to ghi9012 (inclusive)
# This picks def5678 and ghi9012
git cherry-pick abc1234..ghi9012

# Equivalent to:
git cherry-pick def5678 ghi9012

# Cherry-pick a range with source tracking
git cherry-pick -x abc1234..ghi9012

--signoff (-s)

--signoff (-s): Add a "Signed-off-by" trailer to the commit message, commonly required in open-source projects.

# Cherry-pick with sign-off
git cherry-pick --signoff abc1234

# Resulting message includes:
# Signed-off-by: Your Name <you@example.com>

Resolving Cherry-Pick Conflicts

Cherry-pick conflicts happen when the changes in the cherry-picked commit overlap with changes already on the target branch. Since the commit is being applied out of its original context, conflicts are more common than with regular merges.

When a conflict occurs, Git pauses the cherry-pick and marks the conflicting files. You have three options:

1. Resolve and continue

Resolve and continue: Fix the conflict markers in the affected files, stage the resolved files with git add, then run git cherry-pick --continue.

# Cherry-pick triggers a conflict
git cherry-pick abc1234
# CONFLICT (content): Merge conflict in src/auth.js

# 1. Open the file and resolve conflict markers
#    <<<<<<< HEAD
#    const timeout = 5000;
#    =======
#    const timeout = 3000;
#    >>>>>>> abc1234

# 2. Stage the resolved file
git add src/auth.js

# 3. Continue the cherry-pick
git cherry-pick --continue

2. Abort

Abort: Run git cherry-pick --abort to cancel the cherry-pick entirely and return to the state before it started.

# Abort the cherry-pick and return to the previous state
git cherry-pick --abort

3. Skip

Skip: Run git cherry-pick --skip to skip the current commit and move on to the next one (when cherry-picking a range).

# Skip the current conflicting commit in a range
git cherry-pick --skip

Git Revert: Safely Undoing Commits

git revert creates a new commit that undoes the changes introduced by a previous commit. Unlike reset, revert does not rewrite history — it adds to it. This makes revert safe for shared branches because other collaborators will not have their history invalidated.

# Revert the last commit
git revert HEAD

# Revert a specific commit
git revert abc1234

# Revert without auto-committing (stage changes only)
git revert --no-commit abc1234

# Revert multiple commits (creates one revert per commit)
git revert HEAD~3..HEAD

# Revert with a custom message
git revert abc1234 -m 1 --edit

Reverting Merge Commits

Reverting a merge commit is a special case. Merge commits have two parents, so you must tell Git which parent to revert to using the -m flag. In most cases, -m 1 specifies the mainline (the branch you merged into), which is what you typically want.

# Revert a merge commit (keep the mainline, undo the merged branch)
git revert -m 1 <merge-commit-hash>

# -m 1 = keep parent 1 (the branch you merged INTO, e.g., main)
# -m 2 = keep parent 2 (the branch that was merged, e.g., feature)

# Example workflow:
git log --oneline --graph
# *   e5f6g7h (HEAD -> main) Merge branch 'feature-x'
# |\
# | * c3d4e5f Add feature X
# | * a1b2c3d Start feature X
# |/
# * 9z8y7x6 Previous main commit

# Undo the merge but keep main's history:
git revert -m 1 e5f6g7h

Important: After reverting a merge, if you later want to re-merge the same branch, Git will think those changes already exist. You will need to "revert the revert" first.

# If you need to re-merge feature-x later:
# First, revert the revert
git revert <revert-commit-hash>
# Then merge again
git merge feature-x

Git Reset: Moving the Branch Pointer

git reset moves the current branch pointer to a specified commit. Depending on the mode used, it may also modify the staging area (index) and working directory. Understanding the three modes is critical to using reset safely.

--soft: Move HEAD only

Moves HEAD to the target commit. Staging area and working directory are untouched. All changes from the "removed" commits appear as staged (ready to commit). Perfect for squashing commits or re-doing a commit message.

# Undo the last commit, keep everything staged
git reset --soft HEAD~1

# Undo the last 3 commits, keep all changes staged
git reset --soft HEAD~3

# Use case: squash last 3 commits into one
git reset --soft HEAD~3
git commit -m "Combined: feature implementation"

--mixed (default): Move HEAD + unstage

Moves HEAD to the target commit and resets the staging area. Changes remain in the working directory as unstaged modifications. This is the default mode when you run git reset without a flag.

# Undo last commit, unstage changes (default mode)
git reset HEAD~1
# same as:
git reset --mixed HEAD~1

# Unstage a specific file without changing it
git reset HEAD src/app.js
# modern alternative:
git restore --staged src/app.js

--hard: Move HEAD + unstage + discard

Moves HEAD to the target commit, resets the staging area, AND discards all changes in the working directory. This is destructive — uncommitted work is permanently lost (unless recoverable via reflog for committed changes).

# ⚠️ DESTRUCTIVE: Undo last commit AND discard all changes
git reset --hard HEAD~1

# ⚠️ DESTRUCTIVE: Reset to match remote branch exactly
git reset --hard origin/main

# ⚠️ DESTRUCTIVE: Discard all uncommitted changes
git reset --hard HEAD

Comparison Table: git reset modes

ModeHEADStaging Area (Index)Working DirectorySafe?
--softMovedUnchangedUnchangedYes
--mixedMovedResetUnchangedYes
--hardMovedResetReset (deleted)NO
# Visual diagram of git reset modes:
#
# Commit History:  A --- B --- C --- D (HEAD)
#
# git reset --soft B:
#   HEAD -> B,  staging = C+D changes,  working dir = C+D changes
#
# git reset --mixed B:
#   HEAD -> B,  staging = clean,  working dir = C+D changes
#
# git reset --hard B:
#   HEAD -> B,  staging = clean,  working dir = clean (C+D GONE)

Reset vs Revert vs Checkout: When to Use Which

These three commands all "undo" changes, but they work at fundamentally different levels. Choosing the right one depends on whether the commits are pushed and whether you want to preserve history.

  • git reset: git reset: Rewrites history by moving the branch pointer backward. Use for local/unpushed commits only. Cannot be used safely on shared branches.
  • git revert: git revert: Creates a new commit that reverses changes. Safe for shared branches. Does not rewrite history. Use when commits are already pushed.
  • git checkout / restore: git checkout (or git restore): Modifies the working directory without affecting commits or branches. Use for discarding uncommitted changes to specific files.

The golden rule: If the commit has been pushed to a shared remote, use revert. If it is local only, reset is cleaner.

# Decision flowchart:
#
# Is the commit pushed to a shared remote?
# ├── YES → git revert <commit>
# └── NO
#     ├── Want to remove commit entirely? → git reset --hard HEAD~1
#     ├── Want to redo commit?            → git reset --soft HEAD~1
#     └── Want to discard file changes?   → git restore <file>

Undoing a Cherry-Pick

If you cherry-picked a commit and want to undo it, you have two approaches depending on whether you have pushed the cherry-pick:

If not pushed yet

Not pushed yet: Use git reset to move HEAD back before the cherry-picked commit. This cleanly removes it from history.

# Undo the cherry-pick (it was the last commit)
git reset --hard HEAD~1

# Or if you want to keep the changes for review:
git reset --soft HEAD~1

If already pushed

Already pushed: Use git revert on the cherry-picked commit. This creates a new commit that reverses the cherry-pick without rewriting shared history.

# Find the cherry-picked commit hash
git log --oneline -5

# Revert it (safe for shared branches)
git revert <cherry-picked-commit-hash>
git push

Cherry-Pick Across Repositories

Sometimes you need to cherry-pick a commit from a different repository (e.g., from an upstream project or a colleague's fork). Git supports this by adding the other repo as a remote, fetching its commits, and then cherry-picking.

The workflow involves three steps: add the remote, fetch its branches, and cherry-pick the specific commit by its hash.

# Step 1: Add the other repository as a remote
git remote add upstream https://github.com/original/project.git

# Step 2: Fetch commits from the remote
git fetch upstream

# Step 3: Cherry-pick the specific commit
git cherry-pick <commit-hash-from-upstream>

# Full example: cherry-pick a fix from a colleague's fork
git remote add alice https://github.com/alice/project.git
git fetch alice
git log alice/main --oneline -10   # find the commit hash
git cherry-pick abc1234             # apply it

# Clean up: remove the remote when done
git remote remove alice

Interactive Rebase as an Alternative to Cherry-Pick

In many cases, interactive rebase (git rebase -i) can accomplish what cherry-pick does, but with more flexibility. Rebase is better when you want to reorder, squash, edit, or drop commits within the same branch. Cherry-pick is better when you want to copy commits between different branches.

Use rebase instead of cherry-pick when:

  • You want to clean up commits before merging a feature branch
  • You need to reorder commits within the same branch
  • You want to squash multiple commits into one
  • You are moving an entire sequence of commits (not just one or two)
# Interactive rebase: reorder, squash, edit, or drop commits
git rebase -i HEAD~5

# Opens an editor with:
# pick a1b2c3d First commit
# pick d4e5f6g Second commit
# pick h7i8j9k Third commit
# pick l0m1n2o Fourth commit
# pick p3q4r5s Fifth commit
#
# Change "pick" to:
#   squash (s) - combine with previous commit
#   edit (e)   - pause to amend this commit
#   drop (d)   - remove this commit
#   reword (r) - change commit message

Use cherry-pick instead of rebase when:

  • You need to copy specific commits to a completely different branch
  • You want to backport a fix to a release/maintenance branch
  • You only need one or two commits, not a whole sequence
# Cherry-pick is better for cross-branch operations:
git checkout release/2.0
git cherry-pick develop~3      # specific fix from develop
git cherry-pick -x abc1234     # with source tracking

Real-World Scenarios

Let us walk through the most common real-world situations where cherry-pick, revert, and reset are the right tools.

Scenario 1: Hotfix Backport

A critical bug is found in production. The fix was already committed on the develop branch. You need to apply just that fix to the release branch without pulling in unfinished features.

# 1. Find the fix commit on develop
git log develop --oneline --grep="fix critical"
# abc1234 Fix critical auth bypass vulnerability

# 2. Switch to the release branch
git checkout release/1.0

# 3. Cherry-pick the fix with source tracking
git cherry-pick -x abc1234

# 4. Push to the release branch
git push origin release/1.0

# 5. Tag the hotfix release
git tag -a v1.0.1 -m "Hotfix: auth bypass"
git push origin v1.0.1

Scenario 2: Feature Extraction

A developer committed a useful utility function as part of a larger feature branch. Another team needs that utility immediately but cannot wait for the full feature to be merged.

# 1. Find the utility commit in the feature branch
git log feature/big-refactor --oneline
# ghi9012 Add date formatting utility
# def5678 Refactor user module (WIP)
# abc1234 Start big refactor

# 2. Cherry-pick just the utility commit
git checkout main
git cherry-pick ghi9012

# 3. Push so the other team can use it
git push origin main

Scenario 3: Release Branch Patches

You maintain multiple release branches (v1.x, v2.x). A security fix committed on main needs to be applied to all active release branches.

# Security fix committed on main
git log main --oneline -1
# abc1234 Fix: patch XSS vulnerability in input sanitizer

# Apply to all active release branches
git checkout release/1.x
git cherry-pick -x abc1234
git push origin release/1.x

git checkout release/2.x
git cherry-pick -x abc1234
git push origin release/2.x

# Verify the fix is on all branches
git log release/1.x --oneline -1
git log release/2.x --oneline -1

Dangerous Operations and Safety Nets

Some Git operations can cause permanent data loss if used carelessly. Understanding the risks and knowing the recovery mechanisms is essential.

git reset --hard

git reset --hard: Permanently discards uncommitted changes. There is no way to recover unstaged or untracked files after this operation. Always check git status and git stash before using --hard.

# ⚠️ Before using --hard, always check:
git status          # any uncommitted changes?
git stash           # save them first if needed

# If you already ran reset --hard and need to recover:
git reflog
# abc1234 HEAD@{0}: reset: moving to HEAD~3
# def5678 HEAD@{1}: commit: Important work
# ghi9012 HEAD@{2}: commit: More important work

# Recover by resetting to the pre-reset state:
git reset --hard def5678
# Or create a recovery branch:
git branch recovery def5678

git push --force

git push --force: Overwrites the remote branch history. This can destroy other developers' work if they have based commits on the old history. Prefer --force-with-lease which refuses to push if the remote has been updated since your last fetch.

# ⚠️ NEVER force push to shared branches without coordination
# BAD: Overwrites remote history unconditionally
git push --force

# BETTER: Only push if remote hasn't changed since last fetch
git push --force-with-lease

# SAFEST: Fetch first, then force-with-lease
git fetch origin
git push --force-with-lease origin feature-branch

git reflog (Safety Net)

The safety net — git reflog: Git records every HEAD movement in the reflog. Even after a destructive reset, you can often recover commits by finding their hash in the reflog and checking them out or creating a branch from them. The reflog is kept for 90 days by default.

# View the reflog (all HEAD movements)
git reflog

# Example output:
# abc1234 HEAD@{0}: reset: moving to origin/main
# def5678 HEAD@{1}: commit: Add new feature
# ghi9012 HEAD@{2}: cherry-pick: Fix auth bug
# jkl3456 HEAD@{3}: checkout: moving from feature to main

# Recover ANY previous state:
git checkout def5678          # detached HEAD at that commit
git branch recovery def5678   # or create a branch

# The reflog is kept for 90 days by default
# Check the expiry setting:
git config gc.reflogExpire

Frequently Asked Questions

What is the difference between git cherry-pick and git merge?

git merge integrates all commits from one branch into another, creating a merge commit that joins the two histories. git cherry-pick copies only specific individual commits and applies them as new commits on the current branch. Use merge when you want to integrate an entire branch; use cherry-pick when you only need specific commits.

Can I cherry-pick a merge commit?

Yes, but you must specify which parent to use with the -m flag. For example, "git cherry-pick -m 1 <merge-commit-hash>" tells Git to use the first parent (the branch that was merged into) as the base. However, cherry-picking merge commits is generally discouraged because it can cause confusing duplicate changes. Consider cherry-picking the individual commits from the merged branch instead.

How do I undo a git reset --hard?

If you reset --hard and lost commits, use "git reflog" to find the commit hash before the reset, then run "git reset --hard <hash>" or "git branch recovery <hash>" to restore them. However, uncommitted changes (files that were never staged or committed) cannot be recovered by Git after a hard reset.

Should I use git reset or git revert to undo a commit?

Use git reset for local/unpushed commits — it gives a cleaner history by removing the commit entirely. Use git revert for commits that have been pushed to a shared remote — it creates a new "undo" commit without rewriting history. If you are unsure whether others have pulled your commit, always use revert to be safe.

Why does my cherry-pick create a different commit hash than the original?

A commit hash is determined by the commit content, parent, author date, and committer date. When you cherry-pick, the new commit has a different parent and a new committer date, so it gets a different hash even though the code changes are identical. This is by design — Git treats them as distinct commits. Use the -x flag to add a reference to the original commit in the message.

Mastering git cherry-pick, revert, and reset gives you precise control over your repository history. Remember: use reset for local cleanup, revert for shared branches, and cherry-pick for copying specific commits. When in doubt, check git reflog — it is your safety net.

Try the Git Command Generator →

𝕏 Twitterin LinkedIn
È stato utile?

Resta aggiornato

Ricevi consigli dev e nuovi strumenti ogni settimana.

Niente spam. Cancella quando vuoi.

Prova questi strumenti correlati

🔀Git Command Generator.gi.gitignore Generator±Text Diff Checker

Articoli correlati

Git annullare ultimo commit: 5 modi per correggere (mantenere le modifiche)

Impara cinque modi per annullare l'ultimo commit git mantenendo le modifiche.

Git Rebase vs Merge: Quando usare quale (con esempi visivi)

Comprendere la differenza tra git rebase e merge. Quando usare ciascuno ed evitare errori comuni.