[英]Git: went to old commit, now I can't come back
我使用以下代碼移至舊的提交,其中包含已刪除的一些代碼:
git checkout xxx .
但是現在我想回到工作所在,嘗試了以下方法: 我沒有保存或提交最新代碼
git checkout xxx/xxxx .
xxx / xxx是我一直在工作的分支的名稱。
但是我的文件沒有更改,它們仍然包含所有舊代碼,而沒有包含我的新代碼!
任何幫助將不勝感激。
恐怕par的答案可能是正確的答案 。 但是,如果您對文件進行git add
(即使不進行git commit
,結果也可能會很幸運。 如果全部為“ TL; DR”,則可以跳到下面的最后一部分。
為了清楚起見,讓我們首先注意到git checkout
本質上有三種或四種形式: 1
git checkout branchname
git checkout commit-ish
git checkout commit-ish -- path [ path ... ]
git checkout -- path [ path ... ]
第一種形式,您為git checkout
了一個分支名稱, 並且沒有路徑參數 ,試圖將您帶入branch 。 例如, git checkout master
會將您置於分支master
(或者失敗並且什么也不做)。
第二種形式,您給git checkout
一些可以解析為特定提交ID的內容,但由於第一種形式,它不是普通的本地分支名稱 , 而是檢出指定的提交並為您提供“分離的HEAD”。 (或者再次,它可能會失敗並且什么都不做。)內部工作的方式是,您離開之前的分支,而現在位於新的臨時匿名(未命名)分支上,該分支在提交時結束您剛剛退房。
第二種形式是查看舊提交或重建舊提交或簽出標簽並進行構建(或類似操作)的常用方法。 例如,您可以git checkout c0ffee1
(哈希ID)或git checkout v2.7.2
(標簽)。 但這不是你所做的。
第三和第四種形式的動作非常不同。 如果我負責Git的世界,那么它們根本不會被拼寫為git checkout
,因為它們不會帶您進行另一次提交。 它們對HEAD
沒有影響。 如果您在分支上,則留在該分支上。 如果您處於具有特定提交的分離HEAD模式,那么您將處於具有相同特定提交的分離HEAD模式。 但是現在我們進入了第一個復雜性,因為現在我們必須談論索引和工作樹。
git checkout
的前兩種形式相對安全,因為它們確保您不會丟失索引和工作樹中的文件。 您可以使用-f
/ --force
標志來要求它們覆蓋文件。 git checkout
的第三和第四種形式並不安全。 Git認為每個path
參數都是覆蓋給定文件或目錄的請求。
commit-ish
參數是Git可以用來查找提交的任何內容。 這可以是原始哈希ID(例如c0ffee1
)或標簽(例如v2.7.2
,甚至是分支名稱(例如master
。 請注意,我們將分支名稱拆分為git checkout
一種特殊(第一種)形式,但這僅在沒有path
參數的情況下適用 。 當您提供一些path
參數時,像master
這樣的分支名稱就不再特殊了。
順便說一句, --
之前的path
是可選的。 存在它的原因是允許您使用類似於選項的path
。 例如,如果您有一個名為-f
或--force
的文件,則需要能夠告訴Git引用該文件 -f
或--force
,而不是使用-f
或--force
選項。 如果您有一個名為master
的文件,則需要能夠告訴Git您所引用的是名為master
的文件 ,而不是名為master
的分支。 如果省略--
,Git會最好地猜測您是指分支名稱,提交ID,選項還是文件名。 在您的情況下,您使用.
,絕對是文件名,所以git checkout xxx .
成為第三種形式。 當然, .
表示“整個當前目錄,包括其所有文件和子目錄”。
1表格的確切數量取決於您決定如何計算這些表格。 例如,您可以將前兩個折疊為一種形式,然后將第二個折疊為另一種形式,或者將前兩個分開並合並第三和第四種形式。 還有git checkout -m
, git checkout -p
, git checkout --ours
和git checkout --theirs
,它們與這四個主要方法略有不同。 但是在開始使用它們之前,請不要擔心它們。
承諾是Git存在的理由。 他們記錄了您一直以來所做的(或至少已承諾的)所有事情,以便您可以取回它們。 每個提交都以樹狀結構(包含文件和子目錄的目錄等)的形式保存一些文件的完整快照。 它還保存先前(父)提交的ID,以及一些元數據,例如您的姓名和電子郵件地址,時間戳以及提交日志消息。 就是這樣:提交就是保存的樹加上一些元數據。
工作樹也很明顯:Git使您可以工作。 Git內部的提交格式只有Git本身可以使用,但是您需要使用計算機來處理普通文件。 因此,Git可以從提交中填充工作樹-盡管實際上它必須使用其索引(稍后我們將看到)-然后,您將擁有常規文件,常規程序,Web服務器或所有可以使用的常規文件。 由於許多原因,工作樹還可以保存您不會提交的文件以及您從未打算提交的文件。 您將這些文件保留為“未跟蹤”文件(Git會對它們發牢騷,並且您希望將其關閉)。
Git的索引 (也稱為暫存區) (如git diff --staged
)或有時稱為緩存 (如git diff --cached
:與--staged
)具有多個角色,但有趣的是,它是您構建下一個提交的地方 。
當從某個現有存儲庫的克隆開始並簽出某個分支名稱時,Git將從快照中的索引中填充該分支上最新提交的索引。 (此最新提交稱為尖端提交。)因此,現在索引與提交匹配。 因此,如果您現在要進行新的提交, 2您的新提交將具有與當前提交相同的樹,因為您尚未更改索引。
當您運行git add
來更新現有文件時,Git只是將索引副本替換為工作樹中的版本。 現在,您進行的下一次提交將具有新版本。 當您運行git add
以添加一個全新的文件時,Git將該文件從工作樹復制到索引中,現在下一次提交將具有新文件。 如果在文件名上使用git rm
,則會從工作樹和索引中刪除該文件,現在下一次提交將沒有該文件。
(順便說一句,這使我們能夠精確地說出這是什么意思了工作樹文件是“未跟蹤”。文件是未跟蹤 ,當且僅當它是不是在索引中。這就是它-這一切就是這么簡單!現在,當Git對未跟蹤的文件發牢騷時,可以將文件名添加到名為.gitignore
的文件中,這將使Git 關閉它。實際上不會使該文件處於未跟蹤狀態:這取決於文件是否未被跟蹤。它只是使Git關閉,並且在您使用Git的“一次添加多個文件”快捷方式之一時也不會自動添加它。)
2 Git將嘗試阻止您進行與HEAD
提交完全匹配的新提交。 但是,您可以使用--allow-empty
強制其允許提交。 “ Empty”是一種有趣的拼寫方式,因為新提交根本不為空,至少在保存的工作樹方面完全相同 。 (即使它們與HEAD
匹配,也始終允許新的合並提交。)
git checkout
何時覆蓋索引和/或工作樹? 如果我們回到我所謂的git checkout
的第三種形式和第四種形式,我們會看到其中一種具有commit-ish
參數,而另一種則沒有。 這可能應該是更多隱藏的實現細節,但是Git習慣於讓實現細節直接顯示給用戶。
為了git checkout
將文件從提交復制到工作樹,它必須首先將文件寫入索引。 因此,第三種形式git checkout commit-ish -- path
,找到與給定commit-ish
關聯的文件path
的版本,將其復制到索引,然后將索引版本復制到工作樹。
但是,第四種形式沒有commit-ish
參數: git checkout -- path
。 在這種情況下,Git將文件的版本從索引復制到工作樹中。 在大多數情況下,索引上的版本與工作樹中的版本相同 ,因此在大多數情況下,這沒有任何作用。 但是,如果您已經修改了工作樹版本,然后又決定要放棄所做的修改,則可以提取索引版本。
索引版本可能與當前提交( HEAD
)版本相同。 在這種情況下, git checkout -- path
和git checkout HEAD -- path
都將HEAD
版本復制到工作樹中,但是具有顯式HEAD
副本首先將HEAD
版本復制到索引中,結果是相同的因為HEAD
和索引版本仍然相同。
為了完整起見,我將提到前兩個git checkout
形式(“安全”形式) 也將覆蓋索引和工作樹,但是在此不做過多詳細說明,Git嘗試僅覆蓋那些是“安全”的覆蓋,因此您不會丟失未提交的工作。 請參閱Git-在當前分支上有未提交的更改時簽出另一個分支 (更多)。
每個文件始終有多達三個有趣的版本:
HEAD
)提交中的一個; 使用git checkout commit-ish -- path
,如git checkout xxx .
,使HEAD
版本保持不變,但將xxx
(提交)版本復制到索引和工作樹中。 如果索引和工作樹中還有其他版本,那么它們現在就消失了。 如果這些版本與某個提交的版本匹配,則可以將其取回。 如果沒有,Git幫不了您……可能。 但是請看最后一節!
“永遠消失”規則有一個不尋常的例外,盡管使用起來很痛苦。 當您在索引中git add
文件時,Git實際上將文件的副本放入存儲庫中 。 索引僅包含文件的40個字符的SHA-1 哈希 。 這意味着,如果您在git add
-ed文件中git add
了該文件,則該文件將保存在存儲庫中。 如果用另一個版本覆蓋索引版本,則實際上會將“另一個”版本復制到存儲庫中,並將新的哈希值放入索引中。 中間版本不會被刪除! 好吧, 還沒有 。
這些散列但從未提交的文件可以恢復,直到Git“垃圾收集”它們為止。 默認情況下,從git add
-ed到它們至少有14天的時間。 恢復它們的命令是git fsck --lost-found
。
這種恢復方法的問題在於文件名不見了。 git fsck --lost-found
所做的是找到Git對象(提交,樹,標簽和“ blob”,Git稱為存儲的文件),這些對象沒有引用它們。 當您對文件進行git add
-ed時,Git將文件內容存儲為新的blob,然后將blob對象的哈希ID寫入索引,並使用索引來保存文件名 。 然后,當您重寫path
的索引條目時,您會丟失名稱,並且存儲庫blob對象變為未引用 。 --lost-found
選項使git fsck
將原始文件的內容復制到.git/lost-found/other/
,並將其存儲在哈希ID下,因為名稱消失了。 然后,您可以瀏覽每個此類文件以找到所需的文件,並將其移出失物招領處以將其取回。
如果您在未預先存放或提交代碼的情況下簽出分支,則代碼將丟失並且無法檢索。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.