Conditional Git Configuration
blog.scottlowe.org
blog.scottlowe.org
I wrote it up here, if that sounds useful: https://www.brantonboehm.com/code/conditional-git-config/
For years I've been using [direnv](https://direnv.net/) for this, setting environment variables which git picks up. This looks like a more feature complete equivalent, although to be honest I only really need switching of committer email and the SSH key used.
it's much easier to have a conditional git config in ~/.gitconfig that modifies the git ssh command to explicitly use a different ssh key.
Match originalhost bitbucket.org exec "[[ ${PWD}/ == ${HOME}/repos/work/* ]]"
IdentityFile ~/.ssh/keys/work
User me-work
Match originalhost bitbucket.org exec "[[ ${PWD}/ == ${HOME}/repos/personal/* ]]"
IdentityFile ~/.ssh/keys/personal
User me Host project-foo
Hostname github.com
User git
IdentityFile ~/.ssh/some_key
Then I'll pull the project with: git clone project-foo:/Foo/project.gitGenuinely curios. After HTTPS became supported I never looked back. The whole authentication management and Git remote URL syntax are a huge mess I'd never want to touch unless forced to. HTTPS makes it a lot simpler... at least for me. Am I missing something?
I haven't had to set up Git server in a very long time. I'm not even sure if I had to set up HTTPS authentication for it. But, I can see how this may be potentially more difficult as you'd already have SSH set up for accessing the server by the time you get to set up Git.
It's been a lot easier on the client-side though. Especially having to deal with multiple users / multiple servers and having to share configuration between different machines. Usually it just boils down to having ~/.git-credentials and if necessary, adding a similar file in the root of the repo.
But even if they don't, here's an example for Bitbucket (we use self-hosted Bitbucket at work):
https://$USER:$PASSWORD@bitbucket.$COMPANY.com%3a$PORT
and then in ~/.gitconfig [credential]
helper = store
The ~/.git-credentials file may contain multiple lines, and it doesn't have the same problem with escaping as, say, ~/.netrc (because stuff that goes into it needs to be URL-encoded). Also, unlike SSH, it can use password-based authentication (you cannot put your SSH password into ~/.ssh/config). So, this solves the part where you want to authenticate to different Git repos.And if you want to use different personalities for authenticating to the same repo, you can change the location of the ~/.git-credentials file per repo. I was never in a situation where I had more than two roles in the same repo, so, usually, I'd alternate between the global .git-credentials and local-to-repo one. If it becomes really annoying, I'd have two checkouts of the same repo, but with credentials configured for different users.
PS. I would never use a password manger because of trust issues. I don't like random programs having weird integrations with it. Plus, it usually fails to understand the context in which password is to be supplied (eg. tries to show a pop-up window where pop-ups don't work). I store the information I want to be secret encrypted with GPG with a key with a passphrase. I use Emacs to keep a buffer with this file open, if I need passwords and copy from it when necessary. This is less automated and takes extra few seconds to do things that require authentication, but having once lost my password manager database to an upgrade... I'm once burned twice shy.
you can, however, use `ssh-agent` (and should!)
Well, if we expand arbitrary the range of programs reading ~/.ssh/config, then we can put there whatever we want, can't we? The point was that SSH won't read the password from its config file. There's a good reason not to do that: this configuration file shouldn't contain sensitive information because it's a plain text file. But, maybe you can encrypt it -- something I've never explored...
And no, I am not going to store passwords, or auth tokens, in an unencrypted plain text file thank you very much.
In my answer you replied to I suggested storing passwords in a GPG-encryped file. This also extends to .git-credentials (you can use GPG to encrypt it). I usually don't see a merit to that, but if the company policy is to keep such data encrypted, this is what I do.
I already described some of the downsides of ~/.ssh/config. Here are some others:
* You'd need to have the same private key on every computer you use. This is inconvenient, and raises the question of "how do I transfer my key?" And there aren't really good ways... Send with email? -- if your email is breached, then all your computers need to change keys quickly. Some kind of external storage? -- same thing when it's lost.
* I use SSH for a lot of things. I prefer that most things I needs SSH for on a particular computer use the default private key (so that I don't need to edit ~/.ssh/config every time I need to connect somewhere). In my line of work, on average, per day, I need to create couple dozens VMs which I then need to SSH to. Having to configure those to use a different key, even though these are throw-away VMs feels like too much of a chore.
There are downsides from the server admin perspective too. SSH, in general, will need a level of oversight inside an org to provision, expire, sign etc. the keys. Because SSH can, potentially, do a lot more things than HTTPS (when connecting to a server), admins need to be very careful giving SSH access only for the purpose of using Git. I.e. it's easy to give user actual Shell access (by accident) to the Git server instead of giving just Git access.
SSH can be very finicky when it comes to timeouts, encodings and a bunch of other options. But, still, my biggest problem with it is that it can do too much, and in the context of using Git it can bee too much by accident.
No you don't. You use different private keys on different computers, they are really not that expensive to mint. This also simplifies revocation.
> "how do I transfer my key?"
If you really, really need that, it's very simple: curl https://github.com/Joker-vD/keepassdb/raw/master/Joker_vD.kd...
> I prefer that most things I needs SSH for on a particular computer use the default private key
Okay? You use non-default keys for hosts that host git repos, everything else gets the default key. I actually use exactly this configuration: my ~/.ssh/config has 4 entries, 3 of them for git servers I have push access to, and the fourth one is for a personal VPS. Everything else gets offered ~/.ssh/id_rsa
> There are downsides from the server admin perspective too.
That's the server admins' problem, not mine.
> SSH can be very finicky when it comes to timeouts, encodings and a bunch of other options
Can't really relate here, maybe you're right. But in my experience, it either just works fine, or doesn't work at all, no middle ground. Also, "encodings"?
All in all, my experience have been that Git over SSH Just Works™ while Git over HTTPS has all kinds of strange problems, inlcuding half-assedly written HTTP-proxies somewhere in the middle.
In case anyone is stuck in the same way that I was, the trailing slash at the end (which I had previously omitted, not realizing) is necessary for this to work. The docs[0] mention this, but I'd managed to repeatedly miss it:
> If the pattern ends with /, * will be automatically added. For example, the pattern foo/ becomes foo/*. In other words, it matches "foo" and everything inside, recursively.
[0]: https://git-scm.com/docs/git-config#Documentation/git-config...
[1]: https://chezmoi.io/
[2]: https://github.com/cglong/dotfiles/blob/ea33143679e936f4043a...
So “work-github” can be a Host entry (with a HostName of github.com) and key A. While “play-github” would use key B.
Of course, I can test it myself, but since somebody already does that I assumed I could just ask.
That was my interpretation of the original comment. (reading it again, it seems like it was indeed saying what I typed below)
It seems like the reply was saying instead you can do in your .ssh/config file
Host work.github.com
HostName github.com
Host me.github.com
HostName github.com
(or just leave out the .github.com in the Host part)So `ssh git@me.github.com` now works...
And then key the github config off of that? But I didn't try that part. So I could be wrong there.
It works even for same hostname like I did here - https://github.com/hashhar/dotfiles-tmp/blob/master/ssh/.ssh...
Thank you for sharing it!
[credential]
credentialStore = secretservice
helper = /usr/local/bin/git-credential-libsecret
[credential "https://github.com"]
username = akdor1154
[includeif "gitdir:~/src/mywork/"]
path=~/src/mywork/.gitconfig
~/src/mywork/.gitconfig: [user]
email = "12345+akdor1154-mywork@users.noreply.github.com"
signingkey = ~/.ssh/id_rsa.mywork.private
[credential "https://github.com"]
username = akdor1154-mywork
The `username` is passed to the global secret helper and is thus used for it as part of the pick/prompt context for password. I'm using the libsecret helper but this works with any.I wrote about this too https://lesterzone.github.io/blog/2023/01/10/git-work-person...
I consider this type of 'tips' hidden gems
I wrote this about 3 or 4 years ago detailing exactly the same: https://www.leemeichin.com/posts/conditional-git-config.html
What path should go into gitdir if the repos are at S:\Repos\ ?
I think Git on Windows uses forward slashes, but colon is retained, like this:
directory = C:/Program Files (x86)/MyProject
https://gitextensions.github.ioThanks
Maybe it was unusual enough that it didn't sound like a serious response (?), but you're completely right that the article's suggested solution can easily leak stuff by accident if you want to absolutely avoid committing with the wrong identity, thus revealing name, email, timezone, or some other metadata, in a repository where you don't want to.
If I contribute to a legal-gray-area anime fansub I don't want those people (other contributors) to know anything about my IRL identity, for example, so of course I'd reduce the possibility of info leaks due to typos or because my muscle memory betrayed me.
Like I mentioned in a sibling reply, I'd rather use a completely different OS user[1], a Docker container, or a different host.
[1]: That's literally what they are for.
For local Linux projects, I have Docker images for my development environments, one per identity. So I keep my PC as minimal as I can, while the compilers, interpreters, etc, are only installed within a Docker image.
Then I go to a project's directory, run a helper command like `identity1-workspace`, and it (1) starts the Docker container for `identity1`, (2) mounts the current directory to `/workspace` within the container, and (3) also mounts some cache directories, etc.
For other hosts where I don't want to use Docker (e.g. in Raspberry Pis, or in VPS) I just have multiple users and SSH into the one I want to use.
I can do this because I just use nvim and the terminal for everything, so for me there's basically no difference between developing "natively" vs developing inside a Docker container vs developing through SSH. The only difference is whether I run the command `nvim` vs `identity1-workspace` vs `ssh identity1`[1], respectively.
All this sounds like too much work, but I'd rather use (1) Docker, or (2) `adduser` and `ssh`, because those I can do from memory and can trust to be reliable since `identity1` can't push to repos of `identity2` even by accident. In comparison, the article's `includeIf` looks like something that could easily leak an identity by accident, not to mention that I'd forget its syntax after 2 days.
[1]: Well in this case also have to then `cd somewhere ; nvim` because writing a helper for this is no big deal.