I seemed to spend a crazy amount of time trying to work out how to change access to a Git repository (repo) from HTTPS to SSH to avoid password prompts when pushing changes to BitBucket and GitHub. Here, I outline how to do it on Mac OS X, partly to remind myself how to do it in the future, partly to collate all the infomation on the internet about it and partly in the hope that someone else might find it useful.
This rigmarole needs to be done on each computer you intend to access your Git repo with for each repo you have that you want to access by SSH rather than by HTTPS.
Change the remote path of the repo
This is how you tell your Git server how you want to access it.
Open Terminal or your command-line app of choice and navigate to your repo’s directory:
then check to see what you have on your server already:
git remote -v
You should see a pair of entries for every each remote repo (I once had origin and origin2, but that’s another story): one for fetch, which includes pull, and one for push:
origin https://email@example.com/username/reponame.git (fetch) origin https://firstname.lastname@example.org/username/reponame.git (push)
This example shows a repo on BitBucket; GitHub is similar. You can see that the URLs include the https:// protocol. This is what we want to change. Type the following for BitBucket:
git remote set-url origin email@example.com:username/reponame.git
For GitHub, replace bitbucket.org with github.com. Then check what’s there again:
git remote -v
This time, you’ll see something like this:
origin firstname.lastname@example.org:username/reponame.git (fetch) origin email@example.com:username/reponame.git (push)
Error: could not resolve hostname
You might get this error:
ssh: Could not resolve hostname github.com:username: nodename nor servname provided, or not known fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.
If you do, check the output from git remote -v carefully. See if the SSH URL is prefixed with ssh://, like this:
origin ssh://firstname.lastname@example.org:pandammonium/buffer-guess-who.git (fetch) origin ssh://email@example.com:pandammonium/buffer-guess-who.git (push)
If it is, run set-url again, making sure to exclude the ssh:// part of the URL. Run git remote -v again and check the output. You may find that only fetch has changed — if so, read on.
Only fetch changes; push stays the same
If the fetch URL has changed but the push URL has not, like this:
origin firstname.lastname@example.org:pandammonium/buffer-guess-who.git (fetch) origin ssh://email@example.com:pandammonium/buffer-guess-who.git (push)
you’ll need to do the push separately by using the --push option:
git remote set-url --push origin firstname.lastname@example.org:username/reponame.git
You don’t need to do anything further for pull because it does what fetch does (because pull is equivalent to fetch then merge).
I don’t know why it works for some cases but not other ones. It happened for me when I got the could not resolve hostname error above and ran set-url again. It seems that the set-url command is applied to both fetch and push when changing the protocol, but when keeping the (SSH) protocol the same, it only updates fetch.
Create a SSH key
If you don’t already have a SSH key for BitBucket and/or GitHub on the particular computer you’re using, you’ll need to create one for each service that you use and on each computer that you will use to access your repos:
You’ll end up with two SSH keys, one private and one public, which you can see by running:
or you can list all the SHH keys you have by running:
which will output something like
The key without the extension is the private key and the key with the .pub extension is the public key. The private key stays on your computer and the public key is uploaded onto the server.
…recommend you keep the default key name [id_rsa] unless you have a reason to change it. However, I’d recommend using something descriptive, like bitbucket_id_rsa so you know what it’s for later and to avoid accidentally overwriting the key if you create one for bitbucket and one for github and one for your web server and so on.
Add the public SSH key to your remote Git server
The public SSH key (the one with the .pub extension) is for the server, so you need to upload it to your account on the relevant one:
Add the private SSH key to your keychain
If you don’t add the SSH key to your keychain, you’ll have to keep entering the passphrase you set up when creating the SSH key.
Start the SSH agent in the background:
which gives you an agent pid:
Agent pid: 12345
Add the private SSH key (the one with no extension) to your keychain; the -K option is Mac-specific and is what tells your Mac to add it to the keychain as well as wherever else it adds it to:
ssh-add -K ~/.ssh/github_id_rsa
For MacOS Sierra onwards, you must add the SSH key file to the ~/.config file so you don’t have to call the previous command every time you restart your Mac. This also seems to be what makes the whole thing work with Sourcetree.
Create a config file in your SSH keys directory, if there isn’t one already (you can check by using ls ~/.ssh):
I prefer the Nano editor if I’m going to use a command-line editor, but you can use Vim or Emacs or whatever you like, of course.
Add an entry for Host * (any host, I presume) stating that the keychain is to be used and the keys are to be added to said keychain. An identity file entry in your ~/.ssh/config file is needed for each SSH key:
Host * AddKeysToAgent yes UseKeychain yes IdentityFile ~/.ssh/bitbucket_id_rsa IdentityFile ~/.ssh/github_id_rsa
If you don’t add this config file, you’ll have to run ssh-add -K ~/.sshkeyname every time you restart your Mac.
You should now be able to use Git without having to use your password all the time.
Test it works
You can type in the terminal for a GitHub repo:
ssh -T email@example.com
to get the following message:
Hi username! You've successfully authenticated, but GitHub does not provide shell access.
For a BitBucket repo, type:
ssh -T firstname.lastname@example.org
to get the following message:
logged in as pandammonium. You can use Git or hg to connect to Bitbucket. Shell access is disabled.
In your terminal, assuming your repo is up-to-date, you can test everything works by typing the following:
which should give no output if it works. Typing:
should result in an up-to-date message:
If that works, try fetching in Sourcetree then pushing. If you get no errors, you’re good to go.
Error: permission denied
If you get this error, especially in Sourcetree:
email@example.com: Permission denied (publickey). fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.
then check that you have spelt everything correctly and followed every step.
Sources and further reading
In no particular order, the following links helped me get all this working and/or contain more information that might help you understand what’s going on:
- SSH keys (BitBucket; general and BitBucket-specific information)
- Set up an SSH key (BitBucket)
- Generating a new SSH key and adding it to the ssh-agent (GitHub)
- Adding a new SSH key to your GitHub account (GitHub)
- Adding your SSH key to the ssh-agent (GitHub)
- Difficulties with ssh-agent in macOS Sierra (Reddit)
- Testing your SSH connection (GitHub)
- How to remove a ssh key? (Stack Overflow)
- How can I use my existing default ssh key with SourceTree? (Sourcetree)
- SourceTree SSH options on OS X (Superuser)
- How can I permanently add my SSH private key to Keychain so it is automatically available to ssh? (StackExchange)
- Adding a Private Key to Your Mac OSX Keychain (HPCC)
- Why eval the output of ssh-agent? (Stack Exchange)
- How often should an SSH key pair be changed? (Stack Exchange)
- Where are my private/public SSH keys on UNIX? (Superuser; actually refers to Mac OS)
- How to Add a Private Key to Your Mac OS X Keychain (Dev Dustin)
- Changing Git remote URL updates fetch but not push (Stack Overflow)
I am very grateful to all the people who contributed to getting this information out there for me to find. Thank you.