簡體   English   中英

吉特:去做舊提交,現在我不能回來了

[英]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 -mgit checkout -pgit checkout --oursgit 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 -- pathgit 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM