The Day Git Commits Became a Password Puzzle (and How I Solved It)

Hey everyone!

Ever had one of those tech problems that just… won’t… quit? You know, the kind that makes you question your life choices, your career path, and whether you should just go live in a cabin in the woods? Yeah, I had one of those recently, and it involved my good old friend, Git, and a very stubborn GPG password prompt.

The Frustration Begins: “Password, Please… Again?”

It all started innocently enough. I made some changes to look cool in GitHub commits with Verified tag. I was happily coding away, making my usual small, frequent commits (because that’s what good developers do, right?). But every single time I typed git commit -S -m "My awesome changes", my terminal would just… stop. It would hang there, silently judging me, until I eventually realized it was waiting for a GPG password. Again. And again. And again.

Now, I’m all for security, but typing my GPG passphrase for every single commit on my own machine felt like a cruel and unusual punishment. I knew there had to be a way to cache it, to make it remember for at least a reasonable amount of time. I mean, what’s a gpg-agent for if not to be an agent of convenience?

The “Why” Behind the Madness (or, What I Thought Was Happening)

My initial hunch was that my gpg-agent wasn’t configured correctly. I remembered reading about default-cache-ttl and max-cache-ttl settings in gpg-agent.conf. The idea is that you tell GPG to hold onto your passphrase for a longer period, so you’re not constantly re-entering it.

So, my first thought was: “Okay, I need to find gpg-agent.conf and extend those cache times.”

The Troubleshooting Odyssey: A Deep Dive into My Dotfiles

This is where the fun (and the head-scratching) truly began.

  1. Finding gpg-agent.conf: My first hurdle was that the file didn’t even exist in ~/.gnupg/. No problem, I thought, I’ll just create it! So I did, adding the generous default-cache-ttl 34560000 and max-cache-ttl 34560000 (that’s about 400 days, folks!). I also made sure use-agent was enabled in gpg.conf.
  2. Restarting the Agent: After any GPG config change, you need to kill the gpg-agent so it restarts with the new settings. gpgconf --kill gpg-agent became my new favorite command.
  3. The Hanging git commit: This is where things got weird. Instead of asking for a password, git commit just… hung. No prompt, no error, just silence. This usually means the GPG agent is trying to show a prompt but can’t.
  4. Enter pinentry and GPG_TTY: My research pointed to pinentry (the program that displays the password prompt) and the GPG_TTY environment variable. I installed pinentry-mac (because, well, Mac) and configured gpg-agent.conf to use it. I also added export GPG_TTY=$(tty) to my .zshrc to ensure the agent knew which terminal to use.
  5. The gpg2 Error: After all that, I finally got an error message! error: cannot run gpg2: No such file or directory. Progress, right? This meant Git was explicitly looking for gpg2, and it couldn’t find it. My first instinct was to tell Git to use gpg2 globally: git config --global gpg.program gpg2.
  6. The which gpg2 Mystery: But then which gpg2 returned “gpg2 not found,” even though brew install gnupg said it was already installed! This was the most confusing part. Homebrew said it was there, but my shell couldn’t find it.
  7. The PATH Problem: This led me down the rabbit hole of my PATH environment variable. It turned out that while gnupg was installed by Homebrew, the directory where Homebrew puts its executables (/opt/homebrew/bin) wasn’t correctly prioritized in my PATH. Other tools like pyenv and bun were messing with it. The fix involved ensuring eval "$(/opt/homebrew/bin/brew shellenv)" was placed early in my .zshrc.
  8. The Final Revelation: It’s Just gpg, Not gpg2! After all that PATH debugging, I finally ran ls -l /opt/homebrew/Cellar/gnupg/2.4.8/bin/ and discovered the truth: my Homebrew installation of GnuPG 2.4.8 simply named its main executable gpg, not gpg2! The gpg2 error was literally because the file didn’t exist.

My Learnings (and Your Takeaways!)

This whole ordeal was a masterclass in debugging, and here’s what I took away:

  • Read Error Messages Carefully (and Literally!): “No such file or directory” meant exactly that. I was trying to make Git use a program that didn’t exist by that name on my system.
  • Understand Your PATH: The PATH environment variable is fundamental to how your shell finds executables. If which can’t find something, it’s almost always a PATH issue.
  • Homebrew’s brew shellenv is Your Friend: For macOS users, eval "$(/opt/homebrew/bin/brew shellenv)" placed early in your shell config is the most reliable way to ensure Homebrew’s binaries are found.
  • GnuPG Naming Conventions: Be aware that gpg and gpg2 can refer to the same underlying GnuPG 2.x executable, or one might be a symlink to the other, or one might not exist at all depending on your installation. Always check what’s actually there!
  • The gpg-agent is Key: For passphrase caching, the gpg-agent and its gpg-agent.conf settings are paramount.
  • pinentry is the Prompt: If you’re not getting a password prompt, it’s likely a pinentry issue (either not installed, not configured, or GPG_TTY is wrong).

Finally, after changing git config --global gpg.program gpg and ensuring my PATH was clean, I killed the gpg-agent one last time. The next git commit prompted me for my password, and then… glorious silence! No more prompts for days.

It was a journey, but a valuable one. Hopefully, sharing my pain (and solution!) helps someone else avoid a similar headache. Happy committing!


Leave a Reply

Your email address will not be published. Required fields are marked *