Module 4: Version Control with Git & GitHub

Day 4: Git Fundamentals, branching, and merge strategies

Code and Contribute | CNCF Kathmandu

Version Control System

  • Tracks changes in code over time.

  • Enables collaboration among multiple developers.

  • Helps revert or branch safely.

Code and Contribute | CNCF Kathmandu

Types of VCS

  • Local (e.g., RCS)

  • Centralized (e.g., SVN)

  • Distributed (e.g., Git)

Code and Contribute | CNCF Kathmandu

Types of VCS

  • Local (e.g., RCS)

local-vcs

Code and Contribute | CNCF Kathmandu

Types of VCS

  • Centralized (e.g., SVN)

shared-vcs

Code and Contribute | CNCF Kathmandu

Types of VCS

  • Distributed (e.g., Git)

distributed-vcs

Code and Contribute | CNCF Kathmandu

what is Git?

  • A distributed version control system created by Linus Torvalds.

  • Every developer has a full copy of repo history.

  • Enables branching, merging, tagging, and rollback.

Code and Contribute | CNCF Kathmandu

Init Repo

mkdir git-demo && cd git-demo
git init
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --list
Code and Contribute | CNCF Kathmandu

Basic Git Commands

Code and Contribute | CNCF Kathmandu

Create and Commit

echo "Hello world" > app.txt
git add app.txt
git commit -m "Initial commit"
Code and Contribute | CNCF Kathmandu

Config best practices

  • Use git config --global only for personal identity; use repo-specific config (--local) for team projects.

  • Configure your default branch name git config --global init.defaultBranch main.

  • Always verify your configuration: git config --list.

  • When cloning, prefer SSH over HTTPS for secure access.

Code and Contribute | CNCF Kathmandu

Check status and history

git status
git log --oneline
Code and Contribute | CNCF Kathmandu

Stash and Restore

git stash
git stash list
git stash pop
Code and Contribute | CNCF Kathmandu

.gitignore

  • Prevents unnecessary files from being tracked.
  • Example:
*.log
node_modules/
.env
Code and Contribute | CNCF Kathmandu

.gitkeep

  • Placeholder file to keep empty folders in Git.
Code and Contribute | CNCF Kathmandu

Commit best practices

  • Make commits atomic — one logical change per commit.

  • Write meaningful commit messages:

Format: <type>(<scope>): <description>

Example: fix(auth): handle null token error

https://gist.github.com/qoomon/5dfcdf8eec66a051ecd85625518cfd13
Code and Contribute | CNCF Kathmandu

Git Stages

command: git status

Stage Description Command
Working Directory Your Local Files
Staging Area Prepare files to commit git add
Repository Permanent commit history git commit
Code and Contribute | CNCF Kathmandu

Git stages

git-stages

Code and Contribute | CNCF Kathmandu

File Lifecycle

git-file-lifecycle

Code and Contribute | CNCF Kathmandu

Git Stages: Best practices

  • Regularly run git status to understand your state.

  • Understand .gitignore to prevent polluting the repo.

Code and Contribute | CNCF Kathmandu

Branching and Merge Strategies

Code and Contribute | CNCF Kathmandu

Branching

  • Creates a parallel copy of the working directory without copying the entire content
  • Example:
    git branch feature/login
    git checkout feature/login
    git checkout -b feature/login
    
Code and Contribute | CNCF Kathmandu

Branching: hands-on

git branch feature/login
git checkout feature/login
git checkout -b feature/login
Code and Contribute | CNCF Kathmandu

Merging

Merges different branches into one

git checkout main
git merge feature/login
Code and Contribute | CNCF Kathmandu

Merging: hands-on

git checkout main
git merge feature/login
Code and Contribute | CNCF Kathmandu

Merging strategies

