Classroom Glossary Public page

Week 10: Git Intermediate (Branches and Pull Requests)

1,626 words

The actual workflow every working programmer uses every day. Branches, remotes, pull requests, code review. The lab submits Lab 9 as a PR for instructor review.


Theme

FND-101 covered git init, git add, git commit, git push. That is the solo workflow: you and your repo on your laptop. The team workflow has more shape: you work on a branch named for your change, push the branch to a shared remote, open a pull request (PR) that says "please merge this branch into main," another person reviews it, you respond to feedback, the PR is merged.

Most professional Python (and most open-source Python) is built this way. Every commit on the main branch of a serious project has been through a PR. Code review is where bugs are caught, design discussions happen, and junior developers learn from senior ones.

This week's lab is your first real PR: you take Lab 9's disk-usage reporter, push it to a remote (GitHub or GitLab; the academy uses GitLab self-hosted at git.sandhillscto.com but GitHub works equally well for the lab), open a PR with the instructor as reviewer, respond to one round of feedback, and get the PR merged.

By the end of week 10 you can: create and switch branches; push a branch to a remote; open a pull request via the web UI or the gh / glab CLI; respond to a code review comment; merge a PR after approval; resolve a simple merge conflict.

Reading list (~1 hour)

  1. Scott Chacon, Pro Git 2nd ed., Ch 3.1-3.5 ("Git Branching") at https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell. Free online. The canonical reference. Read 3.1 through 3.5; the rebasing material (3.6) is forward-stretch.
  2. GitHub Docs: "Creating an issue or pull request" at https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request. ~10 min. Read the "Creating the pull request" section; skim the rest.
  3. GitLab Docs: "Merge requests" at https://docs.gitlab.com/ee/user/project/merge_requests/. ~10 min. The same concept as GitHub PRs; GitLab calls them merge requests.
  4. Real Python: "Git Tutorial" at https://realpython.com/python-git-github-intro/. ~20 min. Worked examples of the branch / push / PR workflow.

Lecture outline (~1.5 hours, 2 sessions of ~50 min)

Session 1: Branches and remotes

Section 1.1: What a branch is

  • A branch is a pointer to a commit. That is the whole concept.
  • main is a branch. Every other branch starts as a pointer to whatever commit main was at when you branched.
  • Each commit has a parent (the previous commit on the branch). Branches diverge when they have different commits after the branch point; they reunite when one is merged into the other.

Section 1.2: Creating and switching branches

  • Create and switch in one step:
    git checkout -b feature/disk-usage
    
  • Or in two steps (modern):
    git branch feature/disk-usage
    git switch feature/disk-usage
    
  • The branch name feature/disk-usage is a convention: <type>/<short-description>. Common types: feature, bugfix, refactor, docs. Your team's convention may vary; consistency matters more than which one you pick.

Section 1.3: Working on a branch

  • Make changes; git add; git commit (just like main).
  • Multiple commits on a branch are normal: one commit per logical step.
  • git log --all --oneline --graph shows the branch structure.
  • git status shows which branch you are on and any uncommitted changes.

Section 1.4: Pushing a branch to a remote

  • The first push of a new branch:
    git push -u origin feature/disk-usage
    
  • The -u (--set-upstream) tells Git to remember that this local branch tracks the same-named remote branch. Subsequent pushes are just git push.
  • A remote is a name (origin is the conventional default) for a Git URL. git remote -v lists all remotes.

Section 1.5: Remotes

  • git clone sets up a remote called origin automatically.
  • To start from scratch with a new remote:
    git remote add origin git@github.com:yourusername/repo.git
    
  • The URL form git@github.com:... is SSH (requires an SSH key set up with GitHub/GitLab). The HTTPS form https://github.com/... works without SSH but requires a token for write access.
  • For FND-102, the lab uses either GitHub or GitLab; you may need to set up an SSH key. GitHub's guide is at https://docs.github.com/en/authentication/connecting-to-github-with-ssh.

Session 2: Pull requests, code review, merge conflicts

