Open-source project
ahmetsait/toolgit avatar
ahmetsait/toolgit

toolgit: git sub-commands for the things git will not do by itself

Git Productivity Toolkit

565 stars11 forksPythonUnlicense

At a glance

What is it?
ToolGit is a set of standalone scripts named git-something that Git picks up from your PATH, so they run as sub-commands without a plugin system or an alias. They cover the operations you keep scripting by hand, from a clean worktree check to a hard reset of every tracking branch.
Who is it for?
toolgit suits someone who lives in the command line and keeps writing the same shell function for a repository state check or a bulk branch update, because each script is a file you can read and a command that works without an alias or a hook. It does not suit you if you want a single coherent workflow, since there is no plugin manager, no configuration file and no shared command set, and the destructive ones such as git-force-pull are one keystroke away from the safe ones.
Can I use it commercially?
Yes. Unlicense is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 71 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

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

Editorial analysis

How Git finds these, and why there is no installer

ToolGit is a collection of scripts that extend Git with various sub-commands to make life easier, and the whole integration mechanism is a naming convention.

Installing means extracting the project to a folder of your choice and adding that folder path to your PATH environment variable. Git will recognize executable git-* files in PATH and allow using them as sub-commands.

That is the entire design. There is no plugin manager, no package manager, no manifest and no configuration step for the sub-commands themselves. Git already has a documented convention for external sub-commands, and a script named git-forward placed on your PATH is available as git forward. The consequence is that the tool is entirely readable: you can cat the script before you run it, and you can copy one command out of the collection and use it without the rest.

The aliases are opt-in and separate, because the alias syntax and the sub-command syntax are different mechanisms. To install the aliases as well:

sh
git config set --append --global include.path path/to/toolgit/aliases.ini

The aliases file is a Git configuration include, so this appends a path to the global config rather than rewriting it, and it is a single line that can be undone by removing that include.

There is a security consideration that the naming convention makes worth stating. Anything executable named git-something on your PATH becomes a Git sub-command, so the directory you add has to be one you control. A shared machine where several people add entries to a common PATH is a situation where a stray script could shadow a built-in expectation. ToolGit's own scripts are the benign case of that mechanism, but the mechanism is general.

Downloads are offered as a tarball for Posix and a zip for Windows, and the platform badge covers Cygwin and Posix.

The command list, and which ones are destructive

The command table is the documentation, and it is worth reading as two groups.

The state-checking commands return exit codes rather than printing, which is what makes them useful in scripts. git-in-repo returns 0 if the current working directory is a Git repository and non-zero otherwise. git-is-branch-remote returns 0 if the branches refer to a remote branch. git-is-head-detached returns 0 if HEAD is in detached state. git-is-worktree-clean returns 0 if the working tree has no changes or untracked files, and non-zero otherwise.

That group is the interesting part of the project, because a command whose only output is an exit status composes with everything. A prompt that colours itself differently inside a repository, a pre-commit hook that refuses to run on a dirty tree, a CI step that skips work when the tree is already clean: all of those want git-is-worktree-clean or git-in-repo rather than a line of parsing.

The path and inspection commands are the obvious ones. git dir outputs the .git directory path, git root outputs the root path, git main-branch gets the name of the main or default branch, and git graph shows a pretty commit graph.

The history and maintenance commands are the useful ones. git xlog searches history for a string only in added or removed lines, which is the behaviour you want when you are hunting for when a particular change happened rather than where it is now. git-legacy rebases the whole history of the current HEAD on top of a revision, git gc-all expires the reflog and runs a full garbage collection, and git mode-restore restores file modes in the index and the worktree.

And then there are the two that need care. git-force-pull fetches and force pulls remote tracking branches by doing a hard reset, and git-forward fetches and fast forwards all remote tracking branches. The first one discards local commits on every tracking branch, and the fact that both are one word apart in meaning and spelling is the main hazard of the collection.

The mode-restore command, and the problem it solves

The mode-restore command is the one with a worked example in the documentation, and the example tells you exactly what problem it addresses.

The scenario is a working tree where every file has become executable. The status output shows it plainly, with the editorconfig, gitattributes, the launch and tasks JSON files, the readme and the licence file all listed as modified, and the diff explains why, with each file showing an old mode of 100644 and a new mode of 100755. The cause is usually a filesystem that does not preserve Unix permission bits, a Windows checkout, a container volume, or an archive extracted on the wrong platform.

Running the command over the path fixes it:

code
$ git mode-restore .
rwxrwxr-x -> rw-rw-r--: .editorconfig
rwxrwxr-x -> rw-rw-r--: .gitattributes
rwxrwxr-x -> rw-rw-r--: .vscode/launch.json
rwxrwxr-x -> rw-rw-r--: .vscode/tasks.json
rwxrwxr-x -> rw-rw-r--: README.md
rwxrwxr-x -> rw-rw-r--: UNLICENSE.txt

and the status afterwards reports nothing to commit, working tree clean.

The options make it a proper tool rather than a one-off. There is a source option to restore modes from a given tree, and if it is not specified the modes are restored from HEAD when staged is given and otherwise from the index. The worktree and staged flags choose which side is restored, and specifying both restores both. An octal flag prints permissions as numeric octal rather than the rwx letters. A dry-run flag shows what would happen without changing anything, and a quiet flag suppresses the printed changes.

