Skip to content
andrej-dyckPublic

About

πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» A kata to practice crafting a clean Git history ✨

Topics

Resources

Contributing

Stars

8 stars

Watchers

1 watching

Forks

Latest commit

Β 

History

101 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Git Kata - Clean History

CIΒ  Β language-en Β language-de

Git Logo

A clean Git history is linear, coherent, and intentional; and therefore easier to read, navigate, and reason about.

A well-tended Git history tells the story of the project's logical evolution: which changes were made and, more importantly, why. It should help us understand the project, not how the team happened to work.

We should be able to look at the history and answer questions such as: What changed? Why? What decisions led to this change? How has the project evolved? When did a particular change or problem emerge (supported by Git bisect)?

Good commit messages are an important part of a clean Git history, but they are not enough on their own. To improve the readability of the project's history, we want atomic commits (small, coherent, and working) in a logical, linear sequence.

Working with Trunk-based Development or OneFlow helps us to maintain linear history:

Git trunk-based linear history

Or this, when working with merge PRs:

Git oneflow linear history

Compare those linear histories to one that a typical GitFlow produces; and that's only the develop branch with three developers committing work:

typical GitFlow history

But a linear history alone is not necessarily a clean history. The commits themselves should still be atomic, coherent, and intentional. And tending to the Git history requires focus and discipline.

Fortunately, Git allows us to revise the commit history later by rewriting the history. With these tools, we can "refactor" our history before we share it with others.

About this Git Kata

This kata focuses on creating a clean and linear Git history by using Git's history rewriting features.

This kata assumes that you already have basic Git knowledge; e.g., how to stage files, commit changes, push to origin, fetch and pull from remote, branching, and work with the Git log.

Doing this Git Kata

Each exercise is self-contained, even when they refer to previous exercise numbers; the task is described in the README.md file.

Each exercise is safe to experiment with, as a local-only repository is set up by an init.sh bash script and, if stuck, you can always re-run the init.sh again.

It is highly recommended to use a graphical (GUI) Git client for the exercises.

Each exercise ends in a clean working tree (i.e., git status shows no changes), a clean staging area (i.e., git diff --staged shows no changes), and a clean history (i.e., git log --oneline --graph --decorate --all shows the target history).

Each exercise is categorized by a number and a name; e.g., 101-local-amend-commit. The one-hundreds 1xx are exercises with just a local repository; i.e., no origin/remote repository. The two-hundreds 2xx are exercises with a local repository and a local origin/remote repository. The three-hundreds 3xx are advanced Git exercises.

Quick Start

  • Ensure the latest Git is installed
  • Clone this git-kata repository
    git clone --depth 1 https://github.com/andrej-dyck/git-kata.git && cd git-kata && rm -rf .git && rm -rf .github
  • Run ./init.sh and choose which exercise to initialize
  • The exercise will be initialized in <git-kata-root>/exercise
    • Note that by default each exercise uses this folder and overwrites any existing content
    • You can use Docker to run the scripts in isolation
  • Open the created/updated <git-kata-root>/exercise folder with your favorite Git client
  • Consult the README.md in that folder for the description of the task

Optional: Cleanup isn't required, but if you want to, just remove the exercise repository folder and its origin folder exercise-origin.

Isolated Execution with Docker

Use Dockerfile and Docker to run this kata's bash scripts isolated from your operating system.

Run the following command in <git-kata-root>, after cloning this repository:

docker build -t git-kata . && docker run --rm -it -v "./:/git-kata" git-kata

Within the Docker container, use ./init.sh to choose and initialize an exercise. And since /git-kata is mounted to your local folder, you can open the ./exercise folder with your local Git client.

Local Execution

  • On Windows with Git installed, use the Git Bash with sh init.sh to execute the script
  • On Linux / Mac OS X, you might need to make the init.sh executable with chmod +x init.sh

jq Dependency

jq is a lightweight and flexible command-line JSON processor. If you have jq (or jaq) installed and in your PATH, the init.sh script will automatically use it. Otherwise, the scripts will try to use the bundled jq binary (version jq-1.8.2). Or you can install it from here.

Recommended Way of Working with Git as a Team

Atomic Commits

An atomic commit represents a single, self-contained, and coherent unit of change that keeps the codebase in a working and testable state.

If you drop, revert, or extract (cherry-pick) this commit, it should only affect that specific change without breaking anything else.

Rule of thumb: if you cannot easily summarize the change in a single concise subject line, the commit is likely not atomic.

Good Commit Messages

Follow the core principles of good commit messages (adapted with lowercase imperative verbs).

The subject line is a concise summary of what the commit does starting with a lowercase imperative verb (e.g., add, introduce, draft, model, configure, fix).

  • Guideline: "If applied, this commit will ..."

The body (optional, separated by an empty line from the subject) explains why the change was made and the context behind it.

One Main Branch

Maintain one main branch for all environments, including production. It is recommended to protect this main branch from history rewrites.

Every commit on main must be continuously deployed; i.e., it must be releasable and production-ready.

Avoid long-lived branching schemes; e.g., GitFlow's develop, release, and support branches. Read also GitFlow considered harmful

Short-lived Branches

When using branches, keep them short-lived and integrate them into main as soon as possible; ideally within minutes or hours.

Rebase frequently onto main to stay in sync and prevent merge conflicts.

Enforce a Linear Commit History

Keep the commit history linear by using Git rebase. A linear history is easier to read, navigate, and reason about. It better represents the logical evolution of the project.

The idea that Git merge preserves the "true" history, and that this therefore has significant value is a straw-man argument. While merge commits accurately record when branches were integrated, they add noise and make it harder to follow the actual changes. This is especially true when merge conflict resolutions combine unrelated changes.

Keeping the history clean, focused, and linear improves team collaboration and makes maintenance easier with Git's rewriting tools. It also makes debugging with Git, e.g., bisecting, easier because each commit represents a clear step in the development.

The goal is to preserve a history of the project's logical evolution; not the history of branch integration.

Links and Resources

The Git version control system

Other Git Katas

Naming Conventions

Workflows

Merge & Rebase

Tutorials & Talks

Recommended Git Clients

Other Useful Tools

About

πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» A kata to practice crafting a clean Git history ✨

Topics

Resources

Contributing

Stars

8 stars

Watchers

1 watching

Forks

Contributors

Languages