# 60 Days DevOps Challenge: Day 4

### ***Challenge 1:*** *Perform an interactive rebase to modify commit history (rename, squash, reorder commits).*

**Step 1: View Recent Commits**

**First, check the last few commits:**

```plaintext
git log --oneline -n 5

example output

git log --oneline -n 5
119b035  adding more info branch
4679a42  Merge pull request #1 from MusaveerHolalkere25/feature-branch
f338466  updated feature file with more info
```

**Step 2: Start an Interactive Rebase**

**To modify the last 3 commits, run:**

```plaintext
git rebase -i HEAD~3
```

**This opens an editor with commit history:**

```plaintext
pick 6541260 Added feature.txt with new feature
pick d1fec70 Added feature.txt with new feature
pick f338466 updated feature file with more info
```

**Step 3: Modify Commits**

**1️⃣ Rename a Commit Message**

* Change **pick** to **reword** for the commit you want to rename  
    `reword 6541260 Added feature.txt with new feature`
    
    `pick d1fec70 Added feature.txt with new feature`
    
    `pick f338466 updated feature file with more info`
    
* **Save and close the editor.**
    
* Git will prompt you to **edit the commit message**.
    

**2️⃣ Squash Commits (Combine Multiple Commits)**

If you want to **merge multiple commits into one**, change:

```plaintext
pick a1b2c3d Added new feature

pick e4f5g6h Fixed API issue

squash i7j8k9l Updated documentation
```

* Save and close the editor.
    
* Git will show a new editor where you can **combine commit messages**.
    

**3️⃣ Reorder Commits**

**To reorder commits, simply change their order:**

```plaintext
pick e4f5g6h Fixed API issue

pick i7j8k9l Updated documentation

pick a1b2c3d Added new feature
```

* Save and close the editor.
    
* Git will replay commits in the new order.
    

**Step 4: Continue and Push Changes**

**After making changes:**

```plaintext
git rebase --continue
```

If you've already pushed the commits, **force push is required**:

```plaintext
git push origin feature-branch --force
```

🚨 **<mark>Use caution</mark>** <mark> when force-pushing, especially in shared branches.</mark>

### ***Challenge 2:*** *Use* `git cherry-pick` *to apply a specific commit from another branch to your current branch.*

**Step 1: Check the Commit History of the Source Branch**

**Switch to the branch where the commit exists and list recent commits:**

```plaintext
git checkout feature-branch
git log --oneline --graph -n 5
```

**Example output:**

```plaintext
a1b2c3d (HEAD -> feature-branch) Added feature X

e4f5g6h Fixed API issue

i7j8k9l Updated documentation
```

**Step 2: Switch to the Target Branch**

**Move to the branch where you want to apply the commit:**

```plaintext
git checkout main
```

**Step 3: Apply the Specific Commit**

Pick a commit from **feature-branch** (e.g., e4f5g6h) and apply it to main:

```plaintext
git cherry-pick e4f5g6h
```

**If successful, you’ll see:**

```plaintext
[main a9d8e7c] Fixed API issue

 1 file changed, 2 insertions(+), 1 deletion(-)
```

**Step 4: Handle Cherry-Pick Conflicts (If Needed)**

**If there's a conflict:**

1. **Resolve the conflict manually** in the affected files.
    
2. **Mark the conflict as resolved**:  
    `git add <resolved-file>`
    
3. **Continue cherry-picking**:  
    `git cherry-pick --continue`
    

To **abort the cherry-pick** and restore the previous state:

```plaintext
git cherry-pick --abort
```

**Step 5: Push the Changes**

```plaintext
git push origin main
```

### ***Challenge 3:*** *Create a merge conflict scenario and manually resolve it using* `git merge` *and* `git rebase`*.*

**Step 1: Setup a Repository (or Use an Existing One)**

```plaintext
git init merge-conflict-demo

cd merge-conflict-demo

echo "Line 1" > conflict.txt

git add conflict.txt

git commit -m "Initial commit"
```

**Step 2: Create a Feature Branch and Modify the File**

```plaintext
git checkout -b feature-branch

echo "Feature branch change" > conflict.txt

git commit -am "Modified conflict.txt in feature-branch"
```

**Step 3: Modify the File in main (Causing a Conflict)**

```plaintext
git checkout main

echo "Main branch change" > conflict.txt

git commit -am "Modified conflict.txt in main"
```

**Step 4: Merge Feature Branch (Causing a Conflict)**

```plaintext
git merge feature-branch
```

**You’ll see:**

```plaintext
Auto-merging conflict.txt

CONFLICT (content): Merge conflict in conflict.txt

Automatic merge failed; fix conflicts and then commit the result.
```

**Step 4.**

**Resolve Merge Conflict Manually**

**Open conflict.txt, which looks like:**

```plaintext
<<<<<<< HEAD
Main branch change

=======

Feature branch change

>>>>>>> feature-branch
```

**Edit the file to keep both changes:**  
**Main and Feature branch merged successfully.**

**Mark the conflict as resolved**:

```plaintext
git add conflict.txt
git commit -m "Resolved merge conflict"
```

**Step 5: Reset and Use git rebase to Handle the Conflict**

If you prefer rebase, reset, and use:

```plaintext
git checkout main

git reset --hard HEAD~1  # Undo the merge

git rebase feature-branch
```

* If conflicts occur, Git will stop and prompt you to **resolve them manually**.
    
* Follow **Step 4.1** to edit the file, then run:  
    `git add conflict.txt`
    
    `git rebase --continue`
    

**Step 6: Push Changes**

If you modified history using rebase, **force push is needed**:

```plaintext
git push origin main --force
```

### **Challenge 4:** Undo a commit using `git reset` (soft, mixed, and hard) and `git revert` – understand the differences.

You can **undo a commit** using `git reset` (`soft`, `mixed`, `hard`) or `git revert`. Below is a step-by-step guide to understand their differences.

##### **Step 1: Create a Sample Repository and Make Commits**

```plaintext
git init undo-demo cd undo-demo echo "First commit" > file.txt git add file.txt git commit -m "First commit" echo "Second commit" >> file.txt git commit -am "Second commit" echo "Third commit" >> file.txt git commit -am "Third commit"
```

Now, we have 3 commits.

**Check commit history:**

```plaintext
git log --oneline --graph --decorate -n 3
```

**Example output:**

```plaintext
abc1234 (HEAD -> main) Third commit def5678 Second commit ghi7890 First commit
```

##### **Option 1: Undo a Commit Using** `git reset`

###### **1️⃣** `git reset --soft` **(Undo Commit, Keep Changes Staged)**

```plaintext
git reset --soft HEAD~1
```

* Moves HEAD back **one commit**.
    
* Keeps **changes in the staging area** (can be re-committed).
    
* **Example Use Case:** You committed too early and want to edit before recommitting.
    

✅ **Commit undone, but changes remain staged (**`git status` **will show staged files).**

###### **2️⃣** `git reset --mixed` **(Undo Commit, Keep Changes Unstaged)**

```plaintext
git reset --mixed HEAD~1
```

* Moves HEAD back **one commit**.
    
* **Keeps changes in working directory**, but **unstaged** (must `git add` again).
    
* **Example Use Case:** You committed but forgot to stage some files.
    

**Commit undone, but changes remain unstaged (**`git status` **will show modified files).**

###### **3️⃣** `git reset --hard` **(Undo Commit and Discard Changes)**

```plaintext
git reset --hard HEAD~1
```

🚨 **WARNING:** This **deletes the commit and all changes** in your working directory.

* Moves HEAD back **one commit**.
    
* **Deletes all changes** (cannot be recovered unless backed up).
    
* **Example Use Case:** You made a bad commit and want to completely remove it.
    

✅**Commit undone, and changes are lost.**

#### **Reset Summary:**

| **Command** | **Effect** |
| --- | --- |
| `git reset --soft HEAD~1` | Undo commit, keep changes **staged** |
| `git reset --mixed HEAD~1` | Undo commit, keep changes **unstaged** |
| `git reset --hard HEAD~1` | Undo commit and **delete all changes** |

##### **Option 2: Undo a Commit Using** `git revert` **(Safer)**

Unlike `reset`, `git revert` **does not delete history**. Instead, it creates a **new commit that cancels the previous commit**.

```plaintext
git revert HEAD
```

* This **creates a new commit** that **undoes the last commit**.
    
* **Safe for shared repositories** (does not rewrite history).
    
* **Example Use Case:** You already pushed the commit and need to undo it without force-pushing.
    

**It' creates a new commit with reversed changes.**

##### **Step 3: Push Changes (If Reset or Revert Was Used)**

If you've already pushed commits to GitHub, you might need a **force push** after `git reset`:

