[{"data":1,"prerenderedAt":443},["ShallowReactive",2],{"NoscriptNav_XrRK2e2e8meJ0jKVGkb5ULGQDVi3UiFQ9nupAr7Yns":3,"\u002Fideas\u002Fmaking-git-pkgs-feel-like-git":8},["Island",4],{"key":5,"result":6},"NoscriptNav_XrRK2e2e8meJ0jKVGkb5ULGQDVi3UiFQ9nupAr7Yns",{"head":7},{},{"id":9,"title":10,"authors":11,"body":13,"canonicalUrl":430,"canonicalWebsiteName":431,"category":432,"date":433,"description":434,"extension":435,"featured":436,"fullWidthLayout":436,"image":437,"imageAlt":437,"location":437,"meta":438,"metaImage":437,"navigation":204,"path":439,"seo":440,"stem":441,"venue":437,"venueUrl":437,"__hash__":442},"ideas\u002Fideas\u002Fmaking-git-pkgs-feel-like-git.md","Making git-pkgs feel like Git",[12],"andrew",{"type":14,"value":15,"toc":425},"minimark",[16,39,48,56,89,96,107,122,127,130,156,160,167,269,279,283,286,383,386,408,421],[17,18,21,22,26,27,32,33,38],"div",{"className":19},[20],"alert","\n  ",[23,24,25],"strong",{},"Update:","\n  git-pkgs has been\n  ",[28,29,31],"a",{"href":30},"\u002Fideas\u002Frewriting-git-pkgs-in-go","rewritten in Go","\n  and now lives at\n  ",[28,34,37],{"href":35,"target":36},"https:\u002F\u002Fgithub.com\u002Fgit-pkgs\u002Fgit-pkgs","_blank","github.com\u002Fgit-pkgs\u002Fgit-pkgs",".\n",[40,41,42,43,47],"p",{},"Since releasing ",[28,44,46],{"href":45},"\u002Ftools\u002Fgit-pkgs-explore-your-dependency-history","git-pkgs"," I've been focused on one thing: making it feel like you're using git, not some tool that happens to work with git.",[40,49,50,51,55],{},"Git has strong conventions for colors, pagers, environment variables, and configuration that users expect without thinking about them. I wrote about ",[28,52,54],{"href":53},"\u002Freports\u002Fextending-git-functionality","extending git"," a while back, covering the extension points git provides. But knowing the patterns exist is different from implementing them well. If your subcommand ignores these conventions, it feels foreign. Getting them right is fiddly, but people notice when you skip them.",[40,57,58,59,63,64,67,68,71,72,75,76,75,79,75,82,85,86,88],{},"Colors now respect ",[60,61,62],"code",{},"NO_COLOR",", ",[60,65,66],{},"color.ui",", and a tool-specific ",[60,69,70],{},"color.pkgs"," setting. Pagers follow git's precedence chain: ",[60,73,74],{},"GIT_PAGER",", then ",[60,77,78],{},"core.pager",[60,80,81],{},"PAGER",[60,83,84],{},"less -FRSX",". Most tools just check ",[60,87,81],{}," and call it done, but users who've configured git specifically expect consistent behavior.",[40,90,91,92,95],{},"Configuration uses git's own config system (",[60,93,94],{},"git config --add pkgs.ecosystems rubygems",") rather than inventing a new file format, so settings travel with your git configuration and work in CI the same way as locally.",[40,97,98,99,102,103,106],{},"The original version required you to remember to run ",[60,100,101],{},"git pkgs update"," after commits. Now ",[60,104,105],{},"git pkgs init"," installs post-commit and post-merge hooks by default, appending to existing hooks rather than clobbering them.",[40,108,109,110,113,114,117,118,121],{},"I'm also working on bash and zsh tab completion, so ",[60,111,112],{},"git pkgs h\u003Ctab>"," expands to ",[60,115,116],{},"git pkgs history"," and ",[60,119,120],{},"git pkgs blame --\u003Ctab>"," shows the available flags.",[123,124,126],"h2",{"id":125},"new-commands","New commands",[40,128,129],{},"Three new commands since launch:",[131,132,133,144,150],"ul",{},[134,135,136,139,140,143],"li",{},[60,137,138],{},"git pkgs show HEAD~1"," displays dependency changes for a single commit (like ",[60,141,142],{},"git show"," but for dependencies)",[134,145,146,149],{},[60,147,148],{},"git pkgs log --author=dependabot"," lists commits that changed dependencies with author and change counts",[134,151,152,155],{},[60,153,154],{},"git pkgs where nokogiri"," finds where a package is declared in your manifest files",[123,157,159],{"id":158},"diff-driver","Diff driver",[40,161,162,163,166],{},"The feature I'm most pleased with is ",[60,164,165],{},"git pkgs diff-driver",", which installs a git textconv driver that transforms lockfile diffs into dependency changes:",[168,169,174],"pre",{"className":170,"code":171,"language":172,"meta":173,"style":173},"language-bash shiki shiki-themes github-light github-dark","$ git pkgs diff-driver --install\n\n$ git diff HEAD~1 -- Gemfile.lock\nModified: nokogiri 1.16.7 -> 1.18.1\nModified: racc 1.8.1 -> 1.8.2 (nokogiri)\n","bash","",[60,175,176,199,206,225,248],{"__ignoreMap":173},[177,178,181,185,189,192,195],"span",{"class":179,"line":180},"line",1,[177,182,184],{"class":183},"sScJk","$",[177,186,188],{"class":187},"sZZnC"," git",[177,190,191],{"class":187}," pkgs",[177,193,194],{"class":187}," diff-driver",[177,196,198],{"class":197},"sj4cs"," --install\n",[177,200,202],{"class":179,"line":201},2,[177,203,205],{"emptyLinePlaceholder":204},true,"\n",[177,207,209,211,213,216,219,222],{"class":179,"line":208},3,[177,210,184],{"class":183},[177,212,188],{"class":187},[177,214,215],{"class":187}," diff",[177,217,218],{"class":187}," HEAD~1",[177,220,221],{"class":197}," --",[177,223,224],{"class":187}," Gemfile.lock\n",[177,226,228,231,234,237,241,245],{"class":179,"line":227},4,[177,229,230],{"class":183},"Modified:",[177,232,233],{"class":187}," nokogiri",[177,235,236],{"class":197}," 1.16.7",[177,238,240],{"class":239},"sVt8B"," -",[177,242,244],{"class":243},"szBVR",">",[177,246,247],{"class":197}," 1.18.1\n",[177,249,251,253,256,259,261,263,266],{"class":179,"line":250},5,[177,252,230],{"class":183},[177,254,255],{"class":187}," racc",[177,257,258],{"class":197}," 1.8.1",[177,260,240],{"class":239},[177,262,244],{"class":243},[177,264,265],{"class":197}," 1.8.2",[177,267,268],{"class":239}," (nokogiri)\n",[40,270,271,272,63,275,278],{},"Instead of 200 lines of lockfile internals, you see what actually changed. It works for 29 lockfile formats, and once installed it applies to ",[60,273,274],{},"git diff",[60,276,277],{},"git log -p",", and anywhere else git shows diffs.",[123,280,282],{"id":281},"benchmarks","Benchmarks",[40,284,285],{},"I've been testing against popular open source repos to find edge cases and measure performance:",[287,288,289,311],"table",{},[290,291,292],"thead",{},[293,294,295,299,302,305,308],"tr",{},[296,297,298],"th",{},"Repository",[296,300,301],{},"Commits",[296,303,304],{},"Dependencies",[296,306,307],{},"Init Time",[296,309,310],{},"DB Size",[312,313,314,332,349,366],"tbody",{},[293,315,316,320,323,326,329],{},[317,318,319],"td",{},"sinatra\u002Fsinatra",[317,321,322],{},"4,666",[317,324,325],{},"300",[317,327,328],{},"2.7s",[317,330,331],{},"1.7MB",[293,333,334,337,340,343,346],{},[317,335,336],{},"jekyll\u002Fjekyll",[317,338,339],{},"11,857",[317,341,342],{},"371",[317,344,345],{},"5.2s",[317,347,348],{},"3.6MB",[293,350,351,354,357,360,363],{},[317,352,353],{},"pallets\u002Fflask",[317,355,356],{},"5,474",[317,358,359],{},"240",[317,361,362],{},"3.9s",[317,364,365],{},"1.9MB",[293,367,368,371,374,377,380],{},[317,369,370],{},"mastodon\u002Fmastodon",[317,372,373],{},"20,195",[317,375,376],{},"5,346",[317,378,379],{},"238s",[317,381,382],{},"105MB",[40,384,385],{},"Libraries process at 1,000-2,500 commits per second. Mastodon is slower because 26% of its commits touch a manifest file, compared to 3-6% for most repos. One in four commits changing dependencies is a lot of supply chain churn. Once the initial index is built, all the query commands are snappy since they're just SQLite queries, but the init step could still use optimization for larger repos.",[40,387,388,389,395,396,401,402,407],{},"I've submitted a lightning talk to the ",[28,390,394],{"href":391,"rel":392},"https:\u002F\u002Ffosdem.org\u002F2026\u002Fnews\u002F2025-12-10-dev-random\u002F",[393],"nofollow","\u002Fdev\u002Frandom"," track at FOSDEM, and I'm running the ",[28,397,400],{"href":398,"rel":399},"https:\u002F\u002Fnesbitt.io\u002F2025\u002F12\u002F20\u002Ffosdem-2026-package-managers-devroom-schedule",[393],"package managers devroom"," if you want to chat in person. I'm working on enriching the database with metadata from package registries using ",[28,403,406],{"href":404,"rel":405},"https:\u002F\u002Fgithub.com\u002Fpackage-url\u002Fpurl-spec",[393],"PURLs",", which would enable CVE history (which vulnerabilities affected your dependencies over time) and SBOM export from any commit.",[40,409,410,411,416,417,420],{},"If you tried it when it launched and hit rough edges, it's smoother now. I'd love to hear what's working and what's missing, and contributions are welcome. There's a ",[28,412,415],{"href":413,"rel":414},"https:\u002F\u002Fgithub.com\u002Fgit-pkgs\u002Fgit-pkgs\u002Fissues\u002F10",[393],"good first issue"," for adding an ",[60,418,419],{},"--exclude-bots"," flag if you want to get involved.",[422,423,424],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":173,"searchDepth":201,"depth":201,"links":426},[427,428,429],{"id":125,"depth":201,"text":126},{"id":158,"depth":201,"text":159},{"id":281,"depth":201,"text":282},"https:\u002F\u002Fnesbitt.io\u002F2026\u002F01\u002F04\u002Fmaking-git-pkgs-feel-like-git","nesbitt.io","tooling","2026-01-04","What it takes to make a git subcommand feel native.","md",false,null,{},"\u002Fideas\u002Fmaking-git-pkgs-feel-like-git",{"title":10,"description":434},"ideas\u002Fmaking-git-pkgs-feel-like-git","GRqSwk1mFVHbq9vQda6_1IE8V5fvSGPWUEYKR6bI9Wc",1780596105055]