How to Back Up All Your GitHub Repositories on macOS

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:

  1. Check that Git and GitHub CLI are installed
  2. Confirm that you are logged in to GitHub
  3. Retrieve all repositories available to your account
  4. Create a mirror clone of each repository
  5. Download Git LFS objects when available
  6. Record any repositories that failed
  7. 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.


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *