March 27, 202614 min read

Git and GitHub: The Complete Workflow Every Developer Uses

Learn the real GitHub workflow: pull requests, code reviews, Issues, forking, branch protection, and the collaboration patterns used at actual companies.

git github collaboration pull-requests workflow
Ad 336x280

Knowing Git commands is one thing. Using Git and GitHub together the way professional teams actually do -- pull requests, code reviews, branch protection, Issues -- is something else entirely. This guide bridges that gap.

If you're comfortable with git add, git commit, and git push but have never opened a pull request or reviewed someone else's code, this is where you level up from "I use Git" to "I collaborate with Git."

GitHub Is Not Git

This confuses a lot of people, so let's clear it up.

Git is the version control tool that runs on your machine. It tracks changes, manages branches, and stores history locally. GitHub is a website that hosts Git repositories and adds collaboration features on top: pull requests, Issues, Actions, Pages, and a web interface for browsing code.

You can use Git without GitHub. You can't use GitHub without Git. They're complementary, but they're different things.

Other platforms like GitLab and Bitbucket offer similar features. The concepts transfer. But GitHub is where most open-source projects live and where most teams collaborate, so that's what we'll focus on.

Setting Up: Connecting Git to GitHub

Before anything else, you need Git on your machine talking to your GitHub account.

SSH keys (the right way)

Password authentication for Git operations was deprecated on GitHub. SSH keys are the standard.

# Generate an SSH key pair
ssh-keygen -t ed25519 -C "your_email@example.com"

# Start the SSH agent
eval "$(ssh-agent -s)"

# Add your private key
ssh-add ~/.ssh/id_ed25519

# Copy your public key to clipboard (macOS)
pbcopy < ~/.ssh/id_ed25519.pub

# On Linux, just cat it and copy manually
cat ~/.ssh/id_ed25519.pub

Go to GitHub > Settings > SSH and GPG keys > New SSH key. Paste the public key. Done.

Test it:

ssh -T git@github.com
# Should say: Hi username! You've successfully authenticated...

Creating a repo on GitHub

Two common paths:

Starting fresh on GitHub:
  1. Click "New repository" on GitHub
  2. Give it a name, optionally add a README
  3. Clone it to your machine:
git clone git@github.com:yourusername/your-repo.git
cd your-repo
Pushing an existing local project:
# In your project directory
git init
git add .
git commit -m "Initial commit"

# Create the repo on GitHub (empty, no README)
# Then connect and push:
git remote add origin git@github.com:yourusername/your-repo.git
git branch -M main
git push -u origin main

The -u flag sets the upstream, so future git push commands know where to go without you specifying it.

The Branch Workflow

Here's the most important concept in professional Git usage: you almost never commit directly to main.

Instead, the workflow looks like this:

  1. Create a branch for your feature or fix
  2. Do your work on that branch
  3. Push the branch to GitHub
  4. Open a pull request
  5. Get it reviewed
  6. Merge it into main
This is called the GitHub Flow, and it's what most teams use. Let's walk through each step.

Step 1: Create a branch

# Make sure you're on main and up to date
git checkout main
git pull origin main

# Create and switch to a new branch
git checkout -b feature/add-user-search

Branch naming conventions vary by team, but common patterns include:

  • feature/description -- for new features
  • fix/description -- for bug fixes
  • chore/description -- for maintenance tasks
  • docs/description -- for documentation updates
Some teams prefix with a ticket number: feature/PROJ-123-add-user-search. Whatever your team uses, be consistent.

Step 2: Do your work

Write code, make commits. Keep commits focused and descriptive.

# Work, work, work...
git add src/components/SearchBar.jsx
git commit -m "Add SearchBar component with debounced input"

git add src/api/users.js
git commit -m "Add user search API endpoint"

git add src/pages/Users.jsx
git commit -m "Integrate search into Users page"

Notice how each commit does one thing and the message says what it does. Not "WIP" or "stuff" or "asdfasdf." Your future self and your teammates will thank you.

Step 3: Push the branch

git push -u origin feature/add-user-search

This creates the branch on GitHub and pushes your commits to it. The -u flag sets up tracking so you can just git push next time.

Step 4: Open a pull request

Go to your repo on GitHub. You'll usually see a banner saying "feature/add-user-search had recent pushes" with a "Compare & pull request" button. Click it.

Or go to the "Pull requests" tab and click "New pull request."

A good pull request includes:

  • A clear title -- "Add user search functionality" not "PR for thing"
  • A description -- What does this change? Why? Any context a reviewer needs?
  • Screenshots if it's a UI change
  • Testing notes -- How can a reviewer verify this works?
Here's a template many teams use:
## What

Added search functionality to the Users page.

Why

Users currently have to scroll through the entire list to find someone.
This was reported in #42.

