Two Git Accounts on One Computer

& (verbiage overflow)Sat 25 July 2015RSS

Imagine you have two or more different hosted Git accounts, and each is associated with a different SSH key:

~/.ssh/id_rsa_first
~/.ssh/id_rsa_second

How do you make sure the correct key is used with a given account?

I realize now, nine months after initially posting this, that what I've described is needlessly complex.

I've left the original post further down on the page, but in practice when I want to push to or pull from one of my several hosted Git accounts, here is what I do:

  1. First remove all SSH identities from the authentication agent, and then add only the one that is correct for the repository I am working with.

    ssh-add -D
    ssh-add ~/.ssh/id_rsa_second
    

    (In theory it is supposed to be possible to set one's .ssh/config file to use different identities with different accounts, but in practice I find that doesn't always work as expected.)

  2. Set up a script to rewrite the repository’s commit history. This may be useful if the repository has been added to at different times when you were using more than one distinct SSH identity. Configure git-author-rewrite.sh with first and second as below:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #!/bin/sh
    
    git filter-branch --env-filter '
    
    OLD_EMAIL="first@email.com"
    CORRECT_NAME="Second Name"
    CORRECT_EMAIL="second@email.com"
    
    if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
    then
    export GIT_COMMITTER_NAME="$CORRECT_NAME"
    export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
    fi
    if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
    then
    export GIT_AUTHOR_NAME="$CORRECT_NAME"
    export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
    fi
    ' --tag-name-filter cat -- --branches --tags
    
  3. A reader kindly commented to mention setting Git's credential.useHttpPath configuration in a given repository in order to let the Mac OS X KeyChain handle the choice of which identity will be used for authentication. But unhappily that seems to work only for HTTP access, not SSH.


Long-winded original post follows:

Date: 2014-10-13 11:00

I have long wanted to have a Git account where I can keep only finished, presentable code, and have it be completely separate from my messy working account. One way to do this is to use a different hosting service for the two accounts, but it can also be done with both accounts on GitHub itself.

It took me a while to learn all the necessary steps, which I describe below. I am assuming the user-name of your original account is first and the new one second. And I am assuming you are using SSH.

Below there are “one-time” (initialization) steps, and then a separate set of steps that have to be done for each repository you want to move to user second.


One-time set-up steps

  1. (optionally) Use a Git user-switch. Set up gitswitch (published as a Ruby gem):

    gem install gitswitch
    gitswitch init
    

    This program allows you to maintain two or more git user identities on a single computer, and to switch conveniently among them. One will be first, your default identity, the one you have presumably been using all along. Gitswitch will enable you to mark a given repository as belonging to second.

  2. Create a new Git hosting account, associated with a new email address, second@email.com, that you control. Bear in mind that if you are keeping both accounts on the same host (e.g., GitHub) and intend to maintain access to both accounts simultaneously, you will need to log into them from different browsers.

  3. Create a new SSH key. Here I am assuming you have already been using SSH with Git and already have an SSH key for your default user, first. Now create a new SSH key for user second.

    ssh-keygen -t rsa -C "second@email.com"
    

    You will be asked what the name of the new private key ID file should be; use id_rsa_second; private key ID file is presumably id_rsa, though you may like to name it something else. After the program completes make sure it has been created at ~/.ssh/id_rsa_second.

    Add both private key identities to the SSH authentication agent:

    ssh-add ~/.ssh/id_rsa
    ssh-add ~/.ssh/id_rsa_second
    

    If later on you have trouble using SSH, it may be necessary to delete the agent’s record of these IDs, using

    ssh-add -D
    

    After that use ssh-add again as above.

  4. Add your new public key to the Git hosting service. Go to the SSH-key area of your Git hosting service (on GitHub that would be https://github.com/settings/ssh and click “Add SSH key”). Copy the contents of ~/.ssh/id_rsa_second.pub (note the extension .pub) into the key-field and save it to the server with some appropriate name.

  5. Set up .ssh/config for multiple users. Create or open file ~/.ssh/config and enter the following in it:

    Host first
        HostName github.com
        User git 
        IdentityFile ~/.ssh/id_rsa
    
    Host second
        HostName github.com                                                         
        User git 
        IdentityFile ~/.ssh/id_rsa_second
    

    Of course, if you are not using the same host for both accounts, then alter the HostName entries accordingly. Now check that SSH recognizes multiple users:

    ssh -T first
    ssh -T second
    

    You should see evidence of successful log-in after each of these commands.

  6. Set up a script to rewrite the repository’s commit history. Configure git-author-rewrite.sh with first and second as below:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #!/bin/sh
    
    git filter-branch --env-filter '
    
    OLD_EMAIL="first@email.com"
    CORRECT_NAME="Second Name"
    CORRECT_EMAIL="second@email.com"
    
    if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
    then
    export GIT_COMMITTER_NAME="$CORRECT_NAME"
    export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
    fi
    if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
    then
    export GIT_AUTHOR_NAME="$CORRECT_NAME"
    export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
    fi
    ' --tag-name-filter cat -- --branches --tags
    
  7. Now push to remote.


Steps to be repeated for each repository

  1. Enter the directory containing the repository on your local machine. (If you plan to maintain two versions of the same repository, then make a copy of the repository and enter its top-level directory.)

  2. Switch user from first to second. If you are using gitswitch enter:

    gitswitch switch second
    

    If you are not using gitswitch, then edit the [user] section of .git/config so that it reads

    [user]
            email = second@email.com
            name = Second Name
    
  3. Revise your Git configuration file. Rewrite the url line in the [remote "origin"] section of .git/config, using second and Repository_Name:

    url = git@second:second/Repository_Name.git
    

    Note that second appears twice here: normally this line would read

    url = git@github.com:second/Repository_Name.git
    

    but if "github.com:" appeared after the @-sign, default identity first would be used for pushing to the origin, and in that case you would get an error trying to write to second's repository.

  4. Rewrite your commit history. Run git-author-rewrite.sh

    sh git-author-rewrite.sh
    

    Note that if you have to run this script more than once in a given repository for some reason, you must add -f after git filter-branch in the script under "Set up a script to rewrite…", above, in order to force the overwriting of a backup file.

  5. (optionally) Revise your Git log. In order to ensure that all user-references in the log are also to the new account, edit the Git log with filter-branch:

    git filter-branch -f --msg-filter "sed 's/first/second/'" --tag-name-filter cat -- --branches --tags
    

You may like to check to see that first is completely removed from your git log — any remaining occurrences would be listed with:

    grep -FR second@email.com .git/*

and can be edited manually.

  1. Push the repository to second's hosted account. Make sure git will behave as you expect using the --dry-run option first:

    git push --force --tags origin 'refs/heads/*' --dry-run
    

    You should see no error messages, and the output will be as if you had really pushed. Then push to repository, using the same command but without --dry-run.

[end]

Comments are enabled.



All prior articles

  1. Always Use Dry-run Options If Possible
  2. The Imperative Style in Commits and Docstrings