How to install and configure Git for Windows to use OpenSSH on Windows 11 Pro.
All of the bash commands used here can be run in PowerShell as an admin.
Instructions can be performed in any terminal that understands Bash. This does NOT include Windows PowerShell. This does include:
- PowerShell
- Windows Command Prompt
- Windows Terminal with Bash profile
- Git Bash terminal (installed with Git)
- VS Code integrated Bash terminal
Prevent your GitHub email address from being used in commits by:
- User menu > Settings > Left panel > Emails
- Check Keep my emails private.
- Check Block command line pushes that expose my email.
Your Github commit address appears in the fine print under Keep my emails private.
- It looks something like
123456789+username@users.noreply.github.com - In subsequent steps you will use
username@users.noreply.github.com
To use SSH, you will need a key pair for each GitHub account.
- Follow the instructions in Windows OpenSSH to create key pairs.
- Be sure to add keys to the SSH Server and Client.
- GitHub uses Ed25519 encryption for key pairs.
- Your keys should be located in
C:\Users\user\.ssh\. - Keys in this location will be automatically detected during GitHub authentication.
These are used in the examples:
id_github_wcd
id_github_swatchpie
To use SSH, you will need a GitHub PAT for each account.
- User menu > Settings
- Left panel > Developer Settings > Personal Access Tokens
- In the top-right corner, click Add New.
- Name the token and set an expiration date.
- Check the boxes for these scopes:
repo,workflow,read:org,admin:public_key - Click Generate Token. Copy it to a
.envor password manager.
To copy a public key to your clipboard:
Get-Content C:\Users\wcd\.ssh\id_github_wcd.pub | clip
Get-Content C:\Users\wcd\.ssh\id_github_swatchpie.pub | clipPublic keys are added here:
- User menu > Settings
- Left panel > Access: SSH & GPG Keys > New SSH key
- Set an informative 'Title'.
- Set the 'Key Type' to 'Authentication Key'.
- Paste your public key and save.
I have 2 GitHub accounts that I will be using SSH with. To make scenarios like this easier, update the OpenSSH configuration.
# Open the config file
code "$HOME\.ssh\config"
# Add this and save - note `/`
Host github-wcd
HostName github.com
User git
IdentityFile C:/Users/wcd/.ssh/id_github_wcd
Host github-swatchpie
HostName github.com
User git
IdentityFile C:/Users/wcd/.ssh/id_github_swatchpie
# Test
ssh -T github-wcd
ssh -T github-swatchpie
# In subsequent steps you will see:
git@github-wcd
git@github-swatchpie- Download Git for Windows and run the installer.
- Confirm with
git --version.
See the settings I used
Many tutorials say to check for these, but I did not see either during installation:
- Look for and ensure
Add to PATHis enabled. - Look for and ensure
allow Unix-style /mnt drive mappingsis enabled.
Install GitHub CLI
Be sure to look for and enable the Add to Path option.
gh --versionThis step tests that key pairs have been properly configured in OpenSSH and on GitHub.
# Test
ssh -T github-wcd
ssh -T github-swatchpie
# Result should look like:
Hi wcDogg! You've successfully authenticated, but GitHub does not provide shell access.Profiles in Git for Windows are used by GitHub to associate credentials with repositories. In Git there is a global profile and repository profiles.
If you have a single GitHub account, you can create a global Git profile:
# List the global user
git config --global --list
# Add or update the global user
git config --global user.name "wcDogg"
git config --global user.email "wcDogg@users.noreply.github.com"
# Remove the global user
git config --global --unset user.name "wcDogg"
git config --global --unset user.email "wcDogg@users.noreply.github.com"If you have multiple GitHub accounts, you can either:
- Use a global profile in combination with repository profiles as-needed.
- Use all repository profiles.
I strongly recommend using all repository-level profiles!!! This avoids a messy scenario where you are working on a repo for your non-global account, forget to set the repo-level profile, and accidentally make a commit using the wrong profile. In theory, GitHub should reject the push because the emails won't match, but weird things can happen. I feel it's best not to risk it.
The easiest way to use a repository profile is to open the repo in VS Code and use the Terminal from there.
# VS Code repo
# Show the current config
# Use `q` to exit.
git config --list
q
# Add profiles
git config user.name "wcDogg"
git config user.email "wcDogg@users.noreply.github.com"
git config user.name "swatchpie"
git config user.email "swatchpie@users.noreply.github.com"My local repositories are on an external F:/ drive. When working with a git command, I got this error:
fatal: detected dubious ownership in repository at 'F:/wcDogg/windows'
'F:/wcDogg/windows' is on a file system that does not record ownership...To fix or prevent this, you must add the directory explicitly to Git's global configuration. (You cannot use just F:/ here.)
# List safe directories
git config --global --list
# Mark as safe
git config --global --add safe.directory "F:/wcDogg/windows"
git config --global --add safe.directory "F:\\wcDogg\\windows"
git config --global --add safe.directory "F:/Script-Hero"
# Remove all safe directories
git config --global --unset-all safe.directory
# Remove a specific safe directory
git config --global --unset safe.directory F:/wcDogg/windowsAs mentioned above, if you are working with multiple GitHub accounts, I strongly recommend using all repo-level Git profiles. When you do, there are a few things that should become routine. These steps should also be performed for existing local repositories.
For me, the first thing is to mark the directory as safe in my global Git configuration and set the correct Git profile.
# Mark directory as safe
git config --global --add safe.directory "F:/wcDogg/windows"
git config --global --add safe.directory "F:\\wcDogg\\windows"
# Set the Git profile
git config user.name "wcDogg"
git config user.email "wcDogg@users.noreply.github.com"
git config user.name "swatchpie"
git config user.email "SwatchPie@users.noreply.github.com"And the next thing is to update the remote.
# Recall my hosts are:
github-wcd
github-swatchpie
# Check the current remote
git remote -v
# See something like:
origin https://github.com/wcDogg/windows.git (fetch)
origin https://github.com/wcDogg/windows.git (push)
# Update the remote and verify.
git remote set-url origin git@github-wcd:wcDogg/windows.git
git remote -v
# You should see something like the following.
# Note how the remote now uses the `github-wcd` host we configured.
origin git@github-wcd:wcDogg/windows.git (fetch)
origin git@github-wcd:wcDogg/windows.git (push)Once you have completed the steps above, VS Code and GitHub should be communicating.
- Make a test change to the repo.
- Note that VS Code indicates that changes are waiting for commit.
- Switch to the 'Source Control' panel
- Use the VS Code UI to push the change.
Assumes local directory already contains a README.md, .gitignore, and LICENSE.
# Initialize the repo and confirm
git init -b main
# Mark directory as safe and confirm
git config --global --add safe.directory "F:/Script-Hero"
git config --global --list
# Set the Git profile and confirm
git config user.name "wcDogg"
git config user.email "wcDogg@users.noreply.github.com"
git config user.name "swatchpie"
git config user.email "SwatchPie@users.noreply.github.com"
git config --get user.name
git config --get user.email
# Confirm Git initialized correctly
git rev-parse --is-inside-work-tree
# Test GH authentication
gh auth status
>> github.com
>> ✓ Logged in to github.com as SwatchPie (keyring)
>> ✓ Git operations for github.com configured to use ssh protocol.
>> ✓ Token: ghp_************************************
>> ✓ Token scopes: admin:public_key, read:org, repo, workflow
# Create a public repo
gh repo create SwatchPie/script-hero-for-illustrator --public --source . --remote ssh
>> ✓ Created repository SwatchPie/script-hero-for-illustrator on GitHub
>> ✓ Added remote git@github.com:SwatchPie/script-hero-for-illustrator.git
# Create a private repo
gh repo create SwatchPie/script-hero-for-illustrator --private --source . --remote ssh
# Check the remote - note it is not using the SSH hosts
git remote -v
>> ssh git@github.com:SwatchPie/script-hero-for-illustrator.git (fetch)
>> ssh git@github.com:SwatchPie/script-hero-for-illustrator.git (push)
# Update remote to use SSH host
git remote set-url ssh git@github-swatchpie:SwatchPie/script-hero-for-illustrator.git
# Check the remote - it should now use the SSH host
git remote -v
>> ssh git@github-swatchpie:SwatchPie/script-hero-for-illustrator.git (fetch)
>> ssh git@github-swatchpie:SwatchPie/script-hero-for-illustrator.git (push)
# Stage and commit initial content
git add . && git commit -m "Initial commit"
>> 3 files changed, 32 insertions(+)
>> create mode 100644 .gitignore
>> create mode 100644 LICENSE
>> create mode 100644 README.md
# Push to GitHub
git push ssh main
>> Enumerating objects: 5, done.
>> Counting objects: 100% (5/5), done.
>> Delta compression using up to 24 threads
>> Compressing objects: 100% (4/4), done.
>> Writing objects: 100% (5/5), 881 bytes | 881.00 KiB/s, done.
>> Total 5 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
>> To github-swatchpie:SwatchPie/ghithub-test.git
>> * [new branch] main -> main
# Verify on GitHub.
# Make a test change.
# Use VS Code UI to commit the change. # Clone existing repo
git clone https://github.com/SqueezeSoftware/JSONpie.git
# See the status of changes
git status
# Stage changes
git add .
git add file.txt
git add dir-name
# Remove file from stage
git restore --staged README.md
# Commit
git commit -m "Commit message"
# Stage + commit all changes
git add . && git commit -m "Commit message"
# Push changes to current branch
git push
# Push changes to a different branch
git push origin branchname
# View commit history
git log
# You can also do this - press Q to exit
git log --all --graph
# See the contents of a file
cat README.md# Get the commit's ID
git log
# Revert
git reset f73d30fa32fd393813714c533357447f435d3f06
# View un-staged changes
git statusStash only works once the project has an initial commit.
# Stage what will be stashed
git add .
# Review
git status
# Stash
git stash
# Bring stashed back
git stash pop
# Discard stashed - permanent + irreversible
git stash clear# List local branches
git branch
# List remote branches
git branch -r
# Create local + push to GitHub
git branch dbot
git push -u origin dbot
# Switch branches
git checkout dbot
# Merge branch to main
git checkout main
git merge dbot -m "Dependency version updates"
git pushBecause this needs to be routine, I wrote a PowerShell set-sh script to mark directories as safe and set the Git profile. Note it does not update local remotes.
# Open profile
Code $Home\Documents\PowerShell\Profile.ps1
# Set Git profile / GitHub user
function set-gh {
Write-Host "-------------------------------------------------------------"
Write-Host "GITHUB REPO SETTINGS"
Write-Host ""
# Step 1: Confirm the current directory is a GitHub repository
if (-not (Test-Path ".git")) {
Write-Host "ERROR: The current directory is not a Git repository." -ForegroundColor Red
return
}
Write-Host "SUCCESS: Repository found." -ForegroundColor Green
# Step 2: Add the current directory as a safe directory
$currentDir = Get-Location
git config --global --add safe.directory $currentDir
Write-Host "SUCCESS: Git safe directory added: $currentDir" -ForegroundColor Green
# Step 3: Restart the terminal session
Write-Host "WAIT: Refreshing the terminal to apply changes..." -ForegroundColor Yellow
$Host.UI.RawUI.FlushInputBuffer()
[System.Console]::Clear()
Write-Host "-------------------------------------------------------------"
Write-Host "GITHUB REPO SETTINGS"
Write-Host ""
Write-Host "SUCCESS: Repository found." -ForegroundColor Green
Write-Host "SUCCESS: Git safe directory added: $currentDir" -ForegroundColor Green
# Step 4: Confirm the current directory is a safe directory
$safeDirs = git config --global --get-all safe.directory
if ($safeDirs -notcontains $currentDir) {
Write-Host "ERROR: Git safe directory not found: $currentDir" -ForegroundColor Red
return
}
Write-Host "SUCCESS: Git safe directory found: $currentDir" -ForegroundColor Green
# Step 5: Prompt the user to choose a GitHub user
$choice = Read-Host "PROMPT: Which GitHub user do you want to use? [1] wcDogg [2] SwatchPie"
switch ($choice) {
'1' {
git config user.name "wcDogg"
git config user.email "wcDogg@users.noreply.github.com"
Write-Host "SUCCESS: Set Git profile to wcDogg." -ForegroundColor Green
}
'2' {
git config user.name "swatchpie"
git config user.email "SwatchPie@users.noreply.github.com"
Write-Host "SUCCESS: Set Git profile to SwatchPie." -ForegroundColor Green
}
default {
Write-Host "WARNING: Invalid choice. No changes were made." -ForegroundColor Yellow
return
}
}
# Step 6: Confirm the profile has been set
$gitName = git config user.name
$gitEmail = git config user.email
Write-Host "GitHub user: $gitName " -ForegroundColor Cyan
Write-Host "GitHub email: $gitEmail" -ForegroundColor Cyan
Write-Host "-------------------------------------------------------------"
}Once Git for Windows is installed, you can use any bash terminal as an admin to update it.
git --version
git update-git-for-windows
git --versionOnce the GitHub CLI is installed, you can use any bash terminal as an admin to update it.
gh --version
gh upgrade
gh --version