```plaintext
git push origin main --force
```

🚨 **Caution:** This rewrites history and can affect others working on the branch.

If using `git revert`, just push normally:

```plaintext
git push origin main
```

**It’s safer because it does not require force-pushing.**

### ***Challenge 5: Amend the last commit message and add a forgotten file to the last commit using*** `git commit --amend`***.***

We can **amend the last commit message** and **add a forgotten file** using `git commit --amend`. Here’s how:

##### **Step 1: Check Your Last Commit**

```plaintext
git log --oneline -n 1
```

**Example output:**

```plaintext
a1b2c3d (HEAD -> main) Initial commit
```

##### **Step 2: Modify the Last Commit Message**

If you **only want to change the commit message**, run:

```plaintext
git commit --amend -m "Updated commit message"
```

This **replaces the last commit message** without modifying any files.

##### **Step 3: Add a Forgotten File to the Last Commit**

1. **Create or modify the file**:
    

```plaintext
echo "New content" > forgotten.txt
```

**Stage the new file**:

```plaintext
git add forgotten.txt
```

**Amend the last commit to include this file**:

```plaintext
git commit --amend --no-edit
```

* `--no-edit` keeps the **same commit message**.
    
* The commit is now updated to include `forgotten.txt`.
    

##### **Step 4: Push the Amended Commit**

If you have already pushed the commit to GitHub, you need to **force push**:

```plaintext
git push origin main --force
```

🚨 **Caution:** Force-pushing rewrites history and can affect collaborators.

### ***Challenge 6:*** *Set up Git hooks (pre-commit or post-commit) to automate a simple check before committing changes.*

We can set up **Git hooks** to automate checks before committing changes. Below is a step-by-step guide to creating a **pre-commit hook** that checks for trailing whitespace before allowing a commit.

**Step 1: Navigate to the Git Hooks Directory**

**Each Git repository has a .git/hooks/ directory containing sample hooks. Move into your repository:**

```plaintext
cd /path/to/your-repo

cd .git/hooks
```

**Step 2: Create a Pre-Commit Hook**

Create a new **pre-commit** script:

```plaintext
nano pre-commit
#Paste the following script:
```

```plaintext

#!/bin/bash

# Check for trailing whitespace

if git diff --cached --check | grep -q "trailing whitespace"; then

    echo "❌ Commit rejected: Trailing whitespace found!"

    exit 1  # Prevent commit

fi

echo "✅ Pre-commit check passed!"

exit 0  # Allow commit
```

**Explanation:**

* Uses git diff --cached --check to find **trailing whitespace**.
    
* If found, **aborts the commit** (exit 1).
    
* If no issues, **commit proceeds** (exit 0).
    

**Step 3: Make the Hook Executable**

```plaintext
chmod +x pre-commit
```

**Step 4: Test the Hook**

1. **Make a change with trailing whitespace:**   
    `echo "Hello, world!   " >> file.txt  # Extra spaces at the end`
    
    `git add file.txt`
    
    `git commit -m "Test commit"`  
    
2. If there’s trailing whitespace, the commit will be **rejected**:  
      
    ❌ `Commit rejected: Trailing whitespace found!`
    
3. **Fix the issue** by removing trailing spaces, then commit again.
    

**Step 5: (Optional) Create a Post-Commit Hook**

If you want to **log commit details after every commit**, create a **post-commit** hook:

`nano post-commit`

**Add the following:**

```plaintext
#!/bin/bash

echo "✅ Commit successful! Commit Hash: $(git rev-parse HEAD)"

Make it executable:

chmod +x post-commit
```

Make it executable:

`chmod +x post-commit`

**Now, every time you commit, it will log:**

✅ Commit successful! Commit Hash: a1b2c3d

**Bonus: Share Hooks Across Team**

To share hooks across a team:

1. **Move them to a version-controlled directory:**  
    `mkdir -p .githooks`
    
    `mv .git/hooks/pre-commit .githooks/`
    
2. **Configure Git to use this directory:**  
    `git config core.hooksPath .githooks`
    

### ***Challenge 7:*** *Rebase a feature branch on top of the main branch without creating unnecessary merge commits.*

We can **rebase a feature branch on top of the main branch** without creating unnecessary merge commits by following these steps.

**Step 1: Switch to the Feature Branch**

```plaintext
git checkout feature-branch
```

**Ensure you're on the correct branch that needs to be rebased.**

