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

名稱

gitfaq - 關於使用 Git 的常見問題

概要

gitfaq

描述

本常見問題解答中的示例假定使用標準的 POSIX shell(如 bashdash),以及使用者 A U Thor(其在託管服務提供商 git.example.org 上的帳戶名為 author)。

配置

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 目錄,它們必須與主工作區和倉庫同時同步。

  • 您接受目標目錄是源目錄的精確副本,刪除已有的任何資料

  • 在傳輸期間,倉庫(包括所有工作區和 Git 目錄)處於靜止狀態(即,沒有進行任何型別的操作,包括後臺操作如 git gc 和您的編輯器呼叫的操作)。

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

常見問題

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

您可以對您的工作區進行適當的更改,根據需要執行 git add <file>git rm <file> 來暫存它,然後執行 git commit --amend。您的更改將包含在提交中,並且會提示您再次編輯提交訊息;如果您希望完全使用原始訊息,您可以在 git commit 後額外使用 --no-edit 選項,或者在編輯器開啟時直接儲存並退出。

我做了一項有 Bug 的更改,並且它已經被合併到主分支。我應該如何撤銷?

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

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

Git 不提供這樣做的方法。原因是如果 Git 需要覆蓋此檔案(例如在檢出期間),它不知道對檔案的更改是寶貴的應該保留,還是無關緊要可以安全銷燬。因此,它必須採取安全路線,始終保留它們。

嘗試使用 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,然後立即進行合併或變基。請參閱 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 Defender 和 Windows Firewall 之外的 Windows 防毒軟體和防火牆程式,以及過濾代理,都未能達到此標準,結果導致 Git 無法正常工作。鑑於大量問題報告及其糟糕的安全歷史,我們不建議使用這些類別的軟體和裝置。

合併與變基

使用 squash 合併來合併長期分支會發生什麼問題?

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

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

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

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

如果我在兩個分支上都進行了更改,但在一個分支上撤銷了該更改,為什麼這些分支的合併仍然包含該更改?

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

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

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

鉤子

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

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

嘗試使用 pre-commit 鉤子(或者對於提交訊息,使用 commit-msg 鉤子)來檢查這些事情是很常見的,如果您是單獨開發者並希望工具幫助您,這非常有用。然而,在開發者機器上使用鉤子作為策略控制是無效的,因為使用者可以(透過各種其他方式)使用 --no-verify 繞過這些鉤子而不被發現。Git 假定使用者控制其本地倉庫,並且不會試圖阻止或告發使用者。

此外,一些高階使用者發現 pre-commit 鉤子會妨礙使用臨時提交來暫存進行中的工作或建立 fixup 提交的工作流程,因此最好還是將此類檢查推送到伺服器。

跨平臺問題

我使用 Windows,並且我的文字檔案被檢測為二進位制檔案。

當您以 UTF-8 格式儲存文字檔案時,Git 的工作效果最佳。Windows 上的許多程式支援 UTF-8,但有些不支援,只使用小端 UTF-16 格式,Git 會將其檢測為二進位制檔案。如果您的程式不能使用 UTF-8,您可以指定一個工作區編碼,指示您的檔案應該以何種編碼檢出,同時仍將這些檔案以 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] 套件的一部分

scroll-to-top