Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: Switch Git Users CLI (github.com/geongeorge)
140 points by geongeorgek on Nov 12, 2020 | hide | past | favorite | 57 comments



I found this user management strategy somewhere, and it's been working great for me:

  git config --global --unset user.name
  git config --global --unset user.email
  git config --global --unset user.signingkey

  git config --global user.useConfigOnly true

  git config --global user.<id>.name "<name>"
  git config --global user.<id>.email "<email>"

  git config --global alias.identity '! git config user.name "$(git config user.$1.name)"; git config user.email "$(git config user.$1.email)"; :'
So given that I have created two users, e.g. personal and work I run:

  git identity work
in repos that need the work name/e-mail, and

  git identity personal
in the ones that are private.


I find it much easier to use direnv and set GIT_AUTHOR_EMAIL in each of ~/work/.envrc and ~/personal/.envrc No need to reconfigure every repo this way.


Git has conditional includes based on path[1]. My configuration is like:

On ~/.gitconfig

  [user]
    name = personal
    email = personal@example.com

  [includeIF "gitdir:~/workspace/"]
    path = ~/work.gitconfig

Then on ~/work.gitconfig

  [user]
    name = workname
    email = work@example.com
[1] https://git-scm.com/docs/git-config#_conditional_includes


Just a note of caution, when this feature was added tools based on libgit2 did not understand it. I just checked now, libgit2 eventually added support (though it took 5 months since I filed the ticket). But if you're using any tools that have a 3-year-old version of libgit2 they might not understand it.


Woah, this is neat!


I do the same. Also remember the following keyword if you use nested .envrc files: source_up

If you don't use this in any nested envrc files, the settings don't carry over which means your git settings are not maintained. It is kind of an escape hatch as the parent files are not verified in the same way normal envrc files are, but I have found the trade off to be worth it


I also appreciate using `git whoami` to check the current identity settings:

    $ git whoami 
    GIT_COMMITTER_IDENT=Mel Smith <mel@example.org>
    GIT_AUTHOR_IDENT=Mel Smith <mel@example.org>
`whoami` can be implemented via a simple alias:

    git config --global alias.whoami = "! git var -l | grep '^GIT_.*_IDENT'"


Nice! I was thinking of something like this recently, but decided instead to completely divorce personal and work projects. It’s nice to close the work laptop and go open the personal laptop knowing it’s impossible to cross the streams, as well as for the ritual.


Good idea as it makes the trees "self describing".

But: does this edit a file that can appear in .gitignore or are you uploading this to affect all your collaborators?


It writes to `.git/config` in the local repo. (.git/ is ignored by default, obviously.)


> git config --global alias.identity '! git config user.name "$(git config user.$1.name)"; git config user.email "$(git config user.$1.email)"; :'

Why `; :` at the end?


I'm not entirely sure, but `:` means "true" in bash, and if I omit it, something like this happens:

  $ git config alias.foo '! echo "$1";'
  $ git foo bar
  bar
  echo "$1";: bar: command not found
Whereas if I end with `; :` then it works as expected:

  $ git config alias.foo '! echo "$1"; :'
  $ git foo bar
  bar
It seems to execute the last argument (`bar`) as a command without the `:` at the end, and I don't have a `bar` command on my PATH so it angrily fails with exit code 127. If it instead executes `:` however, that will make it happily exit with 0.

It seems to be a Git alias quirk, but I may be incorrect here.


Seems it's to allow implicit and explicit use of arguments passed to alias. Git does the following to the alias string[1]:

  argv_array_pushf(out, "%s \"$@\"", argv[0]);
So, you can have aliases like `!grep foobar` to automatically accept arguments or aliases like yours that use arguments explicitly.

I've done aliases like `!bash -c 'foo $1' sh` before, but on seeing yours, I see that it was unnecessary to re-wrap with bash.

[1] https://github.com/git/git/blob/e31aba42fb12bdeb0f850829e008...


Huh I had no idea $1/$2/etc worked as-is in an alias. The advice I learned years ago for dealing with any alias that needs to do something custom with parameters is to write it like

  git config alias.foo '!f() { actual command goes here }; f'
as that will pass all the args to the shell function. But if git is already setting it up so the args work then suffixing the alias with ";:" seems simpler.


> The advice I learned years ago

Being able to use $1/$2/etc directly might be a relatively new development.

EDIT: Or maybe not. This might have been doable since 2010:

https://github.com/git/git/commit/8dba1e634af1d973a47fca616a...


2010 is still “new development” to me


Interesting! Thanks for digging up the code.


I've been doing the same, this it's pretty good IMO.


This is a great looking tool.

What I have been doing by hand for some time is putting code for different customers in different directories and having a conditional in `~/.gitconfig` to determine what config applies there:

    [includeIf "gitdir:~/projects-private/**"]
      path = ./.gitconfig-private

    [includeIf "gitdir:~/projects-client/**"]
      path = ./.gitconfig-work
Then in .gitconfig-private or .gitconfig-work I have all the usual gitconfig settings that apply, for example the [user] section...

Switching to the right directory thus automatically changes the settings.


The CLI tool looks very great indeed and handy when you keep all the projects in the same top-level directory, or need to be able to change identity while in the same repository.

However, your .gitconfig setup is (for me) way nicer as I already have things split up by GitHub organization, and now my identity can change without having to do anything at all.

