Day 1: Git Mastery Guide for DevOps Engineers — From Zero to Hero

Day 1: Git Mastery Guide for DevOps Engineers — From Zero to Hero


Welcome to Day 1 of our comprehensive DevOps/SRE guide! Today, we’re going to cover Git in-depth, starting from the basics and progressing to advanced scenarios. Each section is structured into three levels—Beginner, Intermediate, and Advanced—to ensure that you gain a solid understanding of every core concept and workflow in Git.

By the end of this session, you’ll have practised over 15 real-world Git scenarios and will be confident in using Git for day-to-day tasks as well as complex workflows in a professional setting. Let’s dive into the complete Git mastery experience!

Environment Setup for Git Scenarios

All scenarios will be executed using the DevOps-Zero-to-Hero repository hosted on an AWS EC2 instance. If you haven’t set up your environment yet, follow these instructions:

  1. Launch an EC2 Instance:
  • Instance Type: t2.medium

  • AMI: Amazon Linux 2 or Ubuntu 20.04

  • Security Group: Open ports 22 (SSH) and 8080 (Jenkins).

  1. Install Git and Other Dependencies:

sudo yum update -y

sudo yum install git docker -y

sudo service docker start

sudo usermod -aG docker ec2-user
  1. Install Jenkins:

sudo yum install java-11-openjdk-devel -y

sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo

sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key

sudo yum install jenkins -y

sudo systemctl start jenkins
  1. Clone the DevOps-Zero-to-Hero Repository:

git clone https://github.com/ruBhutan/DevOps-Zero-to-Hero.git

cd DevOps-Zero-to-Hero

Configuring Git for the First Time: Windows, Linux, and Mac

When setting up Git for the first time on your system, configuring global settings and authenticating with a remote repository (e.g., GitHub, GitLab) is crucial. Let’s break down the step-by-step process for different operating systems.

Step 1: Install Git

  1. Windows:
  • Download and install Git from Git for Windows.

  • Open the Git Bash application to start using Git commands.

  1. Linux:
  • Ubuntu/Debian:

sudo apt update

sudo apt install git
  • CentOS/RHEL:

sudo yum install git
  1. Mac:
  • Use Homebrew:

brew install git

Step 2: Configure Git Username and Email

After installing Git, the first step is to set up your username and email address, which will be associated with every commit you make.

  1. Set Global Username and Email:

git config --global user.name "Your Name"

git config --global user.email "yourname@example.com"
  1. Verify Configuration:

git config --global --list
  • This should show:

user.name=Your Name

user.email=yourname@example.com
  1. Set Up Local Config (For Specific Projects):
  • Navigate to your project directory:

cd /path/to/project
  • Run local configuration:

git config user.name "Project Specific Name"

git config user.email "project.email@example.com"

Step 3: GitHub SSH Key Setup for Authentication

To securely authenticate with GitHub, set up an SSH key. This eliminates the need to enter your GitHub username and password each time you pull or push changes.

  1. Generate a New SSH Key:

ssh-keygen -t ed25519 -C "yourname@example.com"
  • This command will generate a new SSH key pair in the ~/.ssh directory.
  1. Start the SSH Agent:

eval "$(ssh-agent -s)"
  1. Add Your SSH Key to the Agent:

ssh-add ~/.ssh/id_ed25519
  1. Copy the Public Key:

cat ~/.ssh/id_ed25519.pub
  • Copy the output and add it to your GitHub account under Settings > SSH and GPG Keys.
  1. Test Your Connection:

ssh -T git@github.com
  • You should see a message like Hi <username>! You've successfully authenticated.

Step 4: Using HTTPS Instead of SSH (Windows, Linux, and Mac)

If you prefer to use HTTPS for authentication:

  1. Store Credentials Using Credential Manager:

git config --global credential.helper cache
  1. Clone a Repository Using HTTPS:

git clone https://github.com/yourusername/repo.git
  • Git will prompt you to enter your GitHub username and password once, and it will cache them for subsequent operations.

Common Git Configuration Issues and Their Solutions

Issue 1: Git Asks for Username and Password Every Time

Cause:

This usually happens if your SSH key is not added to the agent or if HTTPS is configured incorrectly.

Solution:

  1. For SSH Users:
  • Make sure the SSH key is added to the agent:

ssh-add ~/.ssh/id_ed25519
  1. For HTTPS Users:
  • Use the credential helper:

