简体中文 ▾ 主题 ▾ 最新版本 ▾ git-push 最后更新于 2.53.0

名称

git-push - 更新远程引用及相关对象

概要

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>…​]]

描述

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

最简单的推送方式是 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) 部分。

通过在远程仓库设置钩子(hooks),你可以在每次推送时触发特定的操作。请参阅 git-receive-pack[1] 文档。

选项

<repository>

作为推送操作目标的“远程”仓库。该参数可以是 URL(参见下文 GIT URLS 部分)或远程仓库的名称(参见下文 REMOTES 部分)。

<refspec>...

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

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

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

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

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

你可以使用全称形式编写引用规格(例如 refs/heads/main:refs/heads/main)来指定确切的源和目标,也可以使用简写形式(例如 mainmain:other)。以下是引用规格的展开规则,以及各种其他特殊引用规格形式:

  • 不带 :<dst><src> 表示更新与 <src> 相同的引用,除非 remote.<repository>.push 配置指定了不同的 <dst>。例如,如果 main 是一个分支,那么引用规格 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> 可以包含一个 * 来表示简单的模式匹配。这类似于匹配任何符合该模式的引用的通配符。在 <src><dst> 中必须各只有一个 *。它将通过用源中匹配的内容替换 * 来将引用映射到目标。例如,refs/heads/*:refs/heads/* 将推送所有分支。

  • ^ 开头的引用规格是负向引用规格。这用于指定要排除的引用。如果一个引用至少匹配一个正向引用规格,且不匹配任何负向引用规格,则认为该引用匹配。负向引用规格可以是模式引用规格。它们必须只包含 <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 的特殊语法,而不是引用规格,因为在 git push origin tag v1.0 中,参数 tagv1.0 是分开的。

  • 如果引用规格无法明确展开,则报错并指出尝试过的操作,并根据 advice.pushUnqualifiedRefname 配置(参见 git-config[1])建议你可能想要推送到哪个 refs/ 命名空间。

并非所有更新都被允许:详情请参见下文的推送规则(PUSH RULES)。

--all
--branches

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

--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

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

-d
--delete

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

--tags

除了命令行中显式列出的引用规格外,推送 refs/tags 下的所有引用。

--follow-tags

推送所有在没有此选项时也会被推送的引用,同时推送 refs/tags 中远程仓库缺失但指向正在推送的引用可达的“commit-ish”对象的附注标签。这也可以通过配置变量 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

如果可用,在远程端使用原子事务。要么更新所有引用,要么在出错时不更新任何引用。如果服务器不支持原子推送,则推送将失败。

-o <option>
--push-option=<option>

将给定的字符串传输到服务器,服务器会将其传递给 pre-receive 和 post-receive 钩子。给定的字符串不能包含 NULLF 字符。当给出多个 --push-option=<option> 时,它们将按照在命令行中列出的顺序全部发送到另一端。当命令行中未给出 --push-option=<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=<refname>
--force-with-lease=<refname>:<expect>

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

如果远程引用的当前值是期望值,此选项将覆盖该限制。否则 git push 将失败。

假设你需要对已经发布的内容进行变基(rebase)。为了用变基后的历史替换原来发布的历史,你必须绕过“必须快进”的规则。如果你在变基时,其他人在你原始历史的基础上进行了开发,远程分支的末端可能会随着他们的提交而推进,盲目地使用 --force 推送会丢失他们的工作。

此选项允许你说明:你期望你正在更新的历史正是你变基并想要替换的内容。如果远程引用仍然指向你指定的提交,你可以确定没有其他人对该引用做过任何操作。这就像是在没有显式锁定的情况下对引用进行“租约(lease)”,只有在“租约”仍然有效时,远程引用才会更新。

单独使用 --force-with-lease 而不指定细节,将通过要求所有即将更新的远程引用的当前值与我们拥有的对应远程跟踪分支的值相同,来保护它们。

--force-with-lease=<refname> 在不指定期望值的情况下,如果 <refname> 即将更新,将通过要求其当前值与我们拥有的对应远程跟踪分支相同来(单独)保护它。

--force-with-lease=<refname>:<expect> 将(单独)保护 <refname>,如果它即将更新,则要求其当前值与指定的值 <expect> 相同(该值允许与我们拥有的对应远程跟踪分支不同,或者在使用此形式时我们甚至不需要拥有这样的远程跟踪分支)。如果 <expect> 是空字符串,则指定的引用必须不存在。

请注意,除了显式指定引用的期望当前值的 --force-with-lease=<refname>:<expect> 之外,所有其他形式仍处于实验阶段,随着我们获得更多关于此特性的经验,其语义可能会发生变化。

--no-force-with-lease 将取消命令行中之前所有的 --force-with-lease

关于安全性的一般说明:在不提供期望值的情况下使用此选项,即以 --force-with-lease--force-with-lease=<refname> 的形式,会与任何在后台隐式运行 git fetch 的操作(例如在计划任务中对仓库运行 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              # update 'master' from remote
git tag base master    # mark our base point
git rebase -i master   # rewrite some commits
git push --force-with-lease=master:base master:master

即:为你已经看过并愿意覆盖的上游代码版本创建一个 base 标签,然后重写历史,最后如果远程版本仍处于 base,则强制推送更改到 master,无论你的本地 remotes/origin/master 是否已在后台更新。

或者,在“推送”时指定 --force-if-includes 作为辅助选项连同 --force-with-lease[=<refname>](即不说明远程端引用必须指向哪个确切提交,或哪些远程端引用受到保护)一起使用,将验证可能在后台隐式更新的远程跟踪引用的更新是否已集成到本地,然后才允许强制更新。

-f
--force

通常,git push 会拒绝更新一个不是正在推送的提交的祖先的分支。

此标志会禁用该检查、下文推送规则(PUSH RULES)中的其他安全检查,以及 --force-with-lease 中的检查。它可能导致远程仓库丢失提交;请谨慎使用。

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

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

仅当远程跟踪引用的末端已集成到本地时才强制更新。

此选项启用一项检查,验证远程跟踪引用的末端是否可以从基于其进行重写的本地分支的“reflog”条目之一到达。该检查确保来自远程的任何更新都已合并到本地,否则将拒绝强制更新。

如果传递此选项时未指定 --force-with-lease,或者连同 --force-with-lease=<refname>:<expect> 一起指定,则它是“无效操作(no-op)”。

指定 --no-force-if-includes 会禁用此行为。

--repo=<repository>

此选项相当于 <repository> 参数。如果两者都指定了,则命令行参数优先。

-u
--set-upstream

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

--thin
--no-thin

这些选项将传递给 git-send-pack[1]。当发送方和接收方共享许多共同对象时,瘦传输(thin transfer)会显著减少发送的数据量。默认值为 --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 URLS

通常,URL 包含有关传输协议、远程服务器地址和仓库路径的信息。根据传输协议,其中一些信息可能不存在。

Git 支持 ssh、git、http 和 https 协议(此外,ftp 和 ftps 也可用于获取,但这效率低下且已弃用;请勿使用它们)。

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

可以使用以下语法:

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

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

  • http[s]://<host>[:<port>]/<path-to-git-repo>

  • ftp[s]://<host>[:<port>]/<path-to-git-repo>

ssh 协议还可以使用另一种类似 scp 的语法:

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

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

ssh 和 git 协议还支持 ~<username> 扩展

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

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

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

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

  • /path/to/repo.git/

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

这两种语法大部分是等效的,除了在克隆时,前者意味着 --local 选项。有关详细信息,请参阅 git-clone[1]

git clonegit fetchgit pull(但不是 git push)也将接受一个合适的 bundle 文件。参见 git-bundle[1]

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

  • <transport>::<address>

其中 <address> 可以是路径、服务器和路径,或者是特定远程辅助工具可识别的任意类 URL 字符串。有关详细信息,请参阅 gitremote-helpers[7]

如果存在大量名称相似的远程仓库,并且您想为它们使用不同的格式(以便您使用的 URL 将被重写为可用的 URL),您可以创建以下形式的配置节:

	[url "<actual-url-base>"]
		insteadOf = <other-url-base>

例如,有了这个:

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

“work:repo.git”或“host.xz:/path/to/repo.git”这样的 URL 在任何接受 URL 的上下文中都将被重写为“git://git.host.xz/repo.git”。

如果只想重写推送的 URL,可以创建以下形式的配置节:

	[url "<actual-url-base>"]
		pushInsteadOf = <other-url-base>

例如,有了这个:

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

像“git://example.org/path/to/repo.git”这样的 URL 将被重写为“ssh://example.org/path/to/repo.git”用于推送,但拉取仍将使用原始 URL。

远程仓库

以下名称之一可以用来代替 URL 作为 <repository> 参数

  • Git 配置文件中的远程仓库:$GIT_DIR/config

  • $GIT_DIR/remotes 目录中的文件,或

  • $GIT_DIR/branches 目录中的文件。

所有这些也允许你省略命令行中的引用规范,因为它们各自包含一个 Git 将默认使用的引用规范。

配置文件中的命名远程仓库

您可以选择提供一个您之前使用 git-remote[1]git-config[1] 配置的远程仓库的名称,甚至是通过手动编辑 `$GIT_DIR/config` 文件。此远程仓库的 URL 将用于访问仓库。当您不在命令行上提供 refspec 时,此远程仓库的 refspec 将被默认使用。配置文件中的条目将如下所示

	[remote "<name>"]
		url = <URL>
		pushurl = <pushurl>
		push = <refspec>
		fetch = <refspec>

<pushurl> 仅用于推送。它是可选的,默认为 <URL>。推送到远程会影响所有定义的 pushurl,或者在未定义 pushurl 时影响所有定义的 URL。然而,如果定义了多个 URL,获取操作只会从第一个定义的 URL 中获取。

$GIT_DIR/remotes 中的命名文件

您可以选择提供 `$GIT_DIR/remotes` 中的文件名。此文件中的 URL 将用于访问仓库。当您不在命令行上提供 refspec 时,此文件中的 refspec 将被用作默认。此文件应具有以下格式

	URL: one of the above URL formats
	Push: <refspec>
	Pull: <refspec>

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

$GIT_DIR/branches 中的命名文件

你可以选择提供 $GIT_DIR/branches 中的文件名称。此文件中的 URL 将用于访问仓库。此文件应具有以下格式:

	<URL>#<head>

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

根据操作的不同,如果你在命令行中没有提供引用规范,git 将使用以下引用规范之一。<branch> 是此文件在 $GIT_DIR/branches 中的名称,而 <head> 默认为 master

git fetch 使用

	refs/heads/<head>:refs/heads/<branch>

git push 使用

	HEAD:refs/heads/<head>

上游分支

Git 中的分支可以选择性地拥有一个上游远程分支。Git 默认使用上游分支进行远程操作,例如

  • 它是 `git pull` 或无参数的 `git fetch` 的默认设置。

  • 它是无参数的 `git push` 的默认设置,但有一些例外。例如,您可以使用 `branch.<name>.pushRemote` 选项将推送到与您拉取的远程仓库不同的仓库,并且默认情况下使用 `push.default=simple`,您配置的上游分支名称必须相同。

  • 各种命令,包括 `git checkout` 和 `git 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>` 检出远程跟踪分支将自动创建一个同名本地分支,并将其上游设置为远程分支。

注意
上游分支有时被称为“跟踪信息”,例如“设置分支的跟踪信息”。

输出

“git push”的输出取决于所使用的传输方法;本节描述了通过 Git 协议(本地或通过 ssh)推送时的输出。

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

 <flag> <summary> <from> -> <to> (<reason>)

如果使用了 --porcelain,则输出的每一行格式如下:

 <flag> \t <from>:<to> \t <summary> (<reason>)

仅当使用 --porcelain--verbose 选项时,才会显示处于最新状态(up-to-date)的引用的状态。

<flag>

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

(空格)

表示成功推送的快进更新;

+

表示成功的强制更新;

-

表示成功删除的引用;

*

表示成功推送的新引用;

!

表示被拒绝或推送失败的引用;以及

=

表示引用已是最新,无需推送。

<summary>

对于成功推送的引用,摘要以适合作为 git log 参数的形式显示引用的旧值和新值(在大多数情况下是 <old>..<new>,对于强制非快进更新则是 <old>...<new>)。

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

rejected (被拒绝)

Git 根本没有尝试发送该引用,通常是因为它不是快进更新且你没有强制更新。

remote rejected (远程拒绝)

远程端拒绝了更新。通常由远程端的钩子引起,或者是因为远程仓库启用了以下安全选项之一:receive.denyCurrentBranch(针对推送到已检出分支的情况)、receive.denyNonFastForwards(针对强制非快进更新)、receive.denyDeletesreceive.denyDeleteCurrent。参见 git-config[1]

remote failure (远程故障)

远程端未报告引用的成功更新,可能是由于远程端的临时错误、网络连接中断或其他瞬时错误。

from

正在推送的本地引用的名称,去掉了 refs/<type>/ 前缀。在删除的情况下,本地引用的名称将被省略。

to

正在更新的远程引用的名称,去掉了 refs/<type>/ 前缀。

reason (原因)

人类可读的解释。对于成功推送的引用,不需要解释。对于失败的引用,会描述失败的原因。

推送规则

作为一项安全功能,git push 命令仅允许某些类型的更新,以防止你意外丢失远程端的数据。

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

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

  2. 如果推送目的地是标签refs/tags/*):所有更新都将被拒绝。源可以是任何对象。

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

    • 如果源是树(tree)或 blob 对象,任何更新都将被拒绝。

    • 如果源是标签或提交对象,则允许任何快进更新,即使被快进的不是提交,而是一个标签对象,该标签对象恰好指向一个新的提交,而该提交又是它所替换的上一个标签(或提交)的快进。如果指向相同的提交,也允许用一个完全不同的标签替换现有标签,以及推送已剥离的标签(peeled tag),即推送现有标签对象指向的提交,或现有提交指向的新标签对象。

你可以通过传递 --force 或在引用规格前添加可选的 + 来覆盖这些规则。唯一的例外是,无论如何强制,分支都不会接受非提交对象,且强制推送也不会让远程仓库接受其配置为拒绝的推送。

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

关于快进(FAST-FORWARDS)的说明

当一个更新改变了一个分支(或更广义地说,一个引用),使其从原来指向提交 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,它在 A 的基础上构建 X 和 B 之间的更改。

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

同样,用此提交更新 A 将是快进,你的推送将被接受。

还有另一种常见情况,当你尝试推送时可能会遇到非快进拒绝,即使你推送到一个没有其他人推送的仓库也可能发生。在你推送了提交 A 之后(在本节的第一张图中),通过 git commit --amend 替换它以生成提交 B,然后你尝试将其推送出去,因为你忘了你已经推送过 A 了。在这种情况下,且仅当你确定在此期间没有人获取过你早期的提交 A(并开始在其基础上构建)时,你可以运行 git push --force 来覆盖它。换句话说,git push --force 是专门为确实想要丢失历史记录的情况保留的方法。

示例

git push

其作用类似于 git push <remote>,其中 <remote> 是当前分支的远程仓库(如果当前分支未配置远程仓库,则为 origin)。

git push origin

在没有额外配置的情况下,如果远程分支与当前分支同名,则将当前分支推送到配置的上游(branch.<name>.merge 配置变量),否则报错且不进行推送。

当未给定 <refspec> 时,此命令的默认行为可以通过设置远程仓库的 push 选项或 push.default 配置变量来配置。

例如,要默认仅将当前分支推送到 origin,请使用 git config remote.origin.push HEAD。任何有效的 <refspec>(如以下示例中的引用规格)都可以配置为 git push origin 的默认值。

git push origin :

将“匹配”的分支推送到 origin。有关“匹配”分支的说明,请参见上文 选项(OPTIONS) 部分中的 <refspec>

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);对 devsatellite/dev 执行相同操作。

有关匹配语义的讨论,请参阅上文描述 <refspec>... 的部分。

这是为了通过在相反方向运行的 git push 来模拟在 mothership 上运行的 git fetch,以便集成在 satellite 上完成的工作。当你只能进行单向连接时(即 satellite 可以通过 ssh 接入 mothership,但 mothership 无法发起与 satellite 的连接,因为后者位于防火墙之后或未运行 sshd),这通常是必要的。

satellite 机器上运行此 git push 之后,你将通过 ssh 进入 mothership 并在那里运行 git merge,以完成对在 mothership 上运行以获取在 satellite 上所做更改的 git pull 的模拟。

git push origin HEAD:master

将当前分支推送到 origin 仓库中匹配 master 的远程引用。这种形式方便于推送当前分支,而无需考虑其本地名称。

git push origin master:refs/heads/experimental

通过复制当前的 master 分支在 origin 仓库中创建 experimental 分支。这种形式仅在本地名称和远程名称不同时,才需要在远程仓库中创建新分支或标签;否则,引用名称本身即可工作。

git push origin :experimental

origin 仓库中寻找匹配 experimental 的引用(例如 refs/heads/experimental),并将其删除。

git push origin +dev:master

使用 dev 分支更新 origin 仓库的 master 分支,允许非快进更新。这可能会在 origin 仓库中留下悬空的未引用提交。 考虑以下无法进行快进的情况:

	    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 将不再属于具有符号名称的分支,因此将无法到达。因此,这些提交将被 origin 仓库中的 git gc 命令删除。

安全性

fetch 和 push 协议并非设计用于防止一方窃取另一方仓库中未打算共享的数据。如果您有需要保护免受恶意对等方侵害的私有数据,您的最佳选择是将其存储在另一个仓库中。这适用于客户端和服务器。特别地,服务器上的命名空间对于读取访问控制无效;您应该只授予您信任的客户端对整个仓库的读取访问权限的命名空间读取访问权限。

已知的攻击向量如下:

  1. 受害者发送“have”行,广告它拥有但未明确打算共享的对象 ID,这些对象 ID 可用于优化传输,前提是对方也拥有它们。攻击者选择一个对象 ID X 来窃取并发送一个指向 X 的 ref,但不需要发送 X 的内容,因为受害者已经拥有它。现在受害者认为攻击者拥有 X,并且之后会将 X 的内容发送回攻击者。(此攻击最容易由客户端对服务器执行,通过在客户端有权访问的命名空间中创建指向 X 的 ref,然后获取它。服务器对客户端执行此操作的最可能方式是“合并”X 到一个公共分支,并希望用户在此分支上进行更多工作并将其推回服务器,而不会注意到合并。)

  2. 与 #1 类似,攻击者选择一个对象 ID X 来窃取。受害者发送一个攻击者已拥有的对象 Y,攻击者谎称拥有 X 而不是 Y,因此受害者将 Y 作为 X 的增量发送。增量向攻击者揭示了 X 中与 Y 相似的区域。

配置

本节中以下所有内容均从 git-config[1] 文档中选择性地包含。内容与彼处相同:

push.autoSetupRemote

如果设置为 true,当当前分支不存在上游跟踪时,在默认推送时假定使用 --set-upstream;此选项对 push.default 的选项 simpleupstreamcurrent 生效。如果你希望新分支默认推送到默认远程仓库(类似于 push.default=current 的行为),并且还希望设置上游跟踪,则此选项非常有用。最能从该选项受益的工作流程是 simple 中央工作流,其中所有分支在远程端都应具有相同的名称。

push.default

定义在未给定引用规格(无论是从命令行、配置还是其他地方)时 git push 应采取的操作。不同的值适用于特定的工作流程;例如,在纯粹的中央工作流中(即获取源等于推送目的地),upstream 可能是你想要的。可能的值为:

nothing

除非给出引用规格,否则不推送任何内容(报错)。这主要是为那些希望通过始终保持显式来避免错误的人准备的。

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 签名,就像将 --signed 传递给 git-push[1] 一样。字符串 if-asked 会导致在服务器支持的情况下对推送进行签名,就像将 --signed=if-asked 传递给 git push 一样。false 值可以覆盖来自低优先级配置文件的值。显式的命令行标志始终覆盖此配置选项。

push.pushOption

当命令行中未给出 --push-option=<option> 参数时,git push 的行为就像该变量的每个 <option> 都作为 --push-option=<option> 给出一样。

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

Example:

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

~/.gitconfig
  push.pushoption = c

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

This will result in only b (a and c are cleared).
push.recurseSubmodules

可以是 checkon-demandonlyno,其行为与 push --recurse-submodules 相同。如果未设置,则默认使用 no,除非设置了 submodule.recurse(在这种情况下,true 值表示 on-demand)。

push.useForceIfIncludes

如果设置为 true,则相当于在命令行中将 --force-if-includes 指定为 git-push[1] 的选项。在推送时添加 --no-force-if-includes 会覆盖此配置设置。

push.negotiate

如果设置为 true,则尝试通过多轮协商来减小发送的包文件大小,在协商中客户端和服务器尝试查找共同的提交。如果为 false,Git 将完全依赖服务器的引用公告来查找共同提交。

push.useBitmaps

如果设置为 false,即使 pack.useBitmapstrue,也会在 git push 中禁用位图,但不妨碍其他 git 操作使用位图。默认值为 true

GIT

Git[1] 套件的一部分