簡體   English   中英

如何從已經重新定位的分支中刪除特定提交?

[英]How to remove specific commit from already rebased branch?

我已經為develop分支打開了一個 PR feature/orphans 從那以后,許多重要的分支被添加到 repo 中,所以我應該重新基於它們。

現在我需要從我的功能分支中刪除一個特定的提交,這對我來說有點棘手。 我試圖遵循類似問題( 411097851400593 )中的幾個答案,但它以某種方式不起作用🤷‍♂️。

我試過這個:

git rebase --onto feature/db-optimization-for-orphans~fcf0c4a feature/db-optimization-for-orphans~2505060 feature/db-optimization-for-orphans

fatal: invalid upstream 'feature/db-optimization-for-orphans~2505060'

我想刪除這個提交#fcf0c4a,這是日志:

* cf83304 - (HEAD -> feature/orphans, origin/feature/orphans) Added orphans collection
* 844eb17 - fix: bugs
* 4f0111f - fix message
* 9093c8d - fix(cat-172): Change data format from object to array
* fcf0c4a - feat(CAT-172): Add new publisher // <---- 👋 I WANT TO REMOVE THIS COMMIT
*   2505060 - (origin/develop, develop) Merge branch 'main' into develop
|\
| * 9600c29 - feat(CAT-111) description
* | 878ee2f - feat(cat-196) description
* | 6b53545 - feat(CAT-18) description
* | 1d2ef2e - saving model name into db + test + version
* | 24eea8e - saving ean values into the db + tests
|/
* 10c205c - Add preprod and prod deployers
* 8a8d0f2 - squashed commits
* 15328b6 - initial commit

有人能告訴我刪除fcf0c4a提交的最佳解決方案是什么嗎?

TL; 博士

我想你想要git rebase -i origin/develop

我有壞消息和好消息要告訴你。

這是壞消息

您只能從分支的末尾刪除提交:

* cf83304 - (HEAD -> feature/orphans, origin/feature/orphans) Added orphans collection
* 844eb17 - fix: bugs
* 4f0111f - fix message

例如,最后有三個提交,我將其名稱縮短為第一個“數字”(最后一個為c ):

... <-4 <-8 <-c

提交cf83304指向后提交844eb17 這就是git log找到844eb17

Git 首先找到cf83304因為分支名稱找到了它:

cf83304 ... feature/orphans

也就是說,名稱feature/orphans指向cf83304

要從分支中刪除提交,我們使分支名稱指向某個較早的提交:

            c   <-- origin/feature/orphans
           /
... <-4 <-8   <-- feature/orphans

所以提交cf83304現在被推到一邊。 (Git 仍然可以找到cf83304 ,因為名稱origin/feature/orphans仍然指向cf83304 。)

一旦我們從分支中刪除了 commit c... ,我們也可以刪除 commit 8...

        8 <-c   <-- origin/feature/orphans
       /
... <-4   <-- feature/orphans

等等。

所以壞消息是:要刪除提交fcf0c4a可以這樣做),您必須從該分支中​​刪除所有后續提交

這是好消息

在我們“刪除”提交之前——它們並沒有真正消失; 如果您知道它們的編號,或者它們還有其他名稱,例如origin/feature/orphans ,我們仍然可以找到它們——我們可以將選定的提交復制新的和改進的提交中

每次提交,在 Git 中:

  • 已編號。 那些大而難看的隨機十六進制數, fcf0c4a等等,唯一地定位了一個特定的提交 Git需要這個數字才能找到提交。

  • 包含兩件事:

    • 每個提交都有每個文件完整快照,因為它在您(或任何人)進行提交時出現。 這些文件以一種特殊的、壓縮的、去重的、只讀的和 Git 專用的形式保存:你無法讀取它們,並且沒有任何東西——甚至 Git 本身——可以覆蓋它們,所以這些不是你想要的文件與。 它們只是永久存儲,以便您以后可以取回它們。

    • 每個提交都包含一些元數據,或關於提交本身的信息。 例如,這包括提交人的姓名和電子郵件地址。 它包括一些日期和時間戳:當您運行git log並查看帶有作者和日期的提交時,這些都來自元數據。 它也包括您在此處看到的日志消息。

      對 Git 本身至關重要的是,每個提交中的元數據存儲了一些先前提交列表的原始哈希 ID(提交編號)。 大多數提交只存儲一個哈希 ID,這就是你在這里得到的; 一些,如2505060 ,是合並提交,存儲兩個哈希 ID; 每個非空存儲庫中至少有一次提交是有史以來第一次提交,例如15328b6 這個首次提交不存儲任何先前提交的 ID,因為沒有任何先前的提交。

除了那些奇怪的特殊情況(合並和第一次提交),然后,我們可以繪制這樣的提交序列:

... <-F <-G <-H

其中每個大寫字母如H代表一些實際的哈希 ID。 (這就是我上面所做的,除了我能夠使用真實哈希 ID 的第一個字符。)字母代表保存的文件和元數據,每個字母中的箭頭代表存儲的前一個-提交哈希 ID:提交H存儲先前提交G的哈希 ID。 我們說H指向G

