Self-hosted service
gitwatch/gitwatch avatar
gitwatch/gitwatch

gitwatch turns inotify events into git commits, and nothing more

Watch a file or folder and automatically commit changes to a git repo easily.

1,744 stars222 forksShellGPL-3.0

At a glance

What is it?
gitwatch is a single Bash script that polls or watches a path and commits changes to a git repository, which makes it a config-versioning tool rather than a backup system. Its value is that it is readable in one sitting; its limitation is that it commits whatever appears, with no filter.
Who is it for?
gitwatch fits a person who wants their application config directory or notes folder versioned without thinking about it, especially with the NixOS module, where a systemd service per watched path is declarative. It is the wrong tool for anything where an unreviewed automatic commit would matter, because the script has no filter, no ignore stage and no review step between a file changing and a commit existing.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 71 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The use case is a program that rewrites its own config

The README gives two motivating cases and both are about software that writes files without asking.

The first is config files. Some programs auto-write their configuration with no Apply button, and even when there is a button, most offer no way back to an earlier version of your settings. Committing the config to a git repository gives you a history and a way back. gitwatch makes that history automatic rather than a habit.

The second is documents, for an editor with no built-in git support, or one whose git support the user does not like. gitwatch commits on save, and paired with an editor's auto-save feature it produces a regular record.

The scope is deliberately minimal. The repository is a bash script, `gitwatch.sh`, with an `entrypoint.sh` for the container, a `Dockerfile`, `docker-compose.yml`, a systemd unit `[email protected]`, a Nix module `module.nix` with `gitwatch.nix` and `flake.nix`, a bpkg plist `com.github.gitwatch.plist`, and a `tests/` directory. There is no daemon in the traditional sense, no database and no state beyond what git holds.

That minimalism is the design. The whole behaviour is legible in a shell script, which is why it can be trusted with a config directory and why it will never grow into something you did not ask for.

Install paths, and the NixOS module is the most interesting one

Installation from source is two commands, and the README notes you may need sudo depending on your PATH:

sh
git clone https://github.com/gitwatch/gitwatch.git
cd gitwatch
[sudo] install -b gitwatch.sh /usr/local/bin/gitwatch

Updating from a source install is the same steps again, or `git pull` if you kept the checkout.

Other routes exist because the project has been packaged by several distributions. bpkg installs it with `[sudo] bpkg install -g gitwatch/gitwatch`. Arch has an AUR package named gitwatch-git, installed with whatever AUR helper you use. And starting from NixOS 24.11 the package is in mainline nixpkgs, with receipts from the repository as an alternative.

The NixOS module is the one worth reading carefully, because it is a different way of using the script than running it by hand. Each watched path becomes a `services.gitwatch.<service name>` submodule:

nix
services.gitwatch.<service name> = {
    enable = true;
    path = "/home/me/my-repo";
    remote = "[email protected]:me/my-repo.git";
    user = "me";
    message = "Auto-commit by gitwatch on %d";
};

The five fields map to what the script does: where to watch, where to push, who commits, and what the message says. `%d` is replaced with the date and time. Declaring this in configuration.nix makes NixOS create a systemd service named `gitwatch-<service name>`, which is what makes running at boot a solved problem rather than a cron entry you write yourself.

A standalone systemd unit template `[email protected]` is also shipped for people not on NixOS.

Docker Compose, where SSH keys and commit identity get mounted in

The container route is the recommended Docker path, and reading its compose file tells you exactly what the tool needs.

The image is `ghcr.io/gitwatch/gitwatch:latest`, published from the repository's GitHub Container Registry. The Dockerfile uses Alpine 3.20 and installs bash, git, inotify-tools and openssh. So the container brings git, inotify-tools and openssh, and nothing else.

Three volumes are mounted, and each one is a decision you have to make:

yaml
- ${HOST_WATCHED_REPO_PATH:-./watched-repo}:/app/watched-repo
- ${HOST_SSH_PRIVATE_KEY_PATH:-~/.ssh/id_rsa}:/root/.ssh/id_rsa:ro
- ${HOST_GITCONFIG_PATH:-~/.gitconfig}:/root/.gitconfig:ro

Your SSH private key is mounted read-only so the container can push, and your `.gitconfig` is mounted read-only so commits carry your name and email rather than the container's defaults. The key is optional: the README lists an SSH key as a prerequisite only for pushing to a remote.

The host paths are set through a local `.env` file with three variables, `HOST_WATCHED_REPO_PATH` defaulting to `./watched-repo`, `HOST_SSH_PRIVATE_KEY_PATH` defaulting to `~/.ssh/id_rsa`, and `HOST_GITCONFIG_PATH` defaulting to `~/.gitconfig`. For deeper changes there is a `docker-compose.override.yml`, which the README notes is ignored by git, so you can tailor the setup without dirtying the tracked file.

