Why VS Code Won’t Publish Your Empty Repo (and How to Fix Git Credentials Once and For All)

- by

I hit two Git papercuts back to back this week, and I suspect I’m not the only one. The first cost me ten minutes of staring at a button that did nothing. The second has cost me, cumulatively, hours across every machine I own.

Here they are, with the fixes.

Papercut 1: “Publish Branch” does nothing

I had a fresh folder, git init done, a GitHub repo created, origin pointing at it. In VS Code’s Source Control panel I clicked Publish Branch โ€” and nothing happened. No error, no dialog, no push.

The clue was in git status:

On branch main

No commits yet

nothing to commit (create/copy files and use "git add" to track)

There was nothing to publish. The folder was genuinely empty โ€” not a single file.

Here’s the thing worth internalising: in Git, a branch is a pointer to a commit. Until you make your first commit, main doesn’t really exist. HEAD points at a branch name that has nothing behind it. When VS Code offers to publish the branch, there’s no branch object to send, and it quietly declines to do anything useful.

You can confirm the remote is fine while the branch is not:

git ls-remote origin

If that returns nothing but exits cleanly, the remote exists, is reachable, and is simply empty. Good news โ€” your problem is local.

The fix: make one commit

That’s it. Give Git something to point at:

printf '# My Project\n' > README.md
git add README.md
git commit -m "Initial commit"
git push -u origin main

The -u matters. It sets origin/main as the upstream for your local main, which is what flips VS Code from Publish Branch to the normal sync arrows.

This is also why GitHub nudges you to “Add a README” when creating a repo through the web UI. It’s not decoration โ€” it’s making sure the repo has a commit so everything downstream behaves.

Papercut 2: Git doesn’t know my GitHub credentials

This is the one that has bitten me on laptop after laptop.

GitHub removed password authentication for Git operations back in 2021. So git push over HTTPS needs a Personal Access Token instead of your password. Which means, on every new machine, you either generate a PAT in the GitHub web UI, scope it correctly, and paste it into a terminal prompt you hope caches it โ€” or you generate an SSH key, add it to the agent, register the public key with GitHub, and remember to use SSH remote URLs.

Both work. Both are tedious. Both are easy to get subtly wrong, and the failure mode is a confusing 403 weeks later when a token silently expires.

Enter the GitHub CLI

gh is GitHub’s official command-line tool. Install it with Homebrew:

brew install gh

Not on a Mac? gh is available everywhere โ€” on Debian and Ubuntu sudo apt install gh, on Fedora sudo dnf install gh, and on Windows winget install --id GitHub.cli. Everything from here on is identical on all three platforms.

Then log in:

gh auth login

It walks you through a short set of questions:

  • What account do you want to log into? โ†’ GitHub.com
  • Preferred protocol for Git operations? โ†’ HTTPS
  • Authenticate Git with your GitHub credentials? โ†’ Yes โ† this is the important one
  • How would you like to authenticate? โ†’ Login with a web browser

Pick the browser option and it shows you an eight-character code, then sends you to GitHub to paste it.

A snag worth knowing about the browser step

When the browser opens, you may land on GitHub’s sign-in / two-factor screen rather than the code box โ€” passkey, authenticator app, recovery codes. This confused me for a minute, because I had a code in my terminal and nowhere obvious to put it.

They are two different steps, in order:

  1. Sign in to GitHub in that browser (passkey, or authenticator app six-digit code). GitHub needs to know who you are before it can authorise anything.
  2. Then you get the Device Activation page, and that’s where the eight-character code from your terminal goes.

Don’t reach for the 2FA recovery codes here โ€” those are for genuine lockouts. And note the terminal code expires after about 15 minutes; if you’ve dawdled, Ctrl+C and re-run gh auth login for a fresh one.

The bit that actually fixes credentials

Here’s the real gotcha, and the reason I’m writing this section.

That third question โ€” “Authenticate Git with your GitHub credentials?” โ€” is the one that solves the problem. It configures Git to ask gh for a token on every push and pull. But it can be skipped, or answered “No”, or not offered at all if you authenticate a different way.

Check whether it actually took effect:

git config --get-all credential.https://github.com.helper

You want to see something like:

!/opt/homebrew/bin/gh auth git-credential

If it comes back empty, it didn’t take. Fix it with one command:

gh auth setup-git

That’s the command to remember. It’s the whole trick. Run it on every machine and git push over HTTPS simply works from then on โ€” no PAT juggling, no SSH keys, no “Support for password authentication was removed” wall.

Verify the whole picture with gh auth status:

github.com
  โœ“ Logged in to github.com account yourname (keyring)
  - Active account: true
  - Git operations protocol: https
  - Token: gho_************************************
  - Token scopes: 'gist', 'read:org', 'repo', 'workflow'

Watch out for stale credentials

On my Mac, credential.helper was already set to osxkeychain, with a GitHub entry whose username was a string of digits. That’s a user-ID-style entry, typically written by VS Code’s or GitHub Desktop’s built-in GitHub authentication rather than by you.

Entries like that work right up until they don’t, and then you get opaque permission errors. The good news: gh auth setup-git writes a github.com-specific helper, and Git prefers the more specific match. So it supersedes the stale entry rather than fighting with it.

If you want to inspect what’s cached:

printf "protocol=https\nhost=github.com\n\n" | git credential fill

While you’re here: skip the whole dance next time

Once gh is authenticated, you don’t need to create repos in the browser at all:

gh repo create my-project --private --source=. --remote=origin --push

That creates the GitHub repo, wires up origin, and pushes โ€” in one command. Though note it still needs at least one commit locally, for exactly the reason in Papercut 1.

Other bits I reach for constantly:

  • gh repo clone owner/name โ€” clone with auth already sorted
  • gh pr create and gh pr checkout 42
  • gh run watch โ€” follow a GitHub Actions run in the terminal
  • gh api โ€” authenticated REST and GraphQL calls without wrangling tokens

The two-line summary

If Publish Branch does nothing, you have no commits. Make one.

If Git keeps forgetting who you are, install gh, run gh auth login, and then run gh auth setup-git to be certain the credential helper is actually wired up.


One caveat: gh only handles GitHub. For GitLab, Bitbucket, or a self-hosted Git server you’re back to your system keychain or SSH keys. But if GitHub is where your work lives, this is ten minutes that pays for itself many times over.

Every command in this article was actually run, on a Mac Studio (M2 Ultra) under macOS Tahoe 26.6.2 in September 2026, with gh 2.100.0 and git 2.50.1 โ€” the terminal output above is real, not reconstructed. The gh auth setup-git section exists precisely because on that machine gh auth login did not wire up the credential helper, and the check came back empty. Tooling moves on, so if a future release of gh changes any of this, the official gh manual is the first place to check.



If you enjoy my content, please consider supporting me on Ko-fi. In return you can browse this whole site without any pesky ads! More details here.

Leave a Comment!

This site uses Akismet to reduce spam. Learn how your comment data is processed.