現在,再次回到壞消息-好消息的主題,壞消息是提交之后就不能再更改了。 (出於多種原因,這是必要的,包括 Git 相當神奇的哈希 ID 方案——這類似於支持加密貨幣的技巧——還包括提交共享相同文件的事實。如果我們能以某種方式更改其中一個文件,這將改變所有共享副本。)好消息是,我們和 Git找到這些提交的方式是通過分支和其他名稱,我們可以並在這些分支和其他名稱中填充不同的提交哈希 ID。

每個名稱僅包含一個哈希 ID。 對於分支名稱,根據定義,該哈希 ID 是分支上的最后一次提交 所以當我們有:

...--F--G--H   <-- somebranch

這意味着根據定義,提交H是分支上的最后一次提交。 這就是我們如何移動名稱以在最后刪除一個或多個提交:

       G--H
      /
...--F   <-- somebranch

現在somebranch指向F而不是HF自動是分支上的最后一次提交

每當我們進行新的提交時,我們都會這樣做:

  1. git switch branchgit checkout branch
  2. 處理 / 使用從提交中復制的文件 Git 選擇的分支名稱;
  3. 使用git add (由於我不會在這里討論的原因);
  4. 運行git commit

最后一步 - git commit步驟 - 通過以下方式進行新提交:

  • 收集適當的元數據:例如,它從user.nameuser.email獲取您的姓名和電子郵件地址;
  • 找出當前提交的哈希 ID,使用步驟 1 中的當前分支名稱:如果它指向F ,那就是當前提交
  • 寫出新的快照和元數據,新提交的箭頭指向當前提交;
  • 最后一招……

但是讓我們先畫出寫出新提交的效果:

       G--H   <-- origin/feature/orphans
      /
...--F   <-- current-branch (HEAD), some-other-branch
      \
       I

我們現在有了這個新的提交I ,它有一個新的、獨特的、又大又丑的哈希 ID。 現在最后一個技巧開始了:Git將新提交的哈希 ID 寫入當前分支名稱:

       G--H   <-- origin/feature/orphans
      /
...--F   <-- some-other-branch
      \
       I   <-- current-branch (HEAD)

這就是分支在 Git 中的生長方式。 我們使用git checkoutgit switch檢查一個——在我的圖中,這意味着我們將特殊名稱HEAD附加到分支名稱; 你可以在你自己的git log輸出中看到這個特殊的名字——並檢查提交讓我們提交中獲得所有保存的文件。 然后我們像往常一樣做我們的工作並進行新的提交。 提交獲得一個新的唯一哈希 ID,Git 將新提交的哈希 ID 填充到當前名稱中,現在名稱指向新的最后一次提交。

這如何幫助你做你想做的事?

讓我們畫一些你有的東西,用我喜歡的一個字母大寫字母的名字替換丑陋的大哈希 ID,以我喜歡的形式繪制它:

...--G--H--I--J--K--L   <-- feature/orphans (HEAD), origin/feature/orphans

這里G代表2505060 - (origin/develop, develop) Merge branch 'main' into develop H代表fcf0c4a - feat(CAT-172): Add new publisher :您要“刪除”的提交。 I代表9093c8d - fix(cat-172): Change data format from object to array ,你想保留的提交。 JKL也是您想要保留的提交。

壞消息是你將不得不彈出你想要保留的提交。 好消息是您可以先將它們復制到新的和改進的提交中。 我們最終會得到:

       H--I--J--K--L   <-- origin/feature/orphans
      /
...--G
      \
       I'-J'-K'-L'  <-- feature/orphans (HEAD)

提交I'-J'-K'-L'將仔細安排提交的副本 我們將對每個副本進行兩項更改:

  1. 每個副本的項將指向右父項:即, I'將直接指向G ,而不是H
  2. 每個副本的快照文件刪除您在提交H所做的更改

現在,清晰但手動且有點痛苦的方法是手動復制您想要復制的每個提交,一次一個。 我們將通過創建一個指向提交G新臨時分支名稱來做到這一點:

       H--I--J--K--L   <-- feature/orphans, origin/feature/orphans
      /
...--G   <-- temp-branch (HEAD)

我們這樣做:

git switch -c temp-branch 2505060

我們現在在這個新的臨時分支上,我們可以看到和使用的文件來自提交G (或者25050602505060 )。

我們現在想讓 Git 找出我們在提交I更改的內容,並在此處和現在進行相同的更改並提交它們 Git 也會從 commit I復制提交消息

執行此簡單“復制一次提交的更改和提交消息”的 Git 命令是git cherry-pick ,因此我們將運行:

git cherry-pick <hash-of-I>

I的(縮寫)哈希是9093c8d因此我們可以輸入它並按ENTER並得到:

       H--I--J--K--L   <-- feature/orphans, origin/feature/orphans
      /
...--G
      \
       I'  <-- temp-branch (HEAD)

然后我們必須用正確的哈希 ID 重復三個git cherry-pick命令。 這將復制JJ'然后KK'然后LL'

       H--I--J--K--L   <-- feature/orphans, origin/feature/orphans
      /
