[英]how to stop tracking files in git for sometime?
我想讓 git 停止跟蹤 git 中的一些文件,以便在 bitbucket 中生成拉取請求。我不是管理員,所以無法在 bitbucket 中進行更改。有什么好的方法嗎? 我知道我可以使用“git rm --cached”,但我希望它用於整個目錄和一些文件,我想在完成拉取請求后將其重新設置為正常。 我更改了 .gitignore 文件,但這僅適用於未跟蹤的文件。 所以,它仍然顯示出變化。 請幫忙。
Git 中的跟蹤文件是 Git 索引中的文件。
這就是完整的定義。 所以git rm --cached
從 Git 的索引中刪除一個文件,確實會使它不被跟蹤。 但這可能不是您想要的,因為 Git 在您運行git commit
時從 Git 索引中的任何內容進行新提交。 除非您不進行任何新提交,否則這些新提交省略文件的事實意味着,與具有該文件的任何提交相比,該文件已被“刪除”。
因此,如果您不打算進行新的提交,從索引中刪除文件只是一個選項——在這種情況下,文件是否被跟蹤可能首先是不相關的。
這里的技巧是了解您實際上並不想停止跟蹤該文件。 相反,您希望在您所做的新提交中使用您在之前的某個提交中擁有的相同文件,而不管該文件在您的工作樹中的外觀如何。 這很容易做到,但在您理解其工作原理之前,我們需要定義很多我剛剛使用的行話。
提交,在 Git 中:
提交的數字是一個奇怪的、隨機的(但實際上不是隨機的)並且非常大的數字(在 1 和 2 160 -1 之間,或者在短尺度系統中大約為 1.4 五分之一),通常以十六進制表示。 每個Git 存儲庫中的每個提交都有一個唯一的編號,這就是為什么這些編號必須如此之大:您所做的任何提交都將具有與以往所有其他提交不同的編號。 這就是 Git 如何判斷兩個不同的存儲庫是否具有相同的提交:兩個提交只有在它們具有相同的數字時才相同。
提交中的快照是所有文件的完整集合,如 Git 索引中所示。 這些文件以特殊的、僅限 Git、壓縮和去重的格式存儲。 重復數據刪除處理了這樣一個事實,即大多數提交實際上與其他提交共享他們的大部分文件:這些文件實際上只存儲一次。
提交中的元數據包含提交人的姓名和 email 地址,以及提交人的日期和時間。 它還包含一些較早提交的原始提交編號(只要有一些較早的提交)。 這就是粘合在一起的東西。
由於實際提交的實際 hash ID(提交編號)又大又丑,讓我們繪制一個小型存儲庫,其中只有三個提交,假設提交的“編號”是A
、 B
和C
,在那個命令。 提交A
是有史以來的第一個提交,之前沒有任何提交,所以它只是坐在那里:
A
但是,提交B
將提交A
作為其先前的提交,因此它向后指向A
:
A <-B
提交C
也同樣向后指向,但是指向B
:
A <-B <-C
每個提交存儲每個文件的完整快照,因此通過提取提交B
和C
並比較提取的文件,Git 可以告訴您B
和C
之間發生了什么變化。
任何提交的所有部分一旦完成,都是完全只讀的。 任何提交的任何部分都不能更改,提交大多是永久性的(它們只有 go 遠,最終,如果你找不到它們——我們稍后會詳細說明)並且完全是只讀的。
現在,因為提交 hash ID 又大又丑,我們(人類)永遠不想使用它們。 所以為了幫助我們,Git 給了我們分支名稱。 分支名稱只是一種存儲一 (1) 次提交的 hash ID 的方法。 我們說分支名稱指向提交,我喜歡這樣畫:
A--B--C <-- main
請注意,我懶得在提交之間繪制箭頭。 他們實際上指向后方(從C
回到B
,然后從B
到A
)但人類傾向於假裝他們 go 向前。 這基本上無關緊要,所以無論如何都不必費心正確地繪制它們,但你應該記住 Git 實際上是倒過來的。 (它有助於稍后處理其他事情。不過我們在這里不需要擔心。)
名稱main
(或master
)當前選擇提交C
。 但這不是永久性的:如果我們git checkout main
,我們select提交C
。 然后,我們做各種事情來准備進行新的提交。
當我們通過運行 go 進行新提交時git commit
, Git 將:
由於存儲庫中可以有多個分支名稱,Git 通過將特殊名稱HEAD
附加到一個分支名稱來記住哪個名稱是當前分支名稱。
讓我們畫這個。 假設我們有:
A--B--C <-- main (HEAD)
現在我們創建一個新的分支名稱,例如 develop 為dev
。 此分支名稱必須指向某個現有的提交。 我們應該選擇哪一個? 我們可以選擇任何現有的提交, A
到C
,但現在明顯要選擇的是當前提交, C
:
A--B--C <-- dev, main (HEAD)
請注意兩個分支名稱如何選擇相同的提交。 因此,如果我們現在將當前分支切換到提交C
,如下所示:
A--B--C <-- dev (HEAD), main
沒有其他需要改變,事實上,沒有其他改變。
現在我們准備一個新的提交——以我們稍后會思考和討論的方式——我們運行git commit
。 Git 打包文件,添加元數據,並創建一個新的提交D
指向現有提交C
:
A--B--C
\
D
分支名稱會發生什么? 我們剛才說: Git 將新提交的 hash ID 寫入當前分支名稱,一個HEAD
附加到。 任何其他分支名稱都不會發生任何其他情況。 所以現在我們有:
A--B--C <-- main
\
D <-- dev (HEAD)
提交D
是我們的新提交; 提交ABC
是我們現有的提交; D
點回C
; 並且main
仍然指向C
因為它沒有移動,但是dev
現在指向我們的新提交D
因為我們當前的分支是dev
。
如果您git checkout main
或git switch main
,Git 將刪除 go 提交D
的文件,並提取 go 提交C
的文件。 那是因為通過更改分支,您已經更改了您正在使用的提交:
A--B--C <-- main (HEAD)
\
D <-- dev
如果你想要 commit- D
文件,你git checkout dev
,這當前意味着commit D
。 分支名稱移動! 提交不會移動:它們是只讀的(完全)和永久的(大部分)。 我們使用分支名稱找到它們,然后(如果需要)向后工作。
讓我們再次檢查dev
並再次提交,然后切換回main
,然后創建一個新的分支feature
並切換到feature
:
D--E <-- dev
/
A--B--C <-- feature (HEAD), main
這次我把dev
畫在上面只是因為我希望我們的新提交到底行的 go。 名稱feature
和main
都是 select 現在提交C
,我們必須使用的文件來自提交C
。 我們進行一些更改並添加並提交並獲得新的提交F
:
D--E <-- dev
/
A--B--C <-- main
\
F <-- feature (HEAD)
如果我們再次提交,我們最終會得到:
D--E <-- dev
/
A--B--C <-- main
\
F--G <-- feature (HEAD)
我們真正做的就是添加提交。 現有的提交仍然存在。 我們可以檢查它們——通過名稱,例如git checkout main
,或者通過原始 hash ID(導致 Git 稱之為分離的 HEAD )——我們可以進行新的提交。 我們永遠無法更改任何現有的提交。
您現在知道 Git 中有哪些分支。但還有更多內容需要學習。
您之前已經提交,所以您知道您運行git checkout
,編輯一些文件,運行git add
,然后運行git commit
。 (或者,也許您使用了稍微低級的快捷git commit -a
。)您的 Git 教程可能提到了索引或臨時區域。 但是,除非它們是非常好的教程,否則它們可能沒有正確涵蓋它。
適當地覆蓋這個東西是必不可少的,因為索引是這里一切的關鍵。 當然,名稱索引很差。 它是如此糟糕,事實上,Git 還有另外兩個名字。 名稱staging area可能是您的教程使用的名稱,是索引的另一個名稱。 第三個名字——現在很少見——是緩存。 這個名字主要出現在像git rm --cached
這樣的標志中。 這三個名字指的是同一個東西。 但是:這是什么東西?
我們已經提到,提交中的文件以特殊的、壓縮的、只讀的、Git-only、去重的格式存儲。 只有Git可以讀取這些文件,實際上沒有任何東西可以寫入它們,甚至 Git 本身也不能。 (Git 可以創建新的,但不能覆蓋現有的。Git 使用的 hash ID 技巧需要這個,所以即使覆蓋它們在物理上是可能的——操作系統通常不會阻止它——這只會破壞存儲庫,然后每個人都很傷心,失業了,我們餓死了,或者中國電影中的其他悲慘經歷。)
那么,我們怎樣才能完成任何工作呢? 為了完成工作,我們需要可以讀寫的普通讀/寫文件。 所以 Git 將從提交中提取文件。 事實上,幾乎所有的版本控制系統都是這樣工作的:你把提交的文件從 VCS 中取出來,然后把它們變成你可以使用的常規文件。
這解釋了你的工作樹。 您的工作樹是您擁有文件的地方,可以隨心所欲地使用。 一旦文件從 Git 中取出,Git 就會放手處理這些事情。 這些現在是你的文件,除非並且直到你告訴 Git 對它們做一些事情,比如用來自其他提交的文件替換它們。
但是索引呢? 為什么會有這個神秘的“集結區”這個東西? 其他版本控制系統似乎沒有——事實上,大多數沒有,或者至少不會像 Git 那樣用它來打你的臉。 所以沒有必要,但是 Git 有,Git 會用它來打你的臉。 你最好了解一下。
Git 對這個暫存區的作用非常簡單:
當您檢出提交時,Git 會將提交的文件復制到暫存區。 這些文件采用特殊的只讀去重格式,因此它們實際上不占用任何空間(盡管每個文件的空間量適中,大約 100 字節左右,用於 Git 在這里需要的緩存數據).
這些文件采用壓縮和去重格式,已准備好 go 進入下一次提交。 也就是說,暫存區,此時此刻,由當前提交組成,它已准備好成為下一個提交。
Git 還將文件從提交/暫存區復制到您的工作樹,將它們擴展為正常可用的文件,以便您可以使用它們。
一旦你處理了一個文件並准備好更新它,Git 會讓你運行git add
文件。 這:
如果它是重復的,Git 然后將重復的“副本”放入索引中,它不占用空間,就像以前一樣。 該文件現在可以提交了。
如果它不是重復的,Git 將准備好的文件放入索引中(盡管從技術上講 Git 將它放在其他地方並僅將對它的引用放入索引中,因此它看起來幾乎與重復的一樣).
不管怎樣,這個文件現在已經准備好提交了——連同索引中已經存在的所有其他文件。 文件的舊版本(如果有的話)已從索引中刪除; 現在當前版本已准備好提交。
所以git add
只是將文件復制到 Git 的索引中,使其准備好提交。 該索引繼續保存建議的下一次提交。 這意味着,在任何時候,索引都會保存您建議的下一次提交。 這就是索引的真正含義,主要是:您提議的下一次提交。 (在有沖突的合並期間,索引扮演了一個擴展的角色,但是 Git 也不會讓你提交,所以即使那樣它仍然是你提議但不能提交的提交。我們不會在這里進一步詳細說明 go,所以我們可以把它留在“建議的下一次提交”。)
當您運行git commit
時,Git 只是打包當時索引中的任何內容。 如果您忘記git add
修改后的文件,Git 將提交該名稱下索引中的任何內容(如果有的話),因為索引中的內容仍然是該文件的舊版本。 而且,如果您git add
文件,也會發生同樣的事情。
很難直接看到索引中的內容,但很容易將索引中的內容與其他內容進行比較:
git diff --cached
將當前提交中的內容與索引中的內容進行比較。 您可以使用git diff --staged
作為同義詞,如果您更喜歡暫存區域這個術語(它比緩存更好)。
因此,如果此處沒有顯示出不同之處,則文件的索引副本與文件的已提交副本相匹配。
git diff
將索引中的內容與工作樹中的內容進行比較。 也就是說,如果此處出現某些內容,則文件的索引副本與工作樹副本不匹配。 差異向您展示了不同之處。
git status
命令為您運行這兩個git diff
命令,但使用--name-status
以便不顯示實際更改。 相反,第一個差異 — HEAD
-vs-index — 告訴您如果您現在提交,哪些文件(如果有的話)會有所不同。 status 命令調用這些文件staged for commit 。 第二個差異是 index-vs-working-tree,它告訴您git add
如果需要,可以添加哪些文件; status 命令調用這些未暫存提交的文件。
不幸的是,拉取請求有點復雜。 要正確理解它們,我們需要意識到必須傳遞Git 次提交。
假設您在 Git 存儲庫中有以下提交鏈:
...--F--G--H <-- main
\
I--J <-- feature (HEAD)
但是您的Git 存儲庫是其他一些 Git 存儲庫的克隆,可能在 GitHub 之上。他們的存儲庫具有提交(您從他們那里獲得)和分支名稱。
你的 Git 沒有使用他們的 Git分支名稱。 你的 Git 取了他們的 Git 分支名稱並重命名。 那是因為你的分支名稱是你的,因為你的 Git 通過添加新提交來移動它們。 畢竟,我們不能(也不想)在添加自己的提交時移動他們的名字。 所以,如果你的 Git——我說的“你的 Git”是指你的 Git 軟件,例如在你的筆記本電腦上運行,使用你的存儲庫——提交H
作為你最新的main
分支提交,並且他們的Git(他們的軟件和他們的存儲庫,超過在 GitHub 上)將提交H
作為他們最新的main
分支提交,你自己的存儲庫實際上會有這個:
...--F--G--H <-- main, origin/main
\
I--J <-- feature (HEAD)
名稱origin/main
是一個遠程跟蹤名稱。 它只記得他們的分支名稱指向的位置。 只要您的 Git 從他們的 Git 獲得更新,您的 Git 就會更新此信息。
現在,如果您自己提交I
和J
,然后運行:
git push origin feature
您的 Git 將調用他們的 Git 並告訴他們提交J
的 hash ID。 他們會查看他們的存儲庫並發現他們沒有這個提交(因為你做了)。 您的 Git 現在有義務向他們提供 commit I
too; 他們不會有那個並且會要求它。 你的 Git 現在必須向他們提供提交H
,但這次他們確實有提交,所以他們會說不,謝謝,我已經有那個了。 這使您的 Git 能夠停止提供提交,因為他們有H
的事實意味着他們也有每個更早的提交。
您的 Git 現在打包I
和J
以及這些新提交所需的任何文件和其他支持對象,並將它們發送過來。 然后你的 Git 會要求他們的 Git 在他們的存儲庫中創建或更新分支名稱feature
。 如果一切順利,他們現在擁有選擇提交J
的名稱feature
——你和他們現在共享——在你的存儲庫中看起來像這樣:
...--F--G--H <-- main, origin/main
\
I--J <-- feature (HEAD), origin/feature
在他們的存儲庫中,他們只有(他們的) main
指向H
和(他們的) feature
指向J
:他們沒有您的 Git 使用的這些特殊的遠程跟蹤名稱; 您的git push
讓他們直接設置分支名稱。 因此,有時他們會拒絕推送,也許是因為他們已經有了該分支名稱,並且他們正在使用它來記住其他一些重要的提交 hash ID。
無論如何,假設他們確實接受了這個推送,你現在可以在 GitHub 上使用他們的分支名稱feature
生成一個拉取請求。 這將要求某人查看這些新提交,如果他們(某人)喜歡它們,則以某種方式將這些新提交合並到他們的分支中。 現在,我們將忽略所有可能的后果,因為這些會變得非常復雜。
假設在提交I
和/或J
中,您更新了一些您不想更新的文件。
你真的不能收回這個。 提交I
和J
永遠保持原樣。 但你不需要。 只需創建一個指向提交H
的新分支名稱並繼續:
...--F--G--H <-- main, origin/main, refeature (HEAD)
\
I--J <-- feature, origin/feature
現在使用 Git 的工具,例如git cherry-pick -n
,從提交I
中獲取更新但尚未提交。 然后使用git restore
命令將一個或多個文件的索引版本放回原處,並使用git commit
進行新的提交:
K <-- refeature (HEAD)
/
...--F--G--H <-- main, origin/main
\
I--J <-- feature, origin/feature
根據需要重復提交J
:
K--L <-- refeature (HEAD)
/
...--F--G--H <-- main, origin/main
\
I--J <-- feature, origin/feature
關閉錯誤的拉取請求,讓 GitHub 刪除他們的feature
( refeature
git push origin --delete feature
),然后推送重新功能並發出新的拉取請求。 決定是否保留自己的feature
分支; 如果你願意,重命名它; 用它做任何你想做的事。 這是你的分支,不是別人的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.