git config --global credential.helper cache

Issue 2: Permission Denied (publickey) Error

Cause:

Your SSH key is not recognized by GitHub.

Solution:

  1. Verify Your SSH Key:
  • Check if the SSH key exists:

ls -al ~/.ssh
  • If no key exists, generate a new one:

ssh-keygen -t ed25519 -C "yourname@example.com"
  1. Add the SSH Key to GitHub:
  • Copy and add the public key (id_ed25519.pub) to GitHub > Settings > SSH and GPG Keys.
  1. Test the Connection:

ssh -T git@github.com

Issue 3: fatal: Authentication Failed for HTTPS

Cause:

This can occur due to incorrect credentials or when GitHub removes password-based authentication in favor of personal access tokens (PATs).

Solution:

  1. Create a Personal Access Token:
  1. Update the Remote URL:

git remote set-url origin https://<username>:<PAT>@github.com/username/repo.git
  1. Push the Changes Again:

git push

Issue 4: git config Not Saving Changes Globally

Cause:

Incorrect usage of the --global flag or a corrupted .gitconfig file.

Solution:

  1. Verify the .gitconfig Location:

git config --global --list
  1. Manually Edit the .gitconfig File:
  • Open the file:

nano ~/.gitconfig
  • Manually add the [user] section:

[user]

name = Your Name

email = yourname@example.com
  1. Save and Retry:
  • Save the file and try running a Git command like git commit again.

Now that our environment is ready, let’s get started with our Beginner-Level Git scenarios!


Level 1: Beginner Git Scenarios

Scenario 1: Setting Up a New Git Repository

  1. Create a new directory and initialize Git:

mkdir beginner-git

cd beginner-git

git init
  1. Create and Commit a README File:

echo "# Beginner Git Project" > README.md

git add README.md

git commit -m "Initial commit with README"
  1. Create a Remote Repository and Push Changes:

git remote add origin https://github.com/yourusername/beginner-git.git

git push -u origin main

Scenario 2: Creating and Managing Branches

  1. Create a New Branch and Switch to It:

git checkout -b feature-branch
  1. Make Changes and Commit in the New Branch:

echo "New Feature" > feature.txt

git add feature.txt

git commit -m "Added a new feature"
  1. Switch Back to main and Merge:

git checkout main

git merge feature-branch
  1. Delete the Feature Branch:

git branch -d feature-branch

Scenario 3: Working with the Staging Area

  1. Create a File and Stage It:

echo "Staged File Content" > stage.txt

git add stage.txt
  1. Modify the File Without Staging:

echo "Unstaged Changes" >> stage.txt
  1. Check the Status and View Differences:

git status

git diff

Scenario 4: Conflict Resolution

  1. Create a Conflict:

git checkout -b conflict-branch

echo "Conflicting Line 1" > conflict.txt

git add conflict.txt

git commit -m "Commit in conflict-branch"

git checkout main

echo "Main Branch Line 1" > conflict.txt

git add conflict.txt

git commit -m "Commit in main"
  1. Merge and Resolve the Conflict:

git merge conflict-branch
  1. Manually Resolve the Conflict in conflict.txt:
  • Edit the file, stage, and commit:

git add conflict.txt

git commit -m "Resolved conflict in conflict.txt"

Scenario 5: Undoing Changes Using git revert

  1. Create an Unwanted Commit:

echo "Sensitive Data" >> secrets.txt

git add secrets.txt

git commit -m "Added sensitive data"
  1. Revert the Commit:

git revert HEAD

Level 2: Intermediate Git Scenarios

Scenario 1: Using git stash for Temporary Work

  1. Make Changes Without Committing:

echo "Temporary changes" >> temp.txt

git add temp.txt
  1. Stash the Changes:

git stash
  1. View and Apply the Stash:

git stash list

git stash pop

Scenario 2: Implementing Git Flow Strategy

  1. Create develop and release Branches:

git checkout -b develop

git checkout -b release/1.0
  1. Create a Feature Branch:

git checkout -b feature-branch

echo "Feature Content" > feature.txt

git add feature.txt

git commit -m "Added new feature"
  1. Merge into develop and release:

git checkout develop

git merge feature-branch

git checkout release/1.0

git merge develop

Scenario 3: Cherry-Picking Across Branches

  1. Create a Branch and Make Commits:

