簡體   English   中英

Git從master重置分支但保留分支提交

[英]Git reset branch from master but preserve branch commit

我不確定是否會問這個問題,因為關於github的場景太多了。

所以我的情況如下:

  1. 我將一個主分支克隆到我的本地工作目錄
  2. 我將新文件添加到本地工作目錄中
  3. 我承諾改變
  4. 然后我做了一個git checkout -b new_branch
  5. 然后我將本地工作目錄推送到new_branch
  6. 主分支有最新版本的問題。 需要回滾到舊版本。

問題:如何進行git reset -hard以便我本地目錄中的所有文件都與master中的先前發行版本相同但保留我的工作? 例如創建新文件。

我嘗試使用git reset來發布SHA,但它保留了本地工作目錄中最新版本的文件。

我試過git reset --hard,它刪除了所有內容以及我自己的工作。

希望這個解釋足夠清楚,可以為您提供幫助。

謝謝。

UPDATE

更清楚地了解我的場景:我將帶有v2標簽的repo master克隆到我的電腦上。 我創建了一個新的測試分支。 我添加新文件並提交然后推送到測試分支。 現在,我的高級開發人員告訴我,帶有v2標簽的主人有問題,請我不要使用。 所以我的測試分支需要使用v1標記回退到master。

如何在不刪除我提交的文件但刪除由master v2標簽創建的文件的情況下執行此操作?

TL; DR

你想要git rebase ,你必須通過強制推進來跟進。

很長,使用更新的問題:

我將帶有v2標簽的repo master克隆到我的電腦[使用git clone <url> ]。 我創建了一個新的測試分支。

我們假設您這樣做:

git checkout -b test v2

其中v2是有問題的標簽。 在Git中,像master這樣的分支名稱和像v2這樣的標記名稱之間存在差異。 差異實際上非常小,並且有兩個非常相關的部分。

  • 分支名稱標識一個特定的提交。 但隨着時間的推移會改變哪些 該名稱標識了我們想要說的“在分支上”的最新最后一次提交。 實際上,在您運行git checkout <branch-name>並開始進行新提交后,它會自動更改。

  • 標簽名稱標識一個特定的提交。 它永遠不應該識別任何其他提交 - 它應該永遠保留提交的名稱。 (可以移動標簽 - 任何人都可以這樣做 - 但為了保持一個人的理智,就是不要這樣做。)而且,在git checkout <tag-name> ,你處於一個稍微奇怪的狀態,即Git調用一個獨立的HEAD

所有這一切的原因是Git真的不是關於分支 ,而是關於提交 提交是Git中的基本單元。 每個提交都有一個很難看的哈希ID,對於那個特定的提交是唯一的。 這些哈希ID實際上是提交的“真實名稱”。 沒有提交可以改變 - 不是一個比特 - 因為哈希ID實際上是提交內容的加密校驗和。 如果你能以某種方式改變內容,那將改變哈希ID,這意味着你有一個新的,不同的提交。

這種加密校驗和的原因也是為什么提交ID看起來如此荒謬,對人類來說幾乎沒用。 它們對人類無用的事實是我們為它們需要名稱的原因,因此我們為什么有分支和標簽名稱。

每個提交都存儲了許多內容,例如提交者的名稱和電子郵件地址(以及時間戳),要顯示的git log的日志消息,以及每個源文件的完整和完整快照。 每個提交存儲的事情之一是其父提交的原始哈希ID。 這些形成了一個向后看的鏈:

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

(字母代表實際的哈希ID)。

給定提交H的哈希ID,Git可以找到實際的提交本身。 該提交包含提交G的哈希ID,因此Git可以找到G 找到G ,Git獲取F的哈希ID,以便它可以找到F ,依此類推。 所以Git一直在向后工作,從最新的和可追溯的時間開始。

由於提交中的任何內容都無法更改,因此我們可以將其繪制為線性序列:

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

只要我們記得很容易倒退而不是前進

進行新提交只需要:

  • 選擇一個分支名稱,該名稱也選擇其最后一次提交:

     ...--F--G--H <-- branch-name (HEAD) 

    選擇分支會將特殊名稱HEAD附加到名稱,因此現在HEAD同時與branch-name和commit H同義。

  • 做一些工作並使用git add將文件復制回Git的索引 (也稱為暫存區域 ,或者有時是緩存 ,具體取決於誰在進行此調用)。

    請注意,如果您對現有文件使用git add ,則它們已經被復制到索引中已經存在的文件頂部的索引中,從而替換了一些舊版本。 如果您對文件使用git add ,它們已被復制到索引中,但它們只是新文件 - 它們不會覆蓋任何以前的索引副本。

  • 運行git commit 這會將索引副本凍結為已提交的版本。 它還會收集您的日志消息,並將您作為此新提交的作者和提交者。 此新提交的父級當前提交H

     ...--F--G--H <-- branch-name (HEAD) \\ I 

    而且,既然新的提交存在(因此已經獲得了自己的新的唯一哈希ID),Git只是將其哈希ID寫入分支名稱,因此branch-name現在指向提交I

     ...--F--G--H \\ I <-- branch-name (HEAD) 

所以,如果你做了git checkout -b test v2 ,那么你的存儲庫中就有這樣的東西。 請注意,名稱v1標識了其他一些(不同的,可能是更早的)提交,所以讓我們全部繪制它們:

...--F   <-- tag: v1
      \
       G--H   <-- tag: v2, test (HEAD)

請注意, git checkout -b name commit-ID設置了一些內容,以便新name指向所選的commit commit-ID ,然后通過從該提交填寫索引和工作樹使其成為當前名稱,並附加特殊名稱HEAD一個命令中的新name

