Back to the tools

Development environment

Git

Git is a distributed version control system: every commit is a content addressed snapshot, while branches, merges and remotes stay cheap local operations.

3 min read

Git is a distributed version control system: every clone is a complete repository with the full history, so history and diffs work without a network.

A repository is a content addressed key value store: the key is a digest computed over the content plus a header. A blob is the content of a file, a tree lists a directory, a commit points at a tree root, at its parents, at the author identity and at the message. Identical content yields the same identifier.

A commit is a snapshot

  • Git stores a series of snapshots, not a list of changes: every commit points at the whole state of the project.
  • Because a commit contains the identifiers of its parents, history is a chain pointing backwards, and a commit’s content plus its ancestry determine its identifier.
  • A commit records two distinct roles, the author and the committer, and both are part of the commit object. Authorship is therefore a fact stored in the repository.
  • Every project of ours is a Git repository, and our deploys are gated on the commit author being the expected one. We treat authorship as verifiable evidence rather than a formality, while Vercel and similar platforms build their own deploys on the same history.

Branches, merges and conflicts

  • A branch is a file holding the identifier of the commit it points at, not a copy of the tree, so creating and deleting one is instant.
  • HEAD tells you which branch is current. Switching reverts the working tree to the target branch’s last commit, and when that cannot be done cleanly Git refuses to switch.
  • A merge joins two lines of history into a commit with two parents, so the joining is part of the history and needs no separate bookkeeping.
  • When changes touch the same region the merge stops and the file receives conflict markers; after resolving them git merge --continue concludes it, while git merge --abort tries to reconstruct the pre-merge state.
  • That reconstruction is not guaranteed with non-trivial uncommitted changes at the start, so merge with a clean working tree.

Remotes

  • A remote names a repository elsewhere and its location. Cloning creates origin, fetching updates remote-tracking branches, and pushing sends local commits.
  • The documented protocols are local, HTTP, SSH and the git scheme. The last uses no encryption and no authentication, so the documentation advises against it: an attacker in the middle can modify the clone.
  • Our own Linux server keeps the bare clone and is reached over SSH, and most of the day to day work happens in VS Code.

The index, or staging area

  • There are three states, modified, staged and committed, and three places: the working tree, the staging area or index, and the Git directory.
  • git add does not commit; it marks the current version of a file for the next snapshot, so a commit is exactly what you assembled on purpose. git diff compares the working tree with the index, git diff --staged the index with the last commit.

What to watch

  • Rewriting history: because a commit contains its parents’ identifiers, changing an old commit changes every identifier after it and forces everyone to re-synchronise.
  • Large files: Git stores every version whole and compresses binaries poorly, so repositories grow quickly and every clone downloads the whole history; such files belong behind an extension.
  • Authorship: the author name and email address are commit data, so a wrong value stays in the history.
  • Git erases little, but uncommitted work can be lost.

Further reading

At CyberElectro Git is the baseline: every project is a repository, and a deploy is gated on the commit author being the expected one.

Tags
  • version control
  • commit
  • branch
  • merge
  • deploy