PyGitUp is a Python port of
aanand/git-up. It not only
fully covers the abilities of git-up and should be a drop-in replacement,
but also extends it slightly.
git pull has two problems:
- It merges upstream changes by default, when it's really more polite to rebase over them, unless your collaborators enjoy a commit graph that looks like bedhead.
- It only updates the branch you're currently on, which means git push will shout at you for being behind on branches you don't particularly care about right now.
I wasn't able to use the original git-up, because I didn't want to install
a whole Ruby suite just for git-up and even with Ruby installed, there were
some problems running on my Windows machine. So, my reasons for writing
and using this port are:
- Windows support.
- Written in Python ;)
- Install
git-upvia uv:$ uv tool install git-up(or via pip:$ pip install git-up) cdto your project's directory.- Run
git upand enjoy!
Homebrew users can also use brew: brew install pygitup
- Clone the repository and
cdinto it. - Install uv.
- Run
uv syncto set up the development environment. - Run your working copy with
uv run git-up. - Run the tests with
uv run pytest. Add--cov=PyGitUpfor a coverage report, or name a file such asPyGitUp/tests/test_version.pyto run a single test module.
For everything else about contributing, see CONTRIBUTING.md. The
exact commands CI runs are in .github/workflows/ci-workflow.yml.
uv tool install git-up installs into a per-user location and puts
git-up on your %PATH%, so it needs no special privileges.
If you install with pip instead and get Access denied errors, use
pip install --user git-up and make sure the user scripts directory
(%APPDATA%\Python\Scripts) is on your %PATH%.
Python 3.10 and upwards are supported :)
git up -hshows a help message.git up -q/git up --quietsuppresses all output except for error messages.git up --no-fetchskips fetching the remote and rebases all local branches.git up -p/git up --pushpushes the changes after pulling successfully, likegit-up.push.autodoes.git up -V/git up --versionshows the current version and optionally checks for updates (seegit-up.updates.checkbelow).
To configure PyGitUp, you can set options in your git config. Run
git config [--global] git-up.[name] [value] to set one of these
options:
git-up.fetch.prune [*true*|false]: If set totrue,PyGitUpwill append the--pruneoption togit fetchand thus remove any remote tracking branches which no longer exist on the remote (see git fetch --help).git-up.fetch.all [true|*false*]: If set tofalse,PyGitUpwill only fetch remotes for which there is at least one local tracking branch. Setting this option will makegit upalways fetch from all remotes, which is useful if e.g. you use a remote to push to your CI system but never check those branches out.git-up.fetch.progress [true|*false*]: If set totrue, show progress and ref updates reported bygit fetch. This output is suppressed when usinggit up --quiet.git-up.push.auto [true|*false*]: Push the current branch after rebasing and fast-forwarding.git-up.push.all [true|*false*]: Push all branches when auto-pushing.git-up.push.tags [true|*false*]: Push tags when auto-pushing.git-up.rebase.arguments [string]: If set,PyGitUpwill use this string as additional arguments when callinggit rebase. Example:--rebase-mergesto recreate merge commits in the rebased branch.git-up.rebase.auto [*true*|false]: If set tofalse,PyGitUpwon't rebase your branches for you but notify you that they diverged. This can be useful if you have a lot of in-progress work that you don't want to deal with at once, but still want to update other branches.git-up.rebase.log-hook [cmd]: Runscmdevery time a branch is rebased or fast-forwarded, with the name of the branch being updated as$1and the name of the branch it is updated to as$2. The hook runs before the update, so$1still resolves to the old head while$2resolves to the new one. This can be used to view logs or diffs of incoming changes. Example:echo "changes on $1:"; git log --oneline --decorate $1..$2.git-up.rebase.show-hashes [true|*false*]: If set totrue,PyGitUpwill show the hashes of the current commit (or the point where the rebase starts) and the target commit likegit pulldoes.git-up.rebase.conflict-resolver [cmd]: If set,PyGitUpwill run this command when a rebase conflict occurs. It runs in the repository's working directory while the rebase is still in progress, so the command can inspect the conflict itself (e.g. viagit status,git diff) and is expected to resolve all conflicts, stage the files, and rungit rebase --continue.Afterwards
git upchecks that the branch actually contains the target commit. Only then does it continue to the next branch. If the command exits non-zero, or exits 0 without completing the rebase (for example because it gave up and rangit rebase --abort),git upreports the branch as unresolved and stops, leaving it for manual resolution.The command is only run for genuine conflicts. Rebases that fail for other reasons — untracked files that would be overwritten, an invalid
git-up.rebase.arguments— report their original error instead.Environment variables
GITUP_BRANCH,GITUP_TARGET, andGITUP_REPO_PATHare also set for the resolver process. Note that the command is run through the system shell, which iscmd.exeon Windows — the single-quoted examples below need double quotes there.Examples:
git config git-up.rebase.conflict-resolver "claude -p 'Resolve the current git rebase conflicts, then run git rebase --continue'" git config git-up.rebase.conflict-resolver "claude -p 'Resolve the current git rebase conflicts, then run git rebase --continue' --dangerously-skip-permissions" git config git-up.rebase.conflict-resolver "aider --message 'Resolve the current git rebase conflicts, then run git rebase --continue'"
Note: AI agents like
claudemay prompt for tool approvals by default. Use--dangerously-skip-permissionsto run fully autonomously, or--allowedTools 'Edit,Read,Bash,Write,Glob,Grep'to scope the permissions.git-up.updates.check [*true*|false]: When runninggit up --version, it shows the version number and checks for updates. If you feel uncomfortable with it, just set it tofalseto turn off the checks.
The original git-up has been written by aanand:
aanand/git-up/.
- Support rebasing branches that are checked out in worktrees. Branches whose worktree has an operation in progress are skipped. Thanks @abersnaze for Pull Request #145.
- Add
git-up.rebase.conflict-resolverto run a command when a rebase conflict occurs. Thanks @abersnaze for Pull Request #146. - Add
git-up.fetch.progressto makegit fetchmore verbose. Thanks @agido-malter for Pull Request #148. - Fix a command injection in
git-up.rebase.log-hook: branch and remote names are now passed to the hook through the environment instead of a command line, so names containing shell metacharacters are no longer executed as commands. This affects both thecmd.exeand theshcode path. - Fix a crash on
gitoutput that is not valid UTF-8. - Fix worktree detection on MinGW.
- Update dependencies.
- Switch to
packaginginstead ofpkg_resources. Closes #139. - Fix top-level directory detection on MinGW.
- Update dependencies and switch to the uv package manager.
- Improve logging when updating large repositories. Thanks @bdmartin for Pull Request #132.
- Drop support for Python 3.7
- Add support for Python 3.11. Thanks @hugovk for Pull Request #118.
- Switch to Python's
argparsefor CLI argument parsing. Thanks @ekohl for Pull Request #96.
- Drop support for Python 3.6 (following GitPython)
- Update PyGitUp's CLI argument parser Click to version 8.0. Thanks @hugovk for Pull Request #109.
- Update other dependencies
- Remove old Python 2 code. Thanks @hugovk for Pull Request #104.
- Update dependencies
- Drop Python 2 support in order to fix Issue 102
- Drop Ruby Bundler integration
- Migrate tests to
py.test
- Upgrade to click>=7.0.0. Thanks @no-preserve-root for Pull Request #87.
- Skip stashing changes when possible. Thanks @Chronial for Pull Request #86.
- Added faster fast-forward on branches that are not checked out. Thanks @Chronial for Pull Request #83.
- Fixed version requirement for Click dependency (#82).
- Fixed crash on Cygwin with rebase log hook enabled (#80).
- Added auto-push support. Thanks @WoLpH for Pull Request #74.
- Added shorthand commandline arguments (
-V, -q, -h, see #73).
- 3rd party dependencies have been updated (see #65).
- Fixed problems when working with branches containing hash signs in their name (#55).
- No longer installs a now unneeded script on
pip install. Thanks @ekohl for Pull Request #60.
- Fixed a bug when working with
git worktree(#58).
- Switched the command line argument parsing library (#53).
- Include tests in PyPI distribution (#51).
- 3rd party dependencies have been updated.
- Dependencies on 3rd party libraries have been loosened to better interact with other installed packages. Thanks MaximilianR for Pull Request #45.
- Added an command line argument to turn of fetching (
--no-fetch). Thanks @buoto for Pull Request #46. - Don't show a stacktrace anymore when stashing fails (#35).
- Fixed a bug that caused problems with submodules if the submodule had unstashed changes/ Thanks @Javex for Pull Request #27.
- Fixed a bug when showing the version on Python 3 #34.
- Support for Python 3 has been added. Thanks @r4ts0n for Pull Request #23 and @Byron for quickly merging a Pull Request in GitPython and releasing a new version on which this release depends.
- Now updates submodules when called from
git submodule foreach(#8).
- Fixed a problem with
setuptools 8.x(#19). - 3rd party dependencies have been updated
- Added an option to show hashes when fast-forwarding/rebasing like
git pulldoes (git-up.rebase.show-hashes). - Fixed a bug when having branches with both local tracking branches and remote tracking branches (#17).
- 3rd party dependencies have been updated to fix a problem with a 3rd party library (#18).
- Fixed some typos in README and
PyGitUpoutput. - 3rd party dependencies have been updated.
ahead of upstreammessages are now cyan (see aanand/git-up#60).- Fixed problem when using % in the log hook (#11).
- Fixed problems with the dependency declaration.
- Fix for #7 (AttributeError: 'GitUp' object has no attribute 'git') introduced by v1.1.0.
Prior to v1.1.0,
PyGitUptried to guess the upstream branch for a local branch by looking for a branch on any remote with the same name. With v1.1.0,PyGitUpstops guessing and uses the upstream branch config instead.This by the way fixes issue #6 (
git updoesn't work with local only branches).Note: This change may break setups, where a local branch accidentally has the same name as a remote branch without any tracking information set. Prior to v1.1.0,
git upwould still fetch and rebase from the remote branch. If you run into troubles with such a setup, setting tracking information usinggit branch -u <remote>/<remote branch> <local branch>should help.3rd party dependencies have been updated.
Allows to run
git up --versionfrom non-git dirs, too.
Finally PyGitUp reaches 1.0.0. You can consider it stable now :)
- Added a comprehensive test suite, now with a coverage of about 90%.
- Lots of code cleanup.
- Added option
-hto display a help screen (--helpwon't work, becausegitcatches this option and handles it beforePyGitUpcan do). - Added option
--versionto show, what version ofPyGitUpis running. Also checks for updates (can be disabled, see configuration). - Added option
--quietto be quiet and only display error messages.
- Fixed issue #4 (ugly exception if remote branch has been deleted).
- Fixed issue #3 (didn't return to previous branch).
- Fixed problem: check-bundler.rb has not been installed when installing via PyPI (problems with setup.py).
- Incorporated aanand/git-up#41: Support for
bundle install --localandrbenv rehash. - Fixed issue #1 (strange output buffering when having multiple remotes to fetch from).
- Some under-the-hood improvements.
- Initial Release
