Undoing changes in Git can be approached in several ways, depending on what you need to achieve. Here, we’ll cover the most common scenarios and commands for undoing changes in Git with step-by-step examples.
Git Undoing
1. Undoing Unstaged Changes
If you’ve made changes to files in your working directory but haven’t yet staged them, you can revert these changes to the last committed state.
Example: Undo Changes in a File
# View the current status
git status
# Discard changes in a specific file
git checkout -- filename.txt
# Discard changes in all files
git checkout -- .
2. Undoing Staged Changes
If you’ve staged changes (added them to the index) but haven’t committed them yet, you can unstage them while keeping the changes in your working directory.
Example: Unstage a File
# Stage a file
git add filename.txt
# View the current status
git status
# Unstage the file
git reset HEAD filename.txt
# View the status again
git status
3. Undoing the Last Commit
Sometimes, you might commit too early or include unwanted changes in your commit. You can undo the last commit while keeping your changes in the working directory.
Example: Undo the Last Commit but Keep Changes
# Make a commit
git commit -m "Commit message"
# View the log
git log --oneline
# Undo the last commit
git reset --soft HEAD~1
# View the log again
git log --oneline
# Your changes are still in the working directory
git status
If you want to undo the commit and discard the changes:
git reset --hard HEAD~1
4. Undoing Multiple Commits
If you need to undo multiple commits, you can specify how many commits to go back.
Example: Undo the Last 2 Commits
git reset --soft HEAD~2
5. Reverting a Commit
If you want to undo a specific commit but keep the history, you can use git revert
. This creates a new commit that undoes the changes made by the specified commit.
Example: Revert a Specific Commit
# View the log
git log --oneline
# Revert a specific commit
git revert <commit-hash>
# Example
git revert 94ebf5f
6. Using Git Stash
If you want to temporarily save changes that you don’t want to commit yet, you can use git stash
. This is useful if you need to switch branches or pull updates without committing your current changes.
Example: Stash and Apply Changes
# Stash changes
git stash
# View stashed changes
git stash list
# Apply stashed changes
git stash apply
# Apply and remove stashed changes
git stash pop
# Drop a specific stash
git stash drop stash@{0}
7. Changing the Last Commit Message
If you just want to change the commit message of the last commit, you can use the --amend
option.
Example: Amend the Last Commit Message
# Commit with an incorrect message
git commit -m "Wrong message"
# Amend the commit message
git commit --amend -m "Correct message"
8. Discarding Local Changes
If you want to discard all local changes and return to the last committed state, you can use the git reset --hard
command.
Example: Discard All Local Changes
# View the current status
git status
# Discard all local changes
git reset --hard
# View the status again
git status
Summary
- Undo Unstaged Changes:
git checkout -- filename.txt
- Unstage Changes:
git reset HEAD filename.txt
- Undo Last Commit (Keep Changes):
git reset --soft HEAD~1
- Undo Last Commit (Discard Changes):
git reset --hard HEAD~1
- Undo Multiple Commits:
git reset --soft HEAD~2
- Revert a Commit:
git revert <commit-hash>
- Stash Changes:
git stash
,git stash apply
,git stash pop
- Amend Commit Message:
git commit --amend -m "New message"
- Discard All Local Changes:
git reset --hard
Conflict Resolution
Step-by-Step Guide to Conflict Resolution in Git
1. Creating a Conflict
To demonstrate conflict resolution, let’s create a simple conflict.
Initialize a Git Repository
mkdir conflict-demo
cd conflict-demo
git init
Create a File and Commit it
echo "This is line 1" > file.txt
git add file.txt
git commit -m "Initial commit"
Create a New Branch and Modify the File
git checkout -b branch1
echo "This is line 2 from branch1" >> file.txt
git add file.txt
git commit -m "Add line in branch1"
Switch Back to Main and Modify the File
git checkout main
echo "This is line 2 from main" >> file.txt
git add file.txt
git commit -m "Add line in main"
Now we have two branches (main
and branch1
) with different changes to the same line in file.txt
.
2. Merging Branches and Creating a Conflict
Merge branch1
into main
git checkout main
git merge branch1
You will see a conflict message:
Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
Automatic merge failed; fix conflicts and then commit the result.
3. Identifying Conflicts
Open the conflicted file (file.txt
) to see the conflict markers:
This is line 1
<<<<<<< HEAD
This is line 2 from main
=======
This is line 2 from branch1
>>>>>>> branch1
The conflict markers <<<<<<<
, =======
, and >>>>>>>
indicate the conflicting changes.
4. Resolving Conflicts
You need to decide how to combine the changes. Let’s manually resolve the conflict by choosing one of the changes or combining them.
Option 1: Choose One Change
echo "This is line 1" > file.txt
echo "This is line 2 from main" >> file.txt
Option 2: Combine Changes
echo "This is line 1" > file.txt
echo "This is line 2 from main and branch1" >> file.txt
Remove the conflict markers after making your changes.
5. Marking the Conflict as Resolved
Once the conflict is resolved, you need to add the file and commit the changes.
git add file.txt
git commit -m "Resolved conflict between main and branch1"
6. Completing the Merge
After committing the resolved conflict, the merge is complete. Verify the merge status:
git status
Example of Conflict Resolution Workflow
Let’s walk through an example where we choose to combine changes from both branches.
Initial Setup and Creation of Conflict
mkdir example-repo
cd example-repo
git init
echo "Hello, World!" > greetings.txt
git add greetings.txt
git commit -m "Initial commit"
# Create a new branch and make changes
git checkout -b new-feature
echo "Hello from the new feature branch!" >> greetings.txt
git add greetings.txt
git commit -m "Add greeting from new feature branch"
# Switch back to main branch and make conflicting changes
git checkout main
echo "Hello from the main branch!" >> greetings.txt
git add greetings.txt
git commit -m "Add greeting from main branch"
Attempting to Merge and Resolving Conflicts
# Attempt to merge new-feature into main
git merge new-feature
You’ll see a conflict message. Open greetings.txt
:
Hello, World!
<<<<<<< HEAD
Hello from the main branch!
=======
Hello from the new feature branch!
>>>>>>> new-feature
- Resolving the Conflict by Combining Changes
Edit greetings.txt
to combine the changes:
Hello, World!
Hello from the main branch!
Hello from the new feature branch!
Adding and Committing the Resolved File
git add greetings.txt
git commit -m "Resolved conflict by combining greetings"
Using Git Tools for Conflict Resolution
Many tools can help you resolve conflicts more easily, such as:
- Git GUI Tools: Tools like GitKraken, SourceTree, and GitHub Desktop provide visual interfaces for resolving conflicts.
- Editor Support: Modern code editors like VSCode have built-in support for resolving conflicts, showing side-by-side comparisons and allowing you to choose changes easily.
Summary
- Identify Conflicts: Use
git status
to identify conflicted files. - Edit Conflicted Files: Manually resolve conflicts by editing the files.
- Mark as Resolved: Use
git add
to mark conflicts as resolved. - Commit Changes: Commit the resolved changes to complete the merge.
Cherry Picking
Step-by-Step Guide to Cherry Picking in Git
1. Initial Setup
Let’s set up a simple Git repository and create some commits to demonstrate cherry picking.
- Initialize a Git Repository
mkdir cherry-pick-demo
cd cherry-pick-demo
git init
- Create an Initial Commit
echo "Initial content" > file.txt
git add file.txt
git commit -m "Initial commit"
- Create a New Branch and Add Commits
git checkout -b feature-branch
echo "Feature 1" >> file.txt
git add file.txt
git commit -m "Add Feature 1"
echo "Feature 2" >> file.txt
git add file.txt
git commit -m "Add Feature 2"
2. Identify the Commit to Cherry Pick
To cherry pick a commit, you need its hash. Use git log
to find the commit hash.
git log --oneline
Output:
abc1234 (HEAD -> feature-branch) Add Feature 2
def5678 Add Feature 1
ghijk90 (main) Initial commit
Suppose you want to cherry pick the commit def5678
(Add Feature 1) onto the main
branch.
3. Cherry Picking the Commit
- Switch to the Target Branch
git checkout main
- Cherry Pick the Commit
git cherry-pick def5678
After running this command, the changes from the def5678
commit will be applied to the main
branch.
4. Handling Conflicts During Cherry Picking
Conflicts can occur if the changes in the commit being cherry-picked conflict with existing changes in the target branch. Here’s how to handle conflicts:
- Create a Conflict Scenario
Let’s modify file.txt
on the main
branch to create a conflict.
echo "Conflicting change on main" >> file.txt
git add file.txt
git commit -m "Add conflicting change on main"
- Attempt to Cherry Pick
Now, try to cherry pick the same commit again.
git cherry-pick def5678
You’ll see a conflict message:
error: could not apply def5678... Add Feature 1
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'
- Resolve the Conflict
Open file.txt
and resolve the conflict. The file will contain conflict markers:
Initial content
Conflicting change on main
<<<<<<< HEAD
=======
Feature 1
>>>>>>> def5678
Resolve the conflict by choosing the appropriate changes:
Initial content
Conflicting change on main
Feature 1
- Mark the Conflict as Resolved and Continue
git add file.txt
git cherry-pick --continue
If you want to abort the cherry-pick operation, you can use:
git cherry-pick --abort
5. Cherry Picking Multiple Commits
You can also cherry pick multiple commits at once. Here’s how:
- Find the Commit Range
Identify the range of commits you want to cherry pick. For example, to cherry pick commits from def5678
to abc1234
, use:
git cherry-pick def5678^..abc1234
- Cherry Pick the Range of Commits
git cherry-pick def5678^..abc1234
This will apply all commits from def5678
to abc1234
onto the current branch.
Summary
- Identify the Commit: Use
git log
to find the commit hash you want to cherry pick. - Switch to the Target Branch: Check out the branch where you want to apply the commit.
- Cherry Pick the Commit: Use
git cherry-pick <commit-hash>
to apply the commit. - Resolve Conflicts: If conflicts occur, resolve them, add the resolved files, and continue the cherry pick operation.
- Cherry Pick Multiple Commits: Use
git cherry-pick <start-commit>^..<end-commit>
to cherry pick a range of commits.
Stash
Stashing in Git allows you to save your current work in progress and switch to a different task without committing the unfinished changes. Later, you can reapply the stashed changes and continue working. This is useful when you need to quickly switch contexts, for example, to fix a bug or review someone else’s code.
Step-by-Step Guide to Using Git Stash
1. Initial Setup
Let’s set up a simple Git repository and create some changes to demonstrate stashing.
- Initialize a Git Repository
mkdir stash-demo
cd stash-demo
git init
- Create an Initial Commit
echo "Initial content" > file.txt
git add file.txt
git commit -m "Initial commit"
- Make Some Changes
Edit file.txt
to add some changes that we will stash.
echo "Work in progress" >> file.txt
2. Stash Your Changes
- Check the Status
Before stashing, check the status to see the changes.
- Stash the Changes
git status
Output:
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: file.txt
no changes added to commit (use "git add" and/or "git commit -a")
Stash your changes using the git stash
command.
git stash
Output:
Saved working directory and index state WIP on main: [commit-hash] Initial commit
- Check the Status Again
Check the status to confirm that the working directory is clean.
git status
Output:
On branch main
nothing to commit, working tree clean
3. List Stashes
You can list all stashes using the git stash list
command.
git stash list
Output:
stash@{0}: WIP on main: [commit-hash] Initial commit
4. Apply Stashed Changes
- Apply the Latest Stash
To reapply the most recent stash, use the git stash apply
command.
git stash apply
- Apply a Specific Stash
If you have multiple stashes, you can apply a specific one using its identifier.
git stash apply stash@{0}
5. Drop or Pop a Stash
- Drop a Stash
To remove a stash after you’ve applied it, use the git stash drop
command.
git stash drop stash@{0}
- Pop a Stash
You can also apply and remove a stash in one step using the git stash pop
command.
git stash pop
6. Stash Untracked Files
By default, git stash
only stashes tracked files. To include untracked files, use the -u
or --include-untracked
option.
git stash -u
7. Stash with a Message
You can provide a message to describe your stash, making it easier to identify later.
git stash push -m "Work in progress on feature X"
8. Creating a Branch from a Stash
If you want to create a new branch from the changes in a stash, use the following commands:
- Create a Branch from a Stash
git stash branch new-branch-name stash@{0}
This creates a new branch new-branch-name
with the stashed changes applied.
Summary
- Stash Changes: Use
git stash
to save your current changes. - List Stashes: Use
git stash list
to view all stashes. - Apply Stash: Use
git stash apply
orgit stash pop
to reapply stashed changes. - Drop Stash: Use
git stash drop
to remove a specific stash. - Stash Untracked Files: Use
git stash -u
to include untracked files. - Stash with a Message: Use
git stash push -m "message"
to describe your stash. - Create Branch from Stash: Use
git stash branch new-branch-name stash@{n}
to create a new branch with stashed changes.