Staying In Sync
Two copies of a repository start drifting apart the moment either one gains a commit. Keeping them in step is two commands, and one message you will meet within your first week.
That message is rejected — non-fast-forward. It sounds hostile and means something completely reasonable, and understanding it is the difference between a calm minute and a bad afternoon.
Fetch: look without touching
git fetch
Git downloads any new commits from origin and updates its record of where the remote's branches are. Your files do not change and your branch does not move.
git status
On branch main Your branch is behind 'origin/main' by 1 commit, and can be fast-forwarded.
This is the safe "what has happened" command, and almost nobody teaches it to beginners. It is worth knowing precisely because it lets you look before you leap.
Pull: fetch, then merge
git pull
git pull is git fetch followed by a merge into your current branch. That is the whole definition, and knowing it is two steps explains everything surprising that pull ever does — including the occasional merge commit and the occasional conflict.
Usually it is a fast-forward, exactly as in Chapter 6: you had no new commits, so your branch simply moves to catch up.
The rejection
Now the other direction. You committed locally, and so did somebody else — or you edited a file through GitHub's web interface on the train:
git push
! [rejected] main -> main (fetch first) error: failed to push some refs to 'github.com:you/sandpiper.git' hint: Updates were rejected because the remote contains work that you do not hint: have locally. This is usually caused by another repository pushing to hint: the same ref. If you want to integrate the remote changes, use hint: 'git pull' before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Read the hint, which is unusually clear: the remote has commits you do not have. Git refuses because completing the push as asked would mean discarding them, and it will not do that on your behalf.
The fix
git pull git push
Pull brings their commits in and merges them with yours. If the two changed the same lines, you get a conflict — exactly the conflict from Chapter 6, with exactly the same markers and the same resolution. Nothing new to learn; it is the same operation with a second copy involved.
Then push. That loop — pull, resolve, push — is the whole of staying in sync, and you will run it for the rest of your career.
The thing not to do
Search for that rejection message and something will tell you to add --force.
git push --force replaces the branch on GitHub with yours. Any commit that was there and is not on your machine stops being part of that branch — which, on a shared project, is somebody's afternoon.
It has legitimate uses on a branch only you work on. This book never needs it, and a beginner reaching for it is almost always solving the wrong problem. Chapter 10 comes back to it as one of the four mistakes that genuinely hurt.
A habit
Pull before you start work, push when you finish a piece of it. Most sync problems are simply two copies left apart for a day, and the fix is a rhythm rather than a technique.
- "Rejected means I did something wrong." It means the other copy moved. Pull, then push. It is the most routine message in Git and it happens to everyone weekly.
- "
--forcemakes the push work." It makes the push win, by discarding whatever was there. On a shared branch that is someone else's work. - "
fetchandpullare the same." Pull is fetch plus a merge. Fetch alone never changes your files, which is what makes it the safe way to look first. - "A conflict during a pull is a different, harder kind of conflict." It is exactly the Chapter 6 conflict, with the same markers and the same resolution. Only the source of the other change is different.
- This loop is underneath every collaboration in Chapter 8. The pull request workflow is this same loop with a website in the middle of it.
- Understanding why Git rejects a push is what stops a beginner reaching for
--force, which is the most expensive habit in this book to acquire. - Pull-before-you-start is one habit that prevents most of the sync problems people report in their first month.
Knowledge Check
What is the difference between git fetch and git pull?
- Fetch downloads and changes nothing at all; pull downloads and then merges it in
- Fetch takes one branch and pull takes every branch in the repository at once
- Fetch works on public repositories and pull requires push access to the remote
- Fetch is the old name for pull, kept for compatibility with older versions
Your push is rejected as non-fast-forward. What has happened?
- The remote has commits that your own copy does not have yet locally
- Your commits are malformed, so the remote refuses to accept them at all
- Your authentication expired part way through the upload of the commits
- The branch you are pushing does not exist on the remote and must be created
A pull produces a merge conflict. How is it different from the one in Chapter 6?
- It is not different, with the same markers, the same resolution, the same abort
- It cannot be aborted, because the incoming commits are already on the remote
- It has to be resolved on GitHub's website rather than in your own editor
- It affects the remote copy immediately, so other people see the conflict too
Why does this book tell you never to use --force on a shared branch?
- Because it replaces the branch, dropping commits that are not on your machine
- Because Git will refuse the command and report an error about shared branches
- Because it uploads your entire working directory rather than only the commits
- Because forced pushes are disabled by default on every hosting service
You got correct