Commits & Branches
18 min
Every Git commit points to exactly one parent commit (except the very first commit, and merge commits, which have two) — the whole repository history is a graph built entirely from these parent pointers. A branch isn't a copy of anything; it's just a movable label pointing at one commit.
commits = {
"c3": "c2",
"c2": "c1",
"c1": None, # the root commit has no parent
}
def commit_history(commits, start):
history = []
current = start
while current is not None:
history.append(current)
current = commits.get(current)
return history
print(commit_history(commits, "c3")) # ['c3', 'c2', 'c1']
Creating a commit is just 'add a new dict entry pointing at the old tip, then update the branch label to the new tip' -- there's no copying of history involved, which is exactly why branches in Git are so cheap to create.
Checking whether one commit is an ancestor of another (was it in that commit's history?) is exactly the same kind of graph-walk as the Debugging course's bisection module — follow parent pointers until you either find the target or run out of history:
def is_ancestor(commits, ancestor, descendant):
current = descendant
while current is not None:
if current == ancestor:
return True
current = commits.get(current)
return False
print(is_ancestor(commits, "c1", "c3")) # True -- c1 is an ancestor of c3
print(is_ancestor(commits, "c3", "c1")) # False -- c3 came AFTER c1, not before
This is exactly what git merge --ff-only checks before allowing a
"fast-forward" merge — if the target branch is already an ancestor of
what you're merging in, Git can just move the pointer forward with no
actual merge commit needed at all.
Model commits as a dict `{commit_id: parent_id}` (the root commit has parent `None`). Write `commit_history(commits, start)`: return the list of commit IDs from `start` back to the root, following parent pointers.
Write `is_ancestor(commits, ancestor, descendant)`: return True if `ancestor` appears anywhere in `descendant`'s history (including itself).
In Git, what does a branch actually point to?
You can model a commit history as a parent-pointer graph, walk it to reconstruct history, and check ancestry relationships between commits.