簡體中文 ▾ 主題 ▾ 最新版本 ▾ gitfaq 上次更新於 2.46.0

名稱

gitfaq - Git 使用常見問題解答

概要

gitfaq

描述

本 FAQ 中的示例假定使用標準的 POSIX shell,如 bashdash,以及名為 author 的使用者,該使用者在託管提供商 git.example.org 上擁有帳戶。

配置

user.name 中應該填什麼?

應填寫您的個人姓名,通常是名和姓的組合。例如,Git 的當前維護者使用“Junio C Hamano”。這將是您所做每次提交中儲存的姓名部分。

此配置不影響遠端服務的身份驗證;有關詳細資訊,請參閱 git-config[1] 中的 credential.username

http.postBuffer 實際上做什麼?

此選項更改 Git 在透過 HTTP 或 HTTPS 將資料推送到遠端時使用的緩衝區大小。如果資料大於此大小,處理 Git HTTP 支援的 libcurl 將使用分塊傳輸編碼,因為它無法提前知道要推送的資料的大小。

將此值保留為預設大小即可,除非您知道遠端伺服器或中間的代理不支援 HTTP/1.1(引入了分塊傳輸編碼)或已知存在分塊資料問題。這通常(錯誤地)被建議作為通用推送問題的解決方案,但由於幾乎所有伺服器和代理都支援至少 HTTP/1.1,增加此值通常無法解決大多數推送問題。不支援 HTTP/1.1 和分塊傳輸編碼的伺服器或代理在當今網際網路上將無法很好地工作,因為它會破壞大量流量。

請注意,增加此值將增加 Git 透過 HTTP 或 HTTPS 進行每次相關推送時使用的記憶體,因為無論是否全部使用,都會分配整個緩衝區。因此,除非您確定需要不同的值,否則最好將其保留為預設值。

如何配置不同的編輯器?

如果您沒有為 Git 指定編輯器,它將預設使用您透過 VISUALEDITOR 環境變數配置的編輯器,或者如果兩者都未指定,則使用系統預設值(通常是 vi)。由於有些人覺得 vi 難以使用或偏愛其他編輯器,因此可能需要更改使用的編輯器。

如果您想為大多數需要編輯器的程式配置通用編輯器,您可以編輯您的 shell 配置檔案(例如 ~/.bashrc~/.zshenv),其中包含設定 EDITORVISUAL 環境變數為適當值的一行。例如,如果您更喜歡 nano 編輯器,則可以寫入以下內容:

export VISUAL=nano

如果您想專門為 Git 配置編輯器,您可以設定 core.editor 配置值或 GIT_EDITOR 環境變數。有關這些選項的查詢順序的詳細資訊,請參閱 git-var[1]

請注意,在所有情況下,編輯器值都將傳遞給 shell,因此包含空格的任何引數都應正確引用。此外,如果您的編輯器正常呼叫時會從終端分離,您應該使用一個不會這樣做的引數來指定它,否則 Git 將看不到任何更改。一個在 Windows 上解決這兩個問題的配置示例是配置 "C:\Program Files\Vim\gvim.exe" --nofork,它用引號括起帶空格的檔名並指定 --nofork 選項以避免程序在後臺執行。

憑據

在透過 HTTP 推送時,如何指定我的憑據?

最簡單的方法是透過 credential.helper 配置使用憑據助手。大多數系統都提供標準選擇以整合到系統憑據管理器中。例如,Git for Windows 提供 wincred 憑據管理器,macOS 有 osxkeychain 憑據管理器,而具有標準桌面環境的 Unix 系統可以使用 libsecret 憑據管理器。所有這些都將憑據儲存在加密儲存中,以確保您的密碼或令牌安全。

此外,您還可以使用 store 憑據管理器(將其儲存在主目錄的檔案中)或 cache 憑據管理器(它不會永久儲存您的憑據,但可以防止您在一段時間內被提示輸入)。

您也可以在被提示時直接輸入密碼。雖然可以將密碼(必須進行百分比編碼)放在 URL 中,但這並不特別安全,並可能導致憑據意外洩露,因此不推薦這樣做。

如何從環境變數讀取密碼或令牌?

