git-identity.el icon indicating copy to clipboard operation
git-identity.el copied to clipboard

Manage multiple Git identities from inside Emacs

  • git-identity.el [[https://melpa.org/#/git-identity][file:https://melpa.org/packages/git-identity-badge.svg]]

[[https://github.com/akirak/git-identity.el/workflows/CI/badge.svg?branch=master]]

Git now has [[https://git-scm.com/docs/git-config#_includes][includeIf]] option, so you don't need this package.

This Emacs package lets you manage local Git identities, i.e. =user.name= and =user.email= options in =.git/config=, from inside Emacs. It is primarily intended for users who meet all of the following assumptions:

  • You use Emacs.
  • You (almost always) use [[https://magit.vc][magit]] for Git operations on your machine(s).
  • You have to handle multiple Git identities on the same machine(s).

I found a CLI tool for the same purpose named [[https://github.com/prydonius/karn][karn]], but since Emacs users are likely to run Git operations from inside Emacs (and the interface is probably Magit), it would be better if you had a solution integrated into Emacs. You only need to set a name and an email address before you perform =git-commit=, and if you use Emacs and Magit, it can be implemented by advising =magit-commit=. This package solves the problem. Now you don't need a global Git configuration file to set your name and email address. ** Features

  • You can configure multiple identities in your =custom-file= or in your init file. No extra dotfile is needed. If you work on multiple machines with different set of identities, you can maintain per-host configuration by just having separate custom files.
  • You can associate identities with repository host names and/or local working tree locations.
  • =git-identity-magit-mode= adds a =:before= hook to =magit-commit= to ensure that a given repository gets an expected identity before =git-commit= is run.
  • For manual operation, it provides a command to select an identity in the current repository interactively.
  • It provides a command (actually a hydra) to review your identity quickly and set a new one if necessary. ** Installation Install =git-identity= from MELPA. ** Configuration
  1. Set =git-identity-default-username=, which is used as the default user name for all repositories.
  2. Configure =git-identity-list= variable. You can use =customize-variable=. Each entry consists of a mandatory e-mail address and options. The user name is optional, and if it is omitted, the default user name is set along with the e-mail address. You can associate repository hosts and/or local repository locations with the identity, which are used to pick an identity for a given repository.
  3. Optionally, you can start =git-identity-magit-mode= in your =~/.emacs.d/.init.el=, which turns on the hook run before =magit-status=.

Below is an example configuration using =use-package=:

#+begin_src emacs-lisp (use-package git-identity :after magit :config (git-identity-magit-mode 1) ;; Bind I to git-identity-info in magit-status (define-key magit-status-mode-map (kbd "I") 'git-identity-info) :custom ;; Warn if the global identity setting violates your policy (git-identity-verify t) ;; The default user name (git-identity-default-username "Akira Komamura"))

;; And set git-identity-list in your custom-file or init file (setq git-identity-list '(("[email protected]" :domains ("github.com") ;; The identity is applied if the remote URL contains this organization as directory :exclude-organizations ("my-company-org") :dirs ("~/.emacs.d" "~/personal")) ("[email protected]" :domains ("github.com") ;; The identity is applied if the remote URL contains this organization as directory :organizations ("my-company-org")) ("[email protected]" :domains ("mycompany.com") :dirs ("~/work")) ("[email protected]" ;; Chinese name for cool Chinese sites! :name "刘文彬"))) #+end_src

Note: The format of =git-identity-list= is going to be changed in an upcoming version. See [[https://github.com/akirak/git-identity.el/issues/2][#2]] for details.

You can restrict an identity to a certain organization (by setting =:organizations=) and prevent it from setting in an organization (by setting =:exclude-organizations=), but those options are effective if and only if you set correct =:domains=. For example, you can have separate identities on GitHub both for personal and for your company, but you have to include =github.com= in =:domains= of both identities. Note: Organization names are case-insensitive.

Caveat: These settings are only effective in your local repositories. Let's assume you join a GitHub organization with your personal account and make commits for the organization locally using an alternative e-mail address. If you approve and merge PRs on the web application, GitHub will make merge commits on behalf of your personal account with your primary e-mail address. If you really want to use separate identities on the same service, you will need mutliple accounts. ** Usage *** Interactive use If you turn on =git-identity-magit-mode=, git-identity checks for an identity every time =magit-commit= is invoked. If there is no local identity setting but a global one, the latter is used, so it won't prompt for an identity.

You can also set the identity manually through =git-identity-set-identity= command.

To check what name and e-mail address are set on the repository, use =git-identity-info= command. *** As a library This package also exposes functions, which you can use as a library for writing your functions. **** Parsing URLs You can use the following functions from the package to extract a components from a Git URL:

  • =git-identity-git-url-host= returns the host name of a URL.
  • =git-identity-git-url-directory= returns the path, excluding the name.

See below for examples:

#+begin_example (git-identity-git-url-host "[email protected]:owner/repo.git") => "github.com" (git-identity-git-url-host "[email protected]:1234123412341234.git") => "gist.github.com" (git-identity-git-url-directory "[email protected]:owner/repo.git") => "owner" (git-identity-git-url-directory "https://github.com:22/owner/repo.git/") => "owner" (git-identity-git-url-directory "ssh://[email protected]:22/path/to/repo.git/") => "path/to" #+end_example

The functions support most of the Git URLs defined in the man page of =git-push (1)=. Below are examples:

#+begin_example ssh://github.com/path/to/repo.git git://github.com/owner/repo.git ftps://github.com:22/owner/repo.git/ [email protected]:owner/repo.git github.com:owner/repo.git [email protected]:1234123412341234.git [email protected]:/owner/repo.git hg::https://hg.sr.ht/~geyaeb/haskell-pdftotext #+end_example

It also supports URLs of Git repositories created using [[https://github.com/felipec/git-remote-hg][git-remote-hg]], which means you can use the package on Mercurial repositories. **** Detecting an identity You can use =git-identity-guess-identity= function to detect an identity of the repository. When it is run without arguments, it returns an identity of the current repository.

Furthermore, there is =git-identity-ancestor-directories-from-url= function, which returns a list of ancestor directories of an identity in your configuration matching a Git URL. This can be used to determine the clone destination of a repository respecting your configuration. ** Changelog *** 0.2.0 [2021-09-05 Sun]

  • Add: Make several functions public by renaming. =git-identity-username=, =git-identity-email=, =git-identity-guess-identity=, =git-identity-git-url-host=, and =git-identity-git-url-directory=.
  • Add =git-identity-ancestor-directories-from-url= function.
  • Change: If there are multiple identities matching the same domain of a URL, pick one without organizations. *** 0.1.2 [2020-12-23 Wed]
  • Add support for organizations and multiple identities on the same hosts *** 0.1.1 [2020-01-25 Sat]
  • Fix the bug of duplicate confirmation in setting an expected identity.
  • Fix the bug of trying to set an identity when the global identity is the same as an expected identity.
  • Add a separate =git-identity-magit.el= for a linting reason. ** License GPL v3