Sharing a new feature branch
You push a new branch with -u so your teammates can see it and later pull and push commands need no arguments.
Git · Command
git push copies your local commits to a remote repository and moves that remote's branches, tags or other refs to match. It sends only the objects the remote does not already have.
Last updated
Git first picks a destination. That is either the remote you name, the current branch's upstream, or origin. Next it decides which refs to push, checking these sources in order: refspecs or flags like --all/--tags on the command line, then remote.<name>.push, then push.default (simple by default). Each refspec has the form [+]<src>[:<dst>] and links a local object to a remote ref. Git sends the objects the remote is missing and asks it to update those refs. The remote applies safety rules, such as allowing only fast-forwards on branches, unless you force the update or a hook or config rejects it.
git push [--all | --branches | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-q | --quiet] [-v | --verbose] [-u | --set-upstream] [-o <string> | --push-option=<string>] [--[no-]signed | --signed=(true|false|if-asked)] [--force-with-lease[=<refname>[:<expect>]] [--force-if-includes]] [--no-verify] [<repository> [<refspec>…]]| Name | Type | Description |
|---|---|---|
<repository> | URL | remote name | remote group | Where to push. This can be a URL, a configured remote, or a remote group. If you leave it out, Git uses the current branch's upstream, or origin. |
<refspec>... | [+]<src>[:<dst>] | Says which source object updates which remote ref. A leading + forces that one ref. An empty <src> deletes <dst>. Patterns with * and exclusions starting with ^ are allowed. |
--force-with-lease[=<refname>[:<expect>]] | option | Forces the update only if the remote ref still has the value you expect. By default, the expected value is your remote-tracking branch. |
-u, --set-upstream | flag | Records an upstream (tracking) reference for every branch that is pushed or already up to date. |
--tags / --follow-tags | flag | --tags pushes every tag. --follow-tags also pushes missing annotated tags that point at commits being pushed. |
-d, --delete | flag | Deletes the listed refs on the remote. |
-n, --dry-run | flag | Runs the whole push except actually sending the updates. |
#!/usr/bin/env bash
set -euo pipefail
cd "$(mktemp -d)"
# A bare repo stands in for the server
git init --bare -q server.git
git clone -q server.git work && cd work
git config user.email dev@example.com
git config user.name Dev
git switch -q -c feature
echo one > a.txt && git add a.txt && git commit -qm "first"
# First push: create remote branch and record upstream
git push -u origin feature
# Preview what would happen
git commit -q --amend -m "first (reworded)"
git push --dry-run --force-with-lease origin feature
# Rewrite history safely: fails if someone else moved the branch
git push --force-with-lease --force-if-includes origin feature
# Tag a release and push it with its commit
git tag -a v1.0 -m "v1.0"
git push --follow-tags
# Clean up the remote branch
git push origin --delete featureThe script creates a local bare remote, publishes a branch with an upstream, previews and then performs a lease-protected force push, pushes an annotated tag along with its commit, and finally deletes the remote branch.
You push a new branch with -u so your teammates can see it and later pull and push commands need no arguments.
After an interactive rebase, you use --force-with-lease (optionally with --force-if-includes) so you don't overwrite commits a reviewer pushed in the meantime.
A pipeline uses --atomic to push a release branch and its tag together, so the remote never ends up with only one of them.
--mirror copies every ref under refs/ to a backup server, including forced updates and deletions.
--force overwrites the remote ref no matter what it currently points to, so other people's commits can be lost. --force-with-lease only overwrites if the remote ref still has the value you expect. By default that is your remote-tracking branch, but you can give an exact commit with <refname>:<expect>. If someone pushed in the meantime, the push fails and you can fetch and look before trying again.
Follow-up: Why add --force-if-includes? A background fetch can quietly update your remote-tracking ref, which makes a plain lease pass anyway. --force-if-includes also checks that the remote tip appears in your local branch's reflog, meaning you actually integrated it.
A branch update is only accepted if the remote's current tip is an ancestor of the commit you push. When someone else has pushed commits you don't have, your push would discard them. Fetch and merge (git pull), or use git pull --rebase to replay your work on top of theirs, then push the result, which is now a fast-forward.
Follow-up: When is a plain --force push acceptable? Only when you really mean to replace history, for example after amending a commit you already pushed, and you're sure nobody has fetched or built on the old commit.
It pushes to the current branch's remote, or origin if none is set. Because no refspec was given, it checks remote.<name>.push and then push.default. With the default simple mode, it pushes the current branch to a remote branch with the same name, and it errors if you're pushing back to your pull remote and the upstream has a different name.
Follow-up: How can you avoid passing -u on every new branch? Set push.autoSetupRemote=true, which turns on --set-upstream automatically the first time you push a branch that has no upstream.
git fetchgit pullgit remotegit configgit receive-packgit send-packgithooksgitrevisions