git
: push.default
The setting determines which local branches git push
and git push <repository>
should push and where. (It also affects some other cases, see git push
.) To put it briefly:
nothing
- push nothing (error out)upstream
- push the current branch to the upstream branchsimple
(the default) - push the current branch to the matching (the same name) upstream branch (upstream
+ branch names should match)current
- push the current branch to the matching branch in the chosen remotematching
- for each local branchl
, if there's a branchr
in the chosen remote that has the same name, then pushl
tor
If push.default
is current
or matching
, it pushes to:
<repository>
, if passedbranch.<name>.pushRemote
, if setremote.pushDefault
, if set- the upstream branch's remote, if the upstream branch is set
- a non-
origin
remote, if there's only 1 remote - the
origin
remote
If the target remote doesn't get chosen, it fails (because then there are multiple options (remotes) and no reason to choose one over the others).
If push.default
is simple
, it pushes to:
<repository>
, if passedbranch.<name>.pushRemote
, if setremote.pushDefault
, if set- the upstream branch's remote
If push.default
is upstream
, the upstream branch's remote can't be overridden.
If the target remote is the local repository (because the upstream branch points to it), there can be no matching branches (a repository can't have 2 branches with the same name). This affects the last 3 modes (simple
, current
, matching
). In this case when push.default
is simple
it fails, when push.default
is current
or matching
no branches are pushed.
If push.default
is current
and the matching branch doesn't exist, it gets created. If push.default
is matching
, no branches get created.
git push
fails when push.default
is upstream
or simple
and the upstream branch is not set. When push.default
is upstream
it also fails when <repository>
is passed, or branch.<name>.pushRemote
or remote.pushDefault
are set. When push.default
is simple
it also fails when the current and upstream branch names don't match. If push.default
is current
or matching
, it fails when it's unable choose a remote.
The relevant docs:
push.default
Defines the action
git push
should take if no refspec is given (whether from the command-line, config, or elsewhere). Different values are well-suited for specific workflows; for instance, in a purely central workflow (i.e. the fetch source is equal to the push destination),upstream
is probably what you want. Possible values are:
nothing
- do not push anything (error out) unless a refspec is given. This is primarily meant for people who want to avoid mistakes by always being explicit.
current
- push the current branch to update a branch with the same name on the receiving end. Works in both central and non-central workflows.
upstream
- push the current branch back to the branch whose changes are usually integrated into the current branch (which is called@{upstream}
). This mode only makes sense if you are pushing to the same repository you would normally pull from (i.e. central workflow).
tracking
- This is a deprecated synonym forupstream
.
simple
- push the current branch with the same name on the remote.If you are working on a centralized workflow (pushing to the same repository you pull from, which is typically
origin
), then you need to configure an upstream branch with the same name.This mode is the default since Git 2.0, and is the safest option suited for beginners.
matching
- push all branches having the same name on both ends. This makes the repository you are pushing to remember the set of branches that will be pushed out (e.g. if you always pushmaint
andmaster
there and no other branches, the repository you push to will have these two branches, and your localmaint
andmaster
will be pushed there).To use this mode effectively, you have to make sure all the branches you would push out are ready to be pushed out before running
git push
, as the whole point of this mode is to allow you to push all of the branches in one go. If you usually finish work on only one branch and push out the result, while other branches are unfinished, this mode is not for you. Also this mode is not suitable for pushing into a shared central repository, as other people may add new branches there, or update the tip of existing branches outside your control.This used to be the default, but not since Git 2.0 (
simple
is the new default).
https://git-scm.com/docs/git-config#Documentation/git-config.txt-pushdefault
remote.pushDefault
The remote to push to by default. Overrides
branch.<name>.remote
for all branches, and is overridden bybranch.<name>.pushRemote
for specific branches.
https://git-scm.com/docs/git-config#Documentation/git-config.txt-remotepushDefault
branch.<name>.pushRemote
When on branch
<name>
, it overridesbranch.<name>.remote
for pushing. It also overridesremote.pushDefault
for pushing from branch<name>
. When you pull from one place (e.g. your upstream) and push to another place (e.g. your own publishing repository), you would want to setremote.pushDefault
to specify the remote to push to for all branches, and use this option to override it for a specific branch.
https://git-scm.com/docs/git-config#Documentation/git-config.txt-branchltnamegtpushRemote
When the command line does not specify where to push with the
<repository>
argument,branch.*.remote
configuration for the current branch is consulted to determine where to push. If the configuration is missing, it defaults toorigin
.When the command line does not specify what to push with
<refspec>...
arguments or--all
,--mirror
,--tags
options, the command finds the default<refspec>
by consultingremote.*.push
configuration, and if it is not found, honorspush.default
configuration to decide what to push (See git-config[1] for the meaning ofpush.default
).When neither the command-line nor the configuration specifies what to push, the default behavior is used, which corresponds to the
simple
value forpush.default
: the current branch is pushed to the corresponding upstream branch, but as a safety measure, the push is aborted if the upstream branch does not have the same name as the local one.
https://git-scm.com/docs/git-push#_description
git push
Works like
git push <remote>
, where<remote>
is the current branch’s remote (ororigin
, if no remote is configured for the current branch).git push origin
Without additional configuration, pushes the current branch to the configured upstream (
branch.<name>.merge
configuration variable) if it has the same name as the current branch, and errors out without pushing otherwise.The default behavior of this command when no
<refspec>
is given can be configured by setting the push option of the remote, or thepush.default
configuration variable.For example, to default to pushing only the current branch to
origin
usegit config remote.origin.push HEAD
. Any valid<refspec>
(like the ones in the examples below) can be configured as the default forgit push origin
.
https://git-scm.com/docs/git-push#_examples
nothing.bats
:
strict() { set -euo pipefail; shopt -s inherit_errexit; "$@"; }
setup() {
[ "$BATS_LIB_PATH" = /usr/lib/bats ] && BATS_LIB_PATH=$HOME/.bats/lib:$BATS_LIB_PATH
bats_load_library bats-support
bats_load_library bats-assert
strict
}
# the state of the cloned repository (1 commit, 1 local branch ba)
# A (HEAD -> ba, origin/ba, origin2/ba, origin3/ba)
@test "fails when no <repository>" {
start_cloned_repo
git config push.default nothing
git commit --allow-empty -m B
run git push
assert_equal "$status" 128
assert_output "fatal: You didn't specify any refspecs to push, and push.default is \"nothing\"."
}
@test "fails when no <refspec>" {
start_cloned_repo
git config push.default nothing
git commit --allow-empty -m B
run git push origin
assert_equal "$status" 128
assert_output "fatal: You didn't specify any refspecs to push, and push.default is \"nothing\"."
}
assert_equal_sha() {
assert_equal "`git rev-parse "$1"`" "`git rev-parse "$2"`"
}
assert_not_equal_sha() {
! assert_equal_sha "$1" "$2"
}
assert_branch() {
git show-ref --verify --quiet "$1"
}
refute_branch() {
run git show-ref --verify --quiet "$1"
assert_equal "$status" 1
}
start_cloned_repo() {
(mk_bare_repos)
cd "$BATS_TEST_TMPDIR"
git clone a.git b
cd b
git config user.email [email protected]
git config user.name "Your Name"
git remote add origin2 ../a2.git
git fetch origin2
git remote add origin3 ../a3.git
git fetch origin3
}
mk_bare_repos() {
cd "$BATS_TEST_TMPDIR"
mkdir a
(cd a
git init
git branch -m ba
git config user.email [email protected]
git config user.name "Your Name"
git commit --allow-empty -m A)
mv a/.git a.git
(cd a.git
git config core.bare true)
cp -r a.git a2.git
cp -r a.git a3.git
}
ml() { for l; do printf '%s\n' "$l"; done; }
upstream.bats
:
strict() { set -euo pipefail; shopt -s inherit_errexit; "$@"; }
setup() {
[ "$BATS_LIB_PATH" = /usr/lib/bats ] && BATS_LIB_PATH=$HOME/.bats/lib:$BATS_LIB_PATH
bats_load_library bats-support
bats_load_library bats-assert
strict
}
# the state of the cloned repository (1 commit, 1 local branch ba)
# A (HEAD -> ba, origin/ba, origin2/ba, origin3/ba)
@test "pushes to the matching upstream branch" {
start_cloned_repo
git config push.default upstream
git commit --allow-empty -m B
git push
assert_equal_sha origin/ba ba
}
@test "pushes to the non-matching upstream branch" {
start_cloned_repo
git config push.default upstream
git push origin ba:bb; git branch -u origin/bb
git commit --allow-empty -m B
git push
assert_equal_sha origin/bb ba
}
@test "pushes to the non-origin upstream branch" {
start_cloned_repo
git config push.default upstream
git branch -u origin2/ba
git commit --allow-empty -m B
git push
assert_equal_sha origin2/ba ba
}
@test "pushes to the local upstream branch" {
start_cloned_repo
git config push.default upstream
git branch bb; git branch -u bb
git commit --allow-empty -m B
git push
assert_equal_sha bb ba
}
@test "fails when no upstream branch" {
start_cloned_repo
git config push.default upstream
git branch --unset-upstream
git commit --allow-empty -m B
run git push
assert_equal "$status" 128
assert_output -p 'fatal: The current branch ba has no upstream branch.'
}
@test "remote.pushDefault can't override the target remote" {
start_cloned_repo
git config push.default upstream
git config remote.pushDefault origin2
git commit --allow-empty -m B
run git push
assert_equal "$status" 128
assert_output -p "`ml \
"fatal: You are pushing to remote 'origin2', which is not the upstream of" \
"your current branch 'ba', without telling me what to push" \
"to update which remote branch."`"
}
@test "branch.<name>.pushRemote can't override the target remote" {
start_cloned_repo
git config push.default upstream
git config branch.ba.pushRemote origin2
git commit --allow-empty -m B
run git push
assert_equal "$status" 128
assert_output -p "`ml \
"fatal: You are pushing to remote 'origin2', which is not the upstream of" \
"your current branch 'ba', without telling me what to push" \
"to update which remote branch."`"
}
@test "<repository> can't override the target remote" {
start_cloned_repo
git config push.default upstream
git commit --allow-empty -m B
run git push origin2
assert_equal "$status" 128
assert_output -p "`ml \
"fatal: You are pushing to remote 'origin2', which is not the upstream of" \
"your current branch 'ba', without telling me what to push" \
"to update which remote branch."`"
}
assert_equal_sha() {
assert_equal "`git rev-parse "$1"`" "`git rev-parse "$2"`"
}
assert_not_equal_sha() {
! assert_equal_sha "$1" "$2"
}
assert_branch() {
git show-ref --verify --quiet "$1"
}
refute_branch() {
run git show-ref --verify --quiet "$1"
assert_equal "$status" 1
}
start_cloned_repo() {
(mk_bare_repos)
cd "$BATS_TEST_TMPDIR"
git clone a.git b
cd b
git config user.email [email protected]
git config user.name "Your Name"
git remote add origin2 ../a2.git
git fetch origin2
git remote add origin3 ../a3.git
git fetch origin3
}
mk_bare_repos() {
cd "$BATS_TEST_TMPDIR"
mkdir a
(cd a
git init
git branch -m ba
git config user.email [email protected]
git config user.name "Your Name"
git commit --allow-empty -m A)
mv a/.git a.git
(cd a.git
git config core.bare true)
cp -r a.git a2.git
cp -r a.git a3.git
}
ml() { for l; do printf '%s\n' "$l"; done; }
simple.bats
:
strict() { set -euo pipefail; shopt -s inherit_errexit; "$@"; }
setup() {
[ "$BATS_LIB_PATH" = /usr/lib/bats ] && BATS_LIB_PATH=$HOME/.bats/lib:$BATS_LIB_PATH
bats_load_library bats-support
bats_load_library bats-assert
strict
}
# the state of the cloned repository (1 commit, 1 local branch ba)
# A (HEAD -> ba, origin/ba, origin2/ba, origin3/ba)
@test "pushes to the matching upstream branch" {
start_cloned_repo
git config push.default simple
git commit --allow-empty -m B
git push
assert_equal_sha origin/ba ba
}
@test "pushes to the matching non-origin upstream branch" {
start_cloned_repo
git config push.default simple
git branch -u origin2/ba
git commit --allow-empty -m B
git push
assert_equal_sha origin2/ba ba
}
@test "fails when no upstream branch" {
start_cloned_repo
git config push.default simple
git branch --unset-upstream
git commit --allow-empty -m B
run git push
assert_equal "$status" 128
assert_output -p 'fatal: The current branch ba has no upstream branch.'
}
@test "fails when the branch names don't match" {
start_cloned_repo
git config push.default simple
git push origin ba:bb; git branch -u origin/bb
git commit --allow-empty -m B
run git push
assert_equal "$status" 128
assert_output -p "`ml \
'fatal: The upstream branch of your current branch does not match' \
'the name of your current branch.'`"
}
@test "remote.pushDefault overrides the target remote" {
start_cloned_repo
git config push.default simple
git config remote.pushDefault origin2
git commit --allow-empty -m B
git push
assert_equal_sha origin2/ba ba
}
@test "branch.<name>.pushRemote overrides remote.pushDefault" {
start_cloned_repo
git config push.default simple
git config remote.pushDefault origin
git config branch.ba.pushRemote origin2
git commit --allow-empty -m B
git push
assert_equal_sha origin2/ba ba
}
@test "<repository> overrides branch.<name>.pushRemote" {
start_cloned_repo
git config push.default simple
git config branch.ba.pushRemote origin
git commit --allow-empty -m B
git push origin2
assert_equal_sha origin2/ba ba
}
@test "simple is the default mode" {
start_cloned_repo
git push origin ba:bb; git branch -u origin/bb
git commit --allow-empty -m B
run git push
assert_equal "$status" 128
assert_output -p "`ml \
'fatal: The upstream branch of your current branch does not match' \
'the name of your current branch.'`"
}
assert_equal_sha() {
assert_equal "`git rev-parse "$1"`" "`git rev-parse "$2"`"
}
assert_not_equal_sha() {
! assert_equal_sha "$1" "$2"
}
assert_branch() {
git show-ref --verify --quiet "$1"
}
refute_branch() {
run git show-ref --verify --quiet "$1"
assert_equal "$status" 1
}
start_cloned_repo() {
(mk_bare_repos)
cd "$BATS_TEST_TMPDIR"
git clone a.git b
cd b
git config user.email [email protected]
git config user.name "Your Name"
git remote add origin2 ../a2.git
git fetch origin2
git remote add origin3 ../a3.git
git fetch origin3
}
mk_bare_repos() {
cd "$BATS_TEST_TMPDIR"
mkdir a
(cd a
git init
git branch -m ba
git config user.email [email protected]
git config user.name "Your Name"
git commit --allow-empty -m A)
mv a/.git a.git
(cd a.git
git config core.bare true)
cp -r a.git a2.git
cp -r a.git a3.git
}
ml() { for l; do printf '%s\n' "$l"; done; }
current.bats
:
strict() { set -euo pipefail; shopt -s inherit_errexit; "$@"; }
setup() {
[ "$BATS_LIB_PATH" = /usr/lib/bats ] && BATS_LIB_PATH=$HOME/.bats/lib:$BATS_LIB_PATH
bats_load_library bats-support
bats_load_library bats-assert
strict
}
# the state of the cloned repository (1 commit, 1 local branch ba)
# A (HEAD -> ba, origin/ba, origin2/ba, origin3/ba)
@test "pushes to the matching upstream branch" {
start_cloned_repo
git config push.default current
git commit --allow-empty -m B
git push
assert_equal_sha origin/ba ba
}
@test "pushes to the matching non-upstream branch" {
start_cloned_repo
git config push.default current
git push origin ba:bb; git branch -u origin/bb
git commit --allow-empty -m B
git push
assert_equal_sha origin/ba ba
}
@test "pushes to the matching non-origin branch" {
start_cloned_repo
git config push.default current
git branch -u origin2/ba
git commit --allow-empty -m B
git push
assert_equal_sha origin2/ba ba
}
@test "doesn't push to a local upstream branch" {
start_cloned_repo
git config push.default current
git branch bb; git branch -u bb
git commit --allow-empty -m B
git push
assert_not_equal_sha bb ba
}
@test "pushes to a non-origin remote when no upstream branch and no other remotes" {
start_cloned_repo
git config push.default current
git commit --allow-empty -m B
git remote rm origin
git remote rm origin3
git push
assert_equal_sha origin2/ba ba
}
@test "pushes to the origin remote when no upstream branch and more than 1 remote" {
start_cloned_repo
git config push.default current
git branch --unset-upstream
git commit --allow-empty -m B
git push
assert_equal_sha origin/ba ba
}
@test "fails when no upstream branch, more than 1 remote, but the origin remote doesn't exist" {
start_cloned_repo
git config push.default current
git commit --allow-empty -m B
git remote rm origin
run git push
assert_equal "$status" 128
assert_output -p 'fatal: No configured push destination.'
}
@test "creates the matching branch when not exists" {
start_cloned_repo
git config push.default current
git checkout -b bb
git push
assert_branch refs/remotes/origin/bb
}
@test "remote.pushDefault overrides the target remote" {
start_cloned_repo
git config push.default current
git config remote.pushDefault origin2
git commit --allow-empty -m B
git push
assert_equal_sha origin2/ba ba
}
@test "branch.<name>.pushRemote overrides remote.pushDefault" {
start_cloned_repo
git config push.default current
git config remote.pushDefault origin
git config branch.ba.pushRemote origin2
git commit --allow-empty -m B
git push
assert_equal_sha origin2/ba ba
}
@test "<repository> overrides branch.<name>.pushRemote" {
start_cloned_repo
git config push.default current
git config branch.ba.pushRemote origin
git commit --allow-empty -m B
git push origin2
assert_equal_sha origin2/ba ba
}
assert_equal_sha() {
assert_equal "`git rev-parse "$1"`" "`git rev-parse "$2"`"
}
assert_not_equal_sha() {
! assert_equal_sha "$1" "$2"
}
assert_branch() {
git show-ref --verify --quiet "$1"
}
refute_branch() {
run git show-ref --verify --quiet "$1"
assert_equal "$status" 1
}
start_cloned_repo() {
(mk_bare_repos)
cd "$BATS_TEST_TMPDIR"
git clone a.git b
cd b
git config user.email [email protected]
git config user.name "Your Name"
git remote add origin2 ../a2.git
git fetch origin2
git remote add origin3 ../a3.git
git fetch origin3
}
mk_bare_repos() {
cd "$BATS_TEST_TMPDIR"
mkdir a
(cd a
git init
git branch -m ba
git config user.email [email protected]
git config user.name "Your Name"
git commit --allow-empty -m A)
mv a/.git a.git
(cd a.git
git config core.bare true)
cp -r a.git a2.git
cp -r a.git a3.git
}
ml() { for l; do printf '%s\n' "$l"; done; }
matching.bats
:
strict() { set -euo pipefail; shopt -s inherit_errexit; "$@"; }
setup() {
[ "$BATS_LIB_PATH" = /usr/lib/bats ] && BATS_LIB_PATH=$HOME/.bats/lib:$BATS_LIB_PATH
bats_load_library bats-support
bats_load_library bats-assert
strict
}
# the state of the cloned repository (1 commit, 1 local branch ba)
# A (HEAD -> ba, origin/ba, origin2/ba, origin3/ba)
@test "pushes to the matching upstream branch" {
start_cloned_repo
git config push.default matching
git commit --allow-empty -m B
git push
assert_equal_sha origin/ba ba
}
@test "pushes to the matching non-upstream branch" {
start_cloned_repo
git config push.default matching
git push origin ba:bb; git branch -u origin/bb
git commit --allow-empty -m B
git push
assert_equal_sha origin/ba ba
}
@test "pushes to the matching non-origin branch" {
start_cloned_repo
git config push.default matching
git branch -u origin2/ba
git commit --allow-empty -m B
git push
assert_equal_sha origin2/ba ba
}
@test "doesn't push to a local upstream branch" {
start_cloned_repo
git config push.default matching
git branch bb; git branch -u bb
git commit --allow-empty -m B
git push
assert_not_equal_sha bb ba
}
@test "pushes to a non-origin remote when no upstream branch and no other remotes" {
start_cloned_repo
git config push.default matching
git commit --allow-empty -m B
git remote rm origin
git remote rm origin3
git push
assert_equal_sha origin2/ba ba
}
@test "pushes to the origin remote when no upstream branch and more than 1 remote" {
start_cloned_repo
git config push.default matching
git branch --unset-upstream
git commit --allow-empty -m B
git push
assert_equal_sha origin/ba ba
}
@test "fails when no upstream branch, more than 1 remote, but the origin remote doesn't exist" {
start_cloned_repo
git config push.default matching
git commit --allow-empty -m B
git remote rm origin
run git push
assert_equal "$status" 128
assert_output -p 'fatal: No configured push destination.'
}
@test "doesn't push when the matching branch doesn't exist" {
start_cloned_repo
git config push.default matching
git checkout -b bb
git push
refute_branch refs/remotes/origin/bb
}
@test "remote.pushDefault overrides the target remote" {
start_cloned_repo
git config push.default matching
git config remote.pushDefault origin2
git commit --allow-empty -m B
git push
assert_equal_sha origin2/ba ba
}
@test "branch.<name>.pushRemote overrides remote.pushDefault" {
start_cloned_repo
git config push.default matching
git config remote.pushDefault origin
git config branch.ba.pushRemote origin2
git commit --allow-empty -m B
git push
assert_equal_sha origin2/ba ba
}
@test "<repository> overrides branch.<name>.pushRemote" {
start_cloned_repo
git config push.default matching
git config branch.ba.pushRemote origin
git commit --allow-empty -m B
git push origin2
assert_equal_sha origin2/ba ba
}
@test "pushes other branches when the upstream branch is non-local" {
start_cloned_repo
git config push.default matching
git commit --allow-empty -m B
git checkout -b bb
git push origin bb; git branch -u origin/bb
git push
assert_equal_sha origin/ba ba
}
@test "doesn't push other branches when the upstream branch is local" {
start_cloned_repo
git config push.default matching
git commit --allow-empty -m B
git checkout -b bb
git branch bc; git branch -u bc
git push
assert_not_equal_sha origin/ba ba
}
@test "pushes other branches when no upstream branch and only 1 remote" {
start_cloned_repo
git config push.default matching
git commit --allow-empty -m B
git checkout -b bb
git remote rm origin
git remote rm origin3
git push
assert_equal_sha origin2/ba ba
}
@test "pushes other branches when no upstream branch and the origin remote exists" {
start_cloned_repo
git config push.default matching
git commit --allow-empty -m B
git checkout -b bb
git push
assert_equal_sha origin/ba ba
}
assert_equal_sha() {
assert_equal "`git rev-parse "$1"`" "`git rev-parse "$2"`"
}
assert_not_equal_sha() {
! assert_equal_sha "$1" "$2"
}
assert_branch() {
git show-ref --verify --quiet "$1"
}
refute_branch() {
run git show-ref --verify --quiet "$1"
assert_equal "$status" 1
}
start_cloned_repo() {
(mk_bare_repos)
cd "$BATS_TEST_TMPDIR"
git clone a.git b
cd b
git config user.email [email protected]
git config user.name "Your Name"
git remote add origin2 ../a2.git
git fetch origin2
git remote add origin3 ../a3.git
git fetch origin3
}
mk_bare_repos() {
cd "$BATS_TEST_TMPDIR"
mkdir a
(cd a
git init
git branch -m ba
git config user.email [email protected]
git config user.name "Your Name"
git commit --allow-empty -m A)
mv a/.git a.git
(cd a.git
git config core.bare true)
cp -r a.git a2.git
cp -r a.git a3.git
}
ml() { for l; do printf '%s\n' "$l"; done; }
$ docker run --rm -itv "$PWD":/app -w /app alpine:3.21
/ # apk add git bash ncurses
/ # git clone https://github.com/bats-core/bats-core ~/.bats
/ # git clone https://github.com/bats-core/bats-support ~/.bats/lib/bats-support
/ # git clone https://github.com/bats-core/bats-assert ~/.bats/lib/bats-assert
/ # ~/.bats/bin/bats .