DevOps Zero to Hero: Part 2 - Git and GitHub Fundamentals
Introduction
Version control is the foundation of modern software development and DevOps practices. In this part, you'll master Git and GitHub, learning how to manage code, collaborate with teams, and set up the foundation for CI/CD pipelines.
Understanding Version Control
Version control systems track changes to files over time, allowing you to:
Revert files to previous states
Compare changes over time
See who modified what and when
Collaborate without overwriting each other's work
Branch out to experiment safely
Git Basics
What is Git?
Git is a distributed version control system created by Linus Torvalds in 2005. Unlike centralized systems, every Git directory on every computer is a full-fledged repository with complete history and version tracking abilities.
Git Architecture
Git has three main states for files:
Working Directory: Where you modify files
Staging Area (Index): Where you prepare commits
Repository: Where Git stores commits permanently
Setting Up Git
Configuration
# Set your identity
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
# Set default branch name
git config --global init.defaultBranch main
# Set default editor
git config --global core.editor "code --wait" # For VS Code
# Check your configuration
git config --list
SSH Key Setup for GitHub
# Generate SSH key
ssh-keygen -t ed25519 -C "your.email@example.com"
# Start SSH agent
eval "$(ssh-agent -s)"
# Add SSH key to agent
ssh-add ~/.ssh/id_ed25519
# Copy public key (then add to GitHub settings)
cat ~/.ssh/id_ed25519.pub
Essential Git Commands
Repository Operations
# Initialize a new repository
git init
# Clone an existing repository
git clone https://github.com/username/repository.git
# Check repository status
git status
# View commit history
git log
git log --oneline --graph --all
Basic Workflow
# Add files to staging area
git add filename.txt
git add . # Add all changes
# Commit changes
git commit -m "Descriptive commit message"
# Push to remote repository
git push origin main
# Pull latest changes
git pull origin main
# Fetch without merging
git fetch origin
Branching and Merging
# Create and switch to new branch
git checkout -b feature/new-feature
# List branches
git branch
git branch -a # Include remote branches
# Switch branches
git checkout main
# Merge branch
git merge feature/new-feature
# Delete branch
git branch -d feature/new-feature # Local
git push origin --delete feature/new-feature # Remote
GitHub Fundamentals
Creating Your First Repository
Log in to GitHub
Click "New repository"
Configure:
Repository name:
devops-web-app
Description: "Sample web application for DevOps learning"
Public repository
Initialize with README
Add .gitignore (Node)
Choose MIT license
Repository Structure
devops-web-app/
├── README.md
├── LICENSE
├── .gitignore
├── .github/
│ └── workflows/
├── src/
│ └── app.js
├── tests/
│ └── app.test.js
├── Dockerfile
├── docker-compose.yml
├── terraform/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
└── package.json
Creating Our Sample Web Application
Let's build a Node.js web server that we'll use throughout this series:
Step 1: Initialize the Project
mkdir devops-web-app
cd devops-web-app
git init
npm init -y
Step 2: Create the Application
Create src/app.js
:
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
// Health check endpoint
app.get('/health', (req, res) => {
res.status(200).json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
environment: process.env.NODE_ENV || 'development'
});
});
// Main endpoint
app.get('/', (req, res) => {
res.json({
message: 'Welcome to DevOps Web App',
version: '1.0.0',
endpoints: {
health: '/health',
info: '/info',
metrics: '/metrics'
}
});
});
// Info endpoint
app.get('/info', (req, res) => {
res.json({
app: 'DevOps Web App',
version: process.env.APP_VERSION || '1.0.0',
node: process.version,
memory: process.memoryUsage(),
pid: process.pid
});
});
// Basic metrics endpoint
app.get('/metrics', (req, res) => {
res.json({
requests_total: global.requestCount || 0,
memory_usage_bytes: process.memoryUsage().heapUsed,
uptime_seconds: process.uptime()
});
});
// Middleware to count requests
app.use((req, res, next) => {
global.requestCount = (global.requestCount || 0) + 1;
next();
});
// Start server
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
console.log(`Health check: http://localhost:${PORT}/health`);
});
module.exports = app;
Step 3: Create Tests
Create tests/app.test.js
:
const request = require('supertest');
const app = require('../src/app');
describe('API Endpoints', () => {
test('GET / should return welcome message', async () => {
const response = await request(app).get('/');
expect(response.status).toBe(200);
expect(response.body.message).toBe('Welcome to DevOps Web App');
});
test('GET /health should return healthy status', async () => {
const response = await request(app).get('/health');
expect(response.status).toBe(200);
expect(response.body.status).toBe('healthy');
});
test('GET /info should return app information', async () => {
const response = await request(app).get('/info');
expect(response.status).toBe(200);
expect(response.body.app).toBe('DevOps Web App');
});
});
Step 4: Update package.json
{
"name": "devops-web-app",
"version": "1.0.0",
"description": "Sample web application for DevOps learning",
"main": "src/app.js",
"scripts": {
"start": "node src/app.js",
"dev": "nodemon src/app.js",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"jest": "^29.5.0",
"nodemon": "^2.0.22",
"supertest": "^6.3.3"
},
"jest": {
"testEnvironment": "node",
"coverageDirectory": "coverage",
"collectCoverageFrom": [
"src/**/*.js"
]
}
}
Step 5: Install Dependencies
npm install
npm install --save-dev jest nodemon supertest
Git Workflow Best Practices
Commit Message Conventions
# Format: <type>(<scope>): <subject>
# Examples:
git commit -m "feat(api): add health check endpoint"
git commit -m "fix(auth): resolve token validation issue"
git commit -m "docs(readme): update installation instructions"
git commit -m "test(api): add integration tests for user endpoints"
git commit -m "refactor(db): optimize query performance"
Types:
feat
: New featurefix
: Bug fixdocs
: Documentation changesstyle
: Code style changes (formatting, semicolons, etc.)refactor
: Code refactoringtest
: Adding or modifying testschore
: Maintenance tasks
Branching Strategies
Git Flow
main
: Production-ready codedevelop
: Integration branchfeature/*
: New featuresrelease/*
: Release preparationhotfix/*
: Emergency fixes
GitHub Flow (Simpler)
main
: Always deployablefeature/*
: All changes
Creating a .gitignore File
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Environment variables
.env
.env.local
.env.*.local
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Build outputs
dist/
build/
*.log
# Test coverage
coverage/
.nyc_output/
# Terraform
*.tfstate
*.tfstate.*
.terraform/
.terraform.lock.hcl
# Docker
*.pid
GitHub Features
Pull Requests
Pull requests are the heart of collaboration on GitHub:
Create a feature branch
git checkout -b feature/add-logging
Make changes and commit
git add .
git commit -m "feat(logging): add winston logger"
Push to GitHub
git push origin feature/add-logging
Create Pull Request on GitHub
Compare branches
Add description
Request reviewers
Link issues
GitHub Actions Preview
Create .github/workflows/ci.yml
:
name: CI Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Generate coverage report
run: npm run test:coverage
Issues and Project Management
GitHub Issues help track:
Bugs
Feature requests
Tasks
Documentation needs
Example Issue Template (.github/ISSUE_TEMPLATE/bug_report.md
):
---
name: Bug report
about: Create a report to help us improve
title: '[BUG] '
labels: 'bug'
assignees: ''
---
**Describe the bug**
A clear description of the bug.
**To Reproduce**
Steps to reproduce:
1. Go to '...'
2. Click on '....'
3. See error
**Expected behavior**
What you expected to happen.
**Screenshots**
If applicable, add screenshots.
**Environment:**
- OS: [e.g. Ubuntu 20.04]
- Node version: [e.g. 18.0.0]
- Browser: [e.g. Chrome 91]
Advanced Git Techniques
Interactive Rebase
# Rebase last 3 commits
git rebase -i HEAD~3
# Commands in interactive mode:
# pick - use commit
# reword - change commit message
# edit - stop for amending
# squash - combine with previous
# fixup - like squash but discard message
# drop - remove commit
Stashing Changes
# Save current changes
git stash
# List stashes
git stash list
# Apply most recent stash
git stash pop
# Apply specific stash
git stash apply stash@{2}
# Create named stash
git stash save "WIP: working on feature X"
Cherry-picking
# Apply specific commit to current branch
git cherry-pick <commit-hash>
# Cherry-pick range
git cherry-pick <start-commit>..<end-commit>
Hands-on Exercise
Let's practice everything we've learned:
Exercise: Complete Git Workflow
Setup
# Clone your repository
git clone https://github.com/yourusername/devops-web-app.git
cd devops-web-app
Create Feature Branch
git checkout -b feature/add-dockerfile
Create Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "src/app.js"]
Commit and Push
git add Dockerfile
git commit -m "feat(docker): add Dockerfile for containerization"
git push origin feature/add-dockerfile
Create Pull Request
Go to GitHub
Click "Compare & pull request"
Add description
Create PR
Review and Merge
Review changes
Run tests (automated via GitHub Actions)
Merge PR
Troubleshooting Common Git Issues
Merge Conflicts
# When conflicts occur
git status # See conflicted files
# Edit files to resolve conflicts
# Look for <<<<<<< HEAD markers
# After resolving
git add <resolved-files>
git commit -m "resolve merge conflicts"
Undoing Changes
# Undo last commit (keep changes)
git reset --soft HEAD~1
# Undo last commit (discard changes)
git reset --hard HEAD~1
# Revert a pushed commit
git revert <commit-hash>
Fixing Commit Messages
# Change last commit message
git commit --amend -m "New message"
# Change older commit message
git rebase -i HEAD~n # n = number of commits back
# Mark commit as 'reword'
GitHub Security Best Practices
Never commit secrets
Use environment variables
Add .env to .gitignore
Use GitHub Secrets for CI/CD
Enable two-factor authentication
Use signed commits
# Configure GPG signing
git config --global user.signingkey <key-id>
git config --global commit.gpgsign true
Protect branches
Require pull request reviews
Require status checks
Enforce linear history
Key Takeaways
Git is essential for version control and collaboration in DevOps
Proper branching strategies enable parallel development
Commit messages should be clear and follow conventions
GitHub provides powerful collaboration features beyond just hosting code
Pull requests facilitate code review and quality control
GitHub Actions can automate your workflow (we'll explore this more in Part 4)
What's Next?
In Part 3, we'll dive into Docker and containerization. You'll learn:
Docker fundamentals
Creating efficient Dockerfiles
Docker Compose for multi-container applications
Container best practices
Preparing applications for cloud deployment
Additional Resources
Pro Git Book - Comprehensive Git guide
GitHub Learning Lab - Interactive GitHub tutorials
Conventional Commits - Commit message specification
GitHub Flow Guide - GitHub's branching model
Atlassian Git Tutorials - Visual Git guides
Continue your journey with Part 3: Docker Essentials!