簡體   English   中英

刪除沒有父項的 Git 提交

[英]Remove a Git commit with no parent

我正在教授一門關於 Git 的課程。 我不知道如何,但是我的一個學生設法連續獲得了三個沒有以前歷史記錄的提交! 學生甚至設法將那三行提交合並到master (在學生研究並發現--allow-unrelated-histories ),但在歷史樹中可以看到,這三個提交並沒有拆分master ,而是只是掛在那里,像一條尾巴。

之前發生的一件事是,在嘗試將存儲庫fooproj克隆到單獨的目錄fooproj-copy ,學生不小心將其克隆到了fooproj的子目錄中,因此 Bitbucket 將其顯示為遠程存儲庫中的子項目。 我讓學生刪除了這個子目錄fooproj-copy ,然后推送新的提交,所以我認為一切都很好。 我不知道他們是如何在沒有父母的情況下獲得承諾的。

所以我說我會刪除這三個提交(即使它們已經合並到 master 中)。 我做了一個git reset --hard HEAD~1並且它走上了那行提交。 我再次執行了git reset --hard HEAD~1並且它繼續運行。 但是再執行一次git reset --hard HEAD~1 ,我已經到了行尾; 由於這三個提交沒有更多的父母,我收到這個錯誤:

fatal: ambiguous argument 'HEAD~1': unknown revision or path not in the working tree.

所以通常如果我想丟棄一些提交,我只是在提交之前對提交進行硬重置。 但在這種情況下,在我想扔掉的提交之前沒有提交 我怎樣才能擺脫它們?

將其繪制為圖形:

       D
        \
A--B--C--M--N   <-- master
           /
          E

提交ADE都是根提交(沒有父提交)。

根提交很容易進行: git checkout --orphan將您置於未出生的分支上,以便該分支上的下一次提交創建分支本身並創建新的根提交。 或者,從另一個具有無關歷史的存儲庫中git fetch以根提交終止的提交鏈(或復雜圖)。 或者, git commit-tree可以編寫具有任意父項的新提交。

你不能真正直接刪除這些提交——或者任何提交,真的——。 您所能做的就是使它們無法訪問

如果至少有一個名稱指向至少一個提交,通過其祖先鏈最終將您引導至X ,則提交X是可達的。 在這種情況下,名稱master直接指向 commit N ,即合並。 N指向EM ,因此兩者都是可達的。 M指向CD ,所以兩者都是可達的。

在這種情況下,如果您將master指向C ,則所有DMEN將無法訪問。 因此,它們最終會過期並被垃圾收集。 但是如果在N之后有一個提交——我們稱之為F你想保留,你就無能為力了。 您可以復制該提交:

       D
        \
A--B--C--M--N--F   [abandoned]
       \   /
        \ E
         \
          F'       <-- master

並開始使用F' ,副本F ,如尖master -但你們不守這意味着F 您只保留一個不同的提交F'

首先,根據您所說的,您可能不應該在 Git 上教授課程。 本身就已經非常難了,如果是不熟的人教的我不知道學生會怎么做(我不是要人身攻擊,你可能是善意的,已經結束了教這門課是有充分理由的,但我真的認為由熟悉它的人教授這門課很重要)。


現在,從字面上回答這個問題,要刪除沒有父項的 Git 提交,您只需確保不再有任何對它的引用

完成后,git 會在一段時間后自動刪除其文件; 如果您想立即完成(可能是因為該提交或一系列提交占用了大量空間),您可以使用git gc --prune=all

因此,在您的情況下,如果這些提交似乎不屬於其他分支並且沒有任何標簽,則您只需要將分支重置為未引用提交的歷史記錄中的某個點。


但是,如果您想要解決存儲庫的特定問題:

從你所說的看來,學生所做的合並提交似乎將學生的最后一次提交作為其第一個父級,因此至少最初他將 master合並到了他的 branch 中
~命令遵循第一個父母,因此當您執行第一次git reset --hard HEAD~1您將 master 重置為您學生的提交行(並且使用以下 2 個命令您達到了他的第一次提交)。

因此,現在在該存儲庫中,您可能丟失在 master 上的所有其他提交

如果你有另一個克隆,最好回到它,否則你的原始提交行可能仍然被你的 reflog 引用,因此仍然存在。

在接下來的內容中,我假設您在學生合並后還沒有對 master 做出其他承諾,從您所說的來看似乎是這樣。

似乎您沒有使用任何圖形存儲庫瀏覽器,如果是這樣,請輸入gitk --all並使用它來准確查看您的分支/引用的狀態,並按照它進行操作(您必須刷新它)每次修改后手動按 F5)。 沒有圖形瀏覽器,基本上不可能很好地處理 git,但是您嘗試傳遞給 git log 的許多選項。

輸入git reflog並查看它是否列出了您的提交。 您需要找到學生的合並提交或其之前的提交。

為了確保不會丟失任何其他東西,首先在您當前所在的位置添加一個標簽( git tag temp1 )。

然后,如果你發現你的學生的合並提交,首先也標記它( git tag temp-studentmerge <sha1-of-the-commit> ); 當然,代替 <sha1-of-the-commit> 放置該提交的(縮寫)sha1(它列在 git reflog 左側第一列中的那個)。

然后做git reset --hard temp-studentmerge 更新gitk; 你現在應該再次看到你所有的提交。

現在您必須重置為合並的正確父級; 最簡單和最安全的選擇是在圖形瀏覽器中查看其 sha1,然后執行git reset --hard <sha1> 否則git reset --hard HEAD^2應該可以工作,根據您的描述。
^<n>符號引用第 <n> 個父元素,而 ~ <n>符號僅引用第一個父元素。

現在檢查一切是否正確,然后刪​​除臨時標簽( git tag -d temp1git tag -d temp-studentmerge )。

如果你使用 gitk,如果你做了一個簡單的 F5,你仍然會看到學生的提交,而不是 Shift-F5 ( Reload ),你就不會再看到它們了。

提交對象實際上應該仍然在你的存儲庫中的某個地方,因為 git 刪除不再被引用的對象,只有當它們早於某個時間時(我實際上不確定細節)。
如果由於某種原因您想立即刪除它們的任何痕跡,您可以執行git gc --prune=all

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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