credential.helper 配置選項也可以接受一個任意的 shell 命令,該命令在標準輸出上生成憑據協議。這在將憑據傳遞到容器中時非常有用,例如。

可以透過在選項值前加上感嘆號來指定此類 shell 命令。如果您的密碼或令牌儲存在 GIT_TOKEN 中,您可以執行以下命令來設定您的憑據助手:

$ git config credential.helper \
	'!f() { echo username=author; echo "password=$GIT_TOKEN"; };f'
如何更改已儲存在憑據管理器中的密碼或令牌?

通常,如果密碼或令牌無效,Git 會將其擦除並提示輸入新的。但是,有時並非總是如此。要更改密碼或令牌,您可以擦除現有憑據,然後 Git 會提示輸入新的。要擦除憑據,請使用類似以下語法的命令(替換您的使用者名稱和主機名):

$ echo url=https://author@git.example.org | git credential reject
如何使用 HTTP 在同一託管提供商處使用多個帳戶?

區分這些帳戶最簡單的方法通常是在 URL 中使用使用者名稱。例如,如果您在 git.example.org 上擁有 authorcommitter 帳戶,您可以使用 URL https://author@git.example.org/org1/project1.githttps://committer@git.example.org/org2/project2.git。這樣,當您使用憑據助手時,它將自動嘗試查詢您帳戶的正確憑據。如果您已經設定了遠端,您可以使用類似 git remote set-url origin https://author@git.example.org/org1/project1.git 的命令更改 URL(有關詳細資訊,請參閱 git-remote[1])。

如何使用 SSH 在同一託管提供商處使用多個帳戶?

對於大多數支援 SSH 的託管提供商而言,單個金鑰對可以唯一地標識使用者。因此,要使用多個帳戶,需要為每個帳戶建立一個金鑰對。如果您使用的是相對較新的 OpenSSH 版本,您可以使用類似 ssh-keygen -t ed25519 -f ~/.ssh/id_committer 的命令建立新的金鑰對。然後,您可以將公鑰(在本例中為 ~/.ssh/id_committer.pub;請注意 .pub)註冊到託管提供商。

大多數託管提供商使用單個 SSH 帳戶進行推送;也就是說,所有使用者都推送到 git 帳戶(例如 git@git.example.org)。如果您的提供商是這種情況,您可以在 SSH 中設定多個別名,以明確應使用哪個金鑰對。例如,您可以在 ~/.ssh/config 中寫入類似以下內容,並替換正確的私鑰檔案:

# This is the account for author on git.example.org.
Host example_author
	HostName git.example.org
	User git
	# This is the key pair registered for author with git.example.org.
	IdentityFile ~/.ssh/id_author
	IdentitiesOnly yes
# This is the account for committer on git.example.org.
Host example_committer
	HostName git.example.org
	User git
	# This is the key pair registered for committer with git.example.org.
	IdentityFile ~/.ssh/id_committer
	IdentitiesOnly yes

然後,您可以調整您的推送 URL,使其使用 git@example_authorgit@example_committer 而不是 git@example.org(例如,git remote set-url git@example_author:org1/project1.git)。

傳輸

如何在系統之間同步工作目錄?

首先,決定是否要這樣做。Git 在您使用典型的 git pushgit fetch 命令推送或拉取工作時效果最佳,並且它並非旨在跨系統共享工作目錄。這可能存在風險,在某些情況下可能導致儲存庫損壞或資料丟失。

通常,這樣做會導致 git status 需要重新讀取工作目錄中的每個檔案。此外,Git 的安全模型不允許跨不受信任的使用者共享工作目錄,因此僅當工作目錄僅被單個使用者跨所有計算機使用時,同步工作目錄才是安全的。

重要的是不要使用雲同步服務來同步 Git 儲存庫的任何部分,因為這可能導致損壞,例如丟失物件、已更改或新增的檔案、損壞的引用以及各種其他問題。這些服務傾向於逐檔案進行連續同步,並且不瞭解 Git 儲存庫的結構。這尤其糟糕,如果它們在更新過程中同步儲存庫,則極有可能導致不完整或部分的更新,從而導致資料丟失。

