[英]git reset --hard does not seem to be reseting uncommited changes
一個未跟蹤的文件:
因此,該文件現在不在 Git 中,明天也不在 Git 中,因此任何Git操作都不會觸及它。
如果您不希望這些文件出現在下一次提交中,則無需執行其他任何操作。
Git 與files無關。 Git 是關於commits 的。
那些剛接觸 Git 的人通常認為這是關於文件的,但事實並非如此。 提交確實包含文件,但 Git 是關於提交的。 他們可能還認為 Git 是關於分支的,但這也不是真的:分支名稱確實可以幫助您(和 Git)找到提交,但 Git 仍然是關於提交的。
由於 Git是關於提交的,因此您需要了解它們。 以下是您現在需要知道的:
每個提交都有一個編號。 這些不是簡單的計數:我們沒有提交#1,然后是#2,然后是#3,依此類推。 取而代之的是,每次提交都會得到一些非常大、又大又難看的數字,看似隨機,例如5a73c6bdc717127c2da99f57bc630c4efd8aed02
。
此編號對於此特定提交是唯一的。 任何地方的每次提交,無論是誰、何時、何地等,都必須獲得自己唯一的編號。 這就是為什么這個數字必須如此之大。 該數字實際上是加密哈希函數的輸出。
每個提交存儲兩件事:
由於加密編號方案,任何提交的任何部分都不能更改。 一旦你做了一個提交,它就會永遠凍結。 (如果你錯誤地犯了一個錯誤的提交,你可以停止使用它。它會存在很長時間,以防你想要它回來,但最終 Git 會發現你不僅沒有使用它,而且——在合適的條件下——其他人也永遠無法找到和使用它,因此 Git 可以完全刪除它。但這是一個更棘手的問題,我們不會在這里擔心。)
但是如果提交是只讀的(確實是),並且提交中的文件以只有 Git 本身可以讀取(並且確實如此)並且實際上沒有任何內容可以寫入的格式存儲,我們將如何獲得任何實際工作完畢? Git 在這里與所有版本控制系統都有相同的答案,它們都有這種問題。 你不說是在Git中的文件。 相反,您在 Git從Git 中取出的副本上工作。
Git的提取您的需求,文件的這些可用的,可行的副本,當你檢查出有犯git checkout
或git switch
。 可用文件進入工作區,Git 稱之為工作樹或工作樹。 這非常簡單明了:您的工作樹是您完成工作的地方。 它有您可以查看和使用的文件。 但是這些文件不在 Git 中。
其他(非 Git)版本控制系統以相同的方式開始:您提取提交,並獲得有用的文件。 然后您根據需要編輯文件,當您准備好時,您可以運行,例如hg commit
(對於 Mercurial,一個不同的版本控制系統)。 這個非 Git VCS 計算出您對文件做了什么並進行了新的提交,然后一切就緒。
Git 讓事情變得更加困難。 Git 設置了一個單獨的東西,而不是在您運行git commit
時讀取您的工作樹。 這東西有三個名字,也許是因為第一個名字很糟糕。 這三個名字分別是:
git rm --cached
類的標志中徘徊。這個東西——索引或暫存區——保存你提議的下一次提交。 當你第一次檢出一些提交時,Git 用你提交的所有文件的副本(或“副本”,因為它們已經被去重:索引“副本”是 Git 的內部格式)填充它剛退房。 這些文件也會進入你的工作樹(作為真實的普通文件,而不是奇怪的 Git 化的重復數據刪除魔法)。
當您修改某個文件的工作樹副本時,只會更改該文件的工作樹副本。 Git文件在任何地方都沒有改變。 提議的下一次提交仍保留文件的先前版本,即 Git 從當前提交中提取的文件。
如果您希望 Git 提交更新的副本,而不是您從先前提交中取出的舊副本,您必須告訴 Git 更新其索引/暫存區。 您可以使用git add
執行此操作,例如, git add file.ext
。 這告訴 Git 讀取file.ext
的工作樹版本,將其壓縮為 Git 的內部格式,根據需要安排重復數據刪除,並為下一次提交做好准備。 文件的下一次提交就緒副本進入索引/暫存區,現在您已經更新了建議的下一次提交。
這一切意味着,在任何時候,每個文件都有三個副本(盡管其中一些是 Git 化的,因此進行了重復數據刪除):
HEAD index work-tree
--------- --------- ---------
README.md README.md README.md
main.py main.py main.py
例如,如果當前提交中有三個文件。 HEAD
副本是只讀的(並且去重); 索引副本是可替換的(但也可以去重復); 並且工作樹副本是可用的普通文件,不會進行重復數據刪除,但可以讓您完成工作。
運行git commit
從index 中的副本進行新的提交。 所以這就是這些副本存在的原因:為下一次提交做好准備。
您的工作樹只是您計算機上的一個普通目錄(或文件夾,如果您喜歡該術語)。 因為它是一個普通文件夾,您可以在此處創建新文件,或刪除現有文件。 Git 不會知道或關心您是否這樣做了:提議的下一次提交隱藏在 Git 的索引(或暫存區)中。 它沒有新文件:
HEAD index work-tree
--------- --------- ---------
README.md README.md README.md
main.py main.py main.py
new.txt
現在在您的工作樹中但現在不在Git 索引中的任何新創建的文件都是untracked files 。 這就是未跟蹤文件在 Git 中的定義方式:未跟蹤文件是指確實存在於您的工作樹中但不存在於 Git 索引中的文件。
如果您對這些未跟蹤的文件之一運行git add
,Git 將讀取它,將其壓縮為內部格式,檢查重復項等,並將新文件添加到 Git 的 index ,使其暫存以進行提交。 現在該文件確實存在於 Git 的索引中(以及您的工作樹中),因此它不再是未跟蹤的文件。 通過更改 Git 索引中的文件集,您已經更改了未跟蹤的文件:
HEAD index work-tree
--------- --------- ---------
README.md README.md README.md
main.py main.py main.py
new.txt new.txt
同樣,您可以使用git rm --cached
從 Git 的索引中刪除文件,但將其保留在您的工作樹中。 這會導致被跟蹤的文件變為未跟蹤。 (這是我之前提到的特殊例外:如果文件在您檢出的提交中,但隨后您刪除了索引副本,則它現在未被跟蹤。)
同樣,所有這些更新都發生在 Git 的索引中,您看不到. 沒有明顯的地方可以在 Git 的索引中查找文件。 1但是,如果 Git 對此完全保持沉默,那么 Git 將比現在更難使用。 所以git status
會報告未跟蹤的文件。
運行git status
實際上做了很多事情:
首先,它讓 Git 打印出你當前的分支名稱,以及其他一些有用的信息。
接下來,它讓 Git 將當前提交(即HEAD
)與索引進行比較。 對於所有匹配的文件, git status
什么都不說。 對於任何不同的文件, git status
表示此文件已暫存以進行 commit 。 這意味着文件的索引副本是不同的。
然后git status
讓 Git 將索引文件與工作樹文件進行比較。 對於所有匹配的文件, git status
什么也沒說。 對於任何不同的文件, git status
表示此文件未暫存為 commit 。 這意味着文件的工作樹副本是不同的。 2
盡管如此, git status
忽略任何未跟蹤的文件。 它確實收集了他們的名字:當 Git 運行第二次比較時,任何未跟蹤的文件都會顯示出來,並且 Git 現在擁有未跟蹤文件的完整列表。
現在git status
將顯示未跟蹤的文件,但現在.gitignore
文件會啟動,如果你有的話。
1您可以運行git ls-files --stage
來轉儲 Git 索引中的內容。 但這並不適用於日常工作,也不是完成工作的好方法。
2請注意,所有三個副本都可以不同。 例如,查看一些具有README.md
現有提交。 在文件中添加一行並運行git add README.md
。 然后在文件中添加第二行。 現在三個副本都不同了。 試試git status
。 該文件既“為提交而暫存”,又“未暫存以進行提交”。 這只是意味着 HEAD-vs-index 顯示添加的第一行,而 index-vs-working-tree 顯示添加的第二行。
如果您現在運行git commit
,Git 會提交每個文件的索引副本。 所以新提交中的README.md
文件只添加了一行。 如果您現在運行git add README.md
,Git 會用添加的一行替換索引副本,並使用包含兩行添加的新索引副本。
如果某些文件是未經跟蹤, git status
通常會列出它在這一節中,你在你的輸出不喜歡像節目文件app/Popup.php
。 在此處列出此文件的目的是提醒您尚未添加該文件,並且在您添加之前它不會出現在下一次提交中。
但是,如果該文件不應該出現在下一次提交中,甚至不應該出現在任何提交中呢? 好吧,該問題的一個答案是您現在可以刪除該文件:
rm app/Popup.php
現在它不再在您的工作樹中。 (請注意,此刪除不被Git的完成,甚至GIT中,這是你對自己就是這樣做的東西。)由於這個文件是從來沒有在Git中,它現在消失了,至少盡可能的Git去。 Git 無法幫你找回它。 Git 中從來沒有過!
但是:也許您不想刪除它。 也許你想同時告訴 Git 兩件事:
app/Popup.php
!app/Popup.php
添加到您的索引中! 不應該犯! 要做到這.gitignore
,您可以在.gitignore
文件中列出該文件:
echo app/Popup.php >> .gitignore
或者:
echo /Popup.php >> app/.gitignore
(這些將在此處產生相同的效果)。
在.gitignore
文件中列出一個文件,或者像*.o
或*.pyc
這樣的 glob 模式,告訴 Git:停止抱怨這個文件/與這個模式匹配的文件,當它們未被跟蹤時。 所以這使得git status
更有用:它只警告你現在應該跟蹤的文件。
它還會阻止git add
添加文件。 您可以強制git add
添加文件,但默認情況下, git add
現在不會將文件添加到 Git 的索引中。
如果文件已經在 Git 的索引中,這些都沒有任何影響。 所以.gitignore
並不是真正要忽略的文件列表。 這真的是.git-don't-complain-about-these-files-if-they-are-untracked-and-do-not-add-them-to-the-staging-area-unless-I-force-it-because-they-are-supposed-to-stay-untracked-okay?
, 或類似的東西。 但是將它作為文件名會很瘋狂,所以 Git 只是將其稱為.gitignore
。
未跟蹤的文件不會出現在您的下一次提交中。 說“我該怎么處理這些我在下一次提交中不想要的文件”是沒有意義的:它們已經不會出現在您的下一次提交中。 詢問如何通過不列出文件來使git status
更有用,這很有用。 不過,這真的非常簡單:您只需要習慣 Git 的奇怪之處,即始終為每個文件創建三個副本,並且名稱.gitignore
具有誤導性:這些文件根本沒有被忽略,它們只是默默地未被跟蹤只要它們實際上未被跟蹤(如果不使用類似調試的git ls-files
就很難判斷)。
正如git status
告訴你的那樣,這些文件是untracked 。 這意味着它們超出了 Git 的權限。 因此git reset --hard
對他們沒有任何作用。
告訴 Git 刪除未跟蹤文件的命令是git clean
。 它有多種選擇; 例如,它不會在沒有這樣做的情況下遞歸到文件夾中,所以也許你想要git clean -d
。
(這是一個危險的命令——在錯誤的時間說它基本上可以擦除你的整個硬盤——所以我強烈建議你說git clean -d -n
做一次試運行,看看會先實際發生什么。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.