# Preventing port conflicts in Git worktrees using directory hashing

> Source: <https://wietsevenema.eu/blog/2026/avoiding-port-conflicts-when-using-git-worktree/>
> Published: 2026-10-16 15:41:02+00:00

# Preventing port conflicts in git worktrees using directory hashing

            Coding agents are making `git worktree` popular because worktrees let
            multiple agents work on the same repository at the same time without interfering with
            each other. Each branch is checked out in its own directory: if your
            *main* branch lives in `main/`, a `feature-1` branch can
            sit right next to it in `feature-1/`.
          

            However, directory isolation doesn't isolate network ports. When a server in one
            worktree runs on port `8080`, starting the server in another worktree fails
            because the address is already in use.
          

            At first, I passed ports manually per worktree (like `PORT=8001 make run`),
            which got tedious. Next, I decided to calculate a deterministic port number for each
            worktree, using its absolute directory path. Because each worktree lives in a different
            directory, every environment gets its own port automatically.
          

## Hashing the directory path in your Makefile

            To turn a directory path into a port number, take the SHA-256 hash of the current
            working directory, convert the hex digest to an integer, and map it into 400 buckets of
            5 ports across the `8000` to `9999` range:
          

```
full_path = os.getcwd().encode("utf-8")
path_hash = int(hashlib.sha256(full_path).hexdigest(), 16)
port = 8000 + ((path_hash % 400) * 5)
```

            Spacing base ports 5 numbers apart (`8000`, `8005`,
            `8010`, and so on) gives you 400 buckets, making collisions across a handful
            of local worktrees unlikely while reserving four adjacent ports (`PORT + 1`
            through `PORT + 4`) for secondary services.
          

            To avoid changing your application code, you can compute `PORT` directly in
            your `Makefile` and export it as an environment variable:
          

```
# Allocate a 5-port bucket (8000, 8005, ..., 9995) from the worktree's path
ifeq ($(PORT),)
export PORT := $(shell python3 -c "import hashlib, os; h = hashlib.sha256(os.getcwd().encode('utf-8')).hexdigest(); print(8000 + ((int(h, 16) % 400) * 5))")
else
export PORT
endif

.PHONY: run
run:
	@echo "Starting server on http://localhost:$(PORT)"
	@uvicorn main:app --host 0.0.0.0 --port $(PORT)
```

            If `PORT` isn't already set, Make runs the Python command once
            (`:=`) to calculate the port and exports it to child processes. If you pass a
            port manually (like `PORT=9000 make run`), the `else` branch keeps
            your value and exports it instead. Using `ifeq` with `:=` rather
            than `PORT ?=` avoids re-running the Python subshell every time
            `$(PORT)` is referenced.
          

## Offsetting ports for secondary services

            Because each worktree gets a 5-port bucket, you can assign up to four secondary services
            to `PORT + 1` through `PORT + 4` without colliding with another
            worktree's base port. Here's an example with the Firestore emulator:
          

```
FIRESTORE_PORT ?= $(shell expr $(PORT) + 1)
export FIRESTORE_EMULATOR_HOST ?= 127.0.0.1:$(FIRESTORE_PORT)

.PHONY: emulator
emulator:
	@gcloud emulators firestore start --host-port=$(FIRESTORE_EMULATOR_HOST)
```

`expr $(PORT) + 1` adds `1` to the base port, and exporting
            `FIRESTORE_EMULATOR_HOST` tells the Google Cloud client libraries in your app
            and test runner which emulator port to connect to automatically. Passing
            `--host-port=$(FIRESTORE_EMULATOR_HOST)` binds the Firestore emulator to that
            same address on startup.
