← Lillie AcademyCourse contentslillieearthintelligence.com

Module 0.4: Git and GitHub for Your Portfolio

Phase: 0, Foundations
Level: Beginner
Estimated time: 4 to 6 hours
Prerequisites: Module 0.1 (you have git installed and have made a first commit).
Portfolio thread: turns eo-portfolio into a repository with a clean, professional history, a merged pull request, and a tagged release, which is what a hiring manager actually looks at.


Why this module

Module 0.1 got you committing and pushing. That is enough to save work, but not enough to look like an engineer or to collaborate on a team, and every job in this field assumes both. Recruiters and hiring managers open your GitHub before they read your resume, so a messy history of "stuff" commits, secrets checked in, or a single giant commit quietly costs you. This module makes you fluent: branches, pull requests, resolving conflicts, keeping data and secrets out, writing readable history, and undoing mistakes safely. It is short, but it is one of the highest-return hours in the whole course.

Learning outcomes

After this module you can:

  1. Work on a feature branch and merge it, and explain merge versus rebase.
  2. Open, review, and merge a pull request, and say why teams work this way.
  3. Create and resolve a merge conflict without panic.
  4. Keep data, secrets, and large files out of the repository with a good .gitignore.
  5. Write clear commit messages and undo changes safely (restore, revert, reset).
  6. Tag a release and understand forks for collaboration.

Concept lessons (about 2 hours)

Lesson 1: The model, quickly

Recall from Module 0.1: the working directory holds your files, you add changes to a staging area, commit them as a labeled snapshot, and push to GitHub. A repository is the whole history of those snapshots. Everything below builds on this. Two habits from the start: commit small and often (one logical change per commit), and write messages that a stranger, or future you, can read.

Lesson 2: Branches and merging

A branch is an independent line of work. You create a feature branch, make commits there, and merge it back into main when it is ready, which keeps main always working. The everyday flow:

git switch -c feature/cube-loader   # create and switch to a branch
# ...edit, add, commit...
git switch main
git merge feature/cube-loader       # bring the work into main

If main has not moved, git does a fast-forward (just moves the pointer). If both moved, git makes a merge commit. There is also rebase, which replays your commits on top of the latest main for a linear history; learn merge first and treat rebase as an intermediate tool, because rebasing shared branches can rewrite history others depend on.

Lesson 3: Pull requests and review

On GitHub you rarely push straight to main. You push your branch and open a pull request (PR): a proposal to merge, with a diff, a description, and space for review comments and automated checks (your CI from Module 11). Even working solo, using PRs on your own repository is worth it, because it shows collaboration habits and gives CI a place to run before code lands. Contributing to someone else's project uses the same idea via a fork: you copy their repo, branch, and open a PR back to them.

Lesson 4: Merge conflicts

A conflict happens when two branches change the same lines and git cannot decide which to keep. It is normal, not a failure. Git marks the spot:

<<<<<<< HEAD
your version
=======
their version
>>>>>>> feature/other

You edit the file to the correct final result, remove the markers, then add and commit. The skill is staying calm, reading both sides, and testing after resolving. Practicing this once removes most of the fear.

Lesson 5: Hygiene and safety

Three rules that separate professional repositories from amateur ones:

And git tag v0.1.0 marks a release point you can return to.


Guided lab (about 2 to 3 hours): a professional eo-portfolio history

Open in Colab Open in GitHub Codespaces

Step 1: A feature branch and a pull request

On your eo-portfolio, create a branch, make a small improvement (for example a README badge or a docstring), push it, and open a pull request on GitHub. Review your own diff, let CI run, and merge it. You now have a merged PR on your profile.

Step 2: Create and resolve a conflict

Deliberately edit the same README line on two branches, merge one, then merge the other to trigger a conflict, and resolve it. Doing this on purpose once is the best way to never fear it.

Step 3: A real .gitignore and tested helpers

Add a .gitignore, and add two small tested helpers to your repo that encode the hygiene rules:

"""gitutil.py: repository hygiene helpers (pure, testable)."""
from __future__ import annotations
import re

_CONVENTIONAL = re.compile(r"^(feat|fix|docs|test|refactor|chore|perf|build|ci)(\([\w.\-/]+\))?: .+")

def is_conventional_commit(message):
    """True if the first line follows the conventional-commit style."""
    if not message or not message.strip():
        return False
    return bool(_CONVENTIONAL.match(message.strip().splitlines()[0]))

def gitignore_covers(gitignore_text, path):
    """Rough check that a path would be ignored by these .gitignore rules."""
    for raw in gitignore_text.splitlines():
        line = raw.strip()
        if not line or line.startswith("#"):
            continue
        pat = line.rstrip("/")
        if pat.startswith("*.") and path.endswith(pat[1:]):
            return True
        if path == pat or path.startswith(pat + "/") or ("/" + pat + "/") in ("/" + path):
            return True
    return False
# tests/test_gitutil.py
from eo_portfolio.gitutil import is_conventional_commit, gitignore_covers

def test_conventional_commit():
    assert is_conventional_commit("feat: add cube loader")
    assert is_conventional_commit("fix(cube): handle empty search")
    assert not is_conventional_commit("updated stuff")
    assert not is_conventional_commit("")

def test_gitignore_covers():
    gi = "data/\n__pycache__/\n*.env\n"
    assert gitignore_covers(gi, "data/scene.tif")
    assert gitignore_covers(gi, "src/__pycache__/x.pyc")
    assert gitignore_covers(gi, "secret.env")
    assert not gitignore_covers(gi, "src/cube.py")

Step 4: Tag a release and commit

Tag the state at the end of Foundations, then push tags.

git tag -a v0.1.0 -m "Foundations complete"
git push --tags
git add .gitignore src/eo_portfolio/gitutil.py tests/test_gitutil.py
git commit -m "docs: add gitignore and repo hygiene helpers"
git push

Checkpoint

Self-check (answers below).

  1. Why work on a feature branch instead of committing straight to main?
  2. You committed an API key by mistake. Why is it not enough to just delete it in a new commit?
  3. What is the difference between git revert and git reset, and which is safe on shared history?
  4. What belongs in .gitignore for an EO project, and why?

Interview-style questions (practice out loud).

Answers. (1) A branch keeps main always working and lets you develop and run CI in isolation before merging.
(2) The key remains in the repository's history, so it must be treated as compromised and rotated; removing it in a later commit does not erase it from past commits.
(3) git revert adds a new commit that undoes a previous one and is safe on shared branches because it preserves history; git reset moves the branch pointer and can discard commits, so it is only safe on local, un-pushed work.
(4) Data (data/), caches (__pycache__/, .ipynb_checkpoints/), and secrets (*.env, keys), because data and large files bloat the repo and secrets are a security risk.


Deliverable

An eo-portfolio with a merged pull request, at least one deliberately created and resolved merge conflict, a real .gitignore, tested gitutil.py hygiene helpers, and a v0.1.0 tag. Your repository now reads as professional at a glance.

What a hiring manager sees

Your GitHub is opened before your resume. A clean, conventional commit history, a merged PR, no secrets or data in the tree, and a tagged release signal someone ready to work on a team, which for many reviewers matters as much as the models. Conversely, a repo with committed data, secrets, or a single monolithic commit raises doubts no matter how good the science is.

Currency note

Git itself is stable. The GitHub web interface and CLI (gh) evolve, and defaults change (for example git switch and git restore are the modern alternatives to older checkout usage), so confirm current commands and UI when you follow along. Pin any Python dev dependencies in your Module 0.1 environment file.

Previous0.3 Geospatial Data 101Next1 The Cloud-Native Geospatial Stack