So thanks for sharing that, had no idea it was possible.


Thanks for sharing this. This was what I was missing. Should've done proper research before building.

I'd still think the cli could be useful in some situations. but I agree with others about how big this had to be because I used node.


I think a tool that has a similar UX as your is handy if a person don't care or want to memorise the gitconfig documentation to figure this out. Which, I think, is true even for most of the developers. I have found this one out by a coincidence myself.


This is the approach that works very well for me. Especially since I keep my repos cloned into a directory structure inspired by "go get" using https://github.com/grdl/git-get


A 1000 line YARN lock file for something that would be roughly 20 lines of bash is amazing and terrifying at the same time.


Let’s see the 20 lines of bash that replicate all the features of this utility. Should fit in an HN comment nicely.


> roughly 20 lines of bash

various permutations of which were posted an hour ago and are currently higher than this comment, so I’m confused why multiple people asked to see them ~30½ minutes ago


I asked because none of the posted permutations offer the same feature set. Specifically, the nice thing about this utility (and reason for the dependencies) is interactive adding and selecting of identities. If this can replicated in bash, I am legitimately interested in seeing it, 20 lines or not.


Implement the `git identity` portion listed earlier with a `identity-list` utilizing `git config --get-regexp`. If you need interactivity you can tie it together with fzf and some awk.


So, not a 20 line bash script and at least two big dependencies. Sounds a lot like what you were poking fun at.


> at least two big dependencies

Awk (a fraction of busybox’s 2.1 MBs) is a “big dependency”‽ Or maybe you’re saying git is‽

Edit: quote


Haha ok ok. Awk as a “big” dependency is hyperbole (:

I really am interested in what it would take to pull this off with bash. I may try it myself...


fzf is not a standard tool.


Well no. I don't need multiple versions of the same library to do this, neither do i need the cache engine of some v8 browser either.


Can you show us your take on the bash solution?


Welcome to modern javascript development.


Without using include in gitconfig:

  #!/usr/bin/env zsh
  case $1 in
  foo)
      git config user.name 'John Doe'
      git config user.email john@example.com
      git config user.signingKey 0xFFFFFFFF
      ;;
  bar)
      git config user.name 'Jane Doe'
      git config user.email jane@example.com
      git config user.signingKey 0xEEEEEEEE
      ;;
  baz)
      git config user.name 'My Dog'
      git config user.email dog@example.com
      git config user.signingKey 0xDDDDDDDD
      ;;
  *)
      echo "usage: git-user foo|bar|baz" >&2
      exit 1
      ;;
  esac
Done.

Btw probably want to add signingKey support.



!!! which not only covers the identity scenario but the whole configuration of git, which can be indeed very different from repo to repo, from company to company


I currently use [karn](https://github.com/prydonius/karn) to manage multiple `git` identities. Works pretty nice.


+1 for karn. Works nicely with any directory hierarchy.


I rely on Git's `user.useConfigOnly` option to force myself to set my email per repository. This is what my `user` section in ~/.gitconfig looks like.

  [user]
    name = Gurjeet Singh
    # Tell Git to _not_ guess my name and email based on `whoami` and `hostname`
    useConfigOnly = true
With this in place, whenever I try to commit for the first time in a repository, Git prompts me

  *** Please tell me who you are.*
I then add an email address to the repo-local config based on whether it's work or personal project.

  git config user.email me@example.com


This package installed 47.4MiB of node_modules. Yikes.


What's the point of writing this simple tool in js? I'd like to use it but I don't have npm everywhere (and I don't want to grab a huge tree of dependencies just for editing a couple of lines on a text file...)


I suspect it was written in JS because that was the author’s language of choice. There are lots of ways to manage git identities and this seems like a decent utility for web devs who already have node installed and don’t care to manually edit config files.


Of course, but my point is that these kind of tools are of general interest, and most programmers are not web developers.


Javascript is a general interest language imo.


I think one of the benefit of using js to write cli tool is that you can install them very easily on Windows.


You can have a per-project git config, which to me is a more convenient solution.

For my work repo I configure the repo to use my work identity. For my personal repos I use my personal identity.

I do not need to switch identities when using the same repo.

Doing this, you only setup your identity once per repo not every time, which is safer/less error prone. i.e.: you wont leak your work email on github by accident.


Have seen more than a few attempts to solve this problem. Here's my version[0] and there's many more on GitHub. Wonder if there's an opportunity to unify our efforts & get something into git contrib?

[0] - https://github.com/bobbo/git-profile


This line makes it not very useful, because I have to re-add accounts for each project.

    const store = require("data-store")({ path: process.cwd() + "/data.json" });
I think a `git-user-data.json` file in a centralized location makes far more sense.


Author here! I'm a freelancer working for multiple agencies at the moment. And One of the requirements was that I use the agency email for all commits.

So I made this as a sugar for git config user.email whatever@gmail.com


https://git-scm.com/docs/git-config#_conditional_includes

Is a lot easier then what you came up with.


Thanks. This is perfect but I also like the convenience of a quick switcher for any folder.


I find that creating a separate user account on my system for each client works much better for this kind of thing than trying to configure every application with multiple identities.


More isolation, less accidents. I use this way too.


Looks neat, but I agree with others about using conditional includes instead. Perhaps this tool could build on that?


Could've been written in a 30–40 line shell script, heh.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: