Developing the Git Town source code

May 21, 2026 ยท View on GitHub

This page provides guidance for contributing to the Git Town codebase. For a comprehensive understanding of the architecture, refer to ARCHITECTURE.md.

Folders

This monorepo contains a number of codebases:

  1. internal and pkg: Git Town, a CLI application written in Go
  2. website: the Git Town website, an mdBook application
  3. features: end-to-end tests for Git Town
  4. installer: the Windows installer for Git Town
  5. tools: custom-made linters for the Git Town codebase
  • source code for tool

Setup

  1. install Go version 1.26
  2. install Make
    • Mac and Linux users have this out of the box
    • Windows users can install Make for Windows or run choco install make if Chocolatey is available.
  3. If you want to use the Git Town executable compiled from source, add ~/go/bin (or wherever go install puts executables on your machine) to your $PATH environment variable.
  4. run all CI tests locally: make test
  5. faster smoke test during development: make test-go
  6. install Git Town locally into ~/go/bin: make install
  7. To auto-format changes when committing, run make setup-githooks.

Dependencies

Add an external Go dependency:

  • run go get [dependency] inside the Git Town folder to register the dependency
  • use the new dependency somewhere in the code
  • run go mod vendor to vendor it
  • run go mod tidy to clean up

Update an external Go module:

go get <path>

Update all external Go modules:

make update

Unit tests

Run unit tests for packages containing changes:

make unit

Run all unit tests no matter what has changed:

make unit-all

Run all unit tests with race detection:

make unit-race

Run an individual unit test:

go test src/cmd/root_test.go
go test src/cmd/root_test.go -v -run TestIsAcceptableGitVersion

End-to-end tests

Git Town uses an extensive end-to-end test suite that models a wide variety of situations in which Git Town can be used. This helps ensure Git Town behaves correct in these situations and debug Git Town when it doesn't.

Run all E2E tests

Run the end-to-end tests during development (nicer output):

make cuke

Run the end-to-end tests as they run on CI:

make cukeall

Run a subset of E2E tests

Run all tests in the features/append folder or file:

go test -- features/append

To run individual Cucumber scenarios, add a @this flag to the scenario you want to run. Example:

@this
Scenario: my awesome scenario

Then run only the scenarios that have a @this tag:

make cukethis

Run all files named es.feature:

find . -name es.feature | xargs go test --

Stubbing out forges in E2E tests

Certain tests require that the Git remote points to an actual GitHub, Gitea, GitLab, Bitbucket, or Forgejo address. An example is using this value to determine the forge type. Using such a setting would cause git push operations in this test to also go to GitHub. We want to avoid this, because it's slow, brittle, and stresses the external forges.

To prevent this, keep the actual setting pointed to the local remote. Set an environment variable GIT_TOWN_REMOTE with the desired value of the development remote. Git Town will use that value for internal calculations, instead of what is configured in the repo.

Running on Windows

If Cucumber tests produce garbled output on Windows, try running them inside Git Bash. See this issue for details.

Pausing end-to-end tests

To pause an end-to-end test so that you have time to inspect the status of the Git repository created by the test, add the step And inspect the repo. The test runner will pause and print the path of the test workspace. You can cd into that path in a separate terminal window and inspect the repos there. The developer's repo is in the repo folder. The remote repo (that would normally be on GitHub) is in the origin folder.

Finding the right SHAs

Some end-to-end tests contain placeholders for commit SHAs. To find the correct placeholder, add the step And inspect the commits. This displays all SHAs in the repo, together with the placeholder to be used.

Possible placeholders are defined in here.

Inspecting variables

Inspect basic variables in a unit test:

fmt.Printf("%#v\n", variable)

Inspect more complex variables:

import "github.com/davecgh/go-spew/spew"

spew.Dump(variable)
  • or -
pretty.Ldiff(t, var1, var2)

Debug an E2E test on the CLI

To see the CLI output of the shell commands in a Cucumber test, as well as the Git commands that the Git Town test suite runs under the hood, add a tag @debug above the feature or scenario you want to debug:

@debug @this
Scenario: my awesome scenario

To see all Git commands that the test runner and the Git Town command execute, run the Git Town command with the --verbose option. As an example, if the step When I run "git-town append new" mysteriously fails, you could change it to When I run "git-town append new -v". Also add the tags @debug @this to see the CLI output on the console.

To get a quick glance of which status the repo is at any point in time, insert the step And display "<command_>" running whatever command you want to execute in the Git repo under test. Example: And display "git status".

To manually inspect the local and remote Git repositories used in an end-to-end test, insert the step And inspect the repo. This step will make Cucumber print the path of the workspace and wait until you hit ENTER.

Debug an E2E test in VSCode

Debug a Godog Cucumber feature in VSCode:

  • open main_test.go
  • change the path of the test to execute
  • set a breakpoint in your test code
  • run the debug a test configuration in the debugger

Locate a hanging end-to-end test

End-to-end tests sometimes hang. To find the hanging test, you can execute subsets of tests using go test -- features/<path> where path is either a subfolder or file inside the "features" folder.

Alternatively, open main_test.go, change Format to pretty and Concurrency to 1, and run the entire test suite. The detailed output will give you hints at which test fails.

Automatically update E2E test files

Our E2E tests act as golden files: they embody the expected output that all future test runs must match. When we intentionally change Git Town's behavior, we also need to update possibly hundreds of E2E tests.

To streamline this, run:

make cuke-update

When running in this mode, and an E2E test fails, the test runner updates the .feature file, rather than failing the test. You must then inspect the test files to verify whether the updated tests are correct.

If needed, run make fix to format the recorded Gherkin code.

Configure the Cucumber IDE extension

To configure the official Cucumber extension add this to your settings.json file:

"cucumber.features": [
  "features/**/*.feature",
],
"cucumber.glue": [
  "internal/test/cucumber/steps.go"
]

This enables useful functionality:

  • auto-complete steps in .feature files
  • go to definition on a step in a feature file to see the code that runs when this step executes

Run linters

Quick and efficient linter during development:

make lint

Run all linters reliably like on CI:

make lint-all

If the linter complains about formatting issues, run the formatters:

make fix

The Git Town codebase relies heavily on automation like linters to find as many code smells as possible. If a linter creates false positive warnings, it is okay to disable it for that line.

Debug the dialogs

Run git town debug to see the commands to manually test Git Town's dialogs.

Learn about the code and test architecture

See file ARCHITECTURE.md.

Website

The source code for the website is in the website folder. This folder contains its own Makefile for activities related to working on the website.

To work on the website, cd into the website folder and run make serve to start a local development server. The production site auto-updates on changes to the main branch. The site hoster is Netlify. Netlify configuration is in netlify.toml.