簡體   English   中英

git 重置兩個分支差異中的所有提交

[英]git reset all commits in diff of two branches

在與他人合作時,我通常的工作流程是創建一個功能分支,並讓雙方都提交對該分支的更改。 一起工作時,通常會有無意義的提交,例如“嘿,我做了這個小改動,你覺得呢?” 但是,在我們 PR 並將其合並回 master 之前,我會在我們開始工作之前將git reset重新設置為第一次提交,並將歷史分成合理且合乎邏輯的塊。

但是,這次某位同事並入了master,讓我們的歷史無法閱讀。 或者至少我現在無法git reset因為我們的提交不再是連續的。

我們的分支是 master 的超集,只有額外的提交。 我想刪除所有這些提交但留下工作,允許我將更改提交到邏輯塊中。

想法?

對於未來的人來說,我發現解決我的問題的最佳方法是修補 master 和我自己的差異,然后將更改應用到新分支,然后 PR。 這樣我就可以獲得所有的更改,但會稍微清理一下歷史記錄。

git diff --binary master my-branch > ../thing.patch

然后

git apply ../thing.patch

我知道我的問題有點不穩定,我的解決方案也是如此。 但是,希望這會在將來對某人有所幫助。

我發現您的描述比啟發性更令人困惑。 特別是,合並的存在(或缺乏)對git reset沒有障礙。 git reset所做的是更改當前分支指向的提交 ID。

每當您進行任何提交(甚至是合並)時,git 都會簡單地將的提交添加到您的存儲庫中。 每個提交都有自己的 SHA-1 ID,這是它的“真實名稱”(該提交具有該 ID,該 ID表示該提交,並且沒有其他人會使用該 ID)。 每個提交還包含其父級的 ID。 合並只包含至少兩個父 ID,而常規提交只有一個父 ID。 我們可以說這些父 ID“指向”父提交,因此我們可以繪制提交圖:

A <- B <- C          <-- master
           \
            D <- E   <-- feature

右側的名稱是分支標簽,git 通常1通過包含每個分支尖端的 SHA-1 ID 的文件來實現這些標簽。 也就是說, master的文件有C的 SHA-1,而feature的文件有E的 SHA-1,在這里。

在這張圖片中,我們應該添加HEAD ,它基本上只是 git 用來保存“當前分支”名稱的文件:

A <- B <- C          <-- master
           \
            D <- E   <-- HEAD=feature

git reset的作用現在可以非常簡單地描述為2 :“讀取 HEAD 以查看要更改的分支,然后將存儲在該分支文件中的 SHA-1 ID 更改為git reset命令行上給出的提交 ID。” 因此,如果HEADfeature ,而master說“commit C ”,那么:

git reset [options] master

只是告訴 git:“將C的 ID 寫入feature ”。 [options]部分使reset做得更多或更少,例如更新索引和/或工作樹,或跳過這些更新。)

(當您通過分支名稱或HEAD~2gitrevisions允許的任何名稱命名提交時,git 只會將其轉換為 commit-ID。要查看此操作,請使用git rev-parse ,它會將名稱轉換為提交 ID:

git rev-parse HEAD
git rev-parse master
git rev-parse feature^

等等。)

為了進行新的提交,git 將新的提交寫入存儲庫,將其 parent-ID 設置為當前 ( HEAD ) 提交的 ID (並使用暫存區“進入此提交的內容”)。 然后它將新提交的 ID 寫入當前分支文件,就大功告成了! 分支已被擴展,只需將新的提交 ID 寫入分支文件即可。

為了說明,讓我們向feature添加一個合並提交。 首先,我們需要向master添加更多提交,除非我們要合並另一個分支。 然后我們將添加一個合並提交M 我要停止畫箭頭了,只記得它們指向左側(可能是向左和向上等)。

A - B - C - F - G       <-- master
          \       \
            D - E - M   <-- HEAD=feature

在這里,提交M是一個合並提交,因為它有兩個父級: EG feature分支文件包含M的 ID。 但是git reset的工作方式和往常一樣:如果我們告訴feature指向,比如說,提交D ,提交EM變得不可見(盡管它們仍然在那里),我們看到的是:

A - B - C - F - G       <-- master
          \
            D           <-- HEAD=feature

默認情況下, EM的幽靈版本至少會存在一個月,保存在 git 的“ref-logs”中(如果你使用git refloggit log -g ,你實際上可以看到它們)。 但是由於我們所做的git reset ,它們不再“在” feature分支上,該分支現在以提交D結束。

概括

  • 每個提交都指向其父級。
  • 合並提交指向多個父級。
  • git 分支名稱只是指向分支的頂端,即應被視為“在分支上”的最后一次提交。 該 ID 只是存儲在適當的分支文件中。
  • HEAD告訴 git 你在哪個分支上。
  • 新提交執行git rev-parse HEAD以獲取其父 ID,然后將其新提交 ID 寫入分支文件。 新的合並提交做同樣的事情,除了它們記錄HEAD ID合並分支的 ID。
  • git reset的(現有的)提交 ID 寫入當前分支。 使用--hard它還更新索引/暫存區域和工作樹。 (使用--mixed它只做分支和索引,而使用--soft它只做分支。)

請注意,如果您使用git resetfeature回退到 C,然后開始執行新的git commit ,您只需像以前一樣添加新的提交。 它們與您之前添加的提交具有不同的 ID,因此圖表如下所示:

A - B - C           <-- master
        | \
         \  D - E   <-- [old feature, via reflog]
          \
            X - Y   <-- feature

(我跳到X以明確這些與上面的FG等不同。)


1更具體地說,標簽可以“打包”,因此一堆標簽都共享一個文件。

2可能有點太簡單了,因為git reset還可以選擇更新索引/暫存區和/或工作樹。

你的工作流程很糟糕 :) 而不是重置和重做工作(我不知道你是如何“清理”歷史的)你應該使用rebase命令。

其中的一部分,要恢復合並,您只需將頭部重置為功能分支的最后一次提交。 因此,獲取最后一次提交的 sha1(不是合並提交的 id,而是前一個提交的 id)並執行

git reset --hard shaidofthecommit

此時,您的分支負責人將重置為該提交,您將丟棄合並提交。

我稍微改變了@adammenges 給出的解決方案——因為對我沒有用——。

  • 首先,運行git diff --binary master my-branch > ./changes.patch ## this will create a file called changes.patch with the diff between branches
  • 之后,運行git apply --reverse ./changes.patch ## this will make the reverse changes of the diff

這對我有用:)

暫無
暫無

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

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