可能發生的損壞型別包括對引用的狀態的衝突,導致兩邊都有對方沒有的提交。這可能導致重要物件變得未被引用,並可能被 git gc 裁剪,導致資料丟失。

因此,最好使用正常的推送和拉取機制將您的工作推送到另一個系統或中央伺服器。但是,這並不總是能保留重要的資料,例如 stashes,因此有些人更喜歡跨系統共享工作目錄。

如果您這樣做,推薦的方法是使用 rsync -a --delete-after(最好與加密連線一起使用,例如 ssh)在儲存庫的根目錄上。您應該確保在執行此操作時滿足以下幾點:

  • 如果您有額外的 worktrees 或單獨的 Git 目錄,它們必須與主工作目錄和儲存庫同時同步。

  • 您已準備好將目標目錄作為源目錄的精確副本,刪除其中已有的任何資料

  • 在傳輸期間,儲存庫(包括所有 worktrees 和 Git 目錄)處於靜止狀態(即,沒有任何型別的操作正在進行,包括像 git gc 這樣的後臺操作以及您的編輯器呼叫的操作)。

    請注意,即使有這些建議,以這種方式同步仍然存在一些風險,因為它繞過了 Git 的正常儲存庫完整性檢查,因此建議進行備份。您可能還希望在同步後執行 git fsck 來驗證目標系統上資料的完整性。

常見問題

上一次提交時犯了個錯誤,如何修改?

您可以對工作目錄進行適當的更改,執行 git add <file>git rm <file>(根據需要),以暫存更改,然後執行 git commit --amend。您的更改將包含在提交中,您將被提示再次編輯提交訊息;如果您想逐字使用原始訊息,可以在 git commit 命令中附加 --no-edit 選項,或者在編輯器開啟時直接儲存並退出。

我做了一個有 bug 的更改,並且它已被包含在主分支中。應該如何撤銷?

通常處理這種情況的方法是使用 git revert。這會保留原始更改被做出並被視為有價值貢獻的歷史記錄,但同時引入一個新的提交來撤銷這些更改,因為原始更改存在問題。revert 的提交訊息會指明被 revert 的提交,並且通常會被編輯以包含 revert 原因的解釋。

如何忽略對已跟蹤檔案的更改?

Git 沒有提供這樣的方法。原因在於,如果 Git 需要覆蓋此檔案,例如在 checkout 期間,它不知道檔案中的更改是寶貴的應該保留,還是不相關的可以安全銷燬。因此,它必須採取安全措施並始終保留它們。

嘗試使用 git update-index 的某些功能,即 assume-unchanged 和 skip-worktree 位,是有誘惑力的,但它們在此目的上無法正常工作,不應如此使用。

如果您的目標是修改配置檔案,通常很有幫助的是,在儲存庫中有一個已提交的檔案,該檔案是模板或一組預設值,然後可以將其複製並按需修改。第二個修改後的檔案通常會被忽略,以防止意外提交。

我讓 Git 忽略了各種檔案,但它們仍然被跟蹤

gitignore 檔案確保 Git 未跟蹤的某些檔案保持未跟蹤狀態。但是,有時在將某些檔案新增到 .gitignore 之前,它們可能已被跟蹤,因此它們仍然保持跟蹤狀態。要取消跟蹤和忽略檔案/模式,請使用 git rm --cached <file/pattern> 並將一個模式新增到 .gitignore 中,該模式匹配 <file>。有關詳細資訊,請參閱 gitignore[5]

如何知道是要 fetch 還是 pull?

fetch 會儲存遠端儲存庫的最新更改副本,而不會修改工作目錄或當前分支。然後,您可以隨意檢查、合併、在其之上變基或忽略上游更改。pull 包括一個 fetch,緊接著是一個 merge 或 rebase。請參閱 git-pull[1]

可以使用代理與 Git 一起使用嗎?

是的,Git 支援使用代理。Git 尊重 Unix 上常用的標準 http_proxyhttps_proxyno_proxy 環境變數,並且還可以透過 http.proxy 和 HTTPS 的類似選項進行配置(請參閱 git-config[1])。http.proxy 和相關選項可以按 URL 模式進行自定義。此外,理論上 Git 可以與網路上的透明代理正常工作。