The repository includes the script itself twice over, as mode-restore.sh and as the git-mode-restore entry point, so the command works whether it is invoked through Git or directly.

The history commands, and why xlog is the one worth having

Two of the commands in the collection operate on history rather than on the current state, and both do something the built-in Git commands do not.

git xlog searches history for a string only in added or removed lines. The distinction is the whole point. The ordinary log search, with the pickaxe option, finds commits where a string appears in a diff. Restricting the search to added or removed lines means you find the commit that actually introduced or deleted the text, which is a different question and usually the one you are asking when you are hunting for when a flag was added, when a dependency version changed, or when a line was quietly deleted by a refactor.

git-legacy rebases the whole history of the current HEAD on top of a revision. That is a history rewrite of everything, and the name is the warning. It is the tool you reach for when a commit was made against the wrong base and every commit after it has to move, and it is the tool that will make a shared branch hostile to everyone else. Nothing in the tool stops you running it on a branch other people are pushing to.

The bulk update commands form a pair. git-forward fetches and fast forwards all remote tracking branches, which is the safe version and the one you want on a machine with many remotes where updating them one at a time is tedious. git-force-pull fetches and force pulls remote tracking branches by doing a hard reset, which is the version that discards local commits. Both act on every tracking branch rather than one, so the blast radius is the whole set of remotes you have, and that is the thing to keep in mind before either of them.

The cleanup command, git-gone, deletes local branches that no longer exist on remote, which is the tidy-up you need after a branch has been merged and deleted on the server. It is one of the safer entries in the list, and it is the one most likely to be run regularly.

The main-branch command, git main-branch, gets the name of the main or default branch, which is a small thing that saves a surprising amount of shell scripting when a repository uses something other than master or main, or when the default branch is defined by a remote HEAD rather than by a local branch you happen to have checked out.

How the scripts are discovered, and the licensing

Some commands in the collection are available under two names, and the table shows which are which. Several entries are marked as aliases: git amend amends the currently staged changes, git dir outputs the.git directory path, git gc-all expires the reflog and runs a full garbage collection, and git graph shows a pretty commit graph. The rest are commands in their own right, such as git-gone, git-force-pull, git-forward, git-in-repo and the rest of the state checkers.

The aliasing is done through the aliases.ini file, and the sub-commands through the executable git-* files in the root of the repository. The top level listing makes the structure clear: the git-* files are the implementations, mode-restore.sh is the underlying script for one of them, aliases.ini holds the alias definitions, and everything else is repository furniture. The primary language is recorded as Python, and the.github/ directory and.vscode/ directory are also present.

The licence is the Unlicense, which is a public domain dedication. That is about as permissive as a licence gets, and it is a sensible choice for a set of small scripts whose whole purpose is to be read, copied and modified, because the absence of attribution requirements is what makes the copy-and-paste value proposition work. The UNLICENSE.txt file is in the repository root, and there is no homepage beyond the repository itself.

The last push was on 2026-08-13 and there are no GitHub releases, so the project is distributed as the repository rather than as tagged artefacts. That suits a tool whose scripts you are expected to inspect, and it also means there is no version marker to track what you have installed, so the copy on your PATH is whatever you extracted at the time.

Editorial conclusion

toolgit suits someone who lives in the command line and keeps writing the same shell function for a repository state check or a bulk branch update, because each script is a file you can read and a command that works without an alias or a hook. It does not suit you if you want a single coherent workflow, since there is no plugin manager, no configuration file and no shared command set, and the destructive ones such as git-force-pull are one keystroke away from the safe ones. Before you install it, read the scripts, and on a shared machine remember that the git-* naming means anything on your PATH becomes a Git sub-command, so the directory you add should be one only you control.

Frequently asked questions

How do I install ToolGit?

Extract the project to a folder of your choice and add that folder path to your PATH environment variable. Git will recognize executable git-* files in PATH and allow using them as sub-commands. To also install the aliases, run git config set --append --global include.path path/to/toolgit/aliases.ini.

Which ToolGit commands are useful inside shell scripts?

The ones that return an exit status rather than printing. git-in-repo returns 0 inside a repository, git-is-worktree-clean returns 0 when there are no changes or untracked files, git-is-branch-remote returns 0 when the branches refer to a remote, and git-is-head-detached returns 0 when HEAD is detached.

What does git mode-restore do in ToolGit?

It restores file modes in the index and the worktree, which fixes a tree where every file has become executable, a common result of a filesystem that does not preserve Unix permission bits. It has options for the source tree, the worktree or index side, octal output, a dry run and a quiet mode.

What licence does ToolGit use?

The Unlicense, a public domain dedication with no attribution requirements, which is a sensible choice for small scripts meant to be read, copied and modified. The UNLICENSE.txt file is in the repository root.

Official sources

  1. ahmetsait/toolgit on GitHub
  2. Issues
  3. License: Unlicense
  4. README
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/ahmetsait-toolgit.svg)](https://hysenlabs.com/projects/ahmetsait-toolgit)

Was this useful?