A git subcommand for tracking package dependencies across git history. Analyzes your repository to show when dependencies were added, modified, or removed, who made those changes, and why. This is a rewrite of the original Ruby version.
Installation · Quick start · Commands · Configuration · Contributing
Your lockfile shows what dependencies you have, but it doesn't show how you got here, and git log Gemfile.lock is useless noise. git-pkgs indexes your dependency history into a queryable database so you can ask questions like: when did we add this? who added it? what changed between these two releases? has anyone touched this in the last year?
For best results, commit your lockfiles. Manifests show version ranges but lockfiles show what actually got installed, including transitive dependencies.
It works across many ecosystems (Gemfile, package.json, Dockerfile, GitHub Actions workflows) giving you one unified history instead of separate tools per ecosystem. The database lives in your .git directory where you can use it in CI to catch dependency changes in pull requests.
The core commands (list, history, blame, diff, stale, etc.) work entirely from your git history with no network access. Additional commands fetch external data: vulns checks OSV for known CVEs, while outdated and licenses query ecosyste.ms for registry metadata.
brew tap git-pkgs/git-pkgs
brew install git-pkgsOr download a binary from the releases page.
Or build from source:
go install github.com/git-pkgs/git-pkgs@latestcd your-repo
git pkgs init # analyze history (one-time)
git pkgs list # show current dependencies
git pkgs stats # see overview
git pkgs blame # who added each dependency
git pkgs history # all dependency changes over time
git pkgs history rails # track a specific package
git pkgs why rails # why was this added?
git pkgs diff # HEAD vs working tree
git pkgs diff --from=HEAD~10 # what changed recently?
git pkgs diff main..feature # compare branches
git pkgs vulns # scan for known CVEs
git pkgs vulns blame # who introduced each vulnerability
git pkgs outdated # find packages with newer versions
git pkgs update # update all dependencies
git pkgs add lodash # add a packagegit pkgs initWalks through git history and builds a SQLite database of dependency changes, stored in .git/pkgs.sqlite3.
Options:
--branch=NAME- analyze a specific branch (default: default branch)--since=SHA- start analysis from a specific commit--force- rebuild the database from scratch--no-hooks- skip installing git hooks (hooks are installed by default)
git pkgs infoShows database size and row counts:
Database Info
========================================
Location: /path/to/repo/.git/pkgs.sqlite3
Size: 8.3 MB
Row Counts
----------------------------------------
Branches 1
Commits 3988
Branch-Commits 3988
Manifests 9
Dependency Changes 4732
Dependency Snapshots 28239
----------------------------------
Total 40957
git pkgs list
git pkgs list --commit=abc123
git pkgs list --ecosystem=rubygems
git pkgs list --manifest=Gemfile
git pkgs list --stateless # parse manifests directly, no database neededExample output:
Gemfile (rubygems):
bootsnap >= 0 [runtime]
bootstrap = 4.6.2 [runtime]
bugsnag >= 0 [runtime]
rails = 8.0.1 [runtime]
sidekiq >= 0 [runtime]
...
git pkgs history # all dependency changes
git pkgs history rails # changes for a specific package
git pkgs history --author=alice # filter by author
git pkgs history --since=2024-01-01 # changes after date
git pkgs history --ecosystem=rubygems # filter by ecosystemShows when packages were added, updated, or removed:
History for rails:
2016-12-16 Added = 5.0.0.1
Commit: e323669 Hello World
Author: Andrew Nesbitt <andrew@example.com>
Manifest: Gemfile
2016-12-21 Updated = 5.0.0.1 -> = 5.0.1
Commit: 0c70eee Update rails to 5.0.1
Author: Andrew Nesbitt <andrew@example.com>
Manifest: Gemfile
2024-11-21 Updated = 7.2.2 -> = 8.0.0
Commit: 86a07f4 Upgrade to Rails 8
Author: Andrew Nesbitt <andrew@example.com>
Manifest: Gemfile
Show who added each current dependency:
git pkgs blame
git pkgs blame --ecosystem=rubygemsExample output:
Gemfile (rubygems):
bootsnap Andrew Nesbitt 2018-04-10 7da4369
bootstrap Andrew Nesbitt 2018-08-02 0b39dc0
bugsnag Andrew Nesbitt 2016-12-23 a87f1bf
factory_bot Lewis Buckley 2017-12-25 f6cceb0
faraday Andrew Nesbitt 2021-11-25 98de229
jwt Andrew Nesbitt 2018-09-10 a39f0ea
octokit Andrew Nesbitt 2016-12-16 e323669
omniauth-rails_csrf_protection dependabot[bot] 2021-11-02 02474ab
rails Andrew Nesbitt 2016-12-16 e323669
sidekiq Mark Tareshawty 2018-02-19 29a1c70
git pkgs stats
git pkgs stats --by-author # who added the most dependencies
git pkgs stats --ecosystem=npm # filter by ecosystem
git pkgs stats --since=2024-01-01 # changes after date
git pkgs stats --until=2024-12-31 # changes before dateExample output:
Dependency Statistics
========================================
Branch: main
Commits analyzed: 3988
Commits with changes: 2531
Current Dependencies
--------------------
Total: 250
rubygems: 232
actions: 14
docker: 4
Dependency Changes
--------------------
Total changes: 4732
added: 391
modified: 4200
removed: 141
Most Changed Dependencies
-------------------------
rails (rubygems): 135 changes
pagy (rubygems): 116 changes
nokogiri (rubygems): 85 changes
puma (rubygems): 73 changes
Manifest Files
--------------
Gemfile (rubygems): 294 changes
Gemfile.lock (rubygems): 4269 changes
.github/workflows/ci.yml (actions): 36 changes
git pkgs why railsThis shows the commit that added the dependency along with the author and message.
git pkgs tree
git pkgs tree --ecosystem=rubygemsThis shows dependencies grouped by type (runtime, development, etc).
git pkgs stale # list deps by how long since last touched
git pkgs stale --days=365 # only show deps untouched for a year
git pkgs stale --ecosystem=npm # filter by ecosystemShows dependencies sorted by how long since they were last changed in your repo. Useful for finding packages that may have been forgotten or need review.
git pkgs outdated # show packages with newer versions available
git pkgs outdated --major # only major version updates
git pkgs outdated --minor # minor and major updates (skip patch)
git pkgs outdated --at v2.0 # what was outdated when we released v2.0?
git pkgs outdated --at 2024-03-01 # what was outdated on this date?
git pkgs outdated --stateless # no database neededChecks package registries (via ecosyste.ms) to find dependencies with newer versions available. Major updates are shown in red, minor in yellow, patch in cyan.
The --at flag enables time travel: pass a date (YYYY-MM-DD) or any git ref (tag, branch, commit SHA) to see what was outdated at that point in time. When given a git ref, it uses the commit's date.
git-pkgs can run package manager commands for you, detecting the right tool from your lockfiles:
git pkgs install # install from lockfile
git pkgs install --frozen # CI mode (fail if lockfile would change)
git pkgs add lodash # add a package
git pkgs add rails --dev # add as dev dependency
git pkgs add lodash 4.17.21 # add specific version
git pkgs remove lodash # remove a package
git pkgs update # update all dependencies
git pkgs update lodash # update specific packageSupports 35 package managers including npm, pnpm, yarn, bun, deno, bundler, gem, cargo, go, pip, uv, poetry, conda, composer, mix, rebar3, pub, cocoapods, swift, nuget, maven, gradle, sbt, cabal, stack, opam, luarocks, nimble, shards, cpanm, lein, vcpkg, conan, helm, and brew. The package manager is detected from lockfiles in the current directory.
Use -m to override detection, -x to pass extra arguments to the underlying tool:
git pkgs install -m pnpm # force pnpm
git pkgs install -x --legacy-peer-deps # pass extra flags
git pkgs add lodash -x --save-exact # npm --save-exactOpen the source code of an installed package in your editor:
git pkgs browse lodash # open in $EDITOR
git pkgs browse lodash --path # just print the path
git pkgs browse lodash --open # open in file browser
git pkgs browse serde -m cargo # specify managerUse --path for scripting:
cat $(git pkgs browse lodash --path)/package.jsonReturns exit code 2 if the package manager doesn't support path lookup.
git pkgs licenses # show license for each dependency
git pkgs licenses --permissive # flag copyleft licenses
git pkgs licenses --allow=MIT,Apache-2.0 # explicit allow list
git pkgs licenses --group # group output by license
git pkgs licenses --stateless # no database neededFetches license information from package registries. Exits with code 1 if violations are found, making it suitable for CI.
Scan dependencies for known CVEs using the OSV database. Because git-pkgs tracks the full history of every dependency change, it provides context that static scanners can't: who introduced a vulnerability, when it was fixed, and how long you were exposed.
git pkgs vulns # scan current dependencies
git pkgs vulns v1.0.0 # scan at a tag, branch, or commit
git pkgs vulns -s high # only critical and high severity
git pkgs vulns -e npm # filter by ecosystem
git pkgs vulns -f sarif # output for GitHub code scanningSubcommands for historical analysis:
git pkgs vulns blame # who introduced each vulnerability
git pkgs vulns blame --all-time # include fixed vulnerabilities
git pkgs vulns praise # who fixed vulnerabilities
git pkgs vulns praise --summary # author leaderboard
git pkgs vulns exposure # remediation metrics (CRA compliance)
git pkgs vulns diff main feature # compare vulnerability state between refs
git pkgs vulns log # commits that introduced or fixed vulns
git pkgs vulns history lodash # vulnerability timeline for a package
git pkgs vulns show CVE-2024-1234 # details about a specific CVEOutput formats: text (default), json, and sarif. SARIF integrates with GitHub Advanced Security:
- run: git pkgs vulns --stateless -f sarif > results.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarifVulnerability data is cached locally and refreshed automatically when stale (>24h). Use git pkgs vulns sync --refresh to force an update.
Show SHA256 hashes from lockfiles. Modern lockfiles include checksums that verify package contents haven't been tampered with.
git pkgs integrity # show hashes for current dependencies
git pkgs integrity --drift # detect same version with different hashes
git pkgs integrity -f json # JSON output
git pkgs integrity --stateless # no database neededThe --drift flag scans your history for packages where the same version has different integrity hashes, which could indicate a supply chain issue.
Export dependencies as a Software Bill of Materials in CycloneDX or SPDX format:
git pkgs sbom # CycloneDX JSON (default)
git pkgs sbom --type spdx # SPDX JSON
git pkgs sbom -f xml # XML instead of JSON
git pkgs sbom --name my-project # custom project name
git pkgs sbom --stateless # no database neededIncludes package URLs (purls), versions, and licenses (fetched from registries). Use --skip-enrichment to omit license lookups.
git pkgs diff # HEAD vs working tree
git pkgs diff --from=abc123 --to=def456 # between two commits
git pkgs diff --from=HEAD~10 # HEAD~10 vs working tree
git pkgs diff main..feature --stateless # no database neededWith no arguments, compares HEAD against the working tree (like git diff). Shows added, removed, and modified packages with version info.
Compare dependencies between two manifest files directly, without a git repository or database:
git pkgs diff-file Gemfile.lock.old Gemfile.lock
git pkgs diff-file /path/to/project-a/package.json /path/to/project-b/package.json
git pkgs diff-file before.lock after.lock --filename=Gemfile.lock # override type detection
git pkgs diff-file old.lock new.lock -f json # JSON outputUseful for comparing dependencies across different projects, archived source code without git history, or repositories using other version control systems.
git pkgs show # show dependency changes in HEAD
git pkgs show abc123 # specific commit
git pkgs show HEAD~5 # relative ref
git pkgs show --stateless # no database neededLike git show but for dependencies. Shows what was added, modified, or removed in a single commit.
git pkgs where rails # find in manifest files
git pkgs where lodash -C 2 # show 2 lines of context
git pkgs where express --ecosystem=npmShows which manifest files declare a package and the exact line:
Gemfile:5:gem "rails", "~> 7.0"
Gemfile.lock:142: rails (7.0.8)
Like grep but scoped to manifest files that git-pkgs knows about.
git pkgs log # recent commits with dependency changes
git pkgs log --author=alice # filter by author
git pkgs log -n 50 # show more commitsLike git log but only shows commits that changed dependencies, with the changes listed under each commit.
Find when a dependency-related change was introduced using binary search, similar to git bisect but only considering commits that changed dependencies:
git pkgs bisect start
git pkgs bisect bad HEAD # current version has the problem
git pkgs bisect good v1.0.0 # this version was fine
# git-pkgs checks out a commit with dependency changes
git pkgs bisect good # or bad, depending on your test
# repeat until found
git pkgs bisect reset # end sessionAutomate with a script:
git pkgs bisect start HEAD v1.0.0
git pkgs bisect run sh -c 'capslock | grep -q NETWORK && exit 1 || exit 0'This finds when dependencies gained NETWORK capabilities. Exit codes: 0 = good, 1-124 = bad, 125 = skip.
Find when a GPL license was introduced:
git pkgs bisect start HEAD v1.0.0
git pkgs bisect run sh -c 'git pkgs licenses --allow=MIT,Apache-2.0 --stateless >/dev/null 2>&1'Narrow the search with filters:
git pkgs bisect start --ecosystem=npm HEAD v1.0.0 # only npm changes
git pkgs bisect start --package=lodash HEAD v1.0.0 # only lodash changes
git pkgs bisect start --manifest=package.json HEAD v1.0.0See docs/bisect.md for detailed examples and use cases.
After the initial analysis, the database updates automatically via git hooks installed during init. You can also update manually:
git pkgs reindexTo manage hooks separately:
git pkgs hooks # show hook status
git pkgs hooks --install # install hooks
git pkgs hooks --uninstall # remove hooksAfter updating git-pkgs, you may need to rebuild the database if the schema has changed:
git pkgs upgradeThis is detected automatically and you'll see a message if an upgrade is needed.
git pkgs schema # human-readable table format
git pkgs schema --format=sql # CREATE TABLE statements
git pkgs schema --format=json # JSON structure
git pkgs schema --format=markdown # markdown tablesYou can run git-pkgs in CI to show dependency changes in pull requests. Use --stateless to skip database initialization for faster runs:
# .github/workflows/deps.yml
name: Dependencies
on: pull_request
jobs:
diff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: |
curl -L https://github.com/git-pkgs/git-pkgs/releases/latest/download/git-pkgs-linux-amd64 -o git-pkgs
chmod +x git-pkgs
- run: ./git-pkgs diff --from=origin/${{ github.base_ref }} --to=HEAD --statelessInstall a git textconv driver that shows semantic dependency changes instead of raw lockfile diffs:
git pkgs diff-driver --installNow git diff on lockfiles shows a sorted dependency list instead of raw lockfile changes:
diff --git a/Gemfile.lock b/Gemfile.lock
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,3 +1,3 @@
+kamal 1.0.0
-puma 5.0.0
+puma 6.0.0
rails 7.0.0
-sidekiq 6.0.0Use git diff --no-textconv to see the raw lockfile diff. To remove: git pkgs diff-driver --uninstall
Enable tab completion for commands:
# Bash: add to ~/.bashrc
eval "$(git pkgs completions bash)"
# Zsh: add to ~/.zshrc
eval "$(git pkgs completions zsh)"
# Or auto-install to standard completion directories
git pkgs completions installgit-pkgs respects standard git configuration.
Colors are enabled when writing to a terminal. Disable with NO_COLOR=1, git config color.ui never, or git config color.pkgs never for git-pkgs only.
Pager follows git's precedence: GIT_PAGER env, core.pager config, PAGER env, then less -FRSX. Use --no-pager flag or git config core.pager cat to disable.
Ecosystem filtering lets you limit which package ecosystems are tracked:
git config --add pkgs.ecosystems rubygems
git config --add pkgs.ecosystems npm
git pkgs info --ecosystems # show enabled/disabled ecosystemsIgnored paths let you skip directories or files from analysis:
git config --add pkgs.ignoredDirs third_party
git config --add pkgs.ignoredFiles test/fixtures/package.jsonDirect registry mode skips the ecosyste.ms API and queries package registries directly. Useful for private registries or airgapped environments:
git config pkgs.direct true # enable globally
git config --local pkgs.direct true # enable for this repo only
GIT_PKGS_DIRECT=1 git pkgs outdated # one-off via environmentBy default, git-pkgs uses a hybrid approach: packages with a repository_url qualifier in their PURL (indicating a private registry) are queried directly, while public packages go through ecosyste.ms for efficiency.
Private registries and proxies: For commands that query registries (outdated, licenses, SBOM enrichment), git-pkgs extracts registry URLs from lockfiles when available:
- npm:
package-lock.json,yarn.lock,pnpm-lock.yaml,bun.lock(fromresolvedURLs) - pypi:
Pipfile.lock,poetry.lock,uv.lock(from source configuration) - cargo:
Cargo.lock(fromsourcefield) - composer:
composer.lock(fromdist.url) - gem:
Gemfile.lock(fromremote:sections)
If your lockfile points to a private registry (like Artifactory or GitHub Packages), those URLs are used automatically. However, git-pkgs doesn't currently read config files like .npmrc or .pypirc for registry URLs or credentials. Authenticated private registries aren't supported yet for registry queries.
Commands that run package managers (install, add, remove, update) delegate to the actual CLI tools, which respect their native configuration. See the managers documentation for details.
Environment variables:
GIT_DIR- git directory location (standard git variable)GIT_PKGS_DB- database path (default:.git/pkgs.sqlite3)GIT_PKGS_DIRECT- set to1to query registries directly (skip ecosyste.ms)
git-pkgs uses github.com/git-pkgs/manifests for parsing, supporting:
Actions, Bazel, Cargo, CocoaPods, Composer, Go, Hex, Maven, npm, NuGet, Pub, PyPI, RubyGems, and more.
Bug reports, feature requests, and pull requests are welcome. If you're unsure about a change, open an issue first to discuss it.
MIT