简体中文 ▾ Topics ▾ Latest version ▾ git-push last updated in 2.53.0

名称

git-push - 与相关对象一起更新远程分支引用

概述

git push' [--all | --branches | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
	   [--repo=<仓库>] [-f | --force] [-d | --delete] [--prune] [-q | --quiet] [-v | --verbose]
	   [-u | --set-upstream] [-o <字符串> | --push-option=<字符串>]
	   [--[no-]signed|--signed=(true|false|if-asked)]
	   [--force-with-lease[=<引用名>[:<expect>]] [--force-if-includes]]
	   [--no-verify] [<仓库> [<引用规范>…​]]

描述

将本地仓库中的一个或多个分支、标签或其他引用更新到远程仓库,并发送远程端尚不存在的所有必要数据。

最简单的推送方式是 git push <remote> <branch>。例如,git push origin main 会将本地 main 分支推送到名为 origin 的远程上的 main 分支。

<repository> 参数默认取当前分支的上游仓库;如果没有配置上游,则默认为 origin

Git 会按以下优先顺序决定要推送哪些分支、标签或其他引用:

  1. <refspec> 参数(例如 git push origin main 中的 main),或者 --all--mirror--tags 选项

  2. 被推送目标仓库的 remote.<name>.push 配置

  3. push.default 配置。默认值为 push.default=simple,即推送到与当前分支同名的分支。关于 push.default 的更多信息,请参见下文 CONFIGURATION 一节。

如果你尚未为当前分支设置上游分支,git push 可能会失败,具体取决于 push.default 的设置。关于如何设置和使用上游分支,请参见下文 UPSTREAM BRANCHES 一节。

通过设置 “钩子”,你可以在每次推送到一个仓库时,让它发生有趣的事情。 见 git-receive-pack[1] 的文档。

选项

<仓库>

作为推送操作的目的地的 “远程” 仓库。 这个参数可以是一个网址(见下面 GIT 地址 一节),也可以是一个远程库的名称(见下面 远程仓库 一节)。

<refspec>...

指定要用哪个源对象来更新哪个目标引用。

refspec 的格式为 [+]<src>[:<dst>],例如 mainmain:otherHEAD^:refs/heads/main

<src> 通常是要推送的本地分支名,但也可以是任意的 “SHA-1 expression”(见 gitrevisions[7])。

<dst> 决定了远程端要更新哪个引用。它必须是分支、标签或其他引用的名称,而不能是任意表达式。

+ 是可选的,作用与 --force 相同。