**Step 2: Fetch the Latest Changes from Remote**

Before rebasing, update your local main branch:

```plaintext
git fetch origin

git checkout main

git pull origin main
```

**Now, your main branch is up-to-date.**

**Step 3: Rebase feature-branch on main**

```plaintext
git checkout feature-branch

git rebase main
```

* **This moves all commits from feature-branch to start from the latest main commit.**
    
* It **avoids unnecessary merge commits** and keeps a **clean history**.
    

**Step 4: Handle Conflicts (If Any)**

If there are conflicts, Git will pause the rebase and show:

```plaintext
CONFLICT (content): Merge conflict in file.txt
```

1. **Manually resolve conflicts** in the affected files.
    
2. **Mark conflicts as resolved**:  
    `git add <resolved-file>`
    
3. **Continue the rebase**:  
    `git rebase --continue`
    
4. If needed, **abort the rebase**:  
    `git rebase --abort`
    

**Step 5: Push the Rebasing Changes**

If the feature branch is already pushed to GitHub, **you need to force push**:

```plaintext
git push origin feature-branch --force
```

🚨 **Caution:** This rewrites history. Be careful when working in a shared branch.

**Example Scenario**

**Before Rebase (git log --oneline --graph)**

```plaintext
* commit C (feature-branch)

* commit B (feature-branch)

| * commit M2 (main)

| * commit M1 (main)

|/

* commit A (common ancestor)
```

**After Rebase**

```plaintext
* commit C' (feature-branch, rebased)

* commit B' (feature-branch, rebased)

* commit M2 (main)

* commit M1 (main)

* commit A (common ancestor)
```

 **No extra merge commits. A cleaner history!**

### ***Challenge 8:*** *Create a branch, make multiple commits, then squash them into a single commit using* `git rebase -i`*.*

We can **create a branch, make multiple commits, and squash them into a single commit** using **git rebase -i** by following these steps.

**Step 1: Create a New Branch**

```plaintext
git checkout -b feature-branch
```

**Step 2: Make Multiple Commits**

```plaintext
echo "First change" > file.txt

git add file.txt

git commit -m "First commit"



echo "Second change" >> file.txt

git commit -am "Second commit"



echo "Third change" >> file.txt

git commit -am "Third commit"
```

Now, we have **three separate commits**.

**Check commit history:**

```plaintext
git log --oneline -n 3
```

**Example output:**

```plaintext
abc1234 (HEAD -> feature-branch) Third commit

def5678 Second commit

ghi7890 First commit
```

**Step 3: Start an Interactive Rebase**

To squash the last **3 commits** into **one**, run:

```plaintext
git rebase -i HEAD~3
```

**Step 4: Modify the Rebase Commands**

**The editor will open with:**

```plaintext
pick abc1234 Third commit

pick def5678 Second commit

pick ghi7890 First commit
```

**Change it to:**

```plaintext
pick abc1234 Third commit

squash def5678 Second commit

squash ghi7890 First commit
```

* pick → Keeps the first commit.
    
* squash → Merges the next commits into the first one.
    

**Save and close the editor.**

**Step 5: Edit the Commit Message**

Git will prompt you to edit the commit message:

```plaintext
# This is a combination of 3 commits.

First commit

Second commit

Third commit
```

Edit this into a **single meaningful commit message**:

```plaintext
Combined all feature changes into one commit
```

**Save and close the editor.**

**Step 6: Push the Squashed Commit**

If you already pushed the previous commits, **force push is needed**:

```plaintext
git push origin feature-branch --force
```

🚨 **Caution:** This rewrites history, so be careful in shared branches.

**Step 7: Verify the Squash**

**Check commit history:**

```plaintext
git log --oneline -n 3

#Expected output:

xyz5678 (HEAD -> feature-branch) Combined all feature changes into one commit
```

**Now all three commits are now merged into a single commit.**

*In conclusion, the 60 Days DevOps Challenge: Day 4 provides a comprehensive guide to mastering essential Git operations. By engaging in these challenges, you gain hands-on experience with interactive rebase, cherry-picking, resolving merge conflicts, and understanding the nuances of Git reset and revert. Additionally, setting up Git hooks and practicing rebasing and squashing commits enhance your ability to maintain a clean and efficient commit history. These skills are crucial for effective collaboration and version control in any DevOps environment, empowering you to manage code changes with confidence and precision.*
