$ git push [<repository> [<refspec>...]]
Updates remote references along with their history.
<refspec> tells git which remote reference to update with which local one.
$ git push origin ba:bb
pushes the local branch ba to the branch bb at origin. If the remote branch doesn't exist it gets created.
If :<dst> is not specified, then remote.<name>.push is used as a refmap. I.e. remote.<name>.push determines where to push a given reference.
If remote.origin.push is +refs/heads/*:refs/heads/*, then
$ git push origin ba
is equivalent to git push origin +ba:refs/heads/ba and (force-)pushes the local branch ba to the branch ba at origin.
If remote.origin.push is refs/heads/ba:refs/heads/bb, then the command is equivalent to git push origin ba:refs/heads/bb and pushes the local branch ba to the branch bb at origin.
If remote.<name>.push is not set and push.default is upstream, then it (git push <repository> <refspec> without :<dst>) pushes the local branch to its upstream branch. If push.default is not upstream, to its matching (the same name) branch.
: is a refspec that tells git to update matching branches. I.e. for each local branch l, if there's a remote branch r that has the same name, then push l to r:
$ git push origin :
If <refspec> is not passed, then remote.<name>.push is used instead (git push origin is equivalent to git push origin <remote.origin.push>).
If remote.origin.push is ba, then
$ git push origin
is equivalent to git push origin ba.
If <refspec> is not passed and remote.<name>.push is not set, then see push.default.
The relevant docs:
When the command line does not specify what to push with
<refspec>...arguments or--all,--mirror,--tagsoptions, the command finds the default<refspec>by consultingremote.*.pushconfiguration, and if it is not found, honorspush.defaultconfiguration to decide what to push (See git-config[1] for the meaning ofpush.default).
https://git-scm.com/docs/git-push#_description
If
git push [<repository>]without any<refspec>argument is set to update some ref at the destination with<src>withremote.<repository>.pushconfiguration variable,:<dst>part can be omitted—such a push will update a ref that<src>normally updates without any<refspec>on the command line. Otherwise, missing:<dst>means to update the same ref as the<src>.The special refspec
:(or+:to allow non-fast-forward updates) directs Git to push "matching" branches: for every branch that exists on the local side, the remote side is updated if a branch of the same name already exists on the remote side.
https://git-scm.com/docs/git-push#Documentation/git-push.txt-ltrefspecgt82308203
git push originWithout additional configuration, pushes the current branch to the configured upstream (
branch.<name>.mergeconfiguration 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.defaultconfiguration variable.For example, to default to pushing only the current branch to
originusegit config remote.origin.push HEAD. Any valid<refspec>(like the ones in the examples below) can be configured as the default forgit push origin.git push origin :Push "matching" branches to
origin. See<refspec>in the OPTIONS section above for a description of "matching" branches.git push origin masterFind a ref that matches
masterin the source repository (most likely, it would findrefs/heads/master), and update the same ref (e.g.refs/heads/master) inoriginrepository with it. Ifmasterdid not exist remotely, it would be created.git push origin HEADA handy way to push the current branch to the same name on the remote.
git push mothership master:satellite/master dev:satellite/devUse the source ref that matches
master(e.g.refs/heads/master) to update the ref that matchessatellite/master(most probablyrefs/remotes/satellite/master) in themothershiprepository; do the same fordevandsatellite/dev.See the section describing
<refspec>...above for a discussion of the matching semantics.This is to emulate
git fetchrun on the mothership usinggit pushthat is run in the opposite direction in order to integrate the work done onsatellite, and is often necessary when you can only make connection in one way (i.e.satellitecan ssh intomothershipbutmothershipcannot initiate connection tosatellitebecause the latter is behind a firewall or does not runsshd).After running this
git pushon the satellite machine, you would ssh into the mothership and rungit mergethere to complete the emulation ofgit pullthat were run onmothershipto pull changes made onsatellite.git push origin HEAD:masterPush the current branch to the remote ref matching
masterin theoriginrepository. This form is convenient to push the current branch without thinking about its local name.git push origin master:refs/heads/experimentalCreate the branch
experimentalin theoriginrepository by copying the currentmasterbranch. This form is only needed to create a new branch or tag in the remote repository when the local name and the remote name are different; otherwise, the ref name on its own will work.git push origin +dev:masterUpdate the
originrepository’smasterbranch with thedevbranch, allowing non-fast-forward updates. This can leave unreferenced commits dangling in theoriginrepository. Consider the following situation, where a fast-forward is not possible:o---o---o---A---B origin/master \ X---Y---Z devThe above command would change the
originrepository toA---B (unnamed branch) / o---o---o---X---Y---Z masterCommits
AandBwould no longer belong to a branch with a symbolic name, and so would be unreachable. As such, these commits would be removed by agit gccommand on theoriginrepository.
https://git-scm.com/docs/git-push#_examples
remote.<name>.pushThe default set of "refspec" for git-push[1]. See git-push[1].
https://git-scm.com/docs/git-config#Documentation/git-config.txt-remoteltnamegtpush
a.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)
@test "pushes to the specified branch" {
start_cloned_repo
git commit --allow-empty -m B
git push origin ba:ba
assert_equal_sha origin/ba ba
}
@test "the remote branch gets created when not exists" {
start_cloned_repo
git commit --allow-empty -m B
git push origin ba:bb
assert_branch refs/remotes/origin/bb
}
@test "remote.<name>.push is used as a refmap when no :<dst>" {
start_cloned_repo
git config remote.origin.push refs/heads/ba:bb
git commit --allow-empty -m B
git push origin ba
assert_equal_sha origin/bb ba
}
@test "allows non-fast-forwards when there's a + in remote.<name>.push" {
start_cloned_repo
git config remote.origin.push +refs/heads/ba:ba
git commit --allow-empty --amend -m A2
git push origin ba
assert_equal_sha origin/ba ba
}
@test "but <branch> without refs/heads doesn't match the branch" {
start_cloned_repo
git config remote.origin.push ba:bb
git commit --allow-empty -m B
git push origin ba
refute_branch refs/remotes/origin/bb
}
@test "pushes to the upstream branch when remote.<name>.push is not set and push.default = upstream" {
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 origin ba
assert_equal_sha origin/bb ba
}
@test "pushes to the matching branch when remote.<name>.push is not set and push.default != upstream" {
start_cloned_repo
git push origin ba:bb; git branch -u origin/bb
git commit --allow-empty -m B
git push origin ba
assert_equal_sha origin/ba ba
}
@test "the remote branch gets created when not exists (no :<dst>)" {
start_cloned_repo
git checkout -b bb
git commit --allow-empty -m B
git push origin bb
assert_branch refs/remotes/origin/bb
}
@test ": pushes when the matching branch exists" {
start_cloned_repo
git commit --allow-empty -m B
git push origin :
assert_equal_sha origin/ba ba
}
@test ": doesn't push when no matching branch" {
start_cloned_repo
git checkout -b bb
git commit --allow-empty -m B
git push origin :
refute_branch refs/remotes/origin/bb
}
@test ": pushes other branches" {
start_cloned_repo
git commit --allow-empty -m B
git checkout -b bb
git push origin :
assert_equal_sha origin/ba ba
}
@test "remote.<name>.push is used when no <refspec>" {
start_cloned_repo
git config remote.origin.push ba:bb
git commit --allow-empty -m B
git push origin
assert_equal_sha origin/bb ba
}
@test "push.default doesn't affect it when remote.<name>.push is set" {
start_cloned_repo
git config push.default upstream
git config remote.origin.push ba
git push origin ba:bb; git branch -u origin/bb
git commit --allow-empty -m B
git push origin
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() {
(mkrepo)
cd "$BATS_TEST_TMPDIR"
git clone --bare a a.git
git clone a.git b
cd b
git config user.email [email protected]
git config user.name "Your Name"
}
mkrepo() {
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)
}
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 a.bats