Type Description Command
Fast-forward Directly moves the HEAD git merge --only-ff feature
No fast-forward Keeps merge history git merge --no-ff feature
Recursive Default merge strategy git merge
Squash Combine all the commits `git merge --squash feature
Code and Contribute | CNCF Kathmandu

Merging strategies: Recursive

It is the default merging strategy of git.

Code and Contribute | CNCF Kathmandu

Merging strategies: fast-forward

A fast-forward merge happens when the branch you’re merging can be advanced directly to the tip of another branch — meaning there’s no divergent history.

Code and Contribute | CNCF Kathmandu

Fast-forward hands-on

mkdir ff-merge-demo
cd ff-merge-demo
git init
echo "v1" > app.txt
git add app.txt
git commit -m "Initial commit"
git checkout -b feature-readme
echo "This is README" > README.md
git add README.md
git commit -m "Add README file"
Code and Contribute | CNCF Kathmandu

Fast-forward hands-on

git checkout main
git merge --ff-only feature-readme
git log --oneline --graph --decorate --all

There is no merge commit in history

Code and Contribute | CNCF Kathmandu

Merging strategies: no-ff

By default, Git uses fast-forward (FF) merge if possible — meaning it simply moves the branch pointer forward without creating a new commit.

However, if you want to always create a merge commit, even when a fast-forward is possible, you use no-ff

Code and Contribute | CNCF Kathmandu

Merging strategies: no-ff

Let’s create two branches like fast-forward merge but merge with different command

git merge --no-ff feature-branch -m "Merge feature-branch with --no-ff"

There will be merge commit in the history

Code and Contribute | CNCF Kathmandu

Merging strategies: rebase

A rebase merge is when you take the commits from a feature branch and replay them on top of another branch (usually main). This makes your commit history linear, unlike a normal merge that creates a merge commit.

Code and Contribute | CNCF Kathmandu

git-rebase-merge-diagram

Code and Contribute | CNCF Kathmandu

Merging strategies: rebase

A rebase merge is when you take the commits from a feature branch and replay them on top of another branch (usually main). This makes your commit history linear, unlike a normal merge that creates a merge commit.

Use Case Benefit
Clean commit history Makes main branch readable & concise
Linear workflow Avoid merge commits for a straight history
Code and Contribute | CNCF Kathmandu

Hands-on: rebase

mkdir rebase-merge-demo
cd rebase-merge-demo
git init
echo "Initial version" > app.txt
git add app.txt
git commit -m "Initial commit"
git checkout -b feature-login
echo "Add login UI" >> app.txt
git commit -am "Add login UI"
echo "Add login validation" >> app.txt
git commit -am "Add login validation"
Code and Contribute | CNCF Kathmandu

Hands-on: rebase

git checkout main
echo "Main branch update" >> app.txt
git commit -am "Update from main branch"
git checkout feature-login
git rebase main
# Solve merge conflicts
git add app.txt
git rebase --continue
git checkout main
git merge feature-Login
Code and Contribute | CNCF Kathmandu

Merging strategies: squash

A squash merge is a Git merge strategy that combines all commits from a branch into a single commit before adding them to the target branch.

git-merge-squash-diagram

Code and Contribute | CNCF Kathmandu

Merging strategies: squash

A squash merge is a Git merge strategy that combines all commits from a branch into a single commit before adding them to the target branch.

Use Case Why Squash
Many small commits in a branch Combine them into one meaningful commit
Clean project history Avoid clutter like “typo fix” and “debug log”
Code and Contribute | CNCF Kathmandu

Hands-on: Squash

mkdir squash-merge-demo
cd squash-merge-demo
git init
echo "Initial app setup" > app.txt
git add app.txt
git commit -m "Initial commit"
git checkout -b feature-login
Code and Contribute | CNCF Kathmandu

Hands-on: Squash

echo "Add login page" >> app.txt
git commit -am "Add login page"
echo "Add login validation" >> app.txt
git commit -am "Add login validation"
echo "Fix login bug" >> app.txt
git commit -am "Fix login bug"
git log --oneline --graph --decorate --all
Code and Contribute | CNCF Kathmandu

Hands-on: Squash

git checkout main
git merge --squash feature-login
git commit -m "Add login feature"
Code and Contribute | CNCF Kathmandu

Branching and Merging: Best practices

  • Protect main branches (main, develop) — restrict direct commits

  • Keep branch names descriptive: feature/login, fix/typo-readme, hotfix/security-patch

  • Avoid long-lived feature branches — merge frequently to reduce conflicts

Code and Contribute | CNCF Kathmandu

Branch Protection

  • A set of rules applied to Git branches to enforce safe development.

  • Prevents accidental changes to important branches like main or release.

  • Helps teams maintain code quality and workflow discipline.

Code and Contribute | CNCF Kathmandu

Git workflows: Centralized Workflow

  • One shared branch (e.g., main)

  • All developers push directly

  • Simple but risky

Code and Contribute | CNCF Kathmandu

Git workflows: Git Flow Workflow

Branches:

  • main → stable

  • develop → ongoing work

  • feature/* → new features

  • release/* → prep for release

  • hotfix/* → quick fixes on main

Code and Contribute | CNCF Kathmandu

Git workflows: Git Flow Workflow

gitflow-workflow

Code and Contribute | CNCF Kathmandu

Git workflow: Best Practices

  • Protect production branches with rules (review required, CI checks).

  • Keep your main always deployable.

  • Enforce branch naming and PR templates via GitHub/GitLab rules

Code and Contribute | CNCF Kathmandu

Git Hooks

Git hooks are scripts that automatically run at specific points in the Git workflow, allowing you to automate tasks and enforce standards. They can be used for various actions, such as checking code quality before commits or sending notifications after pushes.

cd .git/hooks
ls
Code and Contribute | CNCF Kathmandu

Git Hooks: hands-on

mv pre-commit.sample pre-commit
  • Insert echo 'Hello World' in the script anywhere

  • Make a minor change and commit.

  • Observe that Hello World is displayed in the terminal output

Code and Contribute | CNCF Kathmandu

Hands-on with pre-commit

  • pre-commit is a Python tool that manages Git hooks.
  • Hooks are configured declaratively in a .pre-commit-config.yaml file.
  • You can install, update, and run hooks easily across teams.
Code and Contribute | CNCF Kathmandu

Hands-on with pre-commit

  • Install pre-commit

    pip install pre-commit
    
  • Create file .pre-commit-config.yaml with following contents

    repos:
    - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.6.0
    hooks:
    - id: trailing-whitespace
    
  • Apply the config file

    pre-commit install
    
Code and Contribute | CNCF Kathmandu

Hands-on with pre-commit

  • Add trailing spaces to any document in the git repo
  • Stage and commit the change
  • Observe that the commit will fail
  • pre-commit can sometimes solve some minor issues
  • We've to commit again
Code and Contribute | CNCF Kathmandu

Git tags and releases

Git tags are markers that point to specific commits in a Git repository's history, often used to signify important points like version releases.

git tag v1.0.0
git log --oneline
git tag -d v1.0.0 # delete a tag
Code and Contribute | CNCF Kathmandu

Git Remote With GitHub

  • Remote is a centrally hosted git repository
  • Your local Git repo knows about this remote so it can push and pull changes.
Code and Contribute | CNCF Kathmandu

Hands-on: git remote

  • Create an empty repo on GitHub

  • Copy the URL

  • Add the URL as remote for the local git repo

    git remote add origin <remote-url-name>
    
  • Push the local repo

    git branch -M main
    git push -u origin main
    
Code and Contribute | CNCF Kathmandu

Team Practice

  • Create a remote repository on GitHub
  • Everyone clones the repository
  • Everyone creates a branch feature/<username>
  • Everyone creates a file name.txt with their name on it
  • Run git pull to sync the local with remote repo
  • Push to the remote repository
  • Create a pull request to the main branch
  • Everyone pulls the changes
Code and Contribute | CNCF Kathmandu

Git config and gpg

Code and Contribute | CNCF Kathmandu

git config: Make specific git configs as per projects

  • Edit the global git config

    git config –global -e
    
  • Include the following content in the global git config file

    [includeIf "gitdir:/home/git-workship/project_1/**"]
      path = /home/git-workshop/project_1/.gitconfig
    [includeIf "gitdir:/hhome/git-workship/project_2/**"]
          path = /home/git-workshop/project_2/.gitconfig
    
    
Code and Contribute | CNCF Kathmandu

git config: Additional configs

git config –global -e
[init]
   defaultBranch = main
[core]
   editor = code --wait # opens vscode
Code and Contribute | CNCF Kathmandu

Signing commits with GPG key

[user]
    name = Jhon Doe
    email = jhon.doe@xyz.com
    signingkey = THIS_IS_MY_GPG_KEY	
[commit]
    gpgsign = true
[tag]
    gpgsign = true
Code and Contribute | CNCF Kathmandu

Build Validation

Code and Contribute | CNCF Kathmandu

Build Validation

Build Validation means running automated checks (build, tests, linting, security scans, etc.) every time a Pull Request (PR) is created or updated — before the code is merged into the main branch.

Code and Contribute | CNCF Kathmandu

Build Validation

  • Prevent broken code from being merged

  • Enforce team quality gates (tests, style, coverage)

  • Save review time — reviewers only see validated code

  • Automate feedback — developers know immediately if their PR fails

Code and Contribute | CNCF Kathmandu

Build Validation hands-on

Step 1: Create a simple python app
Step 2: Create a github workflow
Step 3: Add a new feature branch and open PR
Step 4: Observe build validation

Code and Contribute | CNCF Kathmandu

Build Validation hands-on

mkdir build-validation-demo
cd build-validation-demo
git init
echo "# Build Validation Demo" > README.md
git add README.md
git commit -m "Initial commit"
Code and Contribute | CNCF Kathmandu

Make a GitHub repo and push changes

git remote add origin https://github.com/<your-username>/build-validation-demo.git
git push -u origin main
Code and Contribute | CNCF Kathmandu

Add app feature and test

mkdir app
echo "def add(a, b): return a + b" > app/calculator.py
mkdir tests
echo "from app.calculator import add
def test_add():
    assert add(2,3) == 5" > tests/test_calculator.py
Code and Contribute | CNCF Kathmandu

Push the changes

echo 'pytest' > requirements.txt
git add .
git commit -m "Add simple app and test"
git push

Create GitHub Action Workflow

mkdir -p .github/workflows
nano .github/workflows/ci.yml
Code and Contribute | CNCF Kathmandu
name: Build Validation

on:
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: |
          pip install -r requirements.txt

      - name: Run tests
        run: |
          export PYTHONPATH=$PYTHONPATH:$(pwd)
          pytest -v
Code and Contribute | CNCF Kathmandu

Push the changes

git push origin main
Code and Contribute | CNCF Kathmandu

Create a new feature branch

git checkout -b feature-bugfix
echo "def multiply(a, b): return a * b" >> app/calculator.py
git commit -am "Add multiply function"
git push -u origin feature-bugfix
Code and Contribute | CNCF Kathmandu

Create a Pull Request on GitHub

GitHub Actions will automatically run your CI workflow
You’ll see a check like:

✔️ Build Validation — All checks have passed

If a test fails, you’ll see:

❌ Build Validation — 1 failing test

Code and Contribute | CNCF Kathmandu

Merge Only After Validation

Code and Contribute | CNCF Kathmandu

Thank You

Any Questions?

Code and Contribute | CNCF Kathmandu