The environment block is where the behaviour lives, and it is worth reading as a spec:

yaml
GIT_WATCH_DIR: /app/watched-repo
GIT_REMOTE: origin
GIT_BRANCH: main
PULL_BEFORE_PUSH: "false"
SLEEP_TIME: 2
COMMIT_MSG: "Auto-commit: %d"

`SLEEP_TIME` is the debounce in seconds between a file change and a commit, which is the setting you will adjust first. `PULL_BEFORE_PUSH` set to true runs `git pull --rebase` before every push, which matters when more than one machine watches the same repository.

Image tags and the immutability story

The README documents four classes of Docker tag, and the distinction between them is the useful part.

A full SHA and a short SHA are always published for every build and are described as immutable. `:master` is a moving tag pointing at the latest image built from the master branch. `:vX.Y` and `:X.Y` are published when a release tag is pushed. `:latest` is updated when a `vX.Y` tag is pushed and points at the newest release image.

So there are two moving tags and two stable ones. For a service that pushes to a remote on your behalf, pinning to a full SHA is the defensible choice, because a moving tag can change what commits your config between reboots.

Release history in that context: v0.4 on 2025-08-19, v0.5 on 2026-01-25, v0.6 on 2026-06-11. The last push was on 2026-08-13 and the repository is not archived.

The licence is GPL-3.0, which is worth noting for a project this small because it is copyleft: running it as a tool is unproblematic, and vendoring the script into a product you distribute is a different question that the LICENSE file answers rather than this summary.

The gap in the design: no filter between change and commit

The honest limitation is visible from what is absent.

There is no ignore mechanism comparable to `.gitignore` semantics for deciding what to commit, no exclude pattern in the compose environment, and no review step. The script commits whatever the watched directory produces. If your config directory also holds a log file, a lock file, or a database that writes on every read, those get committed too.

The `SLEEP_TIME` debounce handles one specific problem, an editor writing a file in several steps, but it does not handle a directory where unrelated files change constantly.

The second gap is `COMMIT_MSG`. In the compose file it is `Auto-commit: %d`, a single message format for every commit. There is no documented mechanism for a message that reflects what changed. That is fine for an audit trail and poor for a history you intend to read back, because `git log` becomes a wall of identical lines distinguished only by timestamp.

The third is conflict handling. `PULL_BEFORE_PUSH` exists and defaults to false, which suggests the tool is aware that two watchers on one repository collide. What is not documented is how a push rejection is handled: whether it retries, whether it merges, or whether the commit sits locally forever. If two machines watch one repository, that behaviour is the thing to test.

None of this makes the tool wrong. It makes it a tool for one machine, one directory, one identity, which is the case the README actually describes.

Editorial conclusion

gitwatch fits a person who wants their application config directory or notes folder versioned without thinking about it, especially with the NixOS module, where a systemd service per watched path is declarative. It is the wrong tool for anything where an unreviewed automatic commit would matter, because the script has no filter, no ignore stage and no review step between a file changing and a commit existing. Verify first by pointing GIT_WATCH_DIR at a throwaway repository, letting a single file change go through, and reading the commit that appears, since a misconfigured SLEEP_TIME or a volume mounted at the wrong path produces commits of nothing at all.

Frequently asked questions

How do I install gitwatch?

Clone the repository and run install -b gitwatch.sh /usr/local/bin/gitwatch, with sudo if /usr/local/bin needs it. It is also packaged for bpkg, Arch through the AUR, and NixOS, where it has been in mainline nixpkgs since 24.11.

How do I configure gitwatch in Docker Compose?

Set host volume paths through a local .env file using HOST_WATCHED_REPO_PATH, HOST_SSH_PRIVATE_KEY_PATH and HOST_GITCONFIG_PATH, then set GIT_WATCH_DIR, GIT_REMOTE, GIT_BRANCH, SLEEP_TIME and COMMIT_MSG in the environment section. For deeper changes add a docker-compose.override.yml, which git ignores.

What does the NixOS module for gitwatch create?

Each services.gitwatch.<service name> submodule with enable, path, remote, user and message produces a systemd service named gitwatch-<service name>. The message field uses %d as a placeholder for the date and time.

Does gitwatch ignore files before committing?

The README documents no ignore or exclude option for the watched directory. The script commits what appears there, and SLEEP_TIME controls only the delay after a change, so unrelated files written into the same directory are committed too.

Official sources

  1. gitwatch/gitwatch on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/gitwatch-gitwatch.svg)](https://hysenlabs.com/projects/gitwatch-gitwatch)

Was this useful?