Git and GitHub – Git Undoing, Conflict Resolution, Cherry Picking, Stash (Part-11)

Posted by

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
        
        1. 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.

        1. Initialize a Git Repository
        mkdir cherry-pick-demo
        cd cherry-pick-demo
        git init
        
        1. Create an Initial Commit
        echo "Initial content" > file.txt
        git add file.txt
        git commit -m "Initial commit"
        
        1. 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

        1. Switch to the Target Branch
        git checkout main
        
        1. 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:

        1. 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"
        
        1. 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'
        
        1. 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
        
        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:

        1. 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
        
        1. 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.

        1. Initialize a Git Repository
        mkdir stash-demo
        cd stash-demo
        git init
        
        1. Create an Initial Commit
        echo "Initial content" > file.txt
        git add file.txt
        git commit -m "Initial commit"
        
        1. Make Some Changes

        Edit file.txt to add some changes that we will stash.

        echo "Work in progress" >> file.txt
        

        2. Stash Your Changes

        1. Check the Status

        Before stashing, check the status to see the changes.

        1. 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
        
        1. 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

        1. Apply the Latest Stash

        To reapply the most recent stash, use the git stash apply command.

        git stash apply
        
        1. 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

        1. Drop a Stash

        To remove a stash after you’ve applied it, use the git stash drop command.

        git stash drop stash@{0}
        
        1. 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:

        1. 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 or git 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.
        guest
        0 Comments
        Inline Feedbacks
        View all comments
        0
        Would love your thoughts, please comment.x
        ()
        x