About
git-wip manages Work In Progress (or WIP) branches.
WIP branches are mostly throw-away but capture points of development
between commits. The intent is to tie git-wip into your editor so
that each time you save a file the current working-tree state is
snapshotted in git. git-wip also helps you return to a previous
state of development.
Latest git-wip can be obtained from github.com.
git-wip was written by Bart Trojanowski.
You can find out more from the original blog post.
Note:
git-wipwas originally a bash script (2009). This repository is a C++ rewrite; tag v0.2 is the last bash version. The script was moved toAttic/and is no longer maintained.
TL;DR
$ make $ ./dependencies.sh # assumes you're on a Debian-based distro, and you have `sudo` $ make install # installs to ~/.local by default $ cd my-project $ git wip # snapshot working tree → wip/master
WIP branches are named after the current branch, prefixed with wip/.
If you are working on feature, the WIP branch is wip/feature (stored
as refs/wip/feature).
First snapshot — creates a commit on wip/<branch> rooted at the
current branch HEAD:
--- * --- * --- * ← feature
\
W ← wip/feature
Subsequent snapshots — stacks new WIP commits on top:
--- * --- * --- * ← feature
\
W
\
W ← wip/feature
After a real commit — the next git wip detects that the work branch
has advanced and resets the WIP branch to start from the new HEAD:
--- * --- * --- * --- * ← feature
\ \
W W ← wip/feature
\
W (reachable via reflog as wip/feature@{1})
Old WIP commits are never deleted; they remain reachable through
git reflog show wip/<branch>.
Commands
git wip
Snapshot the working tree with the default message "WIP".
Equivalent to git wip save "WIP".
git wip [--version | -v | version]
Show the version string baked in at build time. Format:
{VERSION}-{YYYYMMDD}-g{HASH}[-dirty], where VERSION comes from the
committed VERSION file, YYYYMMDD is the commit date, and HASH is the
short commit hash. -dirty is appended if the working tree had uncommitted
changes at build time.
$ git wip --version
v0.3-20260518-g93b99ef
git wip save [<message>] [options] [-- <file>...]
Create a new WIP commit.
| Option | Description |
|---|---|
-e, --editor |
Quiet mode — exit 0 silently when there are no changes (for editor hooks) |
-u, --untracked |
Also capture untracked files |
-i, --ignored |
Also capture ignored files |
--no-gpg-sign |
Do not GPG-sign the commit (overrides commit.gpgSign = true) |
If <file>... arguments are given, only those files are snapshotted.
Otherwise all tracked files are updated.
git wip status [-l] [-f] [<ref>]
Show the status of the WIP branch for the current work branch, or another branch.
$ git wip status
branch master has 5 wip commits on refs/wip/master
| Option | Description |
|---|---|
-l, --list |
List each WIP commit: short SHA, subject, and age |
-f, --files |
Show a git diff --stat of changes relative to the work branch HEAD |
<ref> is optional and can be any of:
<branch>wip/<branch>refs/heads/<branch>refs/wip/<branch>
If <ref> is omitted, git wip status uses the current branch.
-l and -f can be combined; each commit line is then followed by its
own per-commit diff stat.
$ git wip status -l
branch master has 5 wip commits on refs/wip/master
1d146bf - WIP (53 minutes ago)
a3f901c - save file.c (50 minutes ago)
...
$ git wip status -f
branch master has 5 wip commits on refs/wip/master
src/main.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
$ git wip status -l -f
branch master has 5 wip commits on refs/wip/master
1d146bf - WIP (53 minutes ago)
src/main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
...
git wip log [options]
Show the git log for the current WIP branch.
| Option | Description |
|---|---|
-p, --pretty |
Compact colourised graph output |
-s, --stat |
Include per-commit diff stat |
-r, --reflog |
Show the reflog instead of the commit graph (useful for recovering old WIP stacks) |
git wip list [-v]
List all WIP refs in the repository.
$ git wip list
wip/master
wip/feature
| Option | Description |
|---|---|
-v, --verbose |
Show how many WIP commits are ahead of the matching work branch; report orphaned WIP refs |
Verbose output example:
$ git wip list -v
wip/master has 5 commits ahead of master
wip/feature has 1 commit ahead of feature
wip/old-branch is orphaned
git wip delete [--yes] [<ref>]
Delete WIP refs.
- With
<ref>, deletes that branch's WIP ref (same ref formats asstatus):<branch>wip/<branch>refs/heads/<branch>refs/wip/<branch>
- With no
<ref>, deletes the current branch's WIP ref and asks for confirmation. --yesskips the confirmation prompt (only relevant when<ref>is omitted).
$ git wip delete
About to delete wip/master [Y/n]
git wip delete --cleanup
Delete orphaned WIP refs (any refs/wip/<branch> where refs/heads/<branch>
does not exist).
Configuration
git-wip reads settings from your .gitconfig under the [git-wip] section.
These provide defaults that can be overridden by command-line flags.
[git-wip] save = untracked # which files to capture: tracked | untracked | ignored | all gpg-sign = false # whether to GPG-sign WIP commits
git-wip.save
Controls which files are captured by git wip save:
| Value | Description |
|---|---|
tracked |
Only tracked files (default behaviour) |
untracked |
Tracked files plus untracked files (equivalent to -u) |
ignored |
Tracked files plus ignored files (equivalent to -i) |
all |
Everything: tracked, untracked, and ignored (equivalent to -a) |
Command-line flags (--untracked, --no-untracked, --ignored, --no-ignored,
--all, --only-tracked) override this setting.
git-wip.gpg-sign
Boolean. If true, WIP commits will be GPG-signed (not yet implemented).
--gpg-sign and --no-gpg-sign override this setting.
Building
Requires: a C++23 compiler, CMake ≥ 3.26, Ninja, and libgit2-dev.
$ ./dependencies.sh # install packages needed to build $ make # Release build → build/src/git-wip $ make TYPE=Debug # Debug build $ make test # Build + run all tests (unit + CLI integration) $ make install # Install to ~/.local (override with PREFIX=...)
Dependencies (spdlog) are fetched automatically by CMake via
FetchContent. libgit2 must be installed system-wide (e.g.
apt install libgit2-dev).
If you'd rather build a static binary (tested on Debian/Ubuntu), you can use:
$ ./dependencies.sh --static # install extra dependencies $ make STATIC=1 # build the static binary
Installation
$ git clone https://github.com/bartman/git-wip.git $ cd git-wip $ ./dependencies.sh $ make $ make install # → ~/.local/bin/git-wip
Or copy the binary manually:
$ cp build/src/git-wip ~/bin/NixOS
git-wip ships a flake (flake.nix) and a self-contained package definition
(nix/package.nix). To install it system-wide on NixOS without touching
your existing flake.nix, drop a git-wip.nix file next to your
configuration.nix and add it to imports.
In configuration.nix:
imports = [ # ...your other imports... ./git-wip.nix ];
In git-wip.nix:
{ config, pkgs, lib, ... }: let # Pin to the most recent release. Update both `ref` and `rev` when bumping. # `rev` must be the full 40-character commit hash; look it up with: # git ls-remote https://github.com/bartman/git-wip.git refs/tags/v0.3 src = builtins.fetchGit { url = "https://github.com/bartman/git-wip.git"; ref = "refs/tags/v0.3"; rev = "0000000000000000000000000000000000000000"; # replace with v0.3's full sha }; # Reproduce the version string shape used by the upstream flake: # {VERSION}-{YYYYMMDD}-g{HASH} baseVersion = lib.fileContents (src + "/VERSION"); buildDate = builtins.substring 0 8 (src.lastModifiedDate or "00000000"); shortHash = src.shortRev or "unknown"; version = "${baseVersion}-${buildDate}-g${shortHash}"; git-wip = pkgs.callPackage (src + "/nix/package.nix") { inherit pkgs version; }; in { environment.systemPackages = [ git-wip ]; }
Apply with sudo nixos-rebuild switch, then verify:
$ git-wip --version v0.3-20260518-g93b99ef
To upgrade to a newer release later, bump ref to the new tag (e.g.
refs/tags/v0.4) and replace rev with the full hash of that tag's commit.
Leaving the rev value invalid will cause Nix to error out — useful, since
it forces you to consciously pin each release.
Editor integration
vim
The vim plugin shells out to git wip on every file save, so the git-wip
binary must be installed and on your PATH before the plugin will do anything.
Easiest way to do this is to run make install.
Verify you're ready with:
(1) With Neovim builtin vim.pack (Neovim v0.12+):
vim.pack.add({ { src = 'https://github.com/bartman/git-wip' } }) require('git-wip').setup({ -- gpg_sign = false, -- background = true, -- untracked = true, -- ignored = false, -- filetypes = { "*" }, })
(2) With lazy.nvim (Neovim only):
{
"bartman/git-wip",
opts = {
gpg_sign = false, -- true enables GPG signing of commits
untracked = true, -- true to include untracked files
ignored = false, -- true to include files ignored by .gitignore
background = false, -- true for async execution if supported (Neovim 0.10+), false for sync
filetypes = { "*" }, -- list of vim file types to call git-wip on
},
},(3) With Vundle (Vim only):
Bundle 'bartman/git-wip', {'rtp': 'vim/'}
(4) Copy the plugin directly:
Not really recommended (or supported), but if you want to you could copy it to your Neovim plugin directory...
$ cp lua/git-wip/init.vim ~/.config/nvim/plugin/lua/git-wip.luaand then in your ~/.config/nvim/init.lua you will need to:
require("git-wip").setup({})
Meanwhile if you use Vim, you can do this:
$ cp vim/plugin/git-wip.vim ~/.vim/plugin/
and Vim will pick it up from there.
(5) Or add an autocommand to your .vimrc:
This bypasses a plugin, just uses the executable.
augroup git-wip autocmd! autocmd BufWritePost * :silent !cd "`dirname "%"`" && git wip save "WIP from vim" --editor -- "`basename "%"`" augroup END
The --editor flag makes git-wip silent when there are no changes.
emacs
Add to your .emacs:
(load "/{path_to_git-wip}/emacs/git-wip.el")
Or copy the contents of emacs/git-wip.el directly into your .emacs.
Sublime Text
A Sublime Text plugin is provided in the sublime/ directory.
Recovery
Find the commit you want to recover:
$ git wip status -l # show current WIP stack $ git reflog show wip/master # show full history including reset points $ git log -g -p wip/master # inspect with diffs
Check out the files from a WIP commit (HEAD stays where it is):
$ git checkout wip/master -- . # restore entire tree $ git checkout <sha> -- path/to/file # restore a single file
The changes land in the index and working tree. Review with:
Adjust with git reset / git add -p as needed, then commit.
Appendix
related projects
- wip.rs is a Rust executable that watches for changes in your work, and invokes
git-wipas needed. (by dlight).