你可以使用完全展开的形式来编写 refspec(例如 refs/heads/main:refs/heads/main),以精确指定源和目标;也可以使用较短的形式(例如 mainmain:other)。下面给出 refspec 的展开规则,以及其他几种特殊的 refspec 形式:

  • 如果 <src> 后面没有 :<dst>,则表示更新与 <src> 相同的引用,除非 remote.<repository>.push 配置指定了不同的 <dst>。例如,如果 main 是一个分支,那么 refspec main 会展开为 main:refs/heads/main

  • 如果 <dst> 可以无歧义地指代 <repository> 远程上的某个引用,则会将其展开为该引用。例如,如果 v1.0 是远程上的一个标签,那么 HEAD:v1.0 会展开为 HEAD:refs/tags/v1.0

  • 如果 <src> 解析为以 refs/heads/refs/tags/ 开头的引用,则会把该前缀补到 <dst> 前面。例如,如果 main 是一个分支,那么 main:other 会展开为 main:refs/heads/other

  • 特殊的引用规范 :(或`+:`以允许非快进式更新)指示 Git 推送“匹配”的分支:对于每一个存在于本地的分支,如果远程仓库已经有一个同名的分支存在,则远程仓库会被更新。

  • 一个 <引用规范> 可以在其<src>中包含一个`*'来表示一个简单的模式匹配。这样的 refspec 的功能就像一个 glob,可以匹配任何具有相同前缀的 ref。匹配 <引用规范> 必须在 <src> 和 <dst> 中都有一个 *。它将通过把 * 替换成从源头匹配的内容来把引用映射到目的地。

  • ^ 开头的 refspec 是负 refspec,用于指定要排除的引用。某个引用只有在至少匹配一个正 refspec 且不匹配任何负 refspec 时,才会被视为匹配。负 refspec 也可以是模式 refspec,但它们只能包含 <src>。此外,也不支持完整写出的十六进制对象名。例如,git push origin refs/heads/*' ^refs/heads/dev-*' 会推送所有分支,但以 dev- 开头的分支除外。

  • 如果 <src> 为空,则会从远程仓库删除 <dst> 引用。例如,git push origin :dev 会删除 dev 分支。

  • tag <tag>refs/tags/<tag>:refs/tags/<tag> 意思相同。 严格来说,这是一种 git push 的特殊语法,而不是 refspec,因为在 git push origin tag v1.0 中,tagv1.0 是两个独立参数。

  • 其他含糊的解决方案可能会在未来加入,但现在我们所尝试的任何其他情况都会出错,并根据`advice.pushUnqualifiedRefname`配置(见git-config[1])建议你可能想推送到哪个类型的引用中。

并非所有更新都是允许的:详情请参见下文的 PUSH RULES。

--all
--branches

推送所有分支(即`refs/heads/`下的引用);不能与其他 < 引用规范 > 一起使用。

--prune

删除没有本地对应分支的远程分支。例如,如果本地的同名分支不存在了,那么远程分支 tmp 将被删除。这也是对引用规范的尊重,例如,git push --prune remote refs/heads/*:refs/tmp/* 将确保如果 refs/heads/foo 不存在,那么远程的 refs/tmp/foo 将被删除。

--mirror

而不是命名每一个要推送的引用,指定`refs/(包括但不限于`refs/heads/refs/remotes/`和`refs/tags/)下的所有引用都被镜像到远程仓库。 新创建的本地引用将被推送到远程端,本地更新的引用将被强制更新到远程端,而删除的引用将从远程仓库删除。 如果配置选项 `remote.<remote>.mirror`被设置,这就是默认的。

-n
--dry-run

做除了实际发送更新外的所有事。

--porcelain

生成机器可读的输出。 每个引用的输出状态行将以制表符分隔,并发送至标准输出而不是标准报错。 将给出引用的完整符号名称。

-d
--delete

所有列出的引用都从远程仓库中删除。这与在所有引用前加冒号的做法相同。

--tags

除了命令行上明确列出的引用规范之外,refs/tags 下的所有引用都被推送。

--follow-tags

推送所有在没有这个选项的情况下会被推送的引用,同时也推送 refs/tags 中的注释标签,这些标签在远程中缺失,但却指向被推送的引用中可以到达的提交号。 这也可以用配置变量 push.followTags 来指定。 更多信息,见 git-config[1] 中的 push.followTags

--signed
--no-signed
--signed=(true|false|if-asked)

对推送请求进行 GPG 签名,以在接收端更新引用时允许钩子检查和/或记录。可选值:

false
--no-signed

不会尝试签名。

true
--signed

如果服务器不支持已签名的推送,推送将失败。

if-asked

当且仅当服务器支持已签名的推送时才签名。如果实际调用 gpg --sign 失败,推送也会失败。关于接收端的细节,见 git-receive-pack[1]

--atomic
--no-atomic

如果有的话,在远程仓库使用一个原子事务。 要么所有的引用都被更新,要么出错时,没有引用被更新。 如果服务器不支持原子推送,推送将会失败。

-X <选项>
--push-option=<选项>

将给定的字符串传输给服务器,服务器将它们传递给 pre-receive 以及 post-receive 钩子。给定的字符串不能包含 NUL 或 LF 字符。 当给出多个 --push-option=< 选项 > 时,它们将按照命令行中列出的顺序全部发送给对方。 当命令行中没有给出 --push-option=< 选项 > 时,将使用配置变量`push.pushOption` 的值来代替。

--receive-pack=<git-receive-pack>
--exec=<git-receive-pack>

到远程的 git-receive-pack 程序的路径。 当通过 ssh 推送到远程仓库,而你没有在默认的 $PATH 目录下的程序时,有时会很有用。

--force-with-lease
--no-force-with-lease
--force-with-lease=<引用名>
--force-with-lease=< 引用名 >:< 期望>

通常,git push 会拒绝更新一个不是用来覆盖它的本地引用的远程父引用。

如果远程引用的当前值是预期值,该选项会覆盖这一限制。否则,git push 会失败。

想象一下,你必须对你已经发表的内容进行重新定位。 你必须绕过“必须快速合并”的规则,以便用重写的历史替换你最初发布的历史。 如果在你重新发布的时候,有其他人在你原来的历史之上构建,那么远程分支的顶端可能会随着他们的提交而前进,而盲目地用 --force 推送会丢失他们的改动。

这个选项允许你说,你希望你更新的历史是你重新建立的,并希望取代它。如果远程引用仍然指向你指定的提交,你就可以确定没有其他人对该引用做了什么。这就像在没有明确锁定的情况下对引用进行“租赁”,只有在 “租赁”仍然有效的情况下才会更新远程引用。

--force-with-lease,不指定细节,将保护所有将被更新的远程引用,要求它们的当前值与我们为它们准备的远程跟踪分支相同。

--force-with-lease=< 引用名 >,没有指定预期值,将保护命名的引用(单独),如果它要被更新,要求其当前值与我们对它的远程跟踪分支相同。

--force-with-lease=< 引用名 >:< 期望 >`将保护指定的引用(单独),如果它要被更新,要求它的当前值与指定的值< 期望 >相同(允许与我们对引用名的远程跟踪分支不同,或者在使用这种形式时,我们甚至不需要有这样一个远程跟踪分支)。 如果< 期望 >`是空字符串,那么命名的引用必须不存在。

请注意,除了`--force-with-lease=< 引用名 >:< 期望 >`以外的所有形式都是明确指定引用的预期当前值,这些形式仍然是试验性的,它们的语义可能会随着我们对这个功能的经验积累而改变。

"--no-force-with-lease " 将取消之前在命令行上所有的 --force-with-lease。

关于安全性的一般说明:在没有预期值的情况下提供这个选项,即 --force-with-lease 或`--force-with-lease=< 引用名 >,会与任何在后台隐式运行 `git fetch 的远程推送的进程产生非常不好的互动,例如仓库中 cronjob 的 git fetch origin

--force 相比,它提供的保护是确保你的工作不基于后续的变化,但如果有后台进程在后台更新引用,这一点就很容易被破坏。除了远程跟踪信息外,我们没有任何你期望看到并愿意去破解可以作为启发式引用的东西。

如果你的编辑器或其他系统在后台为你运行`git fetch`,减轻这种情况的方法是简单地设置另一个远程:

	git remote add origin-push $(git config remote.origin.url)
	git fetch origin-push

现在,当后台进程运行 git fetch origin 时,`origin-push`上的引用将不会被更新,因此,像这样的命令:

	git push --force-with-lease origin-push

除非你手动运行 git fetch origin-push,否则会失败。当然,这种方法完全可以被运行 git fetch --all 的东西打败,在这种情况下,你需要禁用它或者做一些更繁琐的事情,比如:

	git fetch              # 从远程仓库更新 'master' 分支
	git tag base master    # 标记基点
	git rebase -i master   # 重写某些提交
	git push --force-with-lease=master:base master:master

也就是说,为你已经看到并愿意覆盖的上游仓库创建一个`base` 标签,然后重写历史,最后如果远程版本仍然在 base,则强制推送修改到`master`,不管你本地的 remotes/origin/master 在后台更新了什么。

另外,在“推送”时将 --force-if-includes 作为辅助选项与 --force-with-lease[=< 引用名 >] 一起指定(即不说远程的引用必须指向什么确切的提交,或远程的哪些引用被保护),将验证远程跟踪引用的更新是否在允许强制更新之前被整合在本地。

-f
--force

通常,git push 会拒绝更新一个不是用来覆盖它的本地引用的远程父引用。

这个标志禁用了这些检查,并可能导致远程仓库丢失提交,使用时要小心。

注意,--force 适用于所有被推送的引用,因此在 push.default 设置为 matching 的情况下使用它,或者在 remote.*.push 配置了多个推送目的地的情况下,可能会覆盖当前分支以外的引用(包括严格落后于其远程对应的本地引用)。 要强制推送到一个分支,请在推送的引用规范前面使用 +(例如 git push origin +master,强制推送到 master 分支)。详情见上面的 < 引用规范 >... 部分。

--force-if-includes
--no-force-if-includes

只有远程跟踪的引用顶端被整合到本地才强制更新。

这个选项启用了一个检查,以验证远程跟踪引用的顶端是否可以从本地分支的“引用日志”条目中到达,以便进行重写。该检查确保来自远程的任何更新都已纳入本地,如果不是这样,则拒绝强制更新。

如果该选项没有指定 --force-with-lease,或者与 `--force-with-lease=< 引用名 >:< 期望 >`一起指定,则是“无选项”的。

指定 --no-force-if-includes 就可以禁止这种行为。

--repo=<仓库>

这个选项等同于 < 仓库 > 参数。如果两者都被指定,则以命令行参数为优先。

-u
--set-upstream

对于每一个已经更新或成功推送的分支,添加上游(跟踪)引用,由无参数的 git-pull[1] 和其他命令使用。更多信息,见 git-config[1] 中的 branch.< 分支名 >.merge

--thin
--no-thin

这些选项被传递给 git-send-pack[1]。当发送方和接收方有许多共同的对象时,稀松传输会大大减少发送的数据量。默认是 --thin

-q
--quiet

抑制所有的输出,包括更新的引用的列表,除非发生错误。进度不会报告给标准错误流。

-v
--verbose

详细运行。

--progress

当标准错误流连接到终端时,除非指定了 -q,否则默认情况下会在标准错误流上报告进展状态。即使标准错误流没有指向终端,这个标志也会强制显示进度状态。

--no-recurse-submodules
--recurse-submodules=(check|on-demand|only|no)

可用于确保要推送的修订所使用的所有子模块提交在某个远程跟踪分支上可用。可选值:

check

Git 将验证在要推送的修订中发生变化的所有子模块提交在该子模块的至少一个远程上可用。如果有任何提交缺失,推送将中止并以非零状态退出。

on-demand

要推送的修订中发生变化的所有子模块都会被推送。如果 on-demand 无法推送所有必要的修订,也会中止并以非零状态退出。

only

会推送所有子模块,而超级项目(superproject)本身保持不推送。

no

当不需要对子模块递归时,覆盖 push.recurseSubmodules 配置变量。类似于使用 --no-recurse-submodules

当使用 on-demandonly 时,如果一个子模块有 "push.recurseSubmodules={on-demand,only}" 或 submodule.recurse`配置,将发生进一步递归。在这种情况下,`only 被视为 on-demand

--verify
--no-verify

切换 pre-push 钩子(见 githooks[5])。 默认是 --verify,给钩子一个机会来阻止推送。 如果使用 --no-verify,钩子会被完全绕过。

-4
--ipv4

仅使用 IPv4 地址,忽略 IPv6 地址。

-6
--ipv6

仅使用 IPv6 地址,忽略 IPv4 地址。

GIT 地址

通常,地址包含有关传输协议,远程服务器的地址以及仓库路径的信息。对于某些传输协议,一些信息可能会缺失。

Git 支持 ssh,git,http 和 https 协议(此外,可以使用 ftp 和 ftps 进行获取,但这效率低下且不建议使用;请勿使用)。

本地传输(即 git:// 地址)不进行身份验证,在不安全的网络上应谨慎使用。

以下是上述几个传输协议的格式:

  • ssh://[<user>@]<host>[:<port>]/<path-to-git-repo>

  • git://<主机地址>[:<端口>]/<Git 仓库路径>

  • http[s]://<主机地址>[:<端口>]/<Git 仓库路径>

  • ftp[s]://<主机地址>[:<端口>]/<Git 仓库路径>

ssh 协议也可以使用类似 scp 的语法:

  • [<用户名>@]<主机地址>:/<Git 仓库路径>

仅当第一个冒号之前没有斜杠时才能识别此语法。这有助于区分包含冒号的本地路径。例如,可以将本地路径 foo:bar 指定为绝对路径,或者将 ./foo:bar 指定为绝对路径,以避免被误识别为 ssh url。

ssh 和 git 协议还支持 ~<用户名> 扩展:

  • ssh://[<user>@]<host>[:<port>]/~<user>/<path-to-git-repo>

  • git://<主机地址>[:<端口>]/~<用户名>/<Git 仓库路径>

  • [<用户名>@]<主机地址>:~<用户名>/<Git 仓库路径>

对于本地仓库(Git 本身也支持),可以使用以下语法:

  • /path/to/repo.git/

  • file:///path/to/repo.git/

这两种语法几乎是等效的,除了在克隆时,前者隐含使用了 --local 选项。有关详细信息,请参阅 git-clone[1]

git clonegit fetchgit pull(但不包括 git push)也会接受合适的捆绑包文件。参见 git-bundle[1]

当 Git 不知道如何处理某种传输协议时,它会尝试使用 remote-<传输方式> 远程帮助程序(如果存在)。要显式请求远程帮助程序,可以使用以下语法:

  • <传送>::<地址>

其中,<地址> 可以是路径,服务器与路径,也可以是可被调用的特定远程帮助程序识别的类似于网页地址的任意字符串。有关详细信息,请参阅 gitremote-helpers[7]

如果存在大量类似名称的远程仓库,并且您要为其使用不同的格式(这样,您使用的地址将被重写为有效的地址),则可以创建以下形式的配置:

	[url "<实际基础网址>"]
		insteadOf = <其他基础网址>

例如,有如下:

	[url "git://git.host.xz/"]
		insteadOf = host.xz:/path/to/
		insteadOf = work:

诸如 "work:repo.git" 或 "host.xz:/path/to/repo.git" 的地址会在任何类似于 "git://git.host.xz/repo" 地址的上下文中重写。

如果要重写仅用于推送的地址,可以创建表单的配置部分:

	[url <实际基础网址>"]
		pushInsteadOf = <其他基础网址>

例如,有如下:

	[url "ssh://example.org/"]
		pushInsteadOf = git://example.org/

类似于 "git://example.org/path/to/repo.git" 的地址会被重写为 "ssh://example.org/path/to/repo.git",用于推送。但拉取代码时仍然使用原始的地址。

REMOTES

可以用下面的一个名称代替URL作为`<repository>`的参数:

  • 一个远程的配置文件在此仓库的git配置文件: $GIT_DIR/config

  • 这个文件在`$GIT_DIR/remotes`目录下,或者

  • 这个文件在`$GIT_DIR/branches`目录下。

所有这些也允许你从命令行中省略refspec,因为它们都包含一个git默认使用的refspec。

在配置文件中命名为 remote

你可以选择提供你之前用 git-remote[1]git-config[1] 或甚至通过手动编辑 $GIT_DIR/config 文件配置的远程名称。 这个远程的 URL 将被用来访问仓库。 当你没有在命令行上提供引用规范时,这个远程仓库的引用规范将被默认使用。 配置文件中的条目会像这样:

	[remote "<名称>"]
		url = <地址>
		pushurl = <推送地址>
		push = <引用规范>
		fetch = <引用规范>

<推送地址> 仅用于推送。它是可选的,默认为 <URL>。向远程推送会影响所有定义的推送urls,如果没有定义推送urls,则推送到所有定义的url。然而,如果定义了多个 URL,fetch 将只从第一个定义的 URL 获取。

$GIT_DIR/remotes 中的命名文件

你可以选择提供 $GIT_DIR/remotes 中的文件名。 这个文件中的 URL 将被用来访问仓库。 当你没有在命令行上提供引用规范时,该文件中的引用规范将被作为默认使用。 这个文件应该有以下格式:

	URL: one of the above URL format
	Push: <引用规范>
	Pull: <引用规范>

Push: 行被 git push 使用,Pull: 行被 git pullgit fetch 使用。 可以为额外的分支映射指定多个 Push:Pull: 行。

$GIT_DIR/branches 中的命名文件

你可以选择提供 $GIT_DIR/branches 中的文件名。 这个文件中的 URL 将被用来访问仓库。 这个文件应该有以下格式:

	<URL>#<head>

<URL> 是必须的;#<head> 是可选的。

根据不同的操作,如果你没有在命令行上提供一个引用规范,git 会使用以下其中一个。 <分支> 是该文件在 $GIT_DIR/branches 中的名称,<头分支> 默认为 master

git fetch 使用:

	refs/heads/<头分支>:refs/heads/<分支>。

git push 使用:

	HEAD:refs/heads/<头分支>。

上游分支

Git 中的分支可以选择关联一个上游远程分支。对于远程操作,Git 默认会使用该上游分支,例如:

  • 它是无参数执行 git pullgit fetch 时的默认目标。

  • 除少数例外情况外,它也是无参数执行 git push 时的默认目标。例如,你可以使用 branch.<name>.pushRemote 选项,将代码推送到与拉取时不同的远程;并且在默认的 push.default=simple 模式下,你所配置的上游分支必须与当前分支同名。

  • 包括 git checkoutgit status 在内的各种命令都会告诉你:自从当前分支从上游分出以来,当前分支和上游分支各新增了多少个提交。例如会显示“Your branch and origin/main have diverged, and have 2 and 3 different commits each respectively”。

上游信息存储在 .git/config 的 "remote" 和 "merge" 字段中。例如,如果 main 的上游是 origin/main

[branch "main"]
   remote = origin
   merge = refs/heads/main

你可以用 git push --set-upstream <remote> <branch> 显式设置上游分支,但 Git 往往也会自动为你设置上游,例如:

  • 当你克隆一个仓库时,Git 会自动为默认分支设置上游。

  • 如果设置了 push.autoSetupRemote 配置项,那么第一次推送某个分支时,git push 会自动设置其上游。

  • 使用 git checkout <branch> 检出远程跟踪分支时,会自动创建同名本地分支,并将该远程分支设置为其上游。

Note
上游分支有时也被称为 “tracking information”(跟踪信息),例如“set the branch’s tracking information”。

输出

"git push" 的输出取决于所使用的传输方式;本节将介绍通过 Git 协议(本地或通过 ssh)推送时的输出。

推送的状态以表格的形式输出,每一行代表一个引用的状态。每一行的形式是:

 <标志> <概述> <起始> -> <结束> (<原因>)

如果使用了 --porcelain ,那么输出的每一行都是这样的:

 <标志> \t <起始>:<结束> \t <概述> (<原因>)

只有在使用 --porcelain 或 --verbose 选项时,才会显示最新引用的状态。

<标志>

表示引用状态的单个字符:

(空格)

表示推送成功的快速合并;

+

表示一个成功的强制更新;

-

表示一个成功删除的引用;

*

表示成功推送的引用;

!

表示一个被拒绝或推送失败的引用;并且

=

表示一个最新的、不需要推送的引用。

概述

对于一个成功获取的引用,摘要以适合作为 git log 参数的形式显示该引用的新旧值(在大多数情况下是 <旧>..<新>,对于强制非快速合并是 <旧>...<新>)。

对于失败的更新,会给出更多的细节:

已拒绝

Git 根本没有尝试发送引用,典型的原因是它不是快速合并并且你没有强制更新。

远程拒绝

远程仓库拒绝更新。 通常是由远程仓库的钩子引起的,或者是因为远程仓库有以下安全选项:receive.denyCurrentBranch(用于推送到已检出的分支),receive.denyNonFastForwards(用于强制非快进更新),receive.denyDeletes 或者 receive.denyDeleteCurrent。 见 git-config[1]

远程故障

远程端没有报告引用的成功更新,可能是因为远程端的临时错误,网络连接中断,或其他瞬时错误。

起始

被推送的本地引用的名称,减去其 refs/< 类型 >/ 前缀。在删除的情况下,本地引用的名称被省略。

结束

被更新的本地引用的名称,去掉其 refs/< 类型 >/ 前缀。

原因

一个人类可读的解释。在成功推送引用的情况下,不需要解释。对于操作失败的引用,要描述失败的原因。

推送规则

作为一项安全功能,git push 命令只允许某些类型的更新,以防你不小心丢失远程数据。

由于分支和标签的用途不同,推送到分支的安全规则与推送到标签的规则不同。以下规则中,“更新”指除删除和创建之外的任何修改。删除和创建始终允许,除非被配置或钩子禁止。

  1. 如果推送目标是 branchrefs/heads/*):只允许快进更新,这意味着目标必须是源提交的祖先。源必须是一个提交。

  2. 如果推送目标是 tagrefs/tags/*):所有更新都会被拒绝。源可以是任意对象。

  3. 如果推送目标既不是分支也不是标签:

    • 如果源是 tree 或 blob 对象,则任何更新都会被拒绝

    • 如果源是标签对象或提交对象,则允许任何快进更新。即使被快进的并不是提交,而是一个恰好指向新提交的标签对象,并且这个新提交相对于它所替换的上一个标签(或提交)所指向的提交是快进关系,也同样允许。若一个标签被替换为另一个完全不同但指向同一提交的标签,也允许;推送 peeled tag 也允许,即推送现有标签对象所指向的提交,或推送一个指向现有提交的新标签对象。

你可以通过传入 --force,或者在 refspec 前加上可选的前导 + 来覆盖这些规则。唯一的例外是:无论怎样强制,分支都不会接受非提交对象;而且如果远程仓库被配置为拒绝某次推送,强制也无法让它接受。

钩子和配置也可以覆盖或补充这些规则,例如 git-config[1] 中的 receive.denyNonFastForwardsreceive.denyDeletes,以及 githooks[5] 中的 pre-receiveupdate

关于快速合并的说明

当一个更新将原来指向提交 A 的分支(或更普遍的,一个引用)改为指向另一个提交 B 时,当且仅当 B 是 A 的后代时,它被称为快速合并。

在从 A 到 B 的快速合并中,原始提交 A 所建立的提交集是新提交 B 所建立的提交集的一个子集。 因此,它不会丢失任何历史。

相反,非快速合并更新会丢失历史。 例如,假设你和别人从同一个提交 X 开始,你建立了一个通往提交 B 的历史,而另一个人建立了一个通往提交 A 的历史。 这段历史看起来像这样:

      B
     /
 ---X---A

进一步假设对方已经将 A 导致的修改推送回原仓库,你们俩从原仓库中获得原始提交 X。

另一个人所做的推送,将原来指向提交 X 的分支更新为指向提交 A 的分支。

但如果你试图推送,你会试图用提交 B 来更新分支(现在指向 A)。这不是快速合并。 如果你这样做了,提交 A 引入的修改就会丢失,因为大家现在都会在 B 的基础上开始构建。

该命令默认不允许非快速合并的更新,以防止这种历史的损失。

如果你不想丢失你的工作(从 X 到 B 的历史)或对方的工作(从 X 到 A 的历史),你需要先从仓库中获取历史,创建一个包含双方修改的历史,并将结果推送回去。

您可以执行 "git pull",解决潜在的冲突,然后将结果 "git push"。 一个 "git pull "会在提交 A 和 B 之间创建一个合并提交 C。

      B---C
     /   /
 ---X---A

用所产生的合并提交更新 A,将快速合并,你的推送将被接受。

另外,你也可以用 git pull --rebase 将 X 和 B 之间的改动重新归档到 A 之上,然后将结果推送回来。 重置将创建一个新的提交 D,将 X 和 B 之间的修改建立在 A 的基础上。

      B   D
     /   /
 ---X---A

同样的,用这个提交更新 A 会快速合并,你的推送会被接受。

还有一种常见的情况是,当你试图推送时,可能会遇到非快速合并拒绝,即使你推送到一个没有人推送的仓库中也有可能。在你自己推送了提交 A(在本节的第一幅图中),用 git commit --amend 替换后产生了提交 B,你试图推送它,因为忘记了你已经推送了 A。在这种情况下,只有当您确定在此期间没有人取走您先前的提交 A(并在其基础上开始构建),您才能运行 git push --force 来覆盖它。换句话说,git push --force 是一种保留给你确实想失去历史的情况的方法。

实例

git push

像`git push < 远程 >那样工作,其中 < 远程 > 是当前分支的远程仓库(或者`origin,如果没有为当前分支配置远程仓库)。

git push origin

不需要额外配置,如果它与当前分支同名,并且错误输出而不推送其他分支,则将其推送到配置过的上游仓库(branch.<分支名>.merge 配置变量)。

当没有给出< 引用规范 >时,该命令的默认行为可以通过设置远程仓库的 push 选项或 `push.default`配置变量来配置。

例如,要默认只将当前分支推送到 origin,使用 git config remote.origin.push HEAD。 任何有效的< 引用规范 >(比如下面例子中的那些)都可以被配置为 git push origin 的默认值。

git push origin :

将 “匹配” 的分支推送到 origin。关于 “匹配” 分支的描述,请参见上面 选项 部分的 [引用规范]

git push origin master

在源仓库中找到一个与 master 相匹配的引用(最可能的是找到 refs/heads/master),并在 origin 仓库中用它更新相同的引用(例如`refs/heads/master`)。 如果 master 不存在,它将被创建。

git push origin HEAD

一个方便的方法是将当前分支推送到远程的相同名称。

git push mothership master:satellite/master dev:satellite/dev

使用与 "master" 相匹配的源引用(例如,refs/heads/master)来更新 mothership 仓库中与 satellite/master 相匹配的引用(很可能是 refs/remotes/satellite/master);对 dev 和 `satellite/dev`做同样的操作。

关于匹配语义的讨论,请参见上面描述 <引用规范>... 的部分。

这是为了模拟在`母舰` 上运行的 git fetch ,使用 git push 以相反的方向运行,以便整合 卫星 上完成的工作,当你只能以一种方式进行连接时(即卫星可以ssh到母舰,但母舰不能启动与卫星的连接,因为后者在防火墙后面或不运行 sshd),这往往是必要的。

卫星 机器上运行这个 git push 后,你将 ssh 进入 母舰 并在那里运行 git merge ,以完成对 母舰 上运行的 git pull 的模拟,以拉取 卫星 上的修改。

git push origin HEAD:master

推送当前分支到远程引用匹配 masterorigin 仓库中。这种形式可以方便地推送当前分支,而不必考虑其本地名称。

git push origin master:refs/heads/experimental

通过复制当前的 master 分支,在 origin 仓库创建 experimental 分支。 只有在本地名称和远程名称不同的情况下,在远程仓库中创建新的分支或标签时才需要这个表单;否则,引用名称本身就可以了。

git push origin :experimental

origin 仓库(例如`refs/heads/experimental`)中找到一个与 experimental 相匹配的引用,并将其删除。

git push origin +dev:master

用开发分支更新仓库的主分支,允许非快速合并更新。 *这可能会使未引用的提交在源码库中悬空。*考虑以下情况,即不可能进行快速合并:

	    o---o---o---A---B  origin/master
		     \
		      X---Y---Z  dev

上面的命令将 origin 仓库更改为

		      A---B  (unnamed branch)
		     /
	    o---o---o---X---Y---Z  master

提交 A 和 B 将不再属于具有符号名称的分支,因此将不可访问。因此,这些提交将被原始仓库上的“ gitgc”命令删除。

安全

获取和推送协议并不是为了防止一方从另一个仓库窃取不打算共享的数据。如果你有需要保护的私人数据不被恶意的同行窃取,你最好的选择是把它存储在另一个资源库中。这同时适用于客户端和服务器。特别是,服务器上的命名空间对于读取访问控制是无效的;你应该只将命名空间的读取访问权授予那些你信任的可以读取整个仓库的客户。

已知的攻击媒介如下:

  1. 受害者发送 "have" 行,宣传它所拥有的对象的 ID,这些对象并没有明确地打算被共享,但如果对等体也有这些对象,就可以用来优化传输。攻击者选择了一个对象 ID X 来窃取,并向 X 发送了一个引用,但不需要发送 X 的内容,因为受害者已经拥有它。现在,受害者认为攻击者拥有 X,它稍后将 X 的内容发回给攻击者。(这种攻击对于客户端来说在服务器上执行是最直接的,通过在客户端可以访问的命名空间中创建一个 X 的引用,然后获取它。服务器最可能在客户端执行的方式是将 X "合并" 到一个公共分支,并希望用户在这个分支上做额外的工作,然后在没有注意到合并的情况下将其推回给服务器。)

  2. 和 #1 一样,攻击者选择了一个对象 ID X 来偷窃。受害者发送了一个攻击者已经拥有的对象 Y,攻击者谎称拥有 X 而没有 Y,因此受害者发送 Y 作为对 X 的d elta,delta 向攻击者显示了 X 中与 Y 相似的区域。

配置

本节中这一行以下的内容都是从 git-config[1] 文档中摘录的。其内容与那里的内容相同:

push.autoSetupRemote

若设为 true,当当前分支尚无上游跟踪分支时,默认 push 会假定带上 --set-upstream;该选项在 push.defaultsimpleupstreamcurrent 时生效。若你希望新分支默认推送到默认远程(类似 push.default=current 的行为),同时又希望设置上游跟踪关系,则此选项很有用。最可能受益的工作流是 simple 的集中式工作流:期望所有分支在远程都有同名分支。

push.default

定义在未提供 refspec(无论来自命令行、配置或其他来源)时 git push 应采取的动作。不同取值适合不同工作流;例如在纯集中式工作流(即 fetch 源与 push 目的地相同)中,upstream 可能是你想要的。可能取值包括:

nothing

除非给出 refspec,否则不推送任何内容(报错退出)。这主要面向希望始终显式指定以避免失误的人。

current

推送当前分支,以更新接收端上同名分支。适用于集中式与非集中式工作流。

upstream

将当前分支推送回通常会把更改合入当前分支的那个分支(即 @{upstream})。仅当你推送到的仓库与通常拉取的仓库相同(即集中式工作流)时,此模式才有意义。

tracking

这是 upstream 的已弃用同义词。

simple

将当前分支推送到远程上的同名分支。

如果你在集中式工作流中工作(推送到你拉取的同一仓库,通常为 origin),那么需要配置一个同名的上游分支。

自 Git 2.0 起该模式为默认,且对新手来说是最安全的选择。

matching

推送两端同名的所有分支。这会让你推送到的仓库记住将被推送出去的分支集合(例如,如果你总是把 maintmaster 推送到那里而不推送其他分支,那么该远程仓库将拥有这两个分支,你本地的 maintmaster 将被推送过去)。

要有效使用此模式,你必须在运行 git push 之前确保你要推送的 所有 分支都已准备就绪,因为该模式的核心就是让你一次性推送所有分支。如果你通常只完成一个分支的工作并推送结果,而其他分支仍未完成,那么此模式不适合你。此外,此模式也不适合推送到共享的中央仓库,因为其他人可能会在其中新增分支,或在你控制之外更新现有分支的末端。

这曾经是默认值,但自 Git 2.0 起不再如此(新的默认值为 simple)。

push.followTags

若设为 true,则默认启用 --follow-tags。你可以在推送时指定 --no-follow-tags 覆盖此配置。

push.gpgSign

可设为布尔值或字符串 if-asked。值为 true 时,所有推送都会进行 GPG 签名,效果等同于向 git-push[1] 传入 --signed。值为 if-asked 时,若服务器支持则对推送进行签名,效果等同于向 git push 传入 --signed=if-asked。值为 false 可覆盖来自更低优先级配置文件的设置。命令行显式标志总会覆盖此配置选项。

push.pushOption

当命令行未给出任何 --push-option=<option> 参数时,git push 的行为就像为本变量中的每个 <option> 都提供了 --push-option=<option> 一样。

这是一个多值变量。可在更高优先级的配置文件(例如仓库中的 .git/config)中设置空值,以清除从较低优先级配置文件(例如 $HOME/.gitconfig)继承的值。

例如:

/etc/gitconfig
  push.pushoption = a
  push.pushoption = b

~/.gitconfig
  push.pushoption = c

repo/.git/config
  push.pushoption =
  push.pushoption = b

这将导致只剩下 b(a 和 c 被清除)。
push.recurseSubmodules

可以是 checkon-demandonlyno,其行为与 push --recurse-submodules 相同。若未设置,则默认使用 no,除非设置了 submodule.recurse(此时值为 true 表示 on-demand)。

push.useForceIfIncludes

若设为 true,等同于在命令行中为 git-push[1] 指定 --force-if-includes 选项。在 push 时添加 --no-force-if-includes 会覆盖此配置。

push.negotiate

若设为 true,则在客户端和服务器尝试找出共同提交的多轮协商过程中,尝试减小发送的 packfile 大小。若为 false,Git 将仅依赖服务器的 ref 通告来查找共同提交。

push.useBitmaps

若设为 false,则即使 pack.useBitmapstrue,也会禁用 git push 对 bitmap 的使用,但不会 阻止其他 git 操作使用 bitmap。默认值为 true

GIT

属于 git[1] 文档