简体中文 ▾ 主题 ▾ 最新版本 ▾ git-push 最后更新于 2.52.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.*.push 配置

  3. push.default 配置。默认值为 push.default=simple,这将推送到与当前分支同名的分支。有关 push.default 的更多信息,请参阅下面的 配置 部分。

git push 可能会失败,如果你尚未为当前分支设置上游,这取决于 push.default 的设置。有关如何设置和使用上游的更多信息,请参阅下面的 上游分支 部分。

通过在仓库中设置钩子,可以在每次推送到该仓库时执行一些有趣的操作。有关详细信息,请参阅 git-receive-pack[1] 的文档。

选项

<repository>

推送操作的目标“远程仓库”。此参数可以是 URL(请参阅下面的 Git URLS 部分)或远程仓库的名称(请参阅下面的 远程仓库 部分)。

<refspec>…​

指定用什么源对象更新什么目标引用。

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

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

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

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

你可以使用完全展开的形式(例如 refs/heads/main:refs/heads/main)来编写 refspec,这指定了确切的源和目标,或者使用简短的形式(例如 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

  • 特殊的 refspec :(或 +: 以允许非快进更新)指示 Git 推送“匹配”的分支:对于本地存在的每个分支,如果远程端已存在同名分支,则更新远程端。

  • <src> 可能包含一个 * 来指示简单的模式匹配。这就像一个 glob,匹配模式的任何引用。在 <src><dst> 中都只能有一个 *。它通过将 * 替换为从源匹配的内容来映射引用。例如,refs/heads/*:refs/heads/* 将推送所有分支。

  • ^ 开头的 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 是独立的参数。

  • 如果 refspec 无法被明确展开,则会发出错误,指出尝试的内容,并根据 advice.pushUnqualifiedRefname 配置(请参阅 git-config[1])建议你可能想要推送到的 refs/ 命名空间。

并非所有更新都允许:请参阅下面的推送规则以了解详细信息。

--all
--branches

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

--prune

删除没有本地对应项的远程分支。例如,如果名为 tmp 的本地分支不再存在,则远程分支 tmp 将被删除。这也尊重 refspec,例如 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

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

-d
--delete

所有列出的引用都将从远程仓库中删除。这相当于在所有引用前面加上冒号。

--tags

除了在命令行中明确列出的 refspec 外,还将推送 refs/tags 下的所有引用。

--follow-tags

推送没有此选项时会推送的所有引用,还会推送远程仓库中缺失但指向可达于正在推送的引用(ref-ish)的带注释的标签(annotated 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

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

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

将给定字符串发送到服务器,服务器会将其传递给 pre-receive 和 post-receive 钩子。给定字符串不能包含 NUL 或 LF 字符。当给出多个 --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 已经发布的内容。你将不得不绕过“必须快进”规则,以便用 rebase 后的历史替换你最初发布的历史。如果你在 rebase 的同时有人在你的原始历史之上进行了开发,远程分支的尖端可能会因他们的提交而前进,盲目地使用 --force 推送将丢失他们的工作。

此选项允许你说明你期望正在更新的历史是你 rebase 并希望替换的内容。如果远程引用仍然指向你指定的提交,你可以确信没有人对该引用做过任何事情。这就像对引用进行了“租赁”,而没有显式锁定它,并且仅当“租赁”仍然有效时才更新远程引用。

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

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

--force-with-lease=<refname>:<expect> 将保护命名的引用(单独),如果它将要被更新,要求其当前值与指定的 <expect> 值相同(这个值可以与我们为 refname 准备的远程跟踪分支不同,或者在这种情况下我们甚至不需要这样的远程跟踪分支)。如果 <expect> 是空字符串,则命名的引用必须尚不存在。

请注意,除了指定期望值的 --force-with-lease=<refname>:<expect> 之外的所有其他形式(即不说明远程端的精确提交指向什么,或者不说明远程端的哪些引用正在被保护)仍然是实验性的,它们的语义可能会随着我们对该功能积累经验而改变。

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

关于安全的通用说明:在没有指定期望值的情况下提供此选项,即作为 --force-with-lease--force-with-lease=<refname>,与任何在后台隐式运行对要推送的远程仓库进行 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              # 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 在后台如何更新。

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

-f
--force

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

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

请注意,--force 适用于所有被推送的引用,因此与 push.default 设置为 matching 一起使用,或与配置了 remote.*.push 的多个推送目标一起使用,可能会覆盖当前分支之外的引用(包括与其远程对应项严格滞后的本地引用)。要只强制推送一个分支,请在要推送的 refspec 前面加上一个 +(例如,要强制推送到 master 分支,请使用 git push origin +master)。有关详细信息,请参阅上面的 <refspec>... 部分。

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

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

此选项启用了检查,该检查验证远程跟踪引用的尖端是否可达于本地分支基于它的某个“reflog”条目,以进行重写。该检查确保任何来自远程的更新都已在本地合并,如果不是,则拒绝强制更新。

如果选项在没有指定 --force-with-lease 的情况下传递,或者与 --force-with-lease=<refname>:<expect> 一起指定,那么它是一个“无操作”。

指定 --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,则推送所有子模块,而超项目保持未推送。使用 no--no-recurse-submodules 可以覆盖 push.recurseSubmodules 配置变量,当不需要子模块递归时。

使用 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 作为 `` 参数

  • 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>`。推送到一个远程仓库会影响所有定义的 pushurls 或所有定义的 url(如果未定义 pushurls)。但是,抓取只会从第一个定义的 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 pull` 和 `git fetch` 使用。可以指定多个 `Push:` 和 `Pull:` 行以进行额外的分支映射。

$GIT_DIR/branches 中的命名文件

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

	<URL>#<head>

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

根据操作,Git 将使用以下 refspec 之一,如果您不在命令行上提供的话。`<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 选项时,才会显示已更新引用的状态。

标志

一个字符,表示引用的状态

(空格)

表示成功推送的快进;

+

表示成功的强制更新;

-

表示成功删除的引用;

*

表示成功推送的新引用;

!

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

=

表示已更新但无需推送的引用。

摘要

对于成功推送的引用,摘要显示了引用的旧值和新值,格式适用于作为 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. 如果推送目标不是分支或标签

    • 如果源是树或 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 之间) rebase 到 A 之上,然后将结果推回。rebase 将创建一个新的提交 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 <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>... 的部分。

这用于模拟在 mothership 上运行的 git fetch,使用反向方向运行的 git push 来集成在 satellite 上完成的工作,并且当您只能在一个方向上建立连接时(即 satellite 可以 ssh 到 mothership,但 mothership 不能连接到 satellite,因为后者位于防火墙后面或未运行 sshd)通常是必需的。

satellite 机器上运行此 git push 命令后,您将 ssh 到 mothership 并在此处运行 git merge,以完成在 mothership 上运行 git pull 以拉取 satellite 上所做更改的模拟。

git push origin HEAD:master

将当前分支推送到 origin 存储库中与 master 匹配的远程引用。这种形式便于推送当前分支,而不必考虑其本地名称。

git push origin master:refs/heads/experimental

通过复制当前的 master 分支,在 origin 存储库中创建 experimental 分支。仅当本地名称和远程名称不同时,才需要此形式来创建远程存储库中的新分支或标签;否则,仅使用 ref 名称即可。

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 在没有给出 refspec(无论是来自命令行、配置还是其他地方)时应执行的操作。不同的值适用于特定的工作流程;例如,在纯粹的中心化工作流程(即 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 签名,如同将 --signed 传递给 git-push[1]。字符串if-asked会导致在服务器支持的情况下对推送进行签名,如同将 --signed=if-asked 传递给git push。false 值可能会覆盖较低优先级配置文件中的值。显式的命令行标志始终覆盖此配置选项。

push.pushOption

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

这是一个多值变量,并且可以使用空值在较高优先级的配置文件(例如存储库中的 .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

可以是“check”、“on-demand”、“only”或“no”,其行为与“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”,则尝试通过客户端和服务器之间尝试查找共同提交的协商轮次来减小发送的 packfile 的大小。如果设置为“false”,Git 将仅依赖服务器的 ref 广告来查找共同提交。

push.useBitmaps

如果设置为“false”,则禁用“git push”使用位图,即使 pack.useBitmaps 为“true”,但不会阻止其他 git 操作使用位图。默认为 true。

GIT

Git[1] 套件的一部分