git.guide

Pushing, pulling, and the errors you will actually hit

Four messages account for most of the time people lose to remotes. Each one is git declining to guess rather than git failing, and each has a right answer that is duller than the one at the top of the search results.

The model

Your clone has its own copy of everybody else's branches.

When you clone, git makes a full copy of the repository and then keeps a second set of pointers recording where each branch was on the server the last time it looked. Those are remote tracking branches and they are named like origin/main.

origin/main is not the server. It is your note of where the server was. It only updates when you ask git to go and look, which is why your idea of what is on the server can be days old and perfectly consistent with itself.

git fetch            # go and look. Updates origin/main, touches nothing of yours
git status           # now says whether you are ahead, behind, or diverged

Fetch is always safe. It never changes your files or your branches, so there is no situation in which fetching first makes anything worse. Pull is a fetch followed immediately by a merge, and the merge is where the surprises come from.

Error one, and the common one

Updates were rejected because the remote contains work you do not have

Somebody pushed while you were working. Your branch and the server's have both moved on from the point where they agreed, and git will not throw away their commits to make room for yours.

The fix is to bring their work into yours and push the result:

git pull --rebase
git push

--rebase replays your commits on top of theirs, so no merge commit appears for what is really just two people working at the same time. Make it the default and stop thinking about it:

git config --global pull.rebase true

If the rebase hits a conflict, the conflict section on the branches page applies, including the warning that the two sides are labelled the opposite way round during a rebase.

What not to do is force the push. The rejection is git protecting somebody else's commits. Forcing past it removes them from the server, and the person who wrote them will not find out until their next pull.

When you do need to force

Use force with lease, and know what it checks.

There is one legitimate case. You rebased or amended your own branch, so the commits on the server are older versions of your own work and are meant to be replaced.

git push --force-with-lease

Plain --force says overwrite whatever is there. --force-with-lease says overwrite it only if it is still exactly where I last saw it, so if a colleague pushed to your branch in the meantime the push is refused rather than quietly destroying their commit.

One thing to know about it, because it is the sharp edge. The check compares against your remote tracking branch, which is your record of where the server was. If you ran a bare git fetch just beforehand, you updated that record to include their commit, and the lease will then happily approve overwriting it. Fetch, look at what changed, then push.

Never force push a branch other people build on, and never force push main. On a shared branch the operation you want is revert.

Error two

The current branch has no upstream branch

You made a branch locally and pushed it for the first time. Git does not assume which remote or which name you meant.

git push -u origin new-thing

-u records the pairing, so from then on git push and git pull on that branch need no arguments. Or set it once and have every new branch do it:

git config --global push.autoSetupRemote true

Error three

You are in detached HEAD state

Normally HEAD points at a branch and the branch points at a commit. Checking out a commit directly, or a tag, points HEAD straight at the commit with no branch in between.

You can look around and you can even commit. The problem is that nothing is holding on to what you commit, so as soon as you move away those commits are unreachable and are eventually cleared out.

If you have made nothing you want, just leave:

git switch -

If you have committed something worth keeping, give it a branch before you go anywhere:

git switch -c keep-this

And if you have already left and think the commits are gone, they are almost certainly still there. The reflog will find them.

Error four, and it is the one with teeth

Authentication, and the credential you forgot was there

Password authentication for git over HTTPS is gone at the large hosts, so an old script that used one now fails. The replacements are an SSH key, or a token used in place of a password.

The part worth pausing on is where that credential ends up. A repository cloned over HTTPS with a token written into the address records that address, token and all, in plain text, in the repository's own config file.

git remote -v          # look at what your remote address actually says

If there is a token in there, two things follow. It is on every machine holding a clone, web servers included. And on a server whose repository is readable from the internet, that config file is one of the three addresses the check on this site asks for, because it is the file most likely to turn a source code leak into somebody using your account.

Use an SSH key, so the secret lives outside the repository:

git remote set-url origin git@github.com:owner/project.git

Better still, do not give a web server access to your repository at all. Deploy the files rather than the repository and the question stops existing.

Worth the habit

Three things that prevent most of the above

  • Fetch when you start, not when you are ready to push. Finding out that main has moved is cheap at the beginning of a piece of work and expensive at the end of it.
  • Look before you pull. git log --oneline HEAD..origin/main shows exactly what is about to arrive. It takes a second and occasionally changes your plans.
  • Push your branch early, even unfinished. It is a backup of work that otherwise exists on one laptop, and the reflog cannot help you when the laptop is the thing that failed.

Short answers

Questions people actually ask

Why was my push rejected?

Somebody pushed while you were working, so the server has commits you do not. Git will not discard their work to make room for yours. git pull --rebase then git push brings their commits into yours and sends the result.

Can I just force the push?

Not to fix a rejection. The rejection is git protecting somebody else's commits, and forcing past it removes them from the server, which the person who wrote them discovers on their next pull. Force is only legitimate when the commits being replaced are older versions of your own work.

What is the difference between fetch and pull?

Fetch goes and looks, updating your record of where the server was. It never changes your files or your branches, so it is always safe. Pull is a fetch followed immediately by a merge, and the merge is where the surprises come from.

What is a detached HEAD and how do I get out?

You are on a commit rather than on a branch, so nothing is holding on to anything you commit there. If you have made nothing you want, git switch -. If you have, git switch -c a-name before you go anywhere.

Why does git keep asking for a password?

Password authentication over HTTPS is gone at the large hosts. Use an SSH key, or a token through a credential helper. An SSH key is the better answer because the secret then lives outside the repository rather than inside its configuration.

How do I switch a repository from HTTPS to SSH?

git remote set-url origin git@host:owner/project.git. Worth doing if git remote -v shows a token in the address, because that token is sitting in plain text in the repository's own config file on every machine holding a clone.

Every question on this site, in one list

What an exposed repository gives away

Every version of every file, including the secrets you took out again.

How to block it

The rule for Apache, nginx, Caddy, LiteSpeed, IIS and Cloudflare.

Deploying without publishing the repository

Three ways to get code onto a server that cannot leak like this.

All nine guides