簡體中文 ▾ 主題 ▾ 最新版本 ▾ 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] 套件的一部分