對於 SSH,Git 可以使用 OpenSSH 的 ProxyCommand 支援代理。常用的工具有 netcatsocat。但是,它們必須配置為在看到標準輸入的 EOF 時不退出,這通常意味著 netcat 需要 -q,而 socat 需要帶有類似 -t 10 的超時。這是必需的,因為 Git SSH 伺服器知道不再有請求的方法是標準輸入的 EOF,但當發生這種情況時,伺服器可能尚未處理最終請求,因此此時中斷連線會中斷該請求。

~/.ssh/config 中帶有 HTTP 代理的示例配置條目可能如下所示:

Host git.example.org
    User git
    ProxyCommand socat -t 10 - PROXY:proxy.example.org:%h:%p,proxyport=8080

請注意,在所有情況下,為了讓 Git 正常工作,代理必須完全透明。代理不能以任何方式修改、篡改或緩衝連線,否則 Git 幾乎肯定會無法工作。請注意,許多代理,包括許多 TLS 中間裝置、Windows 防病毒和防火牆程式(Windows Defender 和 Windows Firewall 除外)以及過濾代理,都無法滿足此標準,結果導致 Git 出現問題。由於存在大量問題報告和它們糟糕的安全歷史,我們不建議使用這些類別的軟體和裝置。

合併與變基

用 squash merge 合併長期分支時可能出現哪些問題?

總的來說,當使用 squash merge 多次合併兩個分支時,可能會出現各種問題。這些問題包括在 git log 輸出、GUI 或使用 ... 表示法表示範圍時看到額外的提交,以及可能需要反覆重新解決衝突。

當 Git 進行正常合併兩個分支時,它會考慮三個點:兩個分支和一個稱為合併基礎的第三個提交,它通常是這些提交的共同祖先。合併結果是合併基礎和每個頭之間的更改的總和。當您透過常規合併提交來合併兩個分支時,會生成一個新的提交,該提交在它們再次合併時將成為合併基礎,因為現在有了新的共同祖先。Git 不必考慮合併基礎之前的更改,因此您不必重新解決之前解決的任何衝突。

當您執行 squash merge 時,不會建立合併提交;相反,一個分支的更改將作為常規提交應用到另一個分支。這意味著這些分支的合併基礎不會改變,因此當 Git 進行下一次合併時,它會考慮上次考慮的所有更改以及新的更改。這意味著任何衝突都可能需要重新解決。同樣,在 git diffgit log 或 GUI 中使用 ... 表示法表示的任何內容都將顯示自原始合併基礎以來發生的所有更改。

因此,如果您想反覆合併兩個長期分支,最好始終使用常規合併提交。

如果我在兩個分支上都做了一個更改,但在其中一個分支上撤銷了它,為什麼合併這兩個分支時會包含該更改?

預設情況下,當 Git 進行合併時,它使用一種稱為 ort 的策略,該策略進行巧妙的三向合併。在這種情況下,當 Git 執行合併時,它會考慮三個點:兩個頭和一個稱為合併基礎的第三點,它通常是這些提交的共同祖先。Git 完全不考慮這些分支上的歷史或單個提交。

結果是,如果兩邊都有更改,而一邊撤銷了該更改,則結果是包含該更改。這是因為程式碼在一邊發生了變化,而另一邊沒有淨變化,在這種情況下,Git 會採納更改。

如果這對您來說是個問題,您可以改用 rebase,將帶有 revert 的分支變基到另一個分支。在這種情況下,rebase 會撤銷更改,因為 rebase 應用每個單獨的提交,包括 revert。請注意,rebase 會重寫歷史,因此您應避免對已釋出的提交進行 rebase,除非您確定自己對此感到滿意。有關更多詳細資訊,請參閱 git-rebase[1] 中的 NOTES 部分。

鉤子

如何使用鉤子來阻止使用者進行某些更改?

唯一安全地進行這些更改的位置是遠端儲存庫(即 Git 伺服器),通常在 pre-receive 鉤子或持續整合(CI)系統中。這些是有效執行策略的位置。

