Git
英文 ▾ 主題 ▾ 最新版本 ▾ gitfaq 最後更新於 2.46.0

名稱

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

概要

gitfaq

描述

本 FAQ 中的範例假設使用標準 POSIX shell,如 bashdash,以及使用者 A U Thor,他在託管供應商 git.example.org 上擁有帳戶 author

配置

user.name 中應該填寫什麼?

您應該填寫您的個人姓名,通常是使用名字和姓氏的形式。例如,Git 的現任維護者使用「Junio C Hamano」。這將會是您所做的每個 commit 中儲存的姓名部分。

此配置不會影響對遠端服務進行身份驗證;為此,請參閱 git-config[1] 中的 credential.username

http.postBuffer 實際上做什麼?

此選項會變更 Git 在透過 HTTP 或 HTTPS 將資料推送至遠端時使用的緩衝區大小。如果資料大於此大小,libcurl(負責 Git 的 HTTP 支援)將會使用分塊傳輸編碼,因為事先不知道推送資料的大小。

除非您知道遠端伺服器或中間的代理伺服器不支援 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 pushgit fetch 命令推送或提取您的工作時,Git 的效果最佳,並且不是設計為跨系統共享工作樹。這具有潛在風險,並且在某些情況下可能會導致儲存庫損壞或資料遺失。

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

請勿使用雲端同步服務來同步 Git 儲存庫的任何部分,因為這可能會導致損壞,例如遺失物件、變更或新增的檔案、損壞的 refs 以及各種其他問題。這些服務傾向於持續地逐個檔案同步,而且不了解 Git 儲存庫的結構。如果它們在儲存庫更新的中途進行同步,情況會特別糟糕,因為這很可能會導致不完整或部分更新,進而造成資料遺失。

可能會發生的損壞類型的一個例子是 refs 狀態的衝突,導致兩邊在同一個分支上擁有對方沒有的不同提交。這可能會導致重要的物件變成未被參照,並可能被 git gc 修剪,造成資料遺失。

因此,最好使用正常的 push 和 pull 機制,將你的工作推送到另一個系統或中央伺服器。然而,這並非總是能保留重要的資料,例如暫存 (stashes),所以有些人偏好在多個系統間共享工作目錄。

如果你這樣做,建議的方法是在儲存庫的根目錄上使用 rsync -a --delete-after (最好使用加密連線,例如 ssh)。執行此操作時,你應該確保幾件事:

  • 如果你有額外的工作樹 (worktrees) 或獨立的 Git 目錄,它們必須與主要工作樹和儲存庫同時同步。

  • 你能夠接受目標目錄成為來源目錄的精確副本,刪除任何已存在的資料

  • 儲存庫 (包括所有工作樹和 Git 目錄) 在傳輸期間處於靜止狀態 (也就是說,沒有進行任何類型的操作,包括背景操作,例如 git gc 和編輯器調用的操作)。

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

常見問題

我在上一次提交中犯了一個錯誤。我該如何更改?

你可以對你的工作樹進行適當的變更,執行 git add <檔案>git rm <檔案> 來暫存它,然後執行 git commit --amend。你的變更將會被包含在提交中,並且你將會被提示再次編輯提交訊息;如果你希望逐字使用原始訊息,你可以額外使用 git commit--no-edit 選項,或者在你的編輯器開啟時直接儲存並退出。

我做了一個有錯誤的變更,而且它已經被包含在主分支中。我該如何撤銷它?

處理這個問題的通常方法是使用 git revert。這會保留原始變更被進行的歷史記錄,以及它是一個有價值的貢獻,但也引入了一個新的提交來撤銷這些變更,因為原始變更存在問題。還原的提交訊息會指出被還原的提交,並且通常會被編輯以包含關於為什麼進行還原的說明。

我該如何忽略對已追蹤檔案的變更?

Git 沒有提供這樣做的方法。原因是如果 Git 需要覆寫這個檔案,例如在 checkout 期間,它不知道對該檔案的變更是否是重要的、應該被保留的,還是不相關且可以安全銷毀的。因此,它必須採取安全的路線,並且始終保留它們。

嘗試使用 git update-index 的某些功能 (即 assume-unchanged 和 skip-worktree 位元) 很有誘惑力,但這些功能不能正確地達到此目的,不應這樣使用。

如果你的目標是修改組態檔案,通常會有一個已簽入儲存庫的檔案,作為範本或預設值,然後可以將其複製並適當地修改。這個第二個修改過的檔案通常會被忽略,以防止意外提交它。

我要求 Git 忽略各種檔案,但它們仍然被追蹤

gitignore 檔案確保某些未被 Git 追蹤的檔案保持未追蹤狀態。然而,有時特定的檔案可能在將它們加入 .gitignore 之前就被追蹤了,因此它們仍然保持被追蹤狀態。要取消追蹤並忽略檔案/模式,請使用 git rm --cached <檔案/模式>,並在 .gitignore 中加入符合 <檔案> 的模式。請參閱 gitignore[5] 以取得詳細資訊。

