High-level CLI for Git
Git Town solves the problem that using the Git CLI correctly is cumbersome and repetitive, and therefore many developers don't use Git to its full potential.
Git isn't just a version control system; it's a flexible framework for creating various version control workflows. This flexibility means that most of us end up using ad-hoc workflows, either in our heads or through custom Bash scripts tailored to our needs. These manual workflows often lack proper specifications and don't handle errors and edge cases well.
Git Town is a reusable implementation of Git workflows for common usage scenarios like contributing to a centralized code repository on platforms like GitHub, GitLab, or Gitea. Think of Git Town as your Bash scripts for Git, but fully engineered with rock-solid support for many use cases, edge cases, and error conditions.
With Git Town you can keep using Git the way you do now, but with extra commands to create various branch types, keep them in sync, compress, review, and ship them efficiently.
Git Town is compatible with most common branching models like GitHub Flow, Git Flow, GitLab Flow, trunk-based development and even committing straight into the main branch. Git Town has special support for mono-repos and stacked changes. See also this external review.
What our users say
#Gittown you sexy beast! @GitTown
— Elegant Development (@elegant_dev)
Amazing @GitTown rocks! 🚀 https://t.co/S5ouIBx2fe
— Jose Quintana (@joseluis_q)
All doable w/ just git, but git-town is a simpler mental-model and way less keystrokes
— Ryan Nystrom (@_ryannystrom)
I've been using git-town along git-flow for a little while and it's been really nice https://t.co/E5nwHuXP1C
— dan reeves 🏳️🌈 (@dnrvs)
Wow @GitTown looks great, type less git commands for common branch/pr workflows. Similar to @github’s “hub” but looks more powerful, and it logs the fit commands it runs under the hood. pic.twitter.com/PxGs76TS5I
— Gregor (@gr2m)
Supercharge your workflow with Git by relying on this surprisingly powerful and quite useful plugin that provides you with a series of extra Git commands.
Softpedia article about Git Town
Q & A
Does Git Town enforce any specific conventions for branches or commits?
No, Git Town doesn’t impose any rules for branch or commit naming. It works with a wide range of Git branching strategies and workflows. If you find it doesn't mesh with your specific setup, reach out to us.
Which Git branching models does Git Town support?
Git Town is flexible enough to support the most popular branching models like GitHub Flow, Git Flow, GitLab Flow, trunk-based development. It even works if you commit directly to the main branch!
How is Git Town different from the git-flow tool?
git-flow
is a specialized Git extension designed around providing opinionated
support for the Git Flow branching model. It doesn’t help with keeping your
branches or team in sync. Git Town, on the other hand, doesn’t mind which
branching model you use—it focuses on syncing your team's work and keeping
your repo tidy by cleaning up old branches. You can use Git Town alongside
git-flow
if that fits your workflow.
Is Git Town compatible with other Git tools?
Yes, we try to be good citizens in the Git ecosystem. If you run into compatibility issues, please let us know!
Does my whole team have to use Git Town?
No, you can get value from Git Town even if you're the only one using it. It simply automates the Git commands that you would (should) normally run.
Installation
Git Town ships as a single self-contained binary. It calls the Git executable that is already installed on your machine.
macOS
You can install Git Town on macOS via Homebrew:
brew install git-town
Installation via MacPorts is also supported:
sudo port install git-town
Windows
You can install Git Town on Windows using:
- Chocolatey:
choco install git-town
- Scoop:
scoop install git-town
- the Git Town Windows installer
If you use the Windows Subsystem for Linux, please install wsl-open to allow the commands git town repo and git town propose to open a browser window for you.
Linux
On Debian-based systems,
download the .deb
file
matching your CPU architecture and run:
sudo apt-get install git-town_linux_intel_64.deb
On RedHat-based systems
download the .rpm
file
matching your CPU architecture and run
rpm -i git-town_linux_intel_64.rpm
On Arch Linux, install the
git-town package from the AUR. Or
download the matching .pkg.tar.zst
file for your architecture and run:
sudo pacman -U <filename>
You can also install Git Town on Linux via Homebrew for Linux:
brew install git-town
You can also install Git Town manually or compile from source.
BSD
You can install Git Town on BSD via freshports or by downloading the matching binaries from the GitHub release.
manual installation
curl https://www.git-town.com/install.sh | sh
For a fully custom installation,
download the archive matching
your CPU architecture, extract it, and move the git-town
executable into a
directory listed in your $PATH
, for example /usr/local/bin
.
compile from source
If you have the Go compiler installed, you can compile the latest version of Git Town from source by running:
go get github.com/git-town/git-town
New releases
Subscribe to the Git Town release feed to get notifications about new releases.
Uninstall
To remove Git Town from your system:
- Remove the Git Town configuration from your repositories: in each repo, run
git town config remove
- If your operating system or package manager provides an uninstaller for Git Town, run it. If you installed Git Town manually, delete the binary.
Configuration
If your repository already contains a .git-branches.toml
file, you are good to
go. If not or something doesn't work, run Git Town's setup assistant. It walks
you through every configuration option and gives you a chance to adjust it.
git town config setup
More information about the configuration file including how to create one manually is here.
Access Tokens
API access multiplies Git Town's utility:
- if the parent of a branch is not known, Git Town can look for a pull requests of this branch and uses their parent branch
- updates affected pull requests when you prepend, rename, remove branches or change their parent
- click the "merge" button on a pull request from your CLI
Configuring API access takes only one minute. Here is how you do it:
- GitHub: access token
- GitLab: access token
- Bitbucket: username and app password
- gitea: access token
Integration
This page describes how to integrate Git Town into other applications.
Git Town GitHub Action
The Git Town GitHub Action appends a visual breadcrumb of the currently reviewed branch and its location in the its branch stack.
Shell autocompletion
To have your shell auto-complete Git Town commands, set up shell autocompletion
Shell prompt
You can display a reminder for running git town continue
to finish a pending
Git Town command in your shell prompt. Here is how this could look like:
Bash
To add the above status indicator to your shell prompt in Bash, add something
like this to your .bashrc
file:
function git_town_status {
local pending_gittown_command=$(git town status --pending)
if [ -n "$pending_gittown_command" ]; then
echo -e " \033[30;43m $pending_gittown_command \033[0m "
fi
}
PS1='$(git_town_status)> '
Zsh
For zsh, customize the ~/.zshrc
file:
git_town_status() {
local git_status
git_status=$(git town status --pending)
if [[ -n "$git_status" ]]; then
echo "%K{yellow}%F{black} $git_status %f%k "
fi
}
setopt PROMPT_SUBST
PROMPT='$(git_town_status)> '
Fish
To add this example to your
Fish shell prompt,
edit file ~/.config/fish/config.fish
and overwrite the fish_prompt
function:
function fish_prompt
set -f pending_gittown_command (git-town status --pending)
if [ -n "$pending_gittown_command" ]
set -f yellow_pending_gittown_command (set_color -b yellow)(set_color black)(echo " $pending_gittown_command ")(set_color normal)' '
else
set -f yellow_pending_gittown_command ''
end
printf '%s> ' $yellow_pending_gittown_command
end
Lazygit
Example lazygit configuration file to integrate Git Town:
customCommands:
- key: 'Y'
context: 'global'
description: 'Git-Town sYnc'
command: 'git-town sync --all'
stream: true
loadingText: 'Syncing'
- key: 'U'
context: 'global'
description: 'Git-Town Undo (undo the last git-town command)'
command: 'git-town undo'
prompts:
- type: 'confirm'
title: 'Undo Last Command'
body: 'Are you sure you want to Undo the last git-town command?'
stream: true
loadingText: 'Undoing Git-Town Command'
- key: '!'
context: 'global'
description: 'Git-Town Repo (opens the repo link)'
command: 'git-town repo'
stream: true
loadingText: 'Opening Repo Link'
- key: 'a'
context: 'localBranches'
description: "Git-Town Append"
prompts:
- type: 'input'
title: "Enter name of new child branch. Branches off of '{{.CheckedOutBranch.Name}}'"
key: 'BranchName'
command: 'git-town append {{.Form.BranchName}}'
stream: true
loadingText: 'Appending'
- key: 'h'
context: 'localBranches'
description: 'Git-Town Hack (creates a new branch)'
prompts:
- type: 'input'
title: "Enter name of new branch. Branches off of 'Main'"
key: 'BranchName'
command: 'git-town hack {{.Form.BranchName}}'
stream: true
loadingText: 'Hacking'
- key: 'K'
context: 'localBranches'
description: 'Git-Town Delete (deletes the current feature branch and sYnc)'
command: 'git-town delete'
prompts:
- type: 'confirm'
title: 'Delete current feature branch'
body: 'Are you sure you want to delete the current feature branch?'
stream: true
loadingText: 'Deleting Feature Branch'
- key: 'p'
context: 'localBranches'
description: 'Git-Town Propose (creates a pull request)'
command: 'git-town propose'
stream: true
loadingText: 'Creating pull request'
- key: 'P'
context: 'localBranches'
description: "Git-Town Prepend (creates a branch between the curent branch and its parent)"
prompts:
- type: 'input'
title: "Enter name of the for child branch between '{{.CheckedOutBranch.Name}}' and its parent"
key: 'BranchName'
command: 'git-town prepend {{.Form.BranchName}}'
stream: true
loadingText: 'Prepending'
- key: 'S'
context: 'localBranches'
description: 'Git-Town Skip (skip branch with merge conflicts when syncing)'
command: 'git-town skip'
stream: true
loadingText: 'Skiping'
- key: 'G'
context: 'files'
description: 'Git-Town GO aka:continue (continue after resolving merge conflicts)'
command: 'git-town continue'
stream: true
loadingText: 'Continuing'
Commands
Run git town
for an overview of all Git Town commands and
git town help <command>
for help with individual commands. You can call each
Git Town command like git town <command>
. This user manual displays the
commands in the shorter form available after enabling aliases through the
setup assistant.
Basic workflow
Commands to create, work on, and ship features.
- git town hack - create a new feature branch
- git town sync - update the current branch with all ongoing changes
- git town switch - switch between branches visually
- git town propose - propose to ship a branch
- git town ship - deliver a completed feature branch
Additional workflow commands
Commands to deal with edge cases.
- git town delete - delete a feature branch
- git town rename - rename a branch
- git town repo - view the Git repository in the browser
Stacked changes
Commands to develop, review, and ship parts of a larger feature as multiple connected branches.
- git town append - create a new feature branch as a child of the current branch
- git town prepend - create a new feature branch between the current branch and its parent
- git town set-parent - change the parent of a feature branch
- git town diff-parent - display the changes made in a branch
Dealing with errors
Commands to deal with merge conflicts.
- git town continue - continue after you resolved the merge conflict
- git town skip - when syncing all branches, ignore the current branch and continue with the next one
- git town status - display available commands
- git town undo - undo the last completed Git Town command
Git Town installation
Commands that help install Git Town on your computer.
- git town aliases - add or remove shorter aliases for Git Town commands
- git town completion - generate completion scripts for Bash, zsh, fish & PowerShell.
Git Town configuration
Commands that help adapt Git Town's behavior to your preferences.
- git town config - display or update your Git Town configuration
- git town config setup - setup assistant
- git town offline - enable/disable offline mode
Typical development workflow
The following four Git Town commands automate the typical development workflow:
- You start hacking by running git town hack to create a feature branch.
- While coding you run git town sync to keep your feature branch up to date with commits that you or other developers make into the main branch. This prevents your feature branch from deviating too much from the main code line.
- If your team does pull requests, you can run git town propose to create a new pull request.
- git town ship delivers the feature branch.
git town hack
git town hack [--prototype] [branch-name...]
The hack command ("let's start hacking") creates a new feature branch with the given name off the main branch and brings all uncommitted changes over to it.
If your Git workspace is clean (no uncommitted changes), it also
syncs the main branch to ensure you develop on top of the current
state of the repository. If the workspace is not clean (contains uncommitted
changes), git town hack
does not perform this sync to let you commit your open
changes.
Positional arguments
When given a non-existing branch name, git town hack
creates a new feature
branch with the main branch as its parent.
When given an existing contribution, observed, parked, or prototype branch,
git town hack
converts that branch to a feature branch.
When given no arguments, git town hack
converts the current contribution,
observed, parked, or prototype branch into a feature branch.
--detached / -d
The --detached
aka -d
flag does not pull updates from the main or perennial
branch. This allows you to build out your branch stack and decide when to pull
in changes from other developers.
--dry-run
Use the --dry-run
flag to test-drive this command. It prints the Git commands
that would be run but doesn't execute them.
--prototype / -p
Adding the --prototype
aka -p
switch creates a
prototype branch).
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
upstream remote
If the repository contains a remote called upstream
, it also syncs the main
branch with its upstream counterpart. You can control this behavior with the
sync-upstream flag.
configuration
If push-new-branches is set,
git town hack
creates a remote tracking branch for the new feature branch.
This behavior is disabled by default to make git town hack
run fast. The first
run of git town sync
will create the remote tracking branch.
If the configuration setting
create-prototype-branches is set,
git town hack
always creates a
prototype branch.
git town sync
git town sync [--all] [--detached] [--dry-run] [--no-push] [--stack] [--verbose]
The sync command ("synchronize this branch") updates your local Git workspace with what happened in the rest of the repository.
Merge conflicts are not fun and can break code. Minimize them by syncing your
branches frequently. Git town knows how to sync many different types of
branches. When properly configured, git town sync --all
can synchronize all
your local branches the right way without losing changes, even in edge cases.
You can (and should) sync all branches many times per day without thinking about it, even in the middle of ongoing work. If a sync goes wrong, you can safely go back to the exact state you repo was in before the sync by running git town undo.
- pulls and pushes updates from all parent branches and the tracking branch
- deletes branches whose tracking branch was deleted at the remote if they contain no unshipped changes
- safely stashes away uncommitted changes and restores them when done
- does not pull, push, or merge depending on the configured branch type
If the parent branch is not known, Git Town looks for a pull/merge request for this branch and uses its parent branch. Otherwise it prompts you for the parent.
--all / -a
By default this command syncs only the current branch. The --all
aka -a
parameter makes Git Town sync all local branches.
--detached / -d
The --detached
aka -d
flag does not pull updates from the main or perennial
branch at the root of your branch hierarchy. This allows you to keep your
branches in sync with each other and decide when to pull in changes from other
developers.
--dry-run
The --dry-run
flag allows to test-drive this command. It prints the Git
commands that would be run but doesn't execute them.
--no-push
The --no-push
argument disables all pushes of local commits to their tracking
branch.
--stack / -s
The --stack
aka -s
parameter makes Git Town sync all branches in the stack
that the current branch belongs to.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
Configuration
sync-perennial-strategy configures whether perennial branches merge their tracking branch or rebase against it.
sync-feature-strategy configures whether feature branches merge their parent and tracking branches or rebase against them.
If the repository contains a Git remote called upstream
and the
sync-upstream setting is enabled, Git Town
also pulls new commits from the upstream's main branch.
sync-tags configures whether Git Town syncs Git
tags with the origin
remote.
Why does git-sync sometimes update a local branch whose tracking branch was deleted before deleting it?
If a remote branch was deleted at the remote, it is considered obsolete and "git town sync" will remove its local counterpart. To guarantee that this doesn't lose unshipped changes in the local branch, "git town sync" needs to prove that the branch to be deleted contains no unshipped changes.
The easiest way to prove that is when the local branch was in sync with its
tracking branch before Git Town runs git fetch
. This is another reason to run
git town sync
regularly.
If a local shipped branch is not in sync with its tracking branch on your machine, Git Town must check for unshipped local changes by diffing the branch to delete against its parent branch. Only branches with an empty diff can be deleted safely. For this to work, Git Town needs to sync the branch first, even if it's going to be deleted right afterwards.
git town switch
git town switch [--merge] [--all] [--type] [branch-name-regex...]
The switch command displays the branch hierarchy on your machine and allows switching the current Git workspace to another local Git branch using VIM motion commands. It can filter the list of branches to particular branch types and regular expression matches.
git town switch
reminds you about uncommitted changes in your workspace in
case you forgot to commit them to the current branch.
positional arguments
git town switch
interprets all positional arguments as regular expressions.
When receiving regular expressions from the user, it displays only the branches
that match at least one of the regular expressions.
As an example, assuming all your branches start with me-
, you can use this
command to switch to one of them:
git town switch me-
To display all branches starting with me-
and the main branch:
git town switch me- main
--all / -a
The --all
aka -a
flag also displays both local and remote branches.
--display-types / -d
When enabled, this command displays the types for all branches except the main branch and feature branches.
--merge / -m
The --merge
aka -m
flag has the same effect as the
git checkout -m
flag.
--type / -t
The --type
aka -t
flag reduces the list of branches to those that have the
given type(s). For example, to display only observed branches:
Switch to one of your observed branches:
git town switch --type=observed
Branch types can be shortened:
git town switch -t o
This can be further compacted to:
git town switch -to
You can provide multiple branch types separated by ,
, +
, &
, or |
, like
this:
git town switch --type=observed+contribution
This can be shortened to:
git town switch -to+c
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town propose
git town propose [--title <text>] [--body <text>] [--body-file <-|filename>]
The propose command helps create a new pull request (also known as merge request) for the current feature branch. It opens your code hosting platform's website to create a new proposal in your browser and pre-populates information like branch and source/target repository. It also syncs the branch to merge before opening the pull request.
You can create pull requests for repositories hosted on:
--body / -b
When called with the --body
aka -b
flag, it pre-populates the body of the
pull request with the given text.
--body-file / -f
When called with the --body-file
aka -f
flag, it pre-populates the body of
the pull request with the content of the given file. The filename -
reads the
body text from STDIN.
--detached / -d
The --detached
aka -d
flag does not pull updates from the main or perennial
branch. This allows you to build out your branch stack and decide when to pull
in changes from other developers.
--dry-run
Use the --dry-run
flag to test-drive this command. It prints the Git commands
that would be run but doesn't execute them.
--title / -t
When called with the --title <title>
aka -t
flag, the propose command
pre-populate the title of the pull request to the given text.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
Configuration
You can configure the hosting platform type with the hosting-platform setting.
When using SSH identities, this command uses the hostname in the hosting-origin-hostname setting.
Error handling
Sometimes Git Town commands encounter problems that require the human user to make a decision. When this happens, the command stops and prints an error message. When you have resolved the issue, you can either:
- run
git town continue
to continue executing the interrupted command, starting with the operation that failed, - run
git town undo
to undo the Git Town command and go back to where you started.
You can also run git town undo
after a Git Town command finished to undo the
changes it made. Run git town status
to see the status of the running Git Town
command and which Git Town commands you can run to continue or undo it.
git town continue
When a Git Town command encounters a problem that it cannot resolve, for example a merge conflict, it stops to give the user an opportunity to resolve the issue. Once you have resolved the issue, run the continue command to tell Git Town to continue executing the failed command. Git Town will retry the failed operation and execute all remaining operations of the original command.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town skip
The skip command allows to skip a Git branch with merge conflicts when syncing all feature branches.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town status
git town status [--pending]
The status command indicates whether Git Town has encountered a merge conflict and which commands you can run to continue, skip, or undo it.
--pending / -p
The --pending
aka -p
argument causes this command to output only the name of
the pending Git Town command if one exists. This allows displaying a reminder to
run git town continue
into your shell prompt when you encountered a merge
conflict earlier. See Integration on how to
set this up.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
subcommands
The reset subcommand prints the parent branch of the current or given
git town status reset
The status reset command deletes the persisted runstate. This is only needed if the runstate is corrupted and causes Git Town to crash.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town undo
The undo command reverts the last fully executed Git Town command. It performs the opposite activities that the last command did and leaves your repository in the state it was before you ran the problematic command.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
Stacked Changes
Stacked changes implement and review a complex change as a series of smaller feature branches that build on top of each other. Benefits of stacked changes are:
- developer and reviewer maintain momentum and block less on each other
- breaking up the problem of developing/reviewing a complex problem into developing/reviewing many smaller problems
- minimize merge conflicts by shipping parts of a complex change that are already approved separately from parts still under review
The single responsibility principle applies to feature branches the same way it applies to functions, classes, and methods. Feature branches should also perform only one change. Implementing, refactoring, reviewing, and resolving merge conflicts on such single-responsibility branches is easier than with branches that combine unrelated changes.
Git Town provides wide reaching support for stacked changes. When using stacked changes, try to fast-forward your feature branches into the main branch to avoid empty merge conflicts when syncing the stack later. On GitLab that's straightforward. GitHub does not provide a fast-forward merge option out of the box but you can achieve it with the fast-forward ship strategy together with the compress or rebase sync strategy. The Git Town GitHub Action adds a visual description of which branch of the stack the pull request is for.
Example
Let's say we want to add a new feature to an existing codebase. Before we can do that cleanly, we need to get the code base ready:
- Make the architecture more flexible so that we can add the new feature in a clean way.
- Clean up some technical drift: improve the names of variables and functions.
- Build the feature on top of this modernized codebase
Implementing all these changes in a single feature branch is risky. Some changes like the refactor in (1) touch a lot of files that other people might change as well. We want to review and merge them as fast as possible to minimize merge conflicts. Other changes like building the actual feature in (3) will take a while to build. We should therefore make both changes in separate branches. At the same time, the feature (3) depends on the changes in (1) and (2) and drives the changes in (2). We want to develop these changes together. The solution is a stack of feature branches.
Branch 1: refactor
The first feature branch contains the refactor. We create a feature branch named
1-refactor
off the main branch to contain it.
git town hack 1-refactor
git town hack creates a new feature branch off the main branch. We perform the refactor and commit it.
Branch 2: rename foo
With the refactored architecture in place, we can update the names of some
variables, functions, and files whose role has changed as the code base has
evolved. Since these changes require the refactor, we perform them on top of
branch 1-refactor
:
git town append 2-rename-foo
git town append creates a new feature branch on top of the
currently checked out branch (which is 1-refactor
). We now have this lineage:
main
\
1-refactor
\
* 2-rename-foo
Branch 2-rename-foo
builds on top of 1-refactor
and thereby contains all the
changes made there. We commit the changes that rename the foo
variable.
Because we used git town append
to create the new branch, Git Town knows about
the lineage and creates the proposal (aka pull request) for branch
2-rename-foo
against branch 1-refactor
. This way, the proposal for branch
2-rename-foo
shows only the changes made in that branch (renaming the
variable) and not the refactor made in branch 1.
Branch 3: rename bar
This is a different change from renaming foo
and has different reviewers.
Let's perform it in a different branch. Some of these changes might happen on
the same lines on which we also renamed foo
earlier. We don't want to deal
with merge conflicts coming from that. So let's make this change on top of the
change we made in step 2:
git town append 3-rename-bar
The lineage is now:
main
\
1-refactor
\
2-rename-foo
\
* 3-rename-bar
Extend the refactoring
While renaming bar
, we discover another improvement for the architecture.
Let's add it to the refactoring branch.
git checkout 1-refactor
# make the changes and commit them
git checkout 3-rename-bar
Back on branch 3-rename-bar
, the additional refactor we just added isn't
visible because the commit for it exists only in branch 1-refactor
right now.
Let's propagate these changes through the entire branch chain so that they
become visible in branches 2 and 3 as well:
git town sync
Because we created the branches with git town append
, Git Town knows about the
branch lineage and git town sync can update all branches in
the right order. It updates the main
branch, merges main
into branch 1. Then
it merges branch 1 into branch 2 and branch 2 into branch 3.
Shipping the refactor
We got the approval for the refactor from step 1. Let’s ship it!
git town ship 1-refactor
You have to use the git town ship command here because it
updates the lineage that Git Town keeps track of. With branch 1-refactor
shipped, our lineage now looks like this:
main
\
2-rename-foo
\
* 3-rename-bar
If you ship feature branches via the code hosting API or web UI, run
git town sync --all
, or git town sync
on the youngest child branch, to
update the lineage.
Synchronizing our work with the rest of the world
We have been at it for a while. Other developers on the team have made changes
to the codebase as well. We don't want our branches to deviate too much from the
main
branch since that leads to more severe merge conflicts later. Let's get
all our branches in sync with the rest of the world!
git town sync --all
This pulls updates for the main
branch, then merges it into 2-rename-foo
,
then 2-rename-foo
into 3-rename-bar
.
Branch 4: building the new feature
We can now add the new feature on top of the code base we prepared:
git town append 4-add-feature
Let’s stop here and review what we have done.
- Each change happens in its own feature branch.
- Our feature branches build on top of each other and see changes in their parent branches.
- We review and ship each feature branch in the chain in isolation.
git town hack
creates a feature branch as a child of the main branch.git town append
creates a feature branch as a child of the current feature branch.git town sync
keeps a feature branch chain up to date with the rest of the worldgit town ship
ships the oldest feature branch in a branch chain.
Single-responsibility branches are easier to reason about and faster to implement, debug, review, and ship than branches performing multiple changes. They encounter fewer and smaller merge conflicts which are easier to resolve than merge conflicts on branches that implement many different changes. You can review and ship parts of your complex change before the entire change is finished. You can still make different changes in parallel, just commit them to the correct branch.
Best Practices
One change per branch
When you have an idea that is different from what you currently work on, resist the urge to code it in the current feature branch. Implement it in its own feature, parent, or child branch.
If you can't create a new branch right now, write the idea down and implement it later.
Keep the stack in sync
Branch stacks are more susceptible to phantom merge conflicts than regular
branches. Don't forget to populate changes across all branches in your stack by
running git town sync --stack
or git town sync --all
.
Avoid unnecessary stacking
To reduce merge conflicts, feature branches should not diverge too much from the main development branch. Stacking multiple changes on top of each other amplifies this divergence. Overly "tall" stacks are therefore an anti-pattern to avoid. It's often better to work in independent top-level feature branches by default, and only stack branches if the changes they contain really depend on each other. This way you can get your changes reviewed and shipped concurrently and in any order, i.e. faster and with fewer merge conflicts.
Organize branch chains in the order you want to ship
You always have to ship the oldest branch first. You can use git town prepend to insert a feature branch as a parent of the current feature branch or set parent to change the order of branches.
Avoid phantom merge conflicts
To eliminate phantom merge conflicts after shipping the oldest branch in a
stacked change, ship using a
fast-forward merge.
This guarantees that the new commit(s) on the main branch are the exact same
commit(s) from the shipped feature branch. This helps Git recognize those
commits at the next git town sync
operation and omit unnecessary merge and
rebase operations.
GitLab provides fast-forward merges out of the box. GitHub doesn't provide this out-of-the-box, but allows a workaround that you can utilize by using git town ship with the fast-forward shipping strategy. This problem is documented by GitHub.
You might want to compress the feature branch to have only one new commit on the main branch.
git town append
git town append [--prototype] <branch-name>
The append command creates a new feature branch with the given name as a direct child of the current branch and brings over all uncommitted changes to the new branch.
When running without uncommitted changes in your workspace, it also
syncs the current branch to ensure your work in the new branch
happens on top of the current state of the repository. If the workspace contains
uncommitted changes, git town append
does not perform this sync to let you
commit your open changes first and then sync manually.
Positional argument
When given a non-existing branch name, git town append
creates a new feature
branch with the main branch as its parent.
Consider this branch setup:
main
\
* feature-1
We are on the feature-1
branch. After running git town append feature-2
, our
repository will have this branch setup:
main
\
feature-1
\
* feature-2
--detached / -d
The --detached
aka -d
flag does not pull updates from the main or perennial
branch. This allows you to build out your branch stack and decide when to pull
in changes from other developers.
--dry-run
Use the --dry-run
flag to test-drive this command. It prints the Git commands
that would be run but doesn't execute them.
--prototype / -p
Adding the --prototype
aka -p
switch creates a
prototype branch).
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
Configuration
If push-new-branches is set,
git town append
also creates the tracking branch for the new feature branch.
This behavior is disabled by default to make git town append
run fast and save
CI runs. The first run of git town sync
will create the remote tracking
branch.
If the configuration setting
create-prototype-branches is set,
git town append
always creates a
prototype branch.
git town merge
git town merge
The merge command merges the current branch with its parent branch. Both branches must be feature branches.
When using the compress sync strategy, the merged branch will contain two separate commits: one per merged branch. This makes it easy to verify that both branches were merged as expected. To consolidate these commits, run git town sync.
--dry-run
Use the --dry-run
flag to test-drive this command. It prints the Git commands
that would be run but doesn't execute them.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town prepend
git town prepend [--prototype] <branch-name>
The prepend command creates a new feature branch as the parent of the current branch. It does that by inserting the new feature branch between the current feature branch and it's existing parent.
If your Git workspace is clean (no uncommitted changes), it also
syncs the current feature branch to ensure you work on top of the
current state of the repository. If the workspace is not clean (contains
uncommitted changes), git town prepend
does not perform this sync to let you
commit your open changes.
If the branch you call this command from has a proposal, this command updates it. To do so, it pushes the new branch.
Consider this branch setup:
main
\
* feature-2
We are on the feature-2
branch. After running git town prepend feature-1
,
our repository has this branch setup:
main
\
* feature-1
\
feature-2
--detached / -d
The --detached
aka -d
flag does not pull updates from the main or perennial
branch. This allows you to build out your branch stack and decide when to pull
in changes from other developers.
--dry-run
Use the --dry-run
flag to test-drive this command. It prints the Git commands
that would be run but doesn't execute them.
--prototype / -p
Adding the --prototype
aka -p
switch creates a
prototype branch).
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
Configuration
If push-new-branches is set,
git town hack
creates a remote tracking branch for the new feature branch.
This behavior is disabled by default to make git town hack
run fast. The first
run of git town sync
will create the remote tracking branch.
If the configuration setting
create-prototype-branches is set,
git town prepend
always creates a
prototype branch.
git town set-parent
git town set-parent
The set-parent command changes the parent branch for the current branch and updates associated proposals. It prompts the user for the new parent branch.
This command does not update commits, i.e. the new child branches don't see the changes made by their new parent branches. To update the commits, run git town sync.
To demonstrate how git town set-parent works
, let's say we have this branch
hierarchy:
main
\
feature-1
\
* feature-2
feature-1
is a child branch of main
, and feature-2
is a child branch of
feature-1
. Assuming we are on feature-2
, we can make feature-2
a child of
main
by running git town set-parent
and selecting main
in the dialog. We
end up with this branch hierarchy:
main
\
feature-1
\
* feature-2
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town diff-parent
The diff-parent command displays the changes made on a feature branch, i.e. the diff between the current branch and its parent branch.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
Branch Types
Git Town supports many different types of Git branches. When properly
configured, you can run git town sync
or git town sync --all
at any time and
each of your local branches will get synced in the specific ways it's supposed
to get synced or not synced.
Feature branches
Feature branches are the branches on which you typically make changes. They are typically cut from the main branch and get merged back into it. You can also cut feature branches from any other branch type if needed. Feature branches sync with their parent and tracking branch.
Main branch
The main branch is a perennial branch from which feature branches get cut by default. The main branch contains the latest development version of your codebase.
Perennial branches
Perennial branches are long-lived branches. They have no parent and are never
shipped. Typical perennial branches are main
, master
, development
,
production
, staging
, etc. Perennial branches often correspond with a cloud
environment of the same name.
Contribution branches
Contribution branches are for people who contribute commits to somebody else's
feature branch. You cannot propose or
ship contribution branches because those are
responsibilities of the person owning the branch you contribute to. For the same
reason git town sync
does not pull updates from the parent branch of a
contribution branch and always
rebases your local commits.
Syncing removes contribution branches from your machine as soon as their
tracking branch is gone, even if you have unpushed local commits.
Deleting a contribution branch only deletes your local
copy and not the tracking branch.
You can make any feature branch a contribution branch by running git town contribute on it. Convert a contribution branch back to a feature branch by running git town hack on it. You can also define a contribution-regex in your Git configuration or the config file.
Observed branches
Observed branches are for people who want to observe the work of somebody else
without contributing commits to it. Similar to contribution branches, you cannot
propose or ship observed branches,
delete only deletes your local copy and not the tracking
branch, git town sync
always uses the
rebase sync-feature-strategy and
will remove a local observed branch as soon as its tracking branch is gone, even
if there are unmerged local commits.
Unlike with contributing branches, git town sync
does not push your local
commits made to an observed branch to its tracking branch.
You can make any feature branch an observed branch by running git town observe on it. Convert an observed branch back to a feature branch by running git town hack on it. You can also define an observed-regex in your Git configuration or the config file.
Parked Branches
Parked branches don't get synced at all unless you run git town sync
directly
on a parked branch. You might want to park a branch if you
- want to intentionally keep the branch at an older state
- don't want to deal with merge conflicts on this branch right now
- reduce load on your CI server by syncing only your actively developed local branches
You can park any feature branch by running git town park on
it. Unpark a parked branch by running git town hack
on it.
Prototype Branches
A prototype branch is a local-only feature branch that incorporates updates from its parent branch but is not pushed to the remote repository. Prototype branches are useful when:
- the branch contains sensitive information, such as secrets, or potentially problematic code or data that could trigger alerts
- the developer prefers to keep their work private from the rest of the team during the initial stages of development
- you want to reduce CI pressure in the early phases of feature development when there isn't anything to test
Syncing prototype branches follows the sync-prototype-strategy or - if this setting isn't present - the sync-feature-strategy. This allows you to rebase your commits while working locally, and avoid rebasing when your commits become visible to others.
When you propose a prototype branch, it loses its
prototype status since it now has an official tracking branch that other people
look at. In this situation you can keep syncing without pushes by using the
--no-push
sync option.
You can compress and ship prototype branches as usual. Parking and unparking a prototype branch maintains its prototype status. When you change a prototype branch to an observed or contribution branch it loses its prototype status.
To designate any feature branch as a prototype branch, execute git town prototype on it. To convert a prototype branch to a feature branch, use git town hack.
Configuring branch types
You can set the types of indivdiual branches with these commands:
These preferences allow you to configure the types of larger groups of branches:
- create-prototype-branches,
- default-branch-type,
- feature-regex, and
- perennial-regex preferences.
git town contribute
git town contribute [branch-name...]
The contribute command makes some of your branches contribution branches.
When called without arguments, it makes the current branch a contribution branch.
To convert a contribution branch back into a feature branch, use the hack command.
To make the current branch a contribution branch:
git town contribute
Positional arguments
When called with positional arguments, this commands makes the branches with the given names contribution branches.
To make branches "alpha" and "beta" contribution branches:
git town contribute alpha beta
Check out a remote branch (that exists at origin but not on your local machine) and make it a contribution branch:
git town contribute somebody-elses-branch
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town observe
git town observe [branch-name...]
The observe command makes some of your branches observed branches.
To convert an observed branch back into a feature branch, use the hack command.
Positional arguments
Observe the current branch:
git town observe
Observe branches "alpha" and "beta":
git town observe alpha beta
Check out a remote branch (that exists at origin but not on your local machine) and make it observed:
git town observe somebody-elses-branch
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town park
git town park [branch-name...]
The park command parks some of your branches.
To convert a parked branch back into a feature branch, use the hack command.
Positional arguments
Park the current branch:
git town park
Park branches "alpha" and "beta":
git town park alpha beta
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town prototype
git town prototype [branch-name...]
The prototype command marks some of your branches as prototype branches.
To convert a contribution branch back into a feature branch, use the hack command.
Positional arguments
Make the current branch a prototype branch:
git town prototype
Make branches "alpha" and "beta" prototype branches:
git town prototype alpha beta
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
Additional commands
These Git Town commands allow handling edge cases beyond of the basic development workflow outlined earlier.
- git town delete - delete a feature branch
- git town rename - rename a branch
- git town repo - view the Git repository in the browser
git town branch
The branch command is Git Town's equivalent of the git branch command. It displays the local branch hierarchy, and the types of all branches except for main and feature branches.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town compress
git town compress [--message <text>] [--stack]
The compress command squashes all commits on a branch into a single commit. Git Town compresses feature branches and parked branches if they are currently checked out. It doesn't compress perennial, observed, and contribution branches.
Branches must be in sync to compress them, so run git town sync
and resolve
merge conflicts before running this command.
Assuming you have a feature branch with these commits:
$ git log --pretty=format:'%s'
commit 1
commit 2
commit 3
Let's compress these three commits into a single commit:
git town compress
Now your branch has a single commit with the name of the first commit but containing the changes of all three commits that existed on the branch before:
$ git log --pretty=format:'%s'
commit 1
--dry-run
Use the --dry-run
flag to test-drive this command. It prints the Git commands
that would be run but doesn't execute them.
--message / -m
By default the now compressed commit uses the commit message of the first commit
in the branch. You can provide a custom commit message for the squashed commit
with the --message <message>
aka -m
flag, which works similar to the
-m flag for git commit
.
Assuming you have a feature branch with these commits:
$ git log --pretty=format:'%s'
commit 1
commit 2
commit 3
Let's compress these three commits into a single commit:
git town compress -m "compressed commit"
Now your branch has these commits:
$ git log --pretty=format:'%s'
compressed commit
The new compressed commit
now contains the changes from the old commit 1
,
commit 2
, and commit 3
.
--stack / -s
To compress all branches in a branch stack provide the
--stack
aka -s
switch.
If you want to compress your commits every time you sync, choose the compress sync strategy for the respective branch type.
Assuming you have a stacked change consisting of two feature branches. Each branch contains three commits.
main
\
branch-1
| * commit 1a
| * commit 1b
| * commit 1c
branch-2
* commit 2a
* commit 2b
* commit 2c
Let's compress the commits in all branches of this stack:
git town compress --stack
Now your stack contains these branches and commits:
main
\
branch-1
| * commit 1a
branch-2
* commit 2a
As usual, the new commit 1a
contains the changes made in branch 1
, i.e. the
changes from the old commit 1a
, commit 1b
, and commit 1c
. The new
commit 2a
contains the changes made in branch 2
, i.e. the changes from the
old commit 2a
, commit 2b
, and commit 2c
.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town delete
git town delete [branch-name]
The delete command deletes the given branch from the local and remote repository and updates proposals of its child branches to the parent of the deleted branch. It does not remove perennial branches.
Positional arguments
When called without arguments, the delete command deletes the feature branch you are on, including all uncommitted changes.
When called with a branch name, it deletes the given branch.
--dry-run
Use the --dry-run
flag to test-drive this command. It prints the Git commands
that would be run but doesn't execute them.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town rename
git town rename [--force] [old-name] <new-name>
The rename command changes the name of the current branch in the local and origin repository. It requires the branch to be in sync with its tracking branch to avoid data loss. It also updates the proposals for the branch being renamed, as well as proposals of its child branches into the branch being renamed.
Please be aware that most code hosting platforms are unable to update the head branch (aka source branch) of proposals. If you rename a branch that already has a proposal, the existing proposal will most likely end up closed and you have to create a new proposal that supersedes the old one. If that happens, Git Town will notify you. Updating proposals of child branches usually works.
Positional arguments
When called with only one argument, the rename command renames the current branch to the given name.
When called with two arguments, it renames the branch with the given name to the given name.
--dry-run
Use the --dry-run
flag to test-drive this command. It prints the Git commands
that would be run but doesn't execute them.
--force / -f
Renaming perennial branches requires confirmation with the --force
aka -f
flag.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town repo
git town repo [remote-name]
The repo command ("show me the repository") opens the homepage of the current repository in your browser. Git Town can display repositories hosted on GitHub, GitLab, Gitea, and Bitbucket.
Positional arguments
When called without arguments, the repo command shows the repository at the
origin
remote.
When called with an argument, it shows the repository at the remote with the given name.
Configuration
Git Town automatically identifies the hosting platform type through the origin
remote. You can override the type of hosting server with the
hosting-platform setting.
Set the hosting-origin-hostname setting to tell Git Town about the hostname when using ssh identities.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town ship
git town ship [--to-parent] [--message <text>] [branch-name]
Notice: Most people don't need to use this command. The recommended way to
merge your feature branches is to use the web UI or merge queue of your code
hosting service, as you would normally do. git town ship
is for edge cases
like developing in offline mode or when shipping
stacked changes.
The ship command ("let's ship this feature") merges a completed feature branch into the main branch and removes the feature branch.
The branch to ship must be in sync. If it isn't in sync, git town ship
will
exit with an error. When that happens, run git town sync to get the
branch in sync, re-test and re-review the updated branch, and then run
git town ship
again.
Positional argument
When called without a positional argument, the ship command ships the current branch.
When called with a positional argument, it ships the branch with the given name.
--dry-run
Use the --dry-run
flag to test-drive this command. It prints the Git commands
that would be run but doesn't execute them.
--message / -m
Similar to git commit
, the --message <message>
aka -m
parameter allows
specifying the commit message via the CLI.
--strategy / -s
Overrides the configured ship-strategy. Allowed values:
api
fast-forward
squash-merge
--to-parent / -p
The ship command ships only direct children of the main branch. To ship a
child branch, you need to first ship or delete all its ancestor
branches. If you really want to ship into a non-perennial branch, you can
override the protection against that with the --to-parent
aka -p
option.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
Configuration
The configured ship-strategy determines how the ship command merges branches. When shipping stacked changes, use the fast-forward ship strategy to avoid empty merge conflicts.
If you have configured the API tokens for GitHub, GitLab, or Gitea and the branch to be shipped has an open proposal, this command merges the proposal for the current branch on your origin server rather than on the local Git workspace.
If your origin server deletes shipped branches, for example GitHub's feature to automatically delete head branches, you can disable deleting remote branches.
Installation commands
These commands help set up Git Town on your machine.
- git town config setup configures Git Town for your machine
- git town completions generates tab completion in Bash, zsh, fish, or PowerShell
git town completions
git town completion <bash|zsh|fish|powershell>
The completions command outputs shell scripts that enable auto-completion for
Git Town in Bash, Zsh, Fish, or PowerShell. When set up, typing
git-town <tab key>
in your terminal will auto-complete subcommands.
--no-descriptions
The --no-descriptions
flag outputs shorter completions without descriptions of
arguments.
bash
To load autocompletion for Bash, run this command:
source <(git-town completions bash)
To load completions for each session, add the above line to your .bashrc
.
fish
To load autocompletions for Fish, run this command:
git-town completions fish | source
To load completions for each session, add the above line to your
~/.config/fish/config.fish
.
powershell
To install autocompletions for Powershell, run this command:
git-town completions powershell | Out-String | Invoke-Expression
To load completions for each session, add the above line to your PowerShell profile.
zsh
To load autocompletions for Zsh, run this command:
source <(git-town completions zsh)
To load completions for each session, add the above line to your .zshrc
.
To fix the error message command not found: compdef
, run
autoload -Uz compinit
Configuration commands
Git Town prompts for required configuration information during usage. Git Town
stores its configuration data inside
Git configuration data. You can store
configuration values in the local or global Git configuration depending on
whether you want to share config settings between repositories or not. To see
your entire Git configuration, run git config -l
. To see only the Git Town
configuration entries, run git config --get-regexp git-town
. The following
commands read and write the configuration entries for you so that you don't have
to run Git configuration commands manually:
- git town config - display or update your Git Town configuration
- git town config setup - setup assistant for all config settings
- git town offline - enable/disable offline mode
- git town config sync-perennial-strategy - display or set the strategy to update perennial branches
- git town config sync-feature-strategy - display or set the strategy to sync via merges or rebases
git town config [subcommand]
The config command displays and updates the local Git Town configuration.
Subcommands
Running without a subcommand shows the current Git Town configuration.
- The get-parent subcommand prints the parent branch of the current or given branch.
- The remove subcommand deletes all Git Town configuration entries.
- The setup subcommand interactively prompts for all configuration values
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town config get-parent
The get-parent subcommand of the Git Town's config command outputs the name of the parent branch if one exists, otherwise nothing.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town config remove
The remove subcommand of Git Town's config command removes all Git Town related configuration from the current Git repository.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town config setup
This command launches Git Town's setup assistant. The setup assistant walks you through all configuration options for Git Town and gives you a chance to adjust them.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town config setup
This command launches Git Town's setup assistant. The setup assistant walks you through all configuration options for Git Town and gives you a chance to adjust them.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
git town offline
git town offline
The offline configuration command displays or changes Git Town's offline mode. Git Town skips all network operations in offline mode.
Positional arguments
When called without an argument, the offline command displays the current offline status.
When called with yes
, 1
, on
, or true
, this command enables offline mode.
When called with no
, 0
, off
, or false
, it disables offline mode.
--verbose / -v
The --verbose
aka -v
flag prints all Git commands run under the hood to
determine the repository state.
Preferences
You can see all preferences via the config command and change them via the setup assistant.
Git Town can store preferences in two places:
- configuration file
- Git metadata: as entries in the local or global Git configuration
bitbucket-app-password
Git Town can interact with Bitbucket Cloud in your name, for example to update pull requests as branches get created, shipped, or deleted, or to ship pull requests. To do so, Git Town needs your Bitbucket username and an Bitbucket App Password.
An app password is not the password of your Bitbucket account. It's a special
password that you create so that external applications can interact with
Bitbucket in your name. To create an app password in the Bitbucket web UI, click
on the Settings
cogwheel, choose Personal Bitbucket settings
, and then in
the menu on the left App passwords
. You need to enable these permissions:
- repository: read and write
- pull requests: read and write
The best way to enter the Bitbucket app password is via the setup assistant.
config file
Since your App Password is confidential, you cannot add it to the config file.
Git metadata
You can configure the App Password manually by running:
git config [--global] git-town.bitbucket-app-password <token>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
bitbucket-username
Git Town can interact with Bitbucket Cloud in your name, for example to update pull requests as branches get created, shipped, or deleted. To do so, Git Town needs your Bitbucket username and an Bitbucket App Password.
The best way to enter your Bitbucket username is via the setup assistant.
config file
Since usernames are user specific, you cannot add them to the config file.
Git metadata
You can configure the Bitbucket username manually by running:
git config [--global] git-town.bitbucket-username <token>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
Git Town Configuration File
Git Town can be configured through a configuration file with named .git-branches.toml. To create one, execute:
git town config setup
Here is an example configuration file with the default settings:
push-new-branches = false
ship-delete-tracking-branch = true
sync-upstream = true
[branches]
main = "" # must be set by the user
perennials = []
perennial-regex = ""
[hosting]
platform = "" # auto-detect
origin-hostname = "" # use the hostname in the origin URL
[sync-strategy]
feature-branches = "merge"
perennial-branches = "rebase"
contribution-branches
This configuration setting stores the name of all contribution branches.
set contribution branches
To change which branches are contribution branches in your local repo, use the git town contribute command.
The Git Town configuration file does not define team-wide contribution branches because one developer's feature branch is another developer's contribution or observed branch.
view configured contribution branches
To see which branches are configured as contribution branches in your local repo, use the git town config command.
To view how contribution branches are stored in Git metadata:
$ git config list | grep 'git-town.contribution-branches'
git-town.contribution-branches=branch-1 branch-2
contribution-regex
Branches matching this regular expression are treated as contribution branches.
configure in config file
Setting the contribution regex in the config file is only useful when the matching branches should be considered contribution by all team members. This is typically the case for branches generated by external services, like Renovate or Dependabot.
[branches]
contribution-regex = "^renovate/"
configure in Git metadata
To manually set the feature regex, run this command:
git config [--global] git-town.contribution-regex '^renovate/'
The optional --global
flag applies this setting to all Git repositories on
your local machine. When not present, the setting applies to the current repo.
create-prototype-branches configuration setting
The "create-prototype-branches" setting determines whether Git Town creates new branches as prototype branches.
The best way to change this setting is via the setup assistant.
config file
To configure the creation of prototype branches in the configuration file:
create-prototype-branches = true
Git metadata
To configure the creation of prototype branches manually in Git, run this command:
git config [--global] git-town.create-prototype-branches <true|false>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
default-branch-type
This setting defines the default branch type for Git Town when the branch type is unknown. It applies to branches not listed in main-branch, perennial-branches, contribution-branches, observed-branches, parked-branches, or prototype-branches.
Possible values are:
feature
(default)contribution
observed
parked
prototype
If you set this to anything other than feature
, you must also configure the
feature-regex setting. Otherwise, there will be no feature
branches.
configuration via setup assistant
A great way to configure this setting is through the setup assistant.
configure in config file
In the config file, the default branch type is
specified in the [branches]
section:
[branches]
default-type = "feature"
configure in Git metadata
You can manually configure the default branch type using Git metadata:
git config [--global] git-town.default-branch-type "feature"
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
feature-regex
Branches matching this regular expression are treated as feature branches. This setting is relevant only when the default-branch-type setting is set to something different than "feature".
configure in config file
In the config file, define the feature regex within
the [branches]
section:
[branches]
feature-regex = "^my-*"
configure in Git metadata
To manually set the feature regex, use the following command:
git config [--global] git-town.feature-regex '^user-.*'
The optional --global
flag applies this setting to all Git repositories on
your local machine. When not present, the setting applies to the current repo.
gitea-token
Git Town can interact with Gitea in your name, for example to update pull requests as branches get created, shipped, or deleted. To do so, Git Town needs a personal access token for Gitea.
To create an API token, click on your profile image, choose Settings
, and then
in the menu on the left Applications
. You need an API token with these
permissions:
- read and write the repository
The best way to enter your token is via the setup assistant.
config file
Since your API token is confidential, you cannot add it to the config file.
Git metadata
You can configure the API token manually by running:
git config [--global] git-town.gitea-token <token>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
github-token
Git Town can interact with GitHub in your name, for example to update pull
requests as branches get created, shipped, or deleted. To do so, Git Town needs
a
personal access token
with the repo
scope. You can create one in your
account settings.
The best way to enter your token is via the setup assistant.
config file
Since your API token is confidential, you cannot add it to the config file.
Git metadata
You can configure the API token manually by running:
git config [--global] git-town.github-token <token>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
gitlab-token
Git Town can interact with GitLab in your name, for example to update pull
requests as branches get created, shipped, or deleted. To do so, Git Town needs
a
personal access token
with api
scope. You can create one in your account settings.
The best way to enter your token is via the setup assistant.
config file
Since your API token is confidential, you cannot add it to the config file.
Git metadata
You can configure the API token manually by running:
git config [--global] git-town.gitlab-token <token>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
hosting.origin-hostname
If you use SSH identities, you can define the hostname of your source code repository with this setting. The given value should match the hostname in your SSH config file.
The best way to change this setting is via the setup assistant.
config file
In the config file the hosting platform is part of
the [hosting]
section:
[hosting]
origin-hostname = "<hostname>"
Git metadata
To configure the origin hostname in Git, run this command:
git config [--global] git-town.hosting-origin-hostname <hostname>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
hosting.platform
To talk to the API of your code hosting platform, Git Town needs to know which platform (GitHub, Gitlab, Bitbucket, etc) you use.
By default, Git Town determines the code hosting platform by looking at the URL
of the origin
remote. If that's not successful, for example when using private
instances of code hosting platforms, you can tell Git Town through this
configuration setting which code hosting platform you use.
The best way to change this setting is via the setup assistant.
values
You can use one of these values for the hosting platform setting:
- remove the entry or leave it empty for auto-detection
github
gitlab
gitea
bitbucket
config file
In the config file the hosting platform is part of
the [hosting]
section:
[hosting]
platform = "<value>"
Git metadata
To configure the hosting platform in Git, run this command:
git config [--global] git-town.hosting-platform <value>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, this setting applies to the current Git repo.
main-branch
This setting stores the name of the main branch. The main branch is the default parent branch for new feature branches created with git town hack and the default branch into which Git Town ships finished feature branches.
The best way to change this setting is via the setup assistant. Git Town commands also prompt for this setting if needed.
config file
In the config file the main branch is part of the
[branches]
section:
[branches]
main = "config-main"
Git metadata
To configure the main branch in Git, run this command:
git config [--global] git-town.main-branch <value>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
observed-branches
This configuration setting stores the name of all observed branches.
set observed branches
To change which branches are observed branches in your local repo, use the git town contribute command.
The Git Town configuration file does not define team-wide observed branches because one developer's feature branch is another developer's contribution or observed branch.
view configured observed branches
To see which branches are configured as observed branches in your local repo, use the git town config command.
To view how observed branches are stored in Git metadata:
$ git config list | grep 'git-town.observed-branches'
git-town.observed-branches=branch-1 branch-2
observed-regex
Branches matching this regular expression are treated as observed branches.
configure in config file
Setting the observed regex in the config file is only useful when the matching branches should be considered observed by all team members. This is typically the case for branches generated by external services, like Renovate or Dependabot.
[branches]
observed-regex = "^renovate/"
configure in Git metadata
To manually set the feature regex, run this command:
git config [--global] git-town.observed-regex '^renovate/'
The optional --global
flag applies this setting to all Git repositories on
your local machine. When not present, the setting applies to the current repo.
offline
git-town.offline=<true|false>
If you have no internet connection, certain Git Town commands that perform network requests will fail. Enabling offline mode omits all network operations and thereby keeps Git Town working.
This setting applies to all repositories on your local machine.
set via CLI
To put Git Town into offline mode, execute the git town offline command.
Git metadata
git config --global git-town.offline <true|false>
Branch lineage
Configuration entries of the form git-town-branch.<branch>.parent=<branch>
store the parents of Git branches. You can ignore these configuration entries,
Git Town maintains them as it creates and removes feature branches.
parked-branches
This setting stores the name of all parked branches.
set parked branches
To change which branches are parked in your local repo, use the git town park command.
The Git Town configuration file doesn't define team-wide parked branches because developers typically only park branches they own.
view configured parked branches
To see which branches are configured as parked in your local repo, use the git town config command.
To view how parked branches are stored in Git metadata:
$ git config list | grep 'git-town.parked-branches'
git-town.parked-branches=branch-1 branch-2
perennial-branches
Perennial branches are long-lived branches. They have no parent and are never
shipped. Typical perennial branches are main
, master
, development
,
production
, staging
, etc.
You can see the configured perennial branches via the config command and change them via the setup assistant.
configure in config file
In the config file the perennial branches are
defined as part of the [branches]
section:
[branches]
perennials = [ "branch", "other-branch" ]
configure in Git metadata
You can configure the perennial branches manually by running:
git config [--global] git-town.perennial-branches "branch other-branch"
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
bulk-define perennial branches
If you have many perennial branches that follow the same naming schema, like
release-v4.0-rev.1
, release-v4.0-rev.2
, etc, you can define a
regular expression for them instead of listing them one by
one.
perennial-regex
All branches matching this regular expression are considered perennial branches.
configure in config file
In the config file the perennial regex exists inside
the [branches]
section:
[branches]
perennial-regex = "^release-.*"
configure in Git metadata
You can configure the perennial branches manually by running:
git config [--global] git-town.perennial-regex 'release-.*'
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
prototype-branches
This setting stores the name of all prototype branches.
set prototype branches
To change which branches are prototype in your local repo, use the git town park command.
The Git Town configuration file doesn't define team-wide prototype branches because developers typically only park branches they own.
view configured prototype branches
To see which branches are configured as prototype in your local repo, use the git town config command.
To check how prototype branches are stored in Git metadata:
$ git config list | grep 'git-town.prototype-branches'
git-town.prototype-branches=branch-1 branch-2
push-hook configuration setting
The "push-hook" setting determines whether Git Town allows or prevents Git hooks while pushing branches. Hooks are enabled by default. If your Git hooks are slow, you can disable them to speed up branch syncing.
When disabled, Git Town pushes using the --no-verify option. This omits the pre-push hook.
The best way to change this setting is via the setup assistant.
config file
To configure the push hook in the configuration file:
push-hook = false
Git metadata
To configure the push hook manually in Git, run this command:
git config [--global] git-town.push-hook <true|false>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
push-new-branches
By default, Git Town does not push new feature branches to the origin
remote
since that would make creating branches slower and triggers an unnecessary CI
run for a branch containing no changes. Running
git town sync or
git town propose later will push the branch to origin.
If you prefer to push new branches upon creation, enable this configuration
option.
in config file
push-new-branches = true
in Git metadata
To enable pushing new branches in Git, run this command:
git config [--global] push-new-branches <true|false>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, this setting applies to the current Git repo.
ship-delete-tracking-branch
Some code hosting platforms like
GitHub
and
GitLab
can delete the tracking branch when shipping via their API. In this case the
tracking branch is already gone when git town ship
tries to delete it,
resulting in an error. To prevent this error, set the
ship-delete-tracking-branch setting to false
so that Git Town does not try
to delete the tracking branch.
The best way to change this setting is via the setup assistant.
in config file
ship-delete-tracking-branch = false
in Git metadata
To configure this setting in Git, run this command:
git config [--global] git-town.ship-delete-tracking-branch <true|false>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
ship-strategy
The ship-strategy
setting defines how git town ship
merges finished feature branches into the main branch.
options
api
When using the "api" ship-strategy, git town ship presses the "merge" button for the proposal in the web UI of your code hosting platform via an API call.
You need to configure an API token in the setup assistant for this to work.
api
is the default value because it does exactly what you normally do
manually.
fast-forward
The fast-forward
ship strategy prevents false merge conflicts when using
stacked changes. It merges the branch to ship into its
parent (typically the main branch) by running
git merge --ff-only
and then pushes the new commits on the parent branch to origin. This way the
parent branch contains the exact same commits as the branch that has just been
shipped.
For details why this is needed check out this GitHub documentation.
This works on GitHub even if your main branch is protected as long as the associated proposal is green and has been approved! GitHub recognizes that the commits you push have already been tested and approved and allows them to be pushed. For more information, see this StackOverflow answer.
A limitation of the fast-forward
ship strategy is that your feature branch
must be up to date, i.e. the main branch must not have received additional
commits since you last synced your feature branch.
squash-merge
When set to squash-merge
, git town ship merges the
feature branch to ship in your local Git repository. While doing so it squashes
all commits on the feature branch into a single commit and lets you edit the
commit message.
change this setting
The best way to change this setting is via the setup assistant.
config file
Set the ship-strategy in the config file:
ship-strategy = "api"
Git metadata
To manually configure the ship-strategy in Git metadata, run:
git config [--global] git-town.ship-strategy <api|squash-merge>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
sync-feature-strategy
The sync-feature-strategy
setting specifies how to update local feature
branches with changes from their parent and tracking branches.
options
merge
When using the "merge" sync-feature-strategy, git town sync merges the parent and tracking branches into local feature branches.
merge
is the default value because it is the safest and easiest option.
rebase
When set to rebase
, git town sync rebases local feature
branches against their parent branches and then does a safe force-push of your
rebased local commits to the tracking branch. This safe force-push uses Git's
--force-with-lease
and
--force-if-includes
switches to guarantee that the force-push will never overwrite commits on the
tracking branch that haven't been integrated into the local Git history.
If the safe force-push fails, Git Town rebases your local branch against its tracking branch to pull in new commits from the tracking branch. If that leads to conflicts, you have a chance to resolve them and continue syncing by running git town continue.
When continuing the sync this way, Git Town tries again to safe-force-push and rebase until the safe-force-push succeeds without removing commits from the tracking branch that aren't part of the local Git history.
This can lead to an infinite loop if you do an interactive rebase that removes
commits from the tracking branch while syncing it. You can break out of this
infinite loop by doing a less aggressive rebase that doesn't remove the remote
commits. Finish the git town sync
command and then clean up your commits via a
separate interactive rebase after the sync. At this point another sync will
succeed because the commits you have just cleaned up are now a part of your
local Git history.
The rule of thumb is that pulling in new commits via git town sync
and
cleaning up old commits must happen separately from each other. Only then can
Git guarantee that the necessary force-push happens without losing commits.
compress
When using the compress
sync strategy, git town sync
first merges the tracking and parent branches and then
compresses the synced branch.
This sync strategy is useful when you want all your pull requests to always consists of only one commit.
Please be aware that this sync strategy leads to more merge conflicts than the "merge" sync strategy when more than one Git user makes commits to the same branch.
change this setting
The best way to change this setting is via the setup assistant.
config file
In the config file the sync-feature-strategy is part
of the [sync-strategy]
section:
[sync-strategy]
feature-branches = "merge"
Git metadata
To manually configure the sync-feature-strategy in Git, run this command:
git config [--global] git-town.sync-feature-strategy <merge|rebase>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
sync-perennial-strategy
The sync-perennial-strategy setting specifies how to update local perennial branches with changes from their tracking branches.
options
When set to rebase
(the default value), Git Town rebases local perennial
branches against their tracking branch. When set to merge
, it merges the
tracking branch into the local perennial branch.
The best way to change this setting is via the setup assistant.
in config file
In the config file the sync-perennial-strategy is
part of the [sync-strategy]
section:
[sync-strategy]
perennial-branches = "rebase"
in Git metadata
To manually configure the sync-perennial-strategy in Git, run this command:
git config [--global] git-town.sync-perennial-strategy <merge|rebase>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
sync-prototype-strategy
The sync-prototype-strategy
setting specifies how to update local
prototype branches with changes from
their parent and tracking branches. When not set, Git Town uses the
sync-feature-strategy.
options
sync-prototype-strategy
accepts the same options as
sync-feature-strategy.
change this setting
The best way to change this setting is via the setup assistant.
config file
In the config file the sync-prototype-strategy is
part of the [sync-strategy]
section:
[sync-strategy]
prototype-branches = "merge"
Git metadata
To manually configure the sync-prototype-strategy in Git, run this command:
git config [--global] git-town.sync-prototype-strategy <merge|rebase>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
sync-tags
The sync-tags setting configures whether to sync Git tags with the origin
remote.
options
When set to true
(the default value), git town sync
also pulls and pushes
Git tags in addition to branches and commits. When set to false
,
git town sync
does not change Git tags at the local or remote Git repository.
The best way to change this setting is via the setup assistant.
in config file
In the config file the sync-tags setting can be set like this:
sync-tags = true
in Git metadata
To manually configure sync-tags
in Git, run this command:
git config [--global] git-town.sync-tags <true|false>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.
sync-upstream
The sync-upstream setting configures whether to pull in updates from the
upstream
remote. This is intended for codebases that are forks of other
codebases and want to stay in sync with the codebase they are forked from.
options
When set to true
(the default value), git town sync
also updates the local
main-branch with changes from its counterpart in the
upstream
remote. When set to false
, git town sync
does not pull in updates
from upstream even if that remote exists.
The best way to change this setting is via the setup assistant.
in config file
In the config file the sync-upstream setting can be set like this:
sync-upstream = true
in Git metadata
To manually configure sync-upstream
in Git, run this command:
git config [--global] git-town.sync-upstream <true|false>
The optional --global
flag applies this setting to all Git repositories on
your machine. Without it, the setting applies only to the current repository.