git checkout -b experimental-branch

echo "Experimental Feature" > experiment.txt

git add experiment.txt

git commit -m "Added experimental feature"
  1. Cherry-Pick the Commit into main:

git checkout main

git cherry-pick <commit-hash>

Scenario 4: Understanding git fetch and git pull

Concepts:

  • git fetch: Retrieves the latest changes from the remote repository but does not merge them into the current branch.

  • git pull: A combination of git fetch and git merge. It fetches and merges the changes into your local branch.

Real-Time Example:

You’re working on a feature branch while your teammate is making changes to the main branch. Use git fetch to view the latest changes without merging. Use git pull if you want to merge those changes into your branch.

Commands:

  1. Check the Current Status:

git status
  1. Fetch Changes from Remote:

git fetch origin
  1. View the Logs to See Fetched Changes:

git log origin/main
  1. Merge the Fetched Changes:

git merge origin/main
  1. Using git pull Directly:

git pull origin main
  1. Common Issue: Merge Conflicts
  • Problem: Conflicts occur when the same file is modified differently in the local and remote repositories.

  • Solution:

  • View the conflicting files using git status.

  • Manually edit the conflicts in the file, choosing which changes to keep.

  • Stage and commit the resolved files:


git add <conflicted-file>

git commit -m "Resolved merge conflicts in <file>"

Scenario 5: Real-Time Scenario with git fetch and git rebase

Concepts:

  • Use git fetch to see the latest changes and then use git rebase to reapply your commits on top of the latest changes, making your branch history linear and clean.

Commands:

  1. Fetch Changes from the Remote Repository:

git fetch origin
  1. Rebase Your Feature Branch onto the Latest Main:

git checkout feature-branch

git rebase origin/main
  1. Resolve Any Conflicts During Rebase:
  • View the conflicting files and edit manually.

  • Use git rebase --continue to complete the rebase process.

  1. Real-World Issue: Rebasing in a Shared Repository
  • Problem: If you rebase a shared branch, it rewrites the commit history, causing confusion for other team members.

  • Solution: Use git pull --rebase instead of git pull to incorporate changes without rewriting history.

Scenario 6: Handling Fast-Forward Merges

Concepts:

  • A fast-forward merge occurs when the branch being merged has not diverged from the current branch. Git just moves the pointer forward without creating a new commit.

Commands:

  1. Create and Switch to a New Branch:

git checkout -b fast-forward-branch

echo "Fast-forward merge example" >> fast-forward.txt

git add fast-forward.txt

git commit -m "Added fast-forward.txt"
  1. Switch Back to main and Merge:

git checkout main

git merge fast-forward-branch
  1. Verify the Merge with git log:
  • A new commit for merging should not be created in this scenario.

Scenario 7: Using git merge vs git rebase

Concepts:

  • git merge: Combines changes from two branches, preserving their histories.

  • git rebase: Re-applies commits from one branch onto another, creating a linear history.

Hands-On Example:

  1. Create Two Separate Feature Branches:

git checkout -b featureA

echo "Feature A Content" > featureA.txt

git add featureA.txt

git commit -m "Added featureA.txt"

git checkout -b featureB

echo "Feature B Content" > featureB.txt

git add featureB.txt

git commit -m "Added featureB.txt"
  1. Switch Back to main and Merge featureA:

git checkout main

git merge featureA
  1. Rebase featureB on main:

git checkout featureB

git rebase main
  1. Common Issue: Conflicts During Rebase
  • Solution: Use git rebase --abort if you want to cancel the rebase and use git rebase --continue after resolving conflicts.

Scenario 8: Managing Remote Branches

Concepts:

  • Learn how to track and manage remote branches, delete branches on remote, and handle origin references.

Commands:

  1. List All Remote Branches:

git branch -r
  1. Create a Remote Tracking Branch:

git checkout -b new-feature origin/new-feature
  1. Delete a Remote Branch:

git push origin --delete old-feature

Scenario 9: Using git reset vs git revert vs git restore

Each of these commands serves a different purpose in managing your repository’s history. Let’s break them down:

  1. git reset:
  • Purpose: Move the HEAD pointer back to a specific commit, effectively rewriting history.

  • Usage:


git reset --soft HEAD~1   # Move HEAD, keep changes staged