...--G
      \
       I'-J'-K'-L'  <-- temp-branch (HEAD)

一旦我們完成了所有git cherry-pick步驟,我們只需要告訴 Git:嘿 Git,強制名稱feature/orphans指向當前提交,這需要使用git branch -f 然后我們git switch feature/orphans重新開始:

       H--I--J--K--L   <-- origin/feature/orphans
      /
...--G
      \
       I'-J'-K'-L'  <-- feature/orphans (HEAD), temp-branch

然后我們可以完全刪除名稱temp-branch因為我們已經完成了它。

快捷方式

做這些單獨的步驟,創建一個新的,但臨時黨支部名稱,櫻桃采摘提交一個接一個,迫使老枝名字到位,切換舊的分支,並刪除臨時黨支部,在<插入一個很大的痛苦解剖部分在這里>。 我們不必這樣做。 我們有git rebase命令。

git rebase命令主要是執行上述操作的一種奇特方式,只有一個命令 因為這個命令做了很多事情,它有很多部分,我認為這就是你遇到 rebase 問題的地方。

你在這里有很多選擇——有很多方法可以運行git rebase但是我自己通常在這種情況下使用的一種叫做交互式 rebase 你像這樣運行它:

git switch feature/orphans     # if you're not already there
git rebase -i origin/develop

此處的名稱origin/develop是任何名稱(分支或其他名稱),用於選擇您希望提交去的位置 如果您願意,您可以使用原始哈希 ID ( git rebase -i 2505060 ),但我們想挑選出我一直稱之為“commit G ”的提交。 這是副本應該去的地方。

git rebase命令現在將通過列出您現在擁有的提交來計算要復制的提交,不包括那些可從提交G訪問的提交。 沒有深入研究這一切意味着什么,簡短的版本是這列出了提交HIJKL 這是一個太多的提交,但沒關系! 已經上市的這些承諾哈希ID,則-igit rebase -i手段現在你已經列出了提交復制,補字的說明書pick在每個哈希ID的前面。

因此,本說明書將閱讀:

pick fcf0c4a feat(CAT-172): Add new publisher
pick 9093c8d fix(cat-172): Change data format from object to array

對剩下的三個提交依此類推。 現在,由於-igit rebase在此說明表上打開您的編輯器。 您現在的工作是調整這些說明,然后將其寫出來並退出您的編輯器。 1在您的特定情況下,您的工作是更改或刪除提交H想要的提交)的pick命令。 如果您將其更改為dropd ,或者您只是刪除整行, git rebase畢竟不會復制提交H

一旦你寫出指令表, git rebase將繼續執行剩余的pick指令,為每個需要復制的提交運行git cherry-pick 這讓你得到I'-J'-K'-L'承諾。 然后git rebase通過將名稱feature/orphans移動到指向最終復制的提交L'

       H--I--J--K--L   <-- origin/feature/orphans
      /
...--G
      \
       I'-J'-K'-L'  <-- feature/orphans (HEAD)

您現在在您的存儲庫中擁有您想要的一組提交,但還有一件事要做。


1一些編輯器並沒有真正“退出”:他們需要與 Git 溝通他們已經完成了文件的編寫。 這是另一個可以成為絆腳石的地方。 但是 Git 已經有了git commit這個問題,如果你不使用-m標志,通常你不應該使用-m標志。 所以你應該已經解決了這個問題,如果你有這些棘手的編輯器之一。


您現在需要使用git push --force-with-lease

已將提交HIJKL發送到某個其他 Git 存儲庫,您使用名稱origin調用該存儲庫。 您讓其他 Git 存儲庫創建或更新分支名稱feature/orphans 你自己的 Git 通過將他們的feature/orphans你的origin/feature/orphans反映這一點。

您現在需要將I'-J'-K'-L'提交的另一個 Git 存儲庫發送給這個庫——這部分很簡單——然后說服他們應該放棄他們的HIJKL鏈,轉而支持你的新的和改進的I'-J'-K'-L'提交鏈。 這部分需要使用力推

一般來說,Git 真的很喜歡向分支添加新的提交 它不喜歡在最后放棄提交:這通常被認為是不好的或錯誤的。 所以你必須強迫他們的 Git 這樣做。

使用git push --force-with-lease origin feature/orphans ,你讓你的 Git 調用他們的 Git,給他們提交I'-J'-K'-L' ,然后發送以下形式的命令:

我認為您的feature/orphans擁有cf83304 如果是這樣,我命令您將提交L'的哈希 ID 填入其中。 讓我知道我是否正確並且你這樣做了。

他們要么找到正確的事情並服從,要么告訴你他們為什么不這樣做。

您可以使用更簡單的git push --force 這省略了一些安全檢查,通過向他們發送命令:

在您的feature/orphans填充提交L'的哈希 ID! 現在做! 我命令你!

如果出於某種原因,他們L之后選擇了一個更新的提交,這將刪除該提交。 如果您不知道它的哈希 ID,則永遠無法要求它。 通過使用“我認為......所以這樣做”的結構,如果你錯了,你可以在你讓他們放棄以后沒人能再次找到的提交之前看到到底發生了什么。

暫無
暫無

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

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