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:
- Launch an EC2 Instance:
Instance Type:
t2.medium
AMI: Amazon Linux 2 or Ubuntu 20.04
Security Group: Open ports
22
(SSH) and8080
(Jenkins).
- 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
- 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
- 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
- Windows:
Download and install Git from Git for Windows.
Open the Git Bash application to start using Git commands.
- Linux:
- Ubuntu/Debian:
sudo apt update
sudo apt install git
- CentOS/RHEL:
sudo yum install git
- 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.
- Set Global Username and Email:
git config --global user.name "Your Name"
git config --global user.email "yourname@example.com"
- Verify Configuration:
git config --global --list
- This should show:
user.name=Your Name
user.email=yourname@example.com
- 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.
- 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.
- Start the SSH Agent:
eval "$(ssh-agent -s)"
- Add Your SSH Key to the Agent:
ssh-add ~/.ssh/id_ed25519
- 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.
- 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:
- Store Credentials Using Credential Manager:
git config --global credential.helper cache
- 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:
- For SSH Users:
- Make sure the SSH key is added to the agent:
ssh-add ~/.ssh/id_ed25519
- 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:
- 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"
- Add the SSH Key to GitHub:
- Copy and add the public key (
id_
ed25519.pub
) to GitHub > Settings > SSH and GPG Keys.
- 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:
- Create a Personal Access Token:
Go to GitHub Settings > Developer Settings > Personal Access Tokens.
Generate a new token with the necessary scopes (e.g.,
repo
).
- Update the Remote URL:
git remote set-url origin https://<username>:<PAT>@github.com/username/repo.git
- 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:
- Verify the
.gitconfig
Location:
git config --global --list
- Manually Edit the
.gitconfig
File:
- Open the file:
nano ~/.gitconfig
- Manually add the
[user]
section:
[user]
name = Your Name
email = yourname@example.com
- 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
- Create a new directory and initialize Git:
mkdir beginner-git
cd beginner-git
git init
- Create and Commit a README File:
echo "# Beginner Git Project" > README.md
git add README.md
git commit -m "Initial commit with README"
- 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
- Create a New Branch and Switch to It:
git checkout -b feature-branch
- Make Changes and Commit in the New Branch:
echo "New Feature" > feature.txt
git add feature.txt
git commit -m "Added a new feature"
- Switch Back to
main
and Merge:
git checkout main
git merge feature-branch
- Delete the Feature Branch:
git branch -d feature-branch
Scenario 3: Working with the Staging Area
- Create a File and Stage It:
echo "Staged File Content" > stage.txt
git add stage.txt
- Modify the File Without Staging:
echo "Unstaged Changes" >> stage.txt
- Check the Status and View Differences:
git status
git diff
Scenario 4: Conflict Resolution
- 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"
- Merge and Resolve the Conflict:
git merge conflict-branch
- 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
- Create an Unwanted Commit:
echo "Sensitive Data" >> secrets.txt
git add secrets.txt
git commit -m "Added sensitive data"
- Revert the Commit:
git revert HEAD
Level 2: Intermediate Git Scenarios
Scenario 1: Using git stash
for Temporary Work
- Make Changes Without Committing:
echo "Temporary changes" >> temp.txt
git add temp.txt
- Stash the Changes:
git stash
- View and Apply the Stash:
git stash list
git stash pop
Scenario 2: Implementing Git Flow Strategy
- Create
develop
andrelease
Branches:
git checkout -b develop
git checkout -b release/1.0
- Create a Feature Branch:
git checkout -b feature-branch
echo "Feature Content" > feature.txt
git add feature.txt
git commit -m "Added new feature"
- Merge into
develop
andrelease
:
git checkout develop
git merge feature-branch
git checkout release/1.0
git merge develop
Scenario 3: Cherry-Picking Across Branches
- 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"
- 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 ofgit fetch
andgit 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:
- Check the Current Status:
git status
- Fetch Changes from Remote:
git fetch origin
- View the Logs to See Fetched Changes:
git log origin/main
- Merge the Fetched Changes:
git merge origin/main
- Using
git pull
Directly:
git pull origin main
- 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 usegit rebase
to reapply your commits on top of the latest changes, making your branch history linear and clean.
Commands:
- Fetch Changes from the Remote Repository:
git fetch origin
- Rebase Your Feature Branch onto the Latest Main:
git checkout feature-branch
git rebase origin/main
- Resolve Any Conflicts During Rebase:
View the conflicting files and edit manually.
Use
git rebase --continue
to complete the rebase process.
- 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 ofgit 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:
- 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"
- Switch Back to
main
and Merge:
git checkout main
git merge fast-forward-branch
- 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:
- 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"
- Switch Back to
main
and MergefeatureA
:
git checkout main
git merge featureA
- Rebase
featureB
onmain
:
git checkout featureB
git rebase main
- Common Issue: Conflicts During Rebase
- Solution: Use
git rebase --abort
if you want to cancel the rebase and usegit 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:
- List All Remote Branches:
git branch -r
- Create a Remote Tracking Branch:
git checkout -b new-feature origin/new-feature
- 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:
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.
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.
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
- 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.
- 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
orproduction
.
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:
- Switch to a Detached HEAD:
git checkout <commit-hash>
Make Changes and Realize You’re Not on a Branch.
Recover by Creating a New Branch:
git checkout -b recover-branch
git push origin recover-branch
Scenario 12: Managing Remote Branches
- List All Remote Branches:
git branch -r
- Create a Remote Tracking Branch:
git checkout -b new-feature origin/new-feature
- Delete a Remote Branch:
git push origin --delete old-feature
Scenario 13: Git Bisect for Finding Bugs
- Start Bisect:
git bisect start
git bisect bad
git bisect good <commit-hash>
- Mark Commits as Good or Bad Until the Issue is Found:
git bisect good/bad
- End Bisect:
git bisect reset
Scenario 14: Using git reflog
to Recover Lost Commits
- View All Reflog Entries:
git reflog
- Restore a Lost Commit:
git checkout <commit-hash>
- 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
- 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"
- Use Interactive Rebase to Squash Commits:
git rebase -i HEAD~2
- Change
pick
tosquash
for the second commit.
- Verify the Squashed Commit:
git log --oneline
Scenario 16: Managing Submodules in a Large Repository
- Add a Submodule:
git submodule add https://github.com/example/example-submodule.git
- Update Submodules:
git submodule update --remote
- Remove a Submodule:
git rm --cached example-submodule
Scenario 17: Using Git Hooks for Automating Pre-Commit Checks
- 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
- Add a Check for Python Linting:
echo "pylint *.py" >> .git/hooks/pre-commit
- Test the Pre-Commit Hook:
git add .
git commit -m "Testing pre-commit hook"
Scenario 18: Configuring Global Git Aliases for Efficiency
- 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
- 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
- Create a
.gitignore
File:
- Add commonly ignored files:
*.log
*.tmp
node_modules/
- Commit the
.gitignore
:
git add .gitignore
git commit -m "Added .gitignore file"