How

  • Added a SearchBar component with debounced input (300ms)
  • Created a new API endpoint that filters users by name
  • Integrated the search into the existing Users page

Testing

  1. Go to /users
  2. Type a name in the search bar
  3. Results should filter after 300ms of no typing

Step 5: Code review

This is where collaboration actually happens. Reviewers will:

  • Read through your code changes
  • Leave comments on specific lines
  • Ask questions or suggest improvements
  • Approve the PR or request changes
As the author, you:
  • Respond to every comment (even if it's just "good point, fixed")
  • Push new commits to address feedback
  • Don't force-push during review -- it makes it hard for reviewers to see what changed
# After making changes based on review feedback
git add .
git commit -m "Address review: extract search logic to custom hook"
git push

The PR updates automatically with your new commits.

Step 6: Merge

Once approved, you merge the PR. GitHub offers three merge strategies:

Create a merge commit -- preserves all individual commits plus adds a merge commit. Full history, but can be noisy. Squash and merge -- combines all your commits into one. Clean history. This is what most teams prefer for feature branches. Rebase and merge -- replays your commits on top of main. Linear history, no merge commit. Purists love this one.

Most teams pick one strategy and stick with it. Squash-and-merge is the most common default.

After merging, delete the branch. GitHub has a button for this, and most teams enable auto-deletion of merged branches in repo settings.

Keeping Your Branch Up to Date

While you're working on your feature, other people are merging their PRs into main. Your branch can fall behind.

# Option 1: Merge main into your branch
git checkout feature/add-user-search
git fetch origin
git merge origin/main

# Option 2: Rebase your branch on top of main
git checkout feature/add-user-search
git fetch origin
git rebase origin/main

Merge is safer and creates a merge commit. Rebase is cleaner but rewrites history -- never rebase commits that other people are working on.

If you get merge conflicts:

# Git will tell you which files have conflicts
# Open each file, look for the conflict markers:
<<<<<<< HEAD
your code
=======
their code
>>>>>>> origin/main

# Edit the file to resolve the conflict
# Then:
git add resolved-file.js
git commit -m "Resolve merge conflict in resolved-file.js"

GitHub Issues

Issues are GitHub's built-in task tracker. They're more useful than most people realize.

Creating useful Issues

A good Issue includes:

  • Clear title -- "Search doesn't work on mobile" not "Bug"
  • Steps to reproduce (for bugs)
  • Expected vs actual behavior
  • Labels -- bug, feature, documentation, good-first-issue, etc.

Linking Issues to PRs

When your PR fixes an Issue, include a keyword in the PR description:

Fixes #42
Closes #42
Resolves #42

When the PR merges, the Issue automatically closes. This creates a clear paper trail: Issue describes the problem, PR describes the solution, they're linked together.

Issue templates

For team projects, set up Issue templates in .github/ISSUE_TEMPLATE/. This ensures bug reports always include steps to reproduce and feature requests always include a use case. GitHub walks you through creating these in the repo settings.

Forking and Contributing to Open Source

When you want to contribute to a project you don't own, you fork it.

A fork is your personal copy of someone else's repo on your GitHub account. You can do anything you want with it without affecting the original.

# 1. Fork the repo on GitHub (click the Fork button)

# 2. Clone YOUR fork
git clone git@github.com:yourusername/their-repo.git
cd their-repo

# 3. Add the original repo as "upstream"
git remote add upstream git@github.com:original-owner/their-repo.git

# 4. Create a branch for your contribution
git checkout -b fix/typo-in-readme

# 5. Make your changes, commit, push to YOUR fork
git add .
git commit -m "Fix typo in installation instructions"
git push origin fix/typo-in-readme

# 6. Open a PR from your fork to the original repo

When you open the PR on GitHub, it automatically detects that you're proposing changes from your fork to the original repo.

Keeping your fork in sync

git fetch upstream
git checkout main
git merge upstream/main
git push origin main

Do this regularly so your fork doesn't drift too far from the original.

Branch Protection Rules

For team projects, you want to prevent people (including yourself) from pushing directly to main. Branch protection rules enforce this.

Go to Settings > Branches > Add branch protection rule for main:

  • Require a pull request before merging -- no direct pushes to main
  • Require approvals -- at least 1 (or 2) people must approve the PR
  • Require status checks to pass -- CI/CD must be green before merging
  • Require branches to be up to date -- your branch must include the latest main
  • Include administrators -- even admins follow the rules
This might feel restrictive, but it prevents the "someone pushed broken code to main at 5 PM on Friday" scenario that every team experiences exactly once before setting up branch protection.

GitHub Actions (CI/CD Basics)

GitHub Actions lets you automate tasks when certain events happen. The most common: run tests automatically when a PR is opened.

Actions are defined in .github/workflows/ as YAML files:

# .github/workflows/ci.yml
name: CI

on:
pull_request:
branches: [main]
push:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm test
- run: npm run lint

Now every PR automatically runs tests and linting. Combined with branch protection requiring status checks to pass, you can't merge broken code.

Common things teams automate with Actions:

  • Running tests and linters
  • Building and deploying on merge to main
  • Checking for security vulnerabilities
  • Generating code coverage reports
  • Auto-labeling PRs based on files changed

GitHub Pages

GitHub Pages lets you host a static website directly from a repo. It's free and perfect for documentation, portfolios, or project landing pages.

To enable it:

  1. Go to Settings > Pages
  2. Choose a source branch (usually main or gh-pages)
  3. Choose a folder (/ root or /docs)
  4. Your site is live at yourusername.github.io/repo-name
For a personal site, name the repo yourusername.github.io and it'll be available at that URL directly.

You can use plain HTML, Jekyll (GitHub's default static site generator), or any framework that produces static files. Just push the output to the configured branch and folder.

The .gitignore File

Every repo needs one. It tells Git which files to ignore -- things that shouldn't be in version control.

# Dependencies
node_modules/
venv/
__pycache__/

# Build output
dist/
build/
.next/
out/

# Environment variables (NEVER commit these)
.env
.env.local
.env.production

# OS files
.DS_Store
Thumbs.db

# IDE files
.vscode/settings.json
.idea/

GitHub maintains a collection of .gitignore templates at github.com/github/gitignore for virtually every language and framework. Use them as a starting point.

The most critical entry: .env. Accidentally committing API keys or database passwords to a public repo is a common and serious mistake. Even if you delete the file later, it's still in the Git history. If this happens, consider those credentials compromised and rotate them immediately.

The README

Your README is the front page of your project. At minimum it should include:

  • What the project does (one paragraph)
  • How to install and run it
  • How to contribute (for open-source projects)
A good README turns "what is this?" into "I want to try this" in 30 seconds.

Real-World Team Workflows

Here's what a typical day looks like using this workflow:

Morning:
git checkout main
git pull origin main
# Check assigned Issues and pick one to work on
git checkout -b fix/PROJ-89-date-picker-timezone
During the day:
# Write code, make focused commits
git add src/components/DatePicker.jsx
git commit -m "Fix timezone offset in DatePicker component"

git add src/__tests__/DatePicker.test.jsx
git commit -m "Add tests for timezone edge cases"

# Push and open PR git push -u origin fix/PROJ-89-date-picker-timezone
PR description:
Fixes #89

The DatePicker was displaying dates in UTC instead of the user's local
timezone. This caused dates to appear one day off for users west of GMT.

  • Fixed timezone conversion in formatDate utility
  • Added test cases for GMT-12 through GMT+14
Review cycle:
  • Teammate leaves a comment: "Can we also handle the case where the timezone is undefined?"
  • You add a commit addressing it
  • Teammate approves
  • You squash and merge
  • Branch auto-deletes
This loop -- branch, commit, push, PR, review, merge -- happens dozens of times a day on active teams. It becomes second nature fast.

Common Mistakes and How to Avoid Them

Pushing directly to main. Set up branch protection. Even on solo projects, using PRs creates a record of why changes were made. Giant PRs. A PR with 2,000 lines changed is hard to review and likely to have bugs slip through. Aim for PRs under 400 lines. If a feature is big, break it into smaller PRs that build on each other. Vague commit messages. "Fix bug" tells you nothing six months later. "Fix timezone offset causing DatePicker to show wrong date for GMT- users" tells you everything. Not pulling before branching. Always git pull origin main before creating a new branch. Otherwise you're branching from an outdated version of main. Committing .env files. Add .env to .gitignore before your first commit. If you've already committed it, remove it from tracking with git rm --cached .env and rotate all the credentials in it. Force-pushing to shared branches. git push --force rewrites history. On your own feature branch before anyone's looked at it? Fine. On a branch others are working on? You'll destroy their work.

What to Learn Next

This covers the GitHub workflow that 90% of development teams use daily. Once you're comfortable with this, explore:

  • Git hooks -- scripts that run automatically on commit, push, etc.
  • GitHub CLI (gh) -- create PRs, review code, and manage Issues from the terminal
  • Conventional Commits -- a standardized commit message format
  • Trunk-based development -- an alternative to feature branches used by some large teams
  • Git bisect -- finding which commit introduced a bug using binary search
The best way to internalize all of this is to contribute to an open-source project. Find a repo with "good first issue" labels, fork it, fix something small, and open a PR. The maintainers will guide you through their workflow.

Practice these workflows with real projects on CodeUp -- the collaboration patterns you build now are the same ones you'll use every day as a professional developer.

Ad 728x90