git reset --hard HEAD~1   # Move HEAD, discard all changes
  • Example: If you accidentally made a commit, git reset --soft will uncommit the changes and keep them in the staging area for you to modify.
  1. git revert:
  • Purpose: Create a new commit that undoes the changes from a specific commit.

  • Usage:


git revert <commit-hash>
  • Example: If you committed a bug to production, use git revert to create a new commit that removes those changes, preserving history.
  1. git restore:
  • Purpose: Restore a file or set of files to a previous version.

  • Usage:


git restore --source=HEAD~1 README.md
  • Example: Use git restore if you want to discard local changes and return to the last committed state.

Scenario 10: Understanding and Handling Fast-Forward and No-Fast-Forward Merges

  1. Fast-Forward Merge:
  • Concept: A fast-forward merge moves the branch pointer forward without creating a new commit. This happens when there are no divergent commits.

  • Example:


git checkout main

git merge --ff-only feature-branch
  • Use Case: When you want to integrate a feature branch that’s linearly ahead of the current branch.
  1. No-Fast-Forward Merge:
  • Concept: Creates a merge commit, preserving the history of both branches.

  • Example:


git merge --no-ff feature-branch
  • Use Case: Useful for preserving commit history in long-lived branches like main or production.

Scenario 11: Handling Detached HEAD State

Issue:

The detached HEAD state occurs when you check out a specific commit rather than a branch, making any changes non-persistent.

Hands-On:

  1. Switch to a Detached HEAD:

git checkout <commit-hash>
  1. Make Changes and Realize You’re Not on a Branch.

  2. Recover by Creating a New Branch:


git checkout -b recover-branch

git push origin recover-branch

Scenario 12: Managing Remote Branches

  1. List All Remote Branches:

git branch -r
  1. Create a Remote Tracking Branch:

git checkout -b new-feature origin/new-feature
  1. Delete a Remote Branch:

git push origin --delete old-feature

Scenario 13: Git Bisect for Finding Bugs

  1. Start Bisect:

git bisect start

git bisect bad

git bisect good <commit-hash>
  1. Mark Commits as Good or Bad Until the Issue is Found:

git bisect good/bad
  1. End Bisect:

git bisect reset

Scenario 14: Using git reflog to Recover Lost Commits

  1. View All Reflog Entries:

git reflog
  1. Restore a Lost Commit:

git checkout <commit-hash>
  1. Create a New Branch from the Lost Commit:

git checkout -b recovered-branch

Level 3: Advanced Git Scenarios

Scenario 15: Squashing Commits with Interactive Rebase

  1. Create a Feature Branch with Multiple Commits:

git checkout -b squash-feature

echo "Line 1" > squash.txt

git add squash.txt

git commit -m "Added line 1"

echo "Line 2" >> squash.txt

git add squash.txt

git commit -m "Added line 2"
  1. Use Interactive Rebase to Squash Commits:

git rebase -i HEAD~2
  • Change pick to squash for the second commit.
  1. Verify the Squashed Commit:

git log --oneline

Scenario 16: Managing Submodules in a Large Repository

  1. Add a Submodule:

git submodule add https://github.com/example/example-submodule.git
  1. Update Submodules:

git submodule update --remote
  1. Remove a Submodule:

git rm --cached example-submodule

Scenario 17: Using Git Hooks for Automating Pre-Commit Checks

  1. Create a Pre-Commit Hook:

echo "#!/bin/bash" > .git/hooks/pre-commit

echo "echo 'Running pre-commit checks...'" >> .git/hooks/pre-commit

chmod +x .git/hooks/pre-commit
  1. Add a Check for Python Linting:

echo "pylint *.py" >> .git/hooks/pre-commit
  1. Test the Pre-Commit Hook:

git add .

git commit -m "Testing pre-commit hook"

Scenario 18: Configuring Global Git Aliases for Efficiency

  1. Create Aliases for Common Commands:

git config --global alias.co checkout

git config --global alias.br branch

git config --global alias.ci commit

git config --global alias.st status
  1. Verify the Configuration:

git config --global --list
  • You should see the aliases listed:

alias.co=checkout

alias.br=branch

alias.ci=commit

alias.st=status

Scenario 19: Using .gitignore Effectively

  1. Create a .gitignore File:
  • Add commonly ignored files:

*.log

*.tmp

node_modules/
  1. Commit the .gitignore:

git add .gitignore

git commit -m "Added .gitignore file"