通常嘗試使用 pre-commit 鉤子(或對於提交訊息,使用 commit-msg 鉤子)來檢查這些內容,這對於作為 solo 開發人員並且希望工具提供幫助的人來說非常有用。但是,在開發人員機器上使用鉤子作為策略控制無效,因為使用者可以使用 --no-verify 繞過這些鉤子而不被發現(以及其他各種方式)。Git 假定使用者控制其本地儲存庫,並且不嘗試阻止此操作或告發使用者。

此外,一些高階使用者發現 pre-commit 鉤子會阻礙使用臨時提交來暫存待處理工作或建立修復提交的工作流程,因此最好將這些型別的檢查推送到伺服器。

跨平臺問題

我在 Windows 上,文字檔案被檢測為二進位制。

Git 在儲存文字檔案為 UTF-8 時效果最佳。Windows 上的許多程式支援 UTF-8,但有些不支援,只使用小端 UTF-16 格式,Git 將其檢測為二進位制。如果您無法在程式中使用 UTF-8,您可以指定一個工作目錄編碼,指示檔案在 checkout 時應使用何種編碼,同時仍在儲存庫中以 UTF-8 格式儲存這些檔案。這允許像 git-diff[1] 這樣的工具按預期工作,同時仍允許您的工具工作。

要做到這一點,您可以指定一個帶有 working-tree-encoding 屬性的 gitattributes[5] 模式。例如,以下模式將所有 C 檔案設定為使用 UTF-16LE-BOM,這是一種常見的 Windows 編碼:

*.c	working-tree-encoding=UTF-16LE-BOM

您需要執行 git add --renormalize 才能使此設定生效。請注意,如果您要在跨平臺使用的專案上進行這些更改,您可能希望將其放在每個使用者配置檔案中或 $GIT_DIR/info/attributes 檔案中,因為將其放在儲存庫的 .gitattributes 檔案中將適用於儲存庫的所有使用者。

有關規範化行尾的資訊,請參閱以下條目,有關屬性檔案的更多資訊,請參閱 gitattributes[5]

我在 Windows 上,git diff 顯示我的檔案末尾有一個 ^M

預設情況下,Git 期望檔案以 Unix 行結尾儲存。因此,Windows 行結尾中的回車符(^M)被顯示出來,因為它被視為尾部空格。Git 預設僅顯示新行上的尾部空格,而不是現有行上的。

您可以以 Unix 行結尾在儲存庫中儲存檔案,並自動將其轉換為平臺的行結尾。要做到這一點,請將 core.eol 配置選項設定為 native,並參閱 關於推薦儲存設定的問題 以瞭解如何將檔案配置為文字或二進位制。

如果您不想從行結尾中刪除回車符,您還可以使用 core.whitespace 設定來控制此行為。

為什麼我有一個檔案總是被修改?

在內部,Git 始終將檔名儲存為位元組序列,並且不執行任何編碼或大小寫摺疊。但是,Windows 和 macOS 預設都對檔名執行大小寫摺疊。結果是,可能會出現多個檔名僅在大小寫上不同的檔案或目錄。Git 可以很好地處理這一點,但檔案系統只能儲存其中一個檔案,因此當 Git 讀取另一個檔案以檢視其內容時,它看起來已修改。

最好刪除其中一個檔案,這樣您就只有一個檔案。您可以使用類似以下命令(假設有兩個檔案 AFile.txtafile.txt)在其他乾淨的工作目錄上完成此操作:

$ git rm --cached AFile.txt
$ git commit -m 'Remove files conflicting in case'
$ git checkout .

這可以避免觸碰磁碟,但會刪除額外的檔案。您的專案可能傾向於採用命名約定,例如全小寫名稱,以避免再次出現此問題;這樣的約定可以使用 pre-receive 鉤子或作為持續整合(CI)系統的一部分進行檢查。

在任何平臺上,如果您的系統正在使用 smudge 或 clean 過濾器,但之前的檔案提交時沒有執行 smudge 或 clean 過濾器,也可能出現永久修改的檔案。要解決此問題,請在其他乾淨的工作目錄中執行以下命令:

$ git add --renormalize .

GIT

Git[1] 套件的一部分