Section 2.1: Opening a PR

  • After git push -u origin feature/..., the remote (GitHub or GitLab) displays a "Compare and pull request" link. Click it.
  • The PR form has a title and a description:
    • Title: one sentence describing what the PR does (imperative voice, like a commit message: "Add disk-usage reporter with subprocess wrapper")
    • Description: what changed, why, and how to test it. ~3-5 paragraphs. Link to any issue or spec.
  • Pick the target branch (main); pick the source branch (feature/...).
  • Click "Create pull request."
  • The PR has a unique number (#42); other developers reference it as #42.

Section 2.2: Receiving code review

  • The reviewer reads your diff. They may:
    • Approve (the PR is ready to merge).
    • Request changes (the PR has issues you must address).
    • Leave non-blocking comments (suggestions; not required to address).
  • Common review comments:
    • "This function is doing two things; can you split it?"
    • "The error message could be clearer."
    • "Missing test for the edge case where X."
    • "Why not use the stdlib for this instead of rolling your own?"
  • The discipline of receiving review: ASSUME GOOD FAITH. The reviewer's job is to make the code better, not to attack you. Disagree if you have a good reason; agree gracefully when you do not.

Section 2.3: Responding to feedback

  • For each comment, either:
    • Make the change in a new commit; push to the same branch; the PR updates automatically
    • Reply with why you disagree (rare; pick your battles)
  • Multi-commit response is fine. The PR can be squash-merged at the end so the merged commit is clean.
  • Mark each comment as "resolved" after addressing it (GitHub) or click the checkbox (GitLab) so the reviewer knows you handled it.

Section 2.4: Merging the PR

  • After approval, click "Merge pull request" (GitHub) or "Merge" (GitLab).
  • Three merge strategies:
    • Merge commit (default): preserves all commits on the branch plus a merge commit. Most history; least squashed.
    • Squash and merge: combines all branch commits into one new commit on main. Cleanest main history; loses the per-step history on the branch.
    • Rebase and merge: replays branch commits onto main without a merge commit. Linear history; harder for beginners.
  • For FND-102: squash-and-merge is the default. Clean main history; the branch's intermediate commits live in the PR but not on main.
  • After merge, delete the branch (the web UI offers a button).

Section 2.5: Merge conflicts

  • A merge conflict happens when your branch and main both modified the same lines of the same file in incompatible ways.
  • Git pauses the merge and marks the conflicting region in the file:
    <<<<<<< HEAD
    your changes
    =======
    the other side's changes
    >>>>>>> main
    
  • To resolve: edit the file to the version you want (often a combination), remove the marker lines, save.
  • git add file.py marks it as resolved; git commit (or git merge --continue) finishes the merge.
  • Conflicts are normal on long-lived branches. The defense: keep branches short (a few days max); merge / rebase main into your branch frequently.

Labs (~90 minutes)

Lab 10: PR Submission (labs/lab-10-pr-submission.md)

  • Goal: take Lab 9's disk-usage reporter; push it to a remote on a branch; open a PR with the instructor as reviewer; respond to one round of feedback; merge
  • Time: ~90 minutes
  • Artifact: a merged PR + transcript (saved as lab-10-transcript.md in ~/fnd-102/lab-10/)

Independent practice (~4 hours)

  1. Branch hygiene drill (30 min). In any of your existing FND-102 lab repositories: create a branch feature/cleanup, make a small change (rename a variable; add a comment; reformat), commit, push, delete the branch (without merging). Notice that the deleted branch's commit is now unreachable (you can recover via git reflog; useful safety net).
  2. Merge conflict resolution (45 min). Set up a deliberate conflict:
    git switch -c branch-a
    # edit file.py; change line 1
    git commit -am "branch-a change"
    git switch main
    git switch -c branch-b
    # edit file.py; change line 1 differently
    git commit -am "branch-b change"
    git switch main
    git merge branch-a   # this works
    git merge branch-b   # this conflicts
    
    Resolve the conflict; commit; verify with git log --all --graph.
  3. PR template (30 min). Most repos use a .github/pull_request_template.md or .gitlab/merge_request_templates/. Write one for your FND-102 repo with five sections: Summary, What changed, Why, Testing notes, Reviewer asks. Commit it. Future PRs auto-populate from this template.
  4. Read a real PR (30 min). On GitHub, find any Python project you care about (CPython itself: https://github.com/python/cpython/pulls). Read three merged PRs. Notice: how long are the discussions? How many commits? How is the merge done?
  5. Write a good commit message (30 min). Take the last 5 commits in your FND-102 repo. Rewrite each commit message to follow the convention: imperative voice; ~50 character first line; blank line; multi-paragraph body. Use git rebase -i HEAD~5 and pick reword for each. (Forward-stretch: interactive rebase is advanced; only attempt on a private branch with no other team members.)
  6. git diff mastery (15 min). In the REPL of git: git diff (unstaged changes); git diff --cached (staged changes); git diff HEAD~1 (diff from one commit ago); git diff main feature/x (between branches). These four are 90% of the diff invocations you will use.
  7. Optional stretch (60 min). Set up a personal CI: a .github/workflows/test.yml file that runs pytest on every push to your FND-102 repo. Free for public repos. The yaml looks like:
    on: [push]
    jobs:
      test:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v3
          - uses: actions/setup-python@v4
            with:
              python-version: '3.11'
          - run: pip install pytest
          - run: pytest
    
    Forward-pointer to week 13.

Reflection prompts (~30 minutes)

  1. The branch-and-PR workflow is more setup than the solo git add; git commit; git push workflow you knew before. What is the payoff that justifies the setup?
  2. Code review is uncomfortable the first time. What is the worst case if your reviewer finds a real bug in your PR? What is the worst case if they don't?
  3. Squash-and-merge vs merge-commit: which does your Lab 10 PR use? Why? When might the other be better?
  4. Merge conflicts feel scary the first time. After practice 2, do they still feel scary? What did you learn that you would tell a future student?
  5. One thing from this week you want to know more about?

Tool journal (week 10)

  • git switch, git switch -c BRANCH: create and change branches (modern)
  • git checkout -b BRANCH: same in legacy form
  • git push -u origin BRANCH: first push of a new branch
  • git pull vs git fetch: pull = fetch + merge; fetch alone if you want to inspect before merging
  • Pull Request / Merge Request: the team workflow
  • gh CLI (GitHub) or glab CLI (GitLab): PR operations from the terminal
  • Merge strategies: merge commit, squash, rebase
  • git rebase -i HEAD~N: interactive rebase (advanced; private branches only)
  • git reflog: safety net for "where did my branch go?"

What comes next

Week 11 introduces hashing and integrity. Lab 11 is a directory-integrity checker that computes SHA-256 for every file in a tree and detects later modifications. The first place in the course you use cryptographic primitives (the hashing kind, not encryption).