GitHub is reliable, but it should not be the only place where your code exists.
A local backup protects you if:
- A repository is accidentally deleted
- Your GitHub account is locked or compromised
- You lose access to an organization
- A branch or tag is removed
- You want an independent archive of your complete Git history
This guide explains how to back up every GitHub repository available to your account on macOS.
The setup uses:
- macOS
- Terminal
- GitHub CLI
- HTTPS authentication
- A reusable shell script
- Git mirror clones
Using HTTPS is especially useful if you have an SSH key but do not know its passphrase.
The final backup includes public and private repositories, branches, tags, commits, forks, archived repositories, and Git LFS files where available.
What Is a Git Mirror Backup?
A normal Git clone creates a working copy of a repository, usually with one branch checked out.
A mirror clone creates a more complete repository backup.
It includes:
- All branches
- All tags
- All commits
- Remote references
- The complete Git history
Mirror backups do not look like ordinary project folders. They contain the underlying Git database and usually end in .git.
For example:
my-project.git
This is expected.
Step 1: Open Terminal
Open Spotlight with:
Command + Space
Search for:
Terminal
Then press Enter.
Step 2: Check Whether Homebrew Is Installed
Homebrew is a package manager for macOS. We will use it to install the GitHub command-line tools.
Run:
brew --version
If Terminal displays a Homebrew version number, continue to the next step.
If you see:
command not found: brew
install Homebrew with:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Follow the instructions displayed in Terminal.
You may be asked for your Mac login password. Terminal does not display dots or characters while you type a password. This is normal.
Step 3: Install GitHub CLI and Git LFS
Install GitHub CLI and Git LFS:
brew install gh git-lfs
Then initialize Git LFS:
git lfs install
GitHub CLI lets the backup script retrieve a list of repositories available to your account.
Git LFS is used by repositories that store large files outside the normal Git object database.
Step 4: Log In to GitHub Using HTTPS
Run:
gh auth login
Choose the following options when prompted:
What account do you want to log into?
GitHub.com
Then:
What is your preferred protocol for Git operations?
HTTPS
Then:
Authenticate Git with your GitHub credentials?
Yes
Finally:
How would you like to authenticate GitHub CLI?
Login with a web browser
GitHub CLI will display a temporary code.
Press Enter to open GitHub in your browser, enter the code if necessary, and authorize GitHub CLI.
Return to Terminal when the authorization is complete.
Step 5: Confirm Your GitHub Login
Run:
gh auth status
You should see a message confirming that you are logged in to GitHub.
It should also show:
Git operations protocol: https
If it shows SSH instead, change it to HTTPS:
gh config set git_protocol https
Then configure Git to use your GitHub CLI authentication:
gh auth setup-git
This allows the script to clone private repositories over HTTPS without repeatedly asking for a username, password, or SSH-key passphrase.
Step 6: Create a Backup Folder
This guide stores the backup in:
~/github-backup
The ~ character represents your macOS home directory.
First, check whether the folder already exists:
ls -ld ~/github-backup
If Terminal reports that the folder does not exist, continue.
If you already have a folder from an earlier attempt, rename it:
mv ~/github-backup ~/github-backup-old
Now create a clean backup directory:
mkdir -p ~/github-backup
cd ~/github-backup
Step 7: Create the Backup Script
Create a new script using Nano:
nano backup-github.sh
Paste the following script into Nano:
#!/bin/bash
set -u
BACKUP_ROOT="$HOME/github-backup"
REPOSITORIES_DIR="$BACKUP_ROOT/repositories"
FAILED_LOG="$BACKUP_ROOT/failed-repositories.txt"
mkdir -p "$REPOSITORIES_DIR"
: > "$FAILED_LOG"
echo
echo "GitHub repository backup"
echo "========================"
echo
# Confirm required programs are available.
for COMMAND in gh git; do
if ! command -v "$COMMAND" >/dev/null 2>&1; then
echo "Error: '$COMMAND' is not installed."
exit 1
fi
done
# Confirm GitHub CLI authentication.
if ! gh auth status --hostname github.com >/dev/null 2>&1; then
echo "Error: GitHub CLI is not authenticated."
echo "Run: gh auth login"
exit 1
fi
echo "Finding all repositories available to your GitHub account..."
echo
REPOSITORY_LIST="$BACKUP_ROOT/repository-list.tsv"
if ! gh api \
--method GET \
--paginate \
-H "Accept: application/vnd.github+json" \
"/user/repos?per_page=100&affiliation=owner,collaborator,organization_member&sort=full_name" \
--jq '.[] | [.full_name, .clone_url] | @tsv' \
> "$REPOSITORY_LIST"; then
echo "Error: GitHub could not return your repository list."
exit 1
fi
REPOSITORY_COUNT=$(wc -l < "$REPOSITORY_LIST" | tr -d ' ')
echo "Found $REPOSITORY_COUNT repositories."
echo "Backup location: $REPOSITORIES_DIR"
echo
SUCCESS_COUNT=0
FAILURE_COUNT=0
while IFS=$'\t' read -r FULL_NAME CLONE_URL; do
[ -z "$FULL_NAME" ] && continue
OWNER="${FULL_NAME%%/*}"
REPOSITORY="${FULL_NAME#*/}"
OWNER_DIR="$REPOSITORIES_DIR/$OWNER"
REPOSITORY_DIR="$OWNER_DIR/$REPOSITORY.git"
mkdir -p "$OWNER_DIR"
echo "------------------------------------------------------------"
echo "Repository: $FULL_NAME"
if [ -d "$REPOSITORY_DIR" ]; then
echo "Updating existing mirror..."
if git -C "$REPOSITORY_DIR" remote update --prune; then
BACKUP_SUCCEEDED=true
else
BACKUP_SUCCEEDED=false
fi
else
echo "Creating mirror..."
if git clone --mirror "$CLONE_URL" "$REPOSITORY_DIR"; then
BACKUP_SUCCEEDED=true
else
BACKUP_SUCCEEDED=false
fi
fi
if [ "$BACKUP_SUCCEEDED" = true ]; then
if command -v git-lfs >/dev/null 2>&1; then
echo "Fetching Git LFS objects..."
git -C "$REPOSITORY_DIR" lfs fetch --all || {
echo "Warning: some Git LFS objects could not be downloaded."
}
fi
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
echo "Completed: $FULL_NAME"
else
FAILURE_COUNT=$((FAILURE_COUNT + 1))
echo "$FULL_NAME" >> "$FAILED_LOG"
echo "Failed: $FULL_NAME"
fi
echo
done < "$REPOSITORY_LIST"
echo "============================================================"
echo "Backup finished."
echo
echo "Successful repositories: $SUCCESS_COUNT"
echo "Failed repositories: $FAILURE_COUNT"
echo "Backup location: $REPOSITORIES_DIR"
if [ "$FAILURE_COUNT" -gt 0 ]; then
echo "Failure log: $FAILED_LOG"
else
rm -f "$FAILED_LOG"
fi
Step 8: Save the Script
To save the file in Nano, press:
Control + O
Nano will ask you to confirm the filename.
Press Enter.
Then exit Nano with:
Control + X
You should now be back in the normal Terminal window.
Step 9: Make the Script Executable
Run:
chmod +x backup-github.sh
This gives macOS permission to run the file as a shell script.
Step 10: Run the GitHub Backup
Run:
./backup-github.sh
The script will:
- Check that Git and GitHub CLI are installed
- Confirm that you are logged in to GitHub
- Retrieve all repositories available to your account
- Create a mirror clone of each repository
- Download Git LFS objects when available
- Record any repositories that failed
- Display a summary when finished
Example output:
GitHub repository backup
========================
Finding all repositories available to your GitHub account...
Found 42 repositories.
Backup location: /Users/yourname/github-backup/repositories
------------------------------------------------------------
Repository: example-user/example-project
Creating mirror...
Completed: example-user/example-project
The initial backup may take some time if you have many repositories or repositories with a large amount of history.
Step 11: Open the Backup in Finder
When the backup is complete, run:
open ~/github-backup/repositories
The backup should be organized by GitHub user or organization:
repositories/
├── personal-account/
│ ├── project-one.git/
│ ├── project-two.git/
│ └── archived-project.git/
└── organization-name/
├── company-project.git/
└── internal-tool.git/
Each .git folder is a complete mirror of that repository.
Step 12: Count the Backed-Up Repositories
To count the number of mirror repositories in your backup, run:
find ~/github-backup/repositories -type d -name "*.git" | wc -l
Compare that number with the repository count displayed by the backup script.
A difference does not always mean the backup failed. For example, you may have lost access to a repository, or one may have been deleted between runs.
The script also displays the number of successful and failed backups.
How to Update the Backup Later
You do not need to recreate the backup every time.
To update it, return to the backup folder:
cd ~/github-backup
Then run:
./backup-github.sh
The script detects whether a repository has already been backed up.
For existing repositories, it runs:
git remote update --prune
This downloads new branches, tags, commits, and references while removing remote references that no longer exist.
New repositories are cloned automatically.
What the Script Backs Up
The script backs up the Git contents of repositories available to your account, including:
- Public repositories
- Private repositories
- Organization repositories
- Repositories where you are a collaborator
- Forks
- Archived repositories
- All branches
- All tags
- All commits
- Complete Git history
- Git LFS objects where accessible
What the Script Does Not Fully Back Up
A Git mirror does not contain every feature stored by GitHub.
It does not fully preserve:
- Issues
- Issue comments
- Pull requests
- Pull-request review comments
- GitHub Discussions
- GitHub Projects
- Repository settings
- Branch-protection settings
- Actions logs
- Actions artifacts
- Repository secrets
- Deploy keys
- Webhooks
- Uploaded release assets
- Wiki content unless backed up separately
The script protects the code and its Git history, which is the most important part of a repository backup.
A complete GitHub account archive would require additional API exports for GitHub-specific metadata.
Troubleshooting
The Script Asks for an SSH-Key Passphrase
You may see something like:
Enter passphrase for key '/Users/yourname/.ssh/id_ed25519':
This means Git is attempting to use SSH instead of HTTPS.
Check the configured Git protocol:
gh auth status
If it shows SSH, change it:
gh config set git_protocol https
Then configure Git authentication again:
gh auth setup-git
The script in this guide uses the repository’s HTTPS clone URL, so it should not need your SSH key.
What Is id_ed25519?
id_ed25519 is a common filename for an SSH private key:
~/.ssh/id_ed25519
An SSH private key proves your identity when connecting to services such as GitHub or a remote server.
The key may be protected by a passphrase.
That passphrase is not necessarily:
- Your GitHub password
- Your Mac login password
- Your Apple ID password
- A personal access token
If you do not remember the SSH passphrase, using HTTPS authentication is usually the easiest option for this backup process.
Do not delete an unknown SSH key without first checking whether it is used for another service or server.
GitHub CLI Is Not Authenticated
If the script reports:
Error: GitHub CLI is not authenticated.
run:
gh auth login
Choose GitHub.com, HTTPS, and browser authentication.
Then run:
gh auth setup-git
Afterward, start the script again.
A Private Repository Fails to Clone
First, confirm that GitHub CLI recognizes your account:
gh auth status
Then test whether your account can see the repository:
gh repo view OWNER/REPOSITORY
Replace OWNER/REPOSITORY with the actual repository name.
If you no longer have access to the repository, GitHub cannot provide a new backup of it.
Some Git LFS Files Fail
The script may display:
Warning: some Git LFS objects could not be downloaded.
Possible causes include:
- The repository no longer has access to the LFS object
- The object was deleted
- The organization restricts access
- The repository exceeded its LFS allowance
- The original file was never uploaded correctly
The normal Git repository backup may still be successful even when some LFS objects fail.
The Backup Folder Does Not Look Like a Normal Project
A mirror backup is intentionally not a normal working directory.
You may see files such as:
HEAD
config
description
objects
packed-refs
refs
That is normal for a bare or mirror Git repository.
How to Restore a Repository From the Backup
Suppose you have a local mirror backup at:
~/github-backup/repositories/example-user/example-project.git
First, create a new empty repository on GitHub.
Then open the local mirror:
cd ~/github-backup/repositories/example-user/example-project.git
Set the destination repository URL:
git remote set-url origin https://github.com/example-user/restored-project.git
Push the complete mirror:
git push --mirror
This uploads the branches, tags, and Git references contained in the backup.
For Git LFS repositories, also run:
git lfs push --all origin
Be careful with git push --mirror. It makes the destination repository match the mirror and can delete references from the destination that are not present in the backup.
It is safest to restore into a new, empty repository.
Store the Backup Somewhere Safe
A backup stored only on the same Mac is better than no backup, but it is still vulnerable to:
- Drive failure
- Theft
- Accidental deletion
- Malware
- Water or fire damage
Consider copying the entire folder to:
- An external hard drive
- A second computer
- An encrypted cloud-storage service
- A network-attached storage device
The backup folder is located at:
~/github-backup
You can copy the entire folder as one unit.
Final Result
After completing this guide, you will have:
- A local mirror of every GitHub repository available to your account
- Support for private and organization repositories
- Complete branch, tag, commit, and reference history
- Git LFS downloads where available
- A reusable script for future updates
- A log showing repositories that failed to back up
To refresh the backup at any time, run:
cd ~/github-backup
./backup-github.sh
This provides a straightforward local backup of your GitHub code without relying on SSH authentication or manually cloning repositories one by one.
Leave a Reply