我添加新文件並提交

好的,所以你在工作樹中創建了一個新文件(你在哪里工作)並運行git add newfile 這種復制newfile到你的索引有一個索引,僅僅在這一個去工作樹,然后你跑git commit作出新的承諾I

...--F   <-- tag: v1
      \
       G--H   <-- tag: v2
           \
            I   <-- test (HEAD)

然后推送測試分支。

所以此時你跑了:

git push -u origin test

這會將I自己的提交發送給Git在名稱origin下記住的URL的其他Git。 請注意,那里有一個完全獨立的Git存儲庫! 它有自己的分支和標簽,但是因為標簽永遠不會移動 - 或者永遠不應該移動 - 你的標簽和它們的標簽應該都同意哪個提交散列ID v1v2指向。

派出提交I ,你的Git然后問自己的Git來設置或在這種情況下創建 -their自己的分公司名稱test ,指着犯I 據推測,這對他們的Git來說都很好,而不是origin ,所以就這樣做了。

現在,我的高級開發人員告訴我,帶有v2標簽的主人有問題,請我不要使用。 所以我的測試分支需要使用v1標記回退到master。

現有的任何承諾不能改變任何部分。 這意味着提交I被困在原地。 如果你做了一些提交,他們被卡住了:

...--F   <-- tag: v1
      \
       G--H   <-- tag: v2
           \
            I--J--K   <-- test (HEAD)

你需要的是一系列新的提交, 就像 IJK 一樣 ,但在某些方面有所不同。 特別是,您希望將新文件添加到F的快照,如標記v1所指向的那樣。 然后Git應該提交該快照,重新使用來自commit I提交消息,用一個新的和不同的哈希ID進行閃亮的新提交,我們稱之為I' ,表明它是I的副本:

       I'   <-- [somehow remembered as in-progress]
      /
...--F   <-- tag: v1
      \
       G--H   <-- tag: v2
           \
            I--J--K   <-- test

成功將I復制到I' ,您現在希望Git以相同的方式將J復制到J' ,然后將K復制到K'

       I'-J'-K'  <-- [somehow remembered as in-progress]
      /
...--F   <-- tag: v1
      \
       G--H   <-- tag: v2
           \
            I--J--K   <-- test

最后,你希望你的Git將你的test標簽從提交K剝離並將其粘貼到提交K' ,然后照常返回你的分支test ,除非現在這意味着提交K'

       I'-J'-K'  <-- test (HEAD)
      /
...--F   <-- tag: v1
      \
       G--H   <-- tag: v2
           \
            I--J--K   [abandoned]

一旦所有已經發生的事情,你需要發送新的提交I'-J'-K'其他的蠢貨origin ,並告訴origin的Git的把他們的 test指向K'了。

git rebase為你做了第一部分

首先,你應該運行:

git checkout test
git status         # and make sure everything is committed!

如果一切看起來都不錯,那么你只需要:

git rebase --onto v1 v2

這個命令告訴Git: 復制一些提交。 要復制的提交是“on” - 技術上可以從我當前的分支到達,減去任何也在“ v2 ”上的“提交”。 放置它們的地方是在由名稱v1標識的提交之后。

  • 名稱v2名稱提交H ,其名稱提交G ,依此類推。 所以這些提交不會被復制。
  • 當前分支的名稱, test ,提交姓名K ,這名J這名字I這名H ,所以因此IJK是那些復制。
  • --onto v1告訴Git將副本放在何處:提交F ,由v1命名。

在復制過程結束時,Git將標簽test (您當前的分支)拉出提交K並使其指向副本K'

git push現在需要--force

由於您已將commit I發送給origin因此它們具有:

...--F   <-- tag: v1
      \
       G--H   <-- tag: v2
           \
            I   <-- test

作為他們的提交集及其分支和標簽名稱。 當然,當然,如果你從那時起發送了JK ,他們的test點就會提交K 現在我們可以假設他們指向I並且他們沒有 JK ,因為如果他們確實有他們和他們的test點到他們的K副本

請注意,散列ID和底層提交本身在每個 Git存儲庫中都是相同的 這就是散列ID是內容的加密校驗和的原因! 只要您和它們具有相同的內容,您就具有相同的提交,因此它具有相同的哈希ID。 如果您有不同的哈希ID,則具有不同的內容和不同的提交。 我們需要知道的是:你有這個哈希ID嗎?

如果您沒有濫用標記名稱,那么在所有Git存儲庫中它們也是相同的:相同的名稱標識相同的提交。 但是, 分支名稱的目的不同,因為Git是為了不斷添加提交而構建的。

所以現在你已經運行了git rebase放棄了三個舊提交IJK ,現在你將再次運行git push origin test 這將發送他們I'-J'-K' J' I'-J'-K'你的新提交,你有他們沒有,你的Git和他們的Git可以通過哈希ID單獨告訴 - 然后讓他們從任何地方移動他們的測試現在在他們的存儲庫中指向I或者可能是K 你要求他們把它移到指向K'

他們會說沒有 他們會說不的原因是他們的Git會看到將他們的 testIK (無論是現在設置到現在)轉移到K'是否會放棄提交Iif they have those). So instead of politely asking the other Git, the one at J和K if they have those). So instead of politely asking the other Git, the one at if they have those). So instead of politely asking the other Git, the one at原點的if they have those). So instead of politely asking the other Git, the one at , to please if it's OK update測試to point to K',那么你需要這樣做:

git push --force origin test

告訴他們: 移動你的test指向K' ,即使它放棄了一些提交!

(他們仍然可以說“不”,但一般來說,如果你告訴他們強迫你的分支,他們應該允許它。)

暫無
暫無

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

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