我該如何知道我想要執行 fetch 還是 pull?

fetch 會儲存來自遠端儲存庫的最新變更的副本,而不會修改工作樹或目前分支。然後,你可以隨意檢查、合併、基於上游變更重新定基 (rebase),或忽略它們。pull 包含 fetch,然後立即進行合併或重新定基。請參閱 git-pull[1]

我可以在 Git 中使用 Proxy 嗎?

可以,Git 支援使用 Proxy。Git 遵循 Unix 上常用的標準 http_proxyhttps_proxyno_proxy 環境變數,也可以使用 http.proxy 和類似的 HTTPS 選項進行設定 (請參閱 git-config[1])。http.proxy 和相關選項可以根據每個 URL 模式進行自訂。此外,理論上 Git 可以在網路上存在的透明 Proxy 環境下正常運作。

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

一個在 ~/.ssh/config 中具有 HTTP Proxy 的範例組態項目可能如下所示:

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

請注意,在所有情況下,為了使 Git 正常運作,Proxy 必須完全透明。Proxy 不能以任何方式修改、竄改或緩衝連線,否則 Git 幾乎肯定會無法運作。請注意,許多 Proxy,包括許多 TLS 中間盒、Windows 防毒和防火牆程式 (Windows Defender 和 Windows 防火牆除外) 以及過濾 Proxy 都無法符合此標準,因此最終會破壞 Git。由於有許多問題報告及其不良的安全性歷史,我們建議不要使用這些類型的軟體和裝置。

合併與重新定基

當使用 Squash 合併來合併長期分支時,可能會發生哪些問題?

一般來說,當使用 Squash 合併來多次合併兩個分支時,可能會發生各種問題。這些問題可能包括在 git log 輸出、GUI 中,或在使用 ... 表示法來表示範圍時看到額外的提交,以及可能需要一再重新解決衝突的情況。

當 Git 在兩個分支之間進行正常合併時,它會考慮三個點:兩個分支和第三個提交,稱為合併基底,通常是提交的共同祖先。合併的結果是合併基底與每個 head 之間的變更總和。當你使用一般合併提交來合併兩個分支時,這會產生一個新的提交,該提交將會在它們再次合併時最終成為合併基底,因為現在有一個新的共同祖先。Git 不必考慮在合併基底之前發生的變更,因此你不必重新解決你之前解決的任何衝突。

當你執行 Squash 合併時,不會建立合併提交;相反地,一側的變更會作為一般提交應用於另一側。這表示這些分支的合併基底不會改變,因此當 Git 執行下一次合併時,它會考慮上次考慮的所有變更加上新的變更。這表示可能需要重新解決任何衝突。同樣地,在 git diffgit log 或 GUI 中使用 ... 表示法的任何東西都會顯示自原始合併基底以來的全部變更。

因此,如果你想要重複合併兩個長期分支,最好始終使用一般合併提交。

如果我在兩個分支上做了變更,但在其中一個分支上還原了它,為什麼這些分支的合併會包含該變更?

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

因此,如果兩側都有變更,而其中一側還原了該變更,則結果是包含該變更。這是因為程式碼在一側被變更,而另一側沒有淨變更,在這種情況下,Git 會採用該變更。

如果這對你來說是個問題,你可以改為進行重新定基,將具有還原的分支重新定基到另一個分支上。在這種情況下,重新定基會還原變更,因為重新定基會應用每個個別提交,包括還原。請注意,重新定基會重寫歷史記錄,因此除非你確定自己可以接受,否則應避免重新定基已發佈的分支。請參閱 git-rebase[1] 中的 NOTES 區段以取得更多詳細資訊。

鉤子 (Hooks)

我該如何使用鉤子來防止使用者進行某些變更?

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

通常會嘗試使用 pre-commit 鉤子(或針對提交訊息使用 commit-msg 鉤子)來檢查這些事情,如果您是單獨開發並希望工具能幫助您,這很棒。然而,在開發人員機器上使用鉤子作為政策控制並非有效,因為使用者可以使用 --no-verify 略過這些鉤子而不被注意到(還有其他各種方法)。Git 假設使用者可以控制他們的本機儲存庫,並且不會試圖阻止這種情況或告發使用者。

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

跨平台問題

我在 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) 系統的一部分來檢查此類慣例。

如果您的系統上使用塗抹或清除篩選器,但先前提交檔案時沒有執行塗抹或清除篩選器,則也可能在任何平台上發生永久修改的檔案。要解決此問題,請在其他方面乾淨的工作樹上執行以下操作

$ git add --renormalize .

GIT

git[1] 套件的一部分

scroll-to-top