Pushing Changes

git push uploads your new local commits to a remote branch. It only ever adds to the remote's history; if the remote has commits you don't have yet, Git refuses until you've brought them in — so you can't accidentally wipe out a teammate's work.

Pushing new commits

Sam's main is already linked to origin/main from the first push (lesson 30). After making commits, git status shows you're ahead:

$
echo "Omelette: eggs, butter" > omelette.txt
git add . && git commit --quiet -m "Add omelette recipe"
echo "Granola: oats, honey" > granola.txt
git add . && git commit --quiet -m "Add granola recipe"
git status
Output
On branch main
Your branch is ahead of 'origin/main' by 2 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
$
git push
Output
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 10 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 606 bytes | 606.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/sam-rivera/recipes.git
   6b13d64..fa1d869  main -> main

The last line reads: the remote's main moved from one commit to another (a..b is the range of commits that were sent). On GitHub, the output also includes lines starting with remote: — for a new branch, a link to open a pull request.

Upstream branches

A plain git push works because main has an upstream: origin/main. For a new branch, the first push needs the remote and branch name, and -u (--set-upstream) to set up the link:

$
git switch -c desserts
echo "Brownies: chocolate, butter, sugar" > brownies.txt
git add . && git commit --quiet -m "Add brownie recipe"
git push
Output
Switched to a new branch 'desserts'
fatal: The current branch desserts has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin desserts

To have this happen automatically for branches without a tracking
upstream, see 'push.autoSetupRemote' in 'git help config'.
$
git push -u origin desserts
Output
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 10 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 381 bytes | 381.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/sam-rivera/recipes.git
 * [new branch]      desserts -> desserts
branch 'desserts' set up to track 'origin/desserts'.

From then on, git push and git pull on this branch need no arguments. (If you'd like Git to do this automatically for every new branch, set git config --global push.autoSetupRemote true.)

When the push is rejected

Meanwhile Sam's colleague Alex has pushed a commit to main. Sam commits locally and tries to push:

$
echo "Pancakes: flour, milk, eggs, sugar" > pancakes.txt
git commit --quiet -am "Add sugar to pancakes"
git push
Output
To https://github.com/sam-rivera/recipes.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/sam-rivera/recipes.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

The remote has a commit Sam doesn't have, so pushing would throw it away. The fix is to bring Alex's work in first — git pull (lesson 34) — and then push:

$
git pull --no-rebase --no-edit
git push
Output
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 275 bytes | 91.00 KiB/s, done.
From https://github.com/sam-rivera/recipes
   fa1d869..a2f47b9  main       -> origin/main
Merge made by the 'ort' strategy.
 porridge.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 porridge.txt
Enumerating objects: 9, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 10 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 585 bytes | 585.00 KiB/s, done.
Total 5 (delta 2), reused 0 (delta 0), pack-reused 0
To https://github.com/sam-rivera/recipes.git
   a2f47b9..192bd15  main -> main

Force pushing

After rewriting commits you've already pushed — with --amend or a rebase — a normal push is rejected for the same reason. You can override the check with a force push, which makes the remote branch match yours exactly. Use the safer form:

$
git push --force-with-lease

--force-with-lease only overwrites the remote if it's still where you last saw it. If someone pushed in the meantime, it refuses instead of silently deleting their commits — which plain --force would do. Even so, only force-push branches that you alone work on; never force-push main. Many teams protect main on GitHub so that force pushes are blocked entirely.

Other push commands

CommandDoes
git push origin dessertsPush one branch (no upstream needed)
git push origin local-name:remote-namePush to a remote branch with a different name
git push origin --delete dessertsDelete a branch on the remote
git push origin v1.0 / --tagsPush a tag / all tags (lesson 39)
git push --dry-runShow what would be pushed

Before pushing, it's worth checking exactly what's about to go out:

$
echo "Granola: oats, honey, almonds" > granola.txt
git commit --quiet -am "Add almonds to granola"
git log --oneline origin/main..main
Output
9782050 (HEAD -> main) Add almonds to granola

Try it yourself

Push a new branch with -u, then make two more commits and check git status says you're 2 commits ahead before pushing them. Finally delete the branch on the remote with git push origin --delete.