Git & GitHub
Comprehensive notes for fresher developers joining the team.
What is Git?โ
Git is a distributed version control system (VCS) that tracks changes in your source code over time. It lets you save snapshots of your project, collaborate with teammates, and roll back to any previous state without losing work.
GitHub is a cloud-based hosting platform for Git repositories. It adds collaboration features on top of Git โ pull requests, code reviews, issue tracking, and project management.
| Feature | Git | GitHub |
|---|---|---|
| Type | Command-line tool (local) | Web platform (cloud) |
| Purpose | Track code changes | Host & collaborate on repositories |
| Works offline | Yes | No |
| Installed on | Your machine | Accessed via browser / CLI |
| Core feature | Version control | Pull requests, Issues, CI/CD |
Analogy: Git is like track-changes in a document editor. GitHub is like Google Docs โ it stores your document online and lets others view and edit it.
Learning Flowโ
| Phase | Topics |
|---|---|
| Phase 1 โ Setup & Core Concepts | Installation, GitHub account, SSH Authentication, Git Architecture, Basic Workflow |
| Phase 2 โ Essential Commands | init, clone, add, commit, status, log, diff, blame |
| Phase 3 โ Branching & Merging | Branches, Merge, Conflict Resolution |
| Phase 4 โ Remote Repositories | remote, push, pull, fetch, Forks |
| Phase 5 โ GitHub Collaboration | Pull Requests, Code Reviews, Issues, Branch Protection, GitHub Flow |
| Phase 6 โ Best Practices | Commit conventions, .gitignore, UOW Branching, Stash, Common Mistakes |
| Advanced Topics | Rebase, Interactive Rebase, Reset, Revert, Cherry-pick, Reflog, Tags & Releases, git bisect, git worktree, Hooks, Submodules, Signing commits, GitHub Actions |
Phase 1 โ Setup & Core Conceptsโ
1. Installation & Initial Configurationโ
Step 1 โ Create a GitHub account:
- Go to github.com and click Sign up
- Use your FortisureIT email address (
yourname@fortisureit.com) - Choose a username and complete email verification
- After signing in, go to Settings โ Password and authentication and enable Two-Factor Authentication (2FA) โ required for team access
Step 2 โ Install Git:
Download from https://git-scm.com/downloads and follow the installer. Use the recommended settings on each step.
After installing, open Git Bash (search "Git Bash" in the Windows Start menu) โ this is your terminal for running Git commands.
Verify the installation:
git --version
# git version 2.43.0
Step 3 โ Configure your identity (do this once on every machine):
git config --global user.name "your full name"
git config --global user.email "yourname@fortisureit.com"
Use your GitHub username and your FortisureIT email โ these are attached to every commit you make.
Step 4 โ Set your default branch name to main:
git config --global init.defaultBranch main
Step 5 โ Check your configuration:
git config --global --list
# user.name=your-github-username
# user.email=yourname@fortisureit.com
Note:
--globalapplies to all repositories on your machine. Omit it to configure only the current repository.
Optional โ Git GUI clients:
If you prefer a visual interface over the command line, these tools provide a graphical view of commits, branches, and diffs:
| Tool | Description |
|---|---|
| GitHub Desktop | Simple, official GitHub client |
| SourceTree | Feature-rich, shows branch graphs clearly |
| GitKraken | Visual and powerful, good for beginners |
Tip: Learn the command line first โ GUI clients are a supplement, not a replacement.
Practice: Git Installation Guide ยท First-Time Git Setup
2. Authenticating with GitHub โ SSH Setupโ
GitHub requires authentication every time you push, pull, or interact with a private or organisation repository. SSH keys are the recommended method โ you set them up once and never type a password again.
Why SSH over HTTPS? GitHub removed support for password-based HTTPS authentication in 2021. HTTPS now requires a Personal Access Token (PAT) โ a token you generate, copy, and manage yourself. SSH uses a cryptographic key pair stored on your machine: more secure, and completely seamless after the one-time setup. Always use SSH.
Step 1 โ Check for an existing SSH keyโ
Before generating a new key, check whether one already exists on your machine:
ls -al ~/.ssh
Look for files named id_ed25519.pub, id_rsa.pub, or id_ecdsa.pub. If you see one of these, you already have a key pair โ skip to Step 3.
Step 2 โ Generate a new SSH keyโ
ssh-keygen -t ed25519 -C "yourname@fortisureit.com"
-t ed25519โ the key algorithm. Ed25519 is the modern standard, preferred over the older RSA.-C "..."โ a comment/label so you can identify the key on GitHub.
You will be prompted:
Enter file in which to save the key (/c/Users/YourName/.ssh/id_ed25519):
โ Press Enter to accept the default location
Enter passphrase (empty for no passphrase):
โ Set a strong passphrase โ you will enter it once per session when the SSH agent loads
This creates two files in ~/.ssh/:
| File | Purpose |
|---|---|
id_ed25519 | Your private key โ never share this, never move it off your machine |
id_ed25519.pub | Your public key โ this is what you add to GitHub |
Security rule: The private key (
id_ed25519) must never leave your machine. Treat it like a password. Only the.pubfile is shared externally.
Step 3 โ Add the key to the SSH agentโ
The SSH agent holds your private key in memory so you don't have to enter your passphrase every time you push or pull.
# Start the SSH agent in the background
eval "$(ssh-agent -s)"
# Agent pid 1234
# Add your private key to the agent
ssh-add ~/.ssh/id_ed25519
# Enter passphrase for /c/Users/YourName/.ssh/id_ed25519: (enter it)
# Identity added: /c/Users/YourName/.ssh/id_ed25519 (yourname@fortisureit.com)
Make the agent start automatically on Windows (Git Bash):
Add these lines to your ~/.bashrc or ~/.bash_profile so the agent starts every time you open Git Bash:
# Auto-start SSH agent and load key on Git Bash launch
eval "$(ssh-agent -s)" > /dev/null
ssh-add ~/.ssh/id_ed25519 2>/dev/null
Step 4 โ Copy your public keyโ
# Print the public key to the terminal โ select all and copy manually
cat ~/.ssh/id_ed25519.pub
# Windows shortcut โ copy directly to clipboard
clip < ~/.ssh/id_ed25519.pub
The output looks like this (one long line):
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAbc123...xyz yourname@fortisureit.com
Step 5 โ Add the public key to GitHubโ
- Go to github.com โ click your profile picture (top right) โ Settings
- In the left sidebar: SSH and GPG keys
- Click "New SSH key"
- Title: give it a descriptive name โ e.g.,
Work Laptop,Home PC - Key type: leave as
Authentication Key - Key: paste the public key you copied in Step 4
- Click "Add SSH key" โ confirm with your GitHub password or 2FA prompt if asked
Step 6 โ Test the connectionโ
ssh -T git@github.com
Expected success output:
Hi your-username! You've successfully authenticated, but GitHub does not provide shell access.
If you see Permission denied (publickey), the key was not added correctly โ go back to Step 4, re-copy the key carefully (ensure no extra whitespace), and re-add it in GitHub.
Step 7 โ Clone repositories using SSHโ
Now use SSH URLs instead of HTTPS when cloning:
# โ
SSH โ no credential prompts ever
git clone git@github.com:Fortisure-IT/repository-name.git
# โ HTTPS โ requires a token on every push/pull without credential caching
git clone https://github.com/Fortisure-IT/repository-name.git
SSH URL format: git@github.com:organisation-or-username/repo-name.git
Step 8 โ Switch an existing clone from HTTPS to SSHโ
If you already cloned a repository with HTTPS, switch it to SSH without re-cloning:
# Check the current remote URL
git remote -v
# origin https://github.com/Fortisure-IT/repo.git (fetch)
# origin https://github.com/Fortisure-IT/repo.git (push)
# Switch to SSH
git remote set-url origin git@github.com:Fortisure-IT/repo.git
# Verify the change
git remote -v
# origin git@github.com:Fortisure-IT/repo.git (fetch)
# origin git@github.com:Fortisure-IT/repo.git (push)
Alternative: HTTPS with a Personal Access Token (PAT)
If SSH cannot be set up in your environment, HTTPS is still an option โ but GitHub requires a Personal Access Token instead of your account password:
- Go to GitHub โ Settings โ Developer settings โ Personal access tokens โ Tokens (classic)
- Click "Generate new token (classic)"
- Give it a name, set an expiry, and tick the
reposcope (full repository access) - Copy the token โ you will not be shown it again
When Git prompts for credentials over HTTPS, use:
- Username: your GitHub username
- Password: paste your PAT (not your GitHub account password)
To avoid entering it every time, cache it:
# Windows โ stores credentials in Windows Credential Manager (persistent)
git config --global credential.helper manager
# Linux / Mac โ cache in memory for 1 hour
git config --global credential.helper "cache --timeout=3600"
Recommendation: Set up SSH. It is more secure, requires no token management or expiry, and eliminates credential prompts permanently.
3. Git Architecture โ The Three Areasโ
Understanding Git's three-area model is the foundation for everything else.
Working Directory โ Staging Area (Index) โ Local Repository โ Remote Repository
(your files) (git add) (git commit) (git push)
| Area | Description | Command to move to next area |
|---|---|---|
| Working Directory | Files you are actively editing on disk | git add <file> |
| Staging Area | Files queued up for the next commit | git commit -m "message" |
| Local Repository | Committed history stored in .git/ folder | git push |
| Remote Repository | Repository hosted on GitHub | โ |
Key insight: You control exactly what goes into a commit. You can add only specific files to staging even if more files have changed.
4. Basic Git Workflowโ
Every day as a developer, you will repeat this loop:
# 1. Check what changed
git status
# 2. Stage the changes you want to commit
git add filename.js
# or stage everything
git add .
# 3. Commit with a descriptive message
git commit -m "feat: add user login validation"
# 4. Push to the remote repository
git push origin main
Phase 2 โ Essential Commandsโ
5. Creating & Cloning Repositoriesโ
Start a new repository from scratch:
mkdir my-project
cd my-project
git init # creates a .git/ folder โ this is now a Git repo
Clone an existing repository from GitHub:
# Clone using SSH (recommended)
git clone git@github.com:Fortisure-IT/repository-name.git
# After cloning, move into the project folder โ this step is easy to forget
cd repository-name
# Clone into a specific folder name
git clone git@github.com:Fortisure-IT/repository-name.git my-folder
cd my-folder
Tip: Use
git clonewhen joining an existing project. Usegit initonly when starting a brand-new project from scratch.
6. git status โ Your Best Friendโ
git status tells you everything about the current state of your working directory and staging area.
git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed: โ โ
staged (will go into next commit)
modified: src/App.js
Changes not staged for commit: โ โ ๏ธ modified but not staged
modified: src/index.css
Untracked files: โ โ Git doesn't know about these yet
src/NewComponent.js
Run git status constantly โ before staging, before committing, before pushing.
7. git add โ Staging Changesโ
# Stage a single file
git add src/App.js
# Stage multiple files
git add src/App.js src/utils.js
# Stage all changes in the current directory and below
git add .
# Stage parts of a file interactively (advanced โ for careful commits)
git add -p filename.js
| Command | What it stages |
|---|---|
git add . | All new and modified files in current directory |
git add -A | All new, modified, and deleted files across entire repo |
git add src/ | All files inside the src/ folder |
git add *.js | All .js files in current directory |
Best practice: Prefer
git add .or explicit file names. Avoidgit add -Aunless you know exactly what has changed.
8. git commit โ Saving a Snapshotโ
# Commit with a message inline
git commit -m "feat: add login button to header"
# Open editor to write a longer commit message
git commit
# Stage all tracked modified files and commit in one step
git commit -am "fix: correct typo in welcome message"
Important: A commit is permanent in your history. Write clear, descriptive messages โ your teammates (and future you) will thank you.
9. git log โ Viewing Historyโ
# Full log
git log
# Compact one-line view
git log --oneline
# Visual branch graph
git log --oneline --graph --all
# Last N commits
git log -5
# Commits by a specific author
git log --author="John"
Example output of git log --oneline:
a3f92c1 feat: add user profile page โ most recent
8b21d4e fix: resolve null pointer in login
c10a993 chore: update dependencies
7ffe201 feat: initial project setup
Each entry shows: short hash + commit message.
10. git diff โ Seeing What Changedโ
# Show unstaged changes (working directory vs staging area)
git diff
# Show staged changes (staging area vs last commit)
git diff --staged
# Compare two commits
git diff abc1234 def5678
# Compare two branches
git diff main feature/login
Tip: Use
git diff --stagedbefore every commit to review exactly what you are about to save.
11. git blame โ Finding Who Changed Whatโ
git blame shows the commit hash, author, and timestamp for every line in a file. It answers the question: who last changed this line, and when?
# Show blame for an entire file
git blame src/App.js
Example output:
a3f92c1 (John Smith 2024-01-15 09:23 +0000 40) function handleLogin() {
8b21d4e (Sarah Jones 2024-02-03 14:10 +0000 41) const user = auth.getUser();
a3f92c1 (John Smith 2024-01-15 09:23 +0000 42) if (!user) return null;
8b21d4e (Sarah Jones 2024-02-03 14:10 +0000 43) return redirect("/dashboard");
a3f92c1 (John Smith 2024-01-15 09:23 +0000 44) }
Each line shows: commit hash ยท author ยท date ยท line number ยท code
# Blame only a specific range of lines (lines 40 to 60)
git blame -L 40,60 src/App.js
# Ignore whitespace-only changes (useful when code was reformatted)
git blame -w src/App.js
# Show the full commit hash instead of the abbreviated one
git blame --abbrev=0 src/App.js
Workflow: blame โ show โ understand
Once you identify the commit responsible for a line, inspect the full context of that change:
# 1. Find who last changed line 42
git blame -L 42,42 src/App.js
# a3f92c1 (John Smith 2024-01-15 09:23...)
# 2. See the full commit โ message, diff, and all files that changed
git show a3f92c1
When to use git blame:
- Debugging: "who introduced this logic and why?"
- Learning: "who should I ask about how this part of the codebase works?"
- Code review: "was this a recent change or has it always been this way?"
Tip: VS Code with the GitLens extension shows blame information inline โ author and timestamp appear next to each line without opening a terminal.
Note:
git blameshows who made the last edit to a line, not the original author. If a line was only reformatted by a code-style tool, that tool's commit will appear. Usegit blame -wto ignore whitespace changes and surface the meaningful author.
Phase 3 โ Branching & Mergingโ
12. Understanding Branchesโ
A branch is an independent line of development. The default branch is called main. You create a new branch to work on a feature or bug fix without affecting main.
main: A --- B --- C
\
feature/login: D --- E
# List all local branches
git branch
# List all branches including remote
git branch -a
# Create a new branch
git branch feature/login
# Switch to a branch
git checkout feature/login
# Create and switch in one command (preferred)
git checkout -b feature/login
# Modern syntax (Git 2.23+)
git switch -c feature/login
Best practice: Always branch off from
main(or the main development branch). Never commit directly tomain.
13. Merging Branchesโ
Once your feature is ready, you merge it back into main.
# Step 1: Switch to the target branch
git checkout main
# Step 2: Make sure it is up to date
git pull origin main
# Step 3: Merge your feature branch
git merge feature/login
Types of merge:
| Type | When it happens | Result |
|---|---|---|
| Fast-forward | main has no new commits since you branched | Linear history โ no merge commit |
| 3-way merge | Both branches have diverged | Creates a merge commit |
# Force a merge commit even if fast-forward is possible (shows history clearly)
git merge --no-ff feature/login
14. Resolving Merge Conflictsโ
A conflict occurs when the same lines were changed on both branches. Git cannot decide which version to keep โ you must resolve it manually.
# After running git merge, if there's a conflict:
git status # shows conflicted files marked as "both modified"
A conflict looks like this inside the file:
<<<<<<< HEAD
const greeting = "Hello"; โ your current branch version
=======
const greeting = "Hi there"; โ the incoming branch version
>>>>>>> feature/login
Resolution steps:
- Open the conflicted file
- Decide which version to keep (or combine them)
- Delete the conflict markers (
<<<<<<<,=======,>>>>>>>) - Save the file
- Stage and commit the resolution
git add src/App.js
git commit -m "merge: resolve conflict in greeting message"
Tip: VS Code and most IDEs highlight conflicts and provide a visual "Accept Current / Accept Incoming / Accept Both" interface โ use it.
15. Deleting Branchesโ
Once a branch is merged, delete it to keep the repository clean.
# Delete a local branch (safe โ only if fully merged)
git branch -d feature/login
# Force delete (even if not merged)
git branch -D feature/login
# Delete a remote branch
git push origin --delete feature/login
Phase 4 โ Remote Repositoriesโ
16. git remote โ Linking to GitHubโ
A remote is a version of your repository stored on another server (like GitHub).
# View remotes
git remote -v
# Add a remote named "origin"
git remote add origin git@github.com:Fortisure-IT/repo.git
# Change the URL of an existing remote
git remote set-url origin git@github.com:Fortisure-IT/new-repo.git
# Remove a remote
git remote remove origin
When you git clone, Git automatically creates a remote called origin pointing to the cloned URL.
17. git push โ Uploading to GitHubโ
# Push current branch to remote
git push origin main
# Push a new local branch to remote for the first time
git push -u origin feature/login
# The -u flag sets the upstream so future pushes just need: git push
# Push all local branches
git push --all origin
Important: Never force-push to shared branches like
mainunless explicitly instructed by your team lead.
# โ Dangerous โ overwrites remote history
git push --force origin main
# โ
Safer alternative (fails if someone else has pushed)
git push --force-with-lease origin feature/my-branch
18. git pull & git fetch โ Getting Updatesโ
# fetch + merge in one step (commonly used)
git pull origin main
# Download remote changes without merging
git fetch origin
# After fetching, see what changed
git log HEAD..origin/main --oneline
# Then merge manually
git merge origin/main
| Command | What it does |
|---|---|
git fetch | Downloads changes but does NOT update your working files |
git pull | Downloads changes AND merges them into your current branch |
Best practice: Use
git fetchthen review changes before merging, especially on critical branches.
19. Forking a Repositoryโ
A fork is your own personal copy of someone else's repository on GitHub. Used when contributing to open-source or to repositories where you don't have write access.
Team note: Within FortisureIT, you will not use forks for day-to-day work. You push branches directly to the team repository and open PRs there. Forking is an open-source pattern โ learn it so you understand how public contributions work, but it is not part of your regular internal workflow.
Fork workflow:
# 1. Click "Fork" on GitHub โ creates a copy under your account
# 2. Clone YOUR fork (using SSH)
git clone git@github.com:your-username/repo.git
# 3. Add the original repo as upstream
git remote add upstream git@github.com:original-owner/repo.git
# 4. Keep your fork in sync with the original
git fetch upstream
git merge upstream/main
# 5. Push to your fork, then open a Pull Request to the original
Phase 5 โ GitHub Collaborationโ
20. Pull Requests (PRs)โ
A Pull Request is a proposal to merge your branch into another branch on GitHub. It is the primary collaboration mechanism โ teammates review your code before it is merged.
Creating a PR:
- Push your branch to GitHub
- Go to the repository on GitHub
- Click "Compare & pull request"
- Write a clear title and description explaining:
- What you changed
- Why you changed it
- How to test it
- Assign reviewers
- Click "Create pull request"
Team PR title conventions:
| PR type | Title format | Example |
|---|---|---|
| Feature / fix branch โ UOW branch | [#issue-number] Short description | [#1234] Add user login flow |
UOW branch โ main | UOW: Unit of Work Name | UOW: Allstar Teambuilding |
PR checklist before requesting review:
- All tests pass
- No merge conflicts with target branch
- Code is self-reviewed (read your own diff)
- PR title follows the team convention above
- Meaningful PR description is filled in
Best practice: Keep PRs small and focused. One PR = one feature or one bug fix. Large PRs are harder to review and more likely to have hidden bugs.
21. Code Reviewsโ
When you are asked to review a PR:
- Check for logic errors, not just style
- Ask questions, don't give commands: "What happens if this is null?" instead of "Fix this"
- Approve when the code is good enough to ship, not perfect
- Use GitHub's "Request changes" if something must be fixed before merging
Responding to review feedback:
- Address all comments โ either fix the code or explain why you disagree
- Reply "Done" or "Fixed in [commit]" to resolved comments
- Don't push back on style comments defensively โ your team's conventions matter
22. Issues & GitHub Flowโ
Issues are GitHub's task/bug tracker. Use them to report bugs, request features, or discuss ideas.
GitHub Flow โ the simple branching strategy used by most teams:
1. Create a branch from main
git checkout -b feature/add-search-bar
2. Make commits on your branch
git commit -m "feat: add search bar component"
3. Open a Pull Request
โ describe your changes
4. Discuss and review with teammates
5. Merge into main once approved
6. Delete the branch
git branch -d feature/add-search-bar
Key rule:
mainis always deployable. Never commit broken code tomain.
Practice: GitHub Flow Guide ยท About Pull Requests
23. Branch Protection Rulesโ
Branch protection prevents accidental direct pushes to important branches and enforces peer review.
Recommended protections for the team (set in GitHub โ Settings โ Branches):
| Branch | Protections to enable |
|---|---|
main | Require PR before merging, require approvals (min 1), block direct pushes |
uow-id_uow-name | Require PR before merging, optional peer review |
What these rules enforce:
- No one can push directly to
mainโ all changes must go through a PR - A PR must be approved by at least one reviewer before it can be merged
- Optionally: require status checks (CI tests) to pass before merging
Why it matters: Branch protection is your last line of defence against broken code reaching production. Even senior developers benefit from it โ it removes the option to bypass the process accidentally.
Phase 6 โ Best Practicesโ
24. Writing Good Commit Messagesโ
A great commit message communicates why a change was made, not just what.
Team commit format (used for day-to-day commits on feature branches):
[#issue-number] Short description of the change
Examples:
git commit -m "[#1234] Fix user login redirect"
git commit -m "[#1234] Implement user authentication flow"
git commit -m "[#87] Resolve null pointer in dashboard query"
This links every commit directly to the GitHub issue it belongs to โ making history easy to trace.
Conventional Commits format (used for semantic versioning and changelogs):
<type>(<scope>): <short description>
[optional body โ explains what and why]
[optional footer โ references issues, breaking changes]
Types:
| Type | When to use |
|---|---|
feat | A new feature |
fix | A bug fix |
chore | Maintenance tasks (updating deps, configs) |
docs | Documentation changes only |
style | Formatting, missing semicolons โ no logic change |
refactor | Code restructuring โ no feature or bug fix |
test | Adding or updating tests |
Examples:
# โ
Good
git commit -m "feat(auth): add remember-me checkbox to login form"
git commit -m "fix(api): handle null response from user endpoint"
git commit -m "docs: add setup instructions to README"
# โ Bad
git commit -m "fix"
git commit -m "updated stuff"
git commit -m "WIP"
git commit -m "asdfgh"
Rules:
- Subject line โค 72 characters
- Use present tense: "add feature" not "added feature"
- No full stop at the end of the subject line
- Reference issue numbers when applicable:
fix(login): correct redirect โ closes #42
25. .gitignore โ What Not to Trackโ
A .gitignore file tells Git which files to ignore entirely. Never commit:
node_modules/โ install frompackage.jsoninstead.envfiles โ contain secrets and credentials- Build outputs (
dist/,build/,.next/) - OS-generated files (
.DS_Store,Thumbs.db) - IDE config (
.vscode/,.idea/)
Example .gitignore for a JavaScript project:
# Dependencies
node_modules/
# Environment variables
.env
.env.local
.env.production
# Build output
dist/
build/
.next/
# OS files
.DS_Store
Thumbs.db
# IDE
.vscode/
.idea/
# Logs
*.log
npm-debug.log*
Important: If you accidentally committed a file that should be ignored, removing it from
.gitignoreis not enough โ you must also stop tracking it:
git rm --cached .env
git commit -m "chore: remove .env from tracking"
Practice: gitignore.io โ generate
.gitignorefor any stack
26. Branching Strategy & Naming Conventionsโ
The team uses a UOW (Unit of Work) branching model with three tiers:
main
โโโ uow-id_uow-name โ all tickets for a feature set merge here
โโโ issue-number-description โ one branch per GitHub issue
Tier 1 โ Main branch:
| Branch | Purpose |
|---|---|
main | Always stable and production-ready. No direct pushes. |
Tier 2 โ UOW Release branches:
- Naming:
{uow-id}_{uow-name}โ an underscore separates the ID from the name - The UOW ID follows the format
YY.NNorYY.NN-a(year ยท sequence ยท optional variant) - Examples:
26.01-a_smartpo-phase-ii,26.02_director-dashboard - Groups all related feature tickets for a specific unit of work
- Merged into
mainonce all tickets in the UOW are complete and reviewed
Note: The
/releasesuffix was dropped โ it caused conflicts with how Git interprets/in ref names. The UOW branch is now simply the ID and name joined by an underscore.
Tier 3 โ Feature / Issue branches:
- Naming:
issue-number-short-descriptive-textโ no#prefix in the branch name - Examples:
1234-fix-user-login,87-add-search-bar - Created for each individual GitHub issue / ticket
- Always use lowercase with hyphens
- Merged into the corresponding UOW branch (not directly into
main)
Note: The
#symbol is only used when referencing a ticket number inside a commit message (e.g.[#1234] Fix login redirect), not in the branch name itself.
Complete example workflow:
# 1. Create a feature branch from the UOW branch
git checkout 26.01-a_smartpo-phase-ii
git checkout -b 1234-add-new-feature
# 2. Develop and commit โ use # only in the commit message, not the branch name
git commit -m "[#1234] Implement user authentication flow"
# 3. Push and open a PR โ target: 26.01-a_smartpo-phase-ii
git push origin 1234-add-new-feature
# 4. After PR is merged, the UOW branch accumulates all tickets
# 5. When all UOW tickets are done, open a PR: 26.01-a_smartpo-phase-ii โ main
# PR title: "UOW: Smartpo Phase II"
# โ
Good branch names
1234-fix-user-login
87-add-search-bar
26.01-a_smartpo-phase-ii
# โ Bad branch names
#1234-fix-user-login โ don't use # in branch names
my-branch
test123
john-working-on-stuff
HOTFIX
Branch hygiene:
# Regularly sync your branch with the UOW branch to avoid large conflicts later
git fetch origin
git rebase origin/26.01-a_smartpo-phase-ii
# Or merge if rebasing is not appropriate
git merge origin/26.01-a_smartpo-phase-ii
Rule: Create a new branch for every GitHub issue โ no matter how small. Never commit directly to
mainor the UOW release branch.
27. Common Mistakes & How to Fix Themโ
Mistake 1: Committed to the wrong branch
# Move the last commit to a new branch instead
git branch feature/correct-branch # create branch pointing at current commit
git reset HEAD~1 --soft # undo commit on current branch (keep changes staged)
git stash # temporarily store staged changes
git checkout feature/correct-branch # switch to correct branch
git stash pop # restore changes
git commit -m "feat: your message" # commit on the right branch
Mistake 2: Committed a file that should be ignored
git rm --cached path/to/file.env
echo "path/to/file.env" >> .gitignore
git commit -m "chore: untrack sensitive file"
Mistake 3: Wrote a bad commit message
# Only safe to do if you have NOT pushed yet
git commit --amend -m "fix: correct message here"
Mistake 4: Want to discard uncommitted changes to a file
# โ ๏ธ This permanently discards unsaved changes
git checkout -- src/App.js
# Modern syntax
git restore src/App.js
Mistake 5: Accidentally staged a file
git restore --staged src/App.js # unstage but keep the changes
Mistake 6: Pushed a commit that needs to be undone
# โ Don't reset and force-push on a shared branch โ this rewrites history
# and disrupts everyone who has already pulled your commits
git reset --hard HEAD~1
git push --force
# โ
Use git revert instead โ adds a new "undo" commit, safe on shared branches
git revert HEAD
git push
See Advanced Topic A4 for the full git revert reference.
28. Quick Reference โ Most Used Commandsโ
| Task | Command |
|---|---|
| Check status | git status |
| Stage file | git add filename |
| Stage all | git add . |
| Commit | git commit -m "message" |
| View history | git log --oneline |
| See who changed a line | git blame filename |
| Create & switch branch | git checkout -b branch-name |
| Switch branch | git checkout branch-name |
| Merge branch | git merge branch-name |
| Push branch | git push origin branch-name |
| Pull latest | git pull origin main |
| Clone repo | git clone git@github.com:org/repo.git |
| View remotes | git remote -v |
| Delete local branch | git branch -d branch-name |
| Unstage file | git restore --staged filename |
| Discard changes | git restore filename |
| Stash changes | git stash |
| Apply stash | git stash pop |
29. Git Stash โ Temporarily Set Aside Changesโ
Stash is useful when you need to switch branches but aren't ready to commit your work.
# Save your current uncommitted work
git stash
# List all stashes
git stash list
# stash@{0}: WIP on feature/login: a3f9 feat: start login form
# stash@{1}: WIP on main: 8b21 fix: header typo
# Apply the most recent stash and remove it from the stash list
git stash pop
# Apply a specific stash (without removing it)
git stash apply stash@{1}
# Drop a specific stash
git stash drop stash@{0}
# Stash with a description
git stash push -m "half-finished login validation"
Practice: Git Stash Documentation ยท Atlassian Git Stash Tutorial
Summary โ Key Principles for Beginnersโ
- Commit early, commit often โ small commits are easier to review and revert than large ones
- Always branch โ never work directly on
main - Write meaningful commit messages โ your future self will read them
- Pull before you push โ sync with the remote to avoid conflicts
- Never commit secrets โ use
.envfiles and.gitignore - Review your diff before committing โ
git diff --staged - Keep PRs small and focused โ one PR per feature or bug fix
- Ask before force-pushing โ it can overwrite teammates' work
- Set up SSH โ authenticate with a key pair, not a password or token
- Use
git reverton shared branches โ nevergit reset --hardon commits others have pulled git reflogis your safety net โ almost nothing in Git is truly lost within 90 days
Practice: Learn Git Branching (interactive) ยท Pro Git Book (free) ยท GitHub Skills
Advanced Topicsโ
These topics go beyond the day-to-day workflow. Master the core phases above before coming back here. These are not required for your first few weeks โ they are here for when you are ready to go deeper.
A1. git rebase โ Replaying Commitsโ
Rebase moves or replays your commits on top of another branch, producing a clean, linear history instead of a merge commit.
What happens visually:
Before rebase:
main: A --- B --- C
\
feature/login: D --- E
After: git rebase main (run on feature/login)
main: A --- B --- C
\
feature/login: D' --- E' โ commits replayed on top of C
The commits D and E are rewritten as D' and E' โ same changes, new hashes.
# Switch to your feature branch
git checkout feature/login
# Rebase onto main (replay your commits on top of the latest main)
git rebase main
# Rebase onto the remote main (most common daily use)
git rebase origin/main
Handling conflicts during rebase:
Unlike a merge which creates one conflict resolution commit, rebase pauses at each conflicting commit.
# When a conflict occurs, Git pauses and shows:
# CONFLICT (content): Merge conflict in src/App.js
# error: could not apply a3f92c1... feat: add login button
# Step 1: Fix the conflict in the file
# Step 2: Stage the resolved file
git add src/App.js
# Step 3: Continue to the next commit
git rebase --continue
# If you want to skip the conflicting commit entirely (rarely needed)
git rebase --skip
# If you want to cancel the entire rebase and go back to before
git rebase --abort
Rule: Never rebase a branch that has been pushed and shared with others. Rebase rewrites commit hashes โ if someone else has your old commits, their history will diverge from yours.
# โ
Safe โ rebasing a local branch you haven't pushed
git rebase main
# โ
Safe โ rebasing a branch only you are using
git rebase origin/main
# โ Dangerous โ rebasing after others have pulled your branch
git rebase main # then git push --force (disrupts teammates)
Practice: Atlassian Rebase Guide ยท Learn Git Branching
A2. Interactive Rebase โ Rewriting Historyโ
Interactive rebase (git rebase -i) lets you edit, reorder, squash, or drop commits before they are finalised. It is a powerful tool for cleaning up your commit history before opening a PR.
# Interactively rebase the last 3 commits
git rebase -i HEAD~3
# Interactively rebase all commits since branching off main
git rebase -i origin/main
This opens an editor showing your commits oldest-first:
pick a3f92c1 feat: add login form
pick 8b21d4e fix: typo in label
pick c10a993 fix: another typo
# Commands:
# pick = keep the commit as-is
# reword = keep commit but edit its message
# edit = keep commit but pause to amend it
# squash = merge into previous commit (keeps both messages)
# fixup = merge into previous commit (discards this commit's message)
# drop = remove this commit entirely
Common interactive rebase workflows:
Squash multiple commits into one (clean up WIP commits before PR):
# Before squash โ messy history:
# feat: start login form
# fix: forgot semicolon
# fix: another typo
# fix: actually fixed it this time
# Change "pick" to "fixup" on all the fix commits:
pick a3f92c1 feat: start login form
fixup 8b21d4e fix: forgot semicolon
fixup c10a993 fix: another typo
fixup 7ffe201 fix: actually fixed it this time
# After squash โ clean history:
# feat: start login form
Reword a commit message:
pick a3f92c1 feat: add logn form โ typo in message
# Change to:
reword a3f92c1 feat: add logn form
# Git will pause and open the editor for you to fix the message
Drop a commit (remove it from history entirely):
# Change "pick" to "drop" โ that commit's changes disappear
drop 8b21d4e fix: debug console.log accidentally committed
Best practice: Squash or fixup "work-in-progress" commits before opening a PR so reviewers see a clean, logical history. Do this only on branches not yet shared with others.
Practice: Git Interactive Rebase ยท Atlassian Squashing Commits
A3. git reset โ Undoing Commitsโ
git reset moves the HEAD pointer (and optionally the working directory) backward to a previous commit. There are three modes โ understand them before using.
Commit A โ Commit B โ Commit C โ HEAD (currently here)
After git reset HEAD~1 (go back 1 commit), HEAD points to B. What happens to C's changes depends on the mode:
| Mode | Command | HEAD moves | Staging area | Working directory |
|---|---|---|---|---|
| Soft | git reset --soft HEAD~1 | โ Back 1 | Changes from C stay staged | Unchanged |
| Mixed (default) | git reset HEAD~1 | โ Back 1 | Changes from C unstaged | Unchanged |
| Hard | git reset --hard HEAD~1 | โ Back 1 | Cleared | โ Changes from C discarded |
# --soft: undo last commit, keep changes staged (ready to re-commit differently)
git reset --soft HEAD~1
# --mixed (default): undo last commit, keep changes in working directory but unstaged
git reset HEAD~1
git reset --mixed HEAD~1 # same thing
# --hard: undo last commit and discard all changes permanently
git reset --hard HEAD~1
# Reset to a specific commit hash
git reset --hard a3f92c1
# Undo the last 3 commits (soft โ keeps changes)
git reset --soft HEAD~3
Warning:
git reset --hardpermanently discards changes in your working directory. There is no undo unless you usegit reflog(see A8).
When to use each mode:
| Scenario | Mode to use |
|---|---|
| Undo a commit but want to recommit with a better message | --soft |
| Undo a commit and re-stage selectively | --mixed |
| Completely throw away bad commits and their changes | --hard |
| Unstage a file without changing it | git reset HEAD filename (same as git restore --staged) |
Rule: Never use
git reseton commits that have already been pushed to a shared branch. Usegit revertinstead (see A4).
Practice: Atlassian Git Reset Guide
A4. git revert โ Safely Undoing Pushed Commitsโ
git revert creates a new commit that undoes the changes of a previous commit. It does not rewrite history โ it adds to it. This makes it safe to use on shared branches.
# Revert the most recent commit
git revert HEAD
# Revert a specific commit by hash
git revert a3f92c1
# Revert without immediately opening the commit message editor
git revert --no-edit HEAD
# Revert multiple commits (creates one revert commit per commit)
git revert HEAD~3..HEAD
# Stage the revert but don't commit yet (useful to edit the message)
git revert --no-commit HEAD
What revert looks like in history:
A --- B --- C --- D (revert of C)
Commit D contains the exact opposite of what C did โ effectively undoing C while preserving the full history.
reset vs revert โ which to use:
| Situation | Use |
|---|---|
| Commit not yet pushed | git reset (rewrite local history cleanly) |
| Commit already pushed to shared branch | git revert (safe, adds undo commit) |
Need to undo changes on main | Always git revert |
# โ Dangerous on a shared branch โ rewrites history others already have
git reset --hard HEAD~1
git push --force
# โ
Safe on a shared branch โ creates a new undo commit
git revert HEAD
git push
Practice: Git Revert Documentation ยท Atlassian Undoing Changes
A5. Merge Conflicts โ Deep Diveโ
Basic conflict resolution was covered in topic 14. This section covers the full picture โ types of conflicts, strategies, and how to approach complex situations.
Types of conflicts:
| Type | Cause | Example |
|---|---|---|
| Content conflict | Same lines edited differently on both branches | Both branches changed const greeting |
| Rename / move conflict | File renamed on one branch, edited on another | Branch A renames utils.js โ helpers.js; Branch B edits utils.js |
| Delete / modify conflict | One branch deletes a file, the other modifies it | Branch A deletes config.js; Branch B adds a function to it |
| Add / add conflict | Both branches add a new file with the same name but different content | Rare, but occurs in parallel feature work |
Anatomy of a conflict marker โ full example:
<<<<<<< HEAD
// Current branch (the branch you ran git merge FROM)
function getUser(id) {
return users.find(u => u.id === id);
}
||||||| merged common ancestor โ shows the ORIGINAL state before either change (only visible with diff3 style)
function getUser(id) {
return users[id];
}
=======
// Incoming branch (the branch you are merging IN)
function getUser(id) {
const user = users.find(u => u.id === id);
if (!user) throw new Error(`User ${id} not found`);
return user;
}
>>>>>>> feature/user-validation
Enable the 3-way diff style to see the common ancestor (highly recommended):
git config --global merge.conflictstyle diff3
This adds the ||||||| merged common ancestor section which shows what the code looked like before either branch touched it โ makes it much easier to understand who changed what and why.
Aborting a merge:
# If mid-conflict you decide to back out of the merge entirely
git merge --abort
# Verify you are back to the pre-merge state
git status
Checking which files have conflicts:
git status
# Both modified: src/App.js
# Both modified: src/utils.js
# List only conflicted files
git diff --name-only --diff-filter=U
Accepting one side entirely (skipping manual editing):
# Accept your version (HEAD) for a specific file โ discard incoming
git checkout --ours src/App.js
git add src/App.js
# Accept their version (incoming branch) for a specific file โ discard yours
git checkout --theirs src/App.js
git add src/App.js
Caution:
--oursand--theirsthrow away one side completely. Only use this when you are certain the other side's changes are not needed.
After resolving all conflicts:
# Stage every resolved file
git add .
# Complete the merge
git commit
# Git pre-fills a merge commit message โ you can keep or edit it
Practice: Git Merge Conflicts ยท Pro Git โ Basic Merge Conflicts
A6. Merge & Rebase Conflict Toolsโ
VS Code built-in conflict editor:
VS Code highlights conflict regions and shows action buttons inline:
Accept Current Change | Accept Incoming Change | Accept Both Changes | Compare Changes
<<<<<<< HEAD
your version
=======
their version
>>>>>>> feature/login
Use "Compare Changes" to open a side-by-side diff view for complex conflicts.
Configure VS Code as the merge tool:
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
# Launch the merge tool for all conflicted files
git mergetool
Other popular merge tools:
| Tool | Command to configure |
|---|---|
| VS Code | git config --global merge.tool vscode |
| IntelliJ / WebStorm | Built-in via IDE Git menu |
| vimdiff | git config --global merge.tool vimdiff |
| Beyond Compare | git config --global merge.tool bc |
A7. git cherry-pick โ Applying Specific Commitsโ
Cherry-pick copies one or more specific commits from any branch and applies them to your current branch. Useful when you need a bug fix from another branch without merging the entire branch.
# Apply a single commit to the current branch
git cherry-pick a3f92c1
# Apply multiple commits
git cherry-pick a3f92c1 8b21d4e c10a993
# Apply a range of commits (exclusive of the start hash)
git cherry-pick a3f92c1..c10a993
# Cherry-pick without immediately committing (stage the changes only)
git cherry-pick --no-commit a3f92c1
# If a conflict occurs during cherry-pick
git cherry-pick --continue # after resolving conflicts
git cherry-pick --abort # to cancel entirely
Practical example:
# A critical fix was merged to main but you need it on your feature branch now
git checkout feature/dashboard
# Find the fix commit hash on main
git log main --oneline
# a3f92c1 fix(api): handle null response from user endpoint โ you need this
# Apply just that commit to your branch
git cherry-pick a3f92c1
# Now your branch has the fix without merging all of main
Note: Cherry-pick creates a new commit with the same changes but a different hash. The original commit remains on its branch.
Practice: Git Cherry-pick Documentation ยท Atlassian Cherry-pick
A8. git reflog โ The Safety Netโ
git reflog records every movement of HEAD โ including commits, resets, rebases, and checkouts. It is your recovery tool when you think you've lost work.
# View the reflog (chronological list of HEAD positions)
git reflog
# Output:
# a3f92c1 HEAD@{0}: commit: feat: add dashboard
# 8b21d4e HEAD@{1}: reset: moving to HEAD~1
# c10a993 HEAD@{2}: commit: fix: login redirect
# 7ffe201 HEAD@{3}: checkout: moving from main to feature/dashboard
Each entry has a HEAD@{N} reference you can use to restore any state.
Recovering commits after an accidental git reset --hard:
# You ran git reset --hard and lost commits โ find them in reflog
git reflog
# Find the commit hash just before the reset (e.g. c10a993 at HEAD@{2})
# Create a new branch at that point to recover the work
git checkout -b recovery/lost-work c10a993
# Or reset back to it on your current branch
git reset --hard c10a993
Recovering a deleted branch:
# You deleted a branch and realize you needed it
git reflog
# Find the last commit on that branch (e.g. 8b21d4e)
git checkout -b feature/recovered-branch 8b21d4e
Important: Reflog is local only โ it is not pushed to GitHub. It also expires (default 90 days for reachable commits, 30 days for unreachable ones).
Practice: Git Reflog Documentation ยท Atlassian Git Reflog
A9. Merge vs Rebase โ When to Use Whichโ
This is one of the most debated topics in Git. Both achieve the same end goal โ integrating changes from one branch into another โ but with different history shapes.
Merge:
main: A --- B --- C -------M โ merge commit
\ /
feature/login: D --- E
Rebase:
main: A --- B --- C
\
feature/login: D' --- E' โ replayed on top of C, linear history
| Merge | Rebase | |
|---|---|---|
| History shape | Non-linear (has merge commits) | Linear (clean, no merge commits) |
| Preserves original branch history | โ Yes | โ No (rewrites commit hashes) |
| Safe on shared/public branches | โ Yes | โ No |
| Easier to undo | โ Just revert the merge commit | Harder |
Cleaner git log | โ More noise | โ Easier to read |
| Conflict frequency | One set of conflicts per merge | One set of conflicts per replayed commit |
Team guidelines โ when to use each:
| Situation | Recommended approach |
|---|---|
Syncing your feature branch with the latest main | git rebase origin/main (clean history on your branch) |
Merging a finished feature branch into main via PR | Merge (preserves the fact that a feature existed as a branch) |
| Cleaning up commits before opening a PR | git rebase -i (interactive squash/fixup) |
| Hotfix needs to go to multiple branches | git cherry-pick |
Undoing a merged feature in main | git revert (safe, preserves history) |
# Typical daily workflow combining both:
# 1. Keep your branch up to date with main (rebase for linear history)
git fetch origin
git rebase origin/main
# 2. Clean up commits before PR (squash WIP commits)
git rebase -i origin/main
# 3. Push your cleaned branch
git push -u origin feature/my-feature
# (If you've already pushed and need to force after rebase)
git push --force-with-lease origin feature/my-feature
# 4. Open PR โ team reviews โ merge into main via GitHub
Golden rule: Rebase to clean up your own local/feature branch history. Merge to integrate completed work into shared branches.
Practice: Merging vs Rebasing ยท Pro Git โ Rebasing
A10. Tags & Releasesโ
Git tags mark specific commits as significant โ typically a release point. Tags are permanent references unlike branch names.
Two types of tags:
| Type | Command | Description |
|---|---|---|
| Lightweight | git tag v1.0.0 | Just a pointer to a commit โ like a branch that doesn't move |
| Annotated | git tag -a v1.0.0 -m "Release v1.0.0" | Stores tagger name, date, and message โ preferred for releases |
# Create an annotated tag on the current commit
git tag -a v1.0.0 -m "First stable release"
# Tag a specific past commit
git tag -a v0.9.0 a3f92c1 -m "Beta release"
# List all tags
git tag
# View tag details
git show v1.0.0
# Push a tag to GitHub (tags are not pushed by default)
git push origin v1.0.0
# Push all tags at once
git push origin --tags
# Delete a local tag
git tag -d v1.0.0
# Delete a remote tag
git push origin --delete v1.0.0
Team release convention โ Semantic Versioning (MAJOR.MINOR.PATCH):
| Version part | When to increment | Example |
|---|---|---|
MAJOR | Breaking change โ incompatible with previous version | v1.0.0 โ v2.0.0 |
MINOR | New feature added, backwards-compatible | v1.0.0 โ v1.1.0 |
PATCH | Bug fix, backwards-compatible | v1.0.0 โ v1.0.1 |
Creating a GitHub Release:
- Push your tag:
git push origin v1.0.0 - On GitHub: go to Releases โ Draft a new release
- Select the tag, write release notes describing what changed
- Click Publish release
Team convention: Tags and releases are created on
mainafter a UOW branch has been merged.
Practice: Semantic Versioning ยท GitHub Releases
A11. git bisect โ Finding the Commit That Introduced a Bugโ
git bisect performs a binary search through your commit history to find the exact commit that introduced a bug. Instead of checking commits one by one, it halves the search space each step.
# Start a bisect session
git bisect start
# Mark the current commit as broken
git bisect bad
# Mark a known-good commit (e.g. a tag or hash from before the bug appeared)
git bisect good v1.2.0
# Git checks out a commit halfway between โ you test it, then tell Git:
git bisect good # if this commit is fine
git bisect bad # if this commit has the bug
# Git keeps narrowing down until it identifies the first bad commit
# Output: "abc1234 is the first bad commit"
# End the bisect session and return to HEAD
git bisect reset
Automated bisect with a test script:
# If you have a script that exits 0 for good and non-zero for bad:
git bisect start
git bisect bad HEAD
git bisect good v1.2.0
git bisect run npm test -- --testFile=src/login.test.js
# Git automatically runs the script at each step and finds the bad commit
Practice: Git Bisect Documentation ยท Atlassian Git Bisect
A12. git worktree โ Multiple Working Directoriesโ
git worktree lets you check out multiple branches simultaneously in separate directories โ without stashing or switching branches. Useful when you need to review or hotfix another branch while mid-feature.
# Check out a branch into a separate folder (worktree)
git worktree add ../hotfix-branch fix/99-critical-crash
# Now you have two working directories:
# /my-project โ your current branch (feature work in progress)
# /hotfix-branch โ fix/99-critical-crash checked out here
# Work in the hotfix worktree, commit, push โ independently of your main work
# List all worktrees
git worktree list
# Remove a worktree when done
git worktree remove ../hotfix-branch
When to use: You're mid-feature and a critical production bug needs a fix immediately โ instead of stashing everything and switching branches, just add a worktree.
Practice: Git Worktree Documentation
A13. Git Hooks โ Automating Actionsโ
Git hooks are scripts that Git runs automatically before or after events like commit, push, or merge. They live in .git/hooks/ and are local โ not pushed to GitHub.
Common hooks:
| Hook | Triggered | Common use |
|---|---|---|
pre-commit | Before a commit is created | Run linter, formatter, or tests |
commit-msg | After commit message is written | Enforce message format (e.g. [#issue]) |
pre-push | Before git push runs | Run the full test suite |
post-merge | After a merge completes | Auto-install dependencies (npm install) |
Example โ pre-commit hook that runs ESLint:
# .git/hooks/pre-commit
#!/bin/sh
npx eslint src/
if [ $? -ne 0 ]; then
echo "ESLint failed. Fix errors before committing."
exit 1
fi
Make the hook executable:
chmod +x .git/hooks/pre-commit
Sharing hooks with the team using Husky:
Since .git/hooks/ is not committed, use Husky to version-control hooks in your repository:
npm install --save-dev husky
npx husky init
# Create a pre-commit hook
echo "npx lint-staged" > .husky/pre-commit
Practice: Git Hooks Documentation ยท Husky
A14. Git Submodules โ Repositories Inside Repositoriesโ
A submodule embeds one Git repository inside another as a dependency. Useful when your project depends on a shared library or external repo that is maintained separately.
# Add a submodule
git submodule add git@github.com:org/shared-lib.git libs/shared-lib
# This creates a .gitmodules file and a reference commit
# Clone a repo that has submodules (submodules are NOT cloned by default)
git clone --recurse-submodules git@github.com:org/my-project.git
# If you forgot --recurse-submodules at clone time
git submodule update --init --recursive
# Update all submodules to their latest remote commit
git submodule update --remote
# Check submodule status
git submodule status
Important: When you commit in the parent repo after a submodule update, you're committing the new submodule pointer (hash), not the submodule's files.
Caution: Submodules add complexity. Prefer package managers (
npm,pip) for dependencies where possible. Use submodules only when you need to track a specific version of another repo's code directly.
Practice: Pro Git โ Submodules
A15. Signing Commits โ Verifying Authorshipโ
Signing commits with a GPG or SSH key proves that a commit genuinely came from you โ GitHub shows a "Verified" badge on signed commits.
# Generate a GPG key (if you don't have one)
gpg --full-generate-key
# List your GPG keys and get the key ID
gpg --list-secret-keys --keyid-format=long
# Configure Git to use your key
git config --global user.signingkey YOUR_KEY_ID
# Sign commits by default
git config --global commit.gpgsign true
# Sign a single commit manually
git commit -S -m "feat: add signed commit"
# Sign a tag
git tag -s v1.0.0 -m "Signed release v1.0.0"
# Verify a signed commit
git verify-commit HEAD
Add your GPG key to GitHub:
- Export your public key:
gpg --armor --export YOUR_KEY_ID - Go to GitHub โ Settings โ SSH and GPG keys โ New GPG key
- Paste the key and save
A16. GitHub Actions โ CI/CD Basicsโ
GitHub Actions is GitHub's built-in automation platform. It runs workflows in response to repository events (push, PR, release, schedule).
Key concepts:
| Term | Description |
|---|---|
| Workflow | A YAML file in .github/workflows/ defining automation |
| Event | What triggers the workflow (e.g. push, pull_request) |
| Job | A set of steps that run on a runner (virtual machine) |
| Step | A single command or action within a job |
| Action | A reusable unit of work (e.g. actions/checkout) |
Example โ Run tests on every PR:
# .github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
With this workflow, every PR automatically runs the test suite and GitHub blocks merging if tests fail.
Example โ Auto-deploy on push to main:
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- name: Deploy
run: ./scripts/deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
Note: Secrets (like API keys and tokens) are stored in GitHub โ Settings โ Secrets and variables โ Actions โ never hardcoded in workflow files.
Practice: GitHub Actions Quickstart ยท GitHub Actions Documentation