簡體   English   中英

使用 git checkout 切換回之前的提交

[英]Switch back to a previous commit using git checkout

我必須在不覆蓋到目前為止所做的提交的情況下將 master 回滾到以前的狀態。 創建 master 和 rollback master 的新分支的最佳方法是什么。 /| merge branch A into master (commit id a.into.m) | | commit (id a2) branch A | | commit (id 3) branch master | | commit (id a1) branch A \\| new branch A commit (id a0) | commit 2 master (id m2) | commit 1 master (id m1) /| merge branch A into master (commit id a.into.m) | | commit (id a2) branch A | | commit (id 3) branch master | | commit (id a1) branch A \\| new branch A commit (id a0) | commit 2 master (id m2) | commit 1 master (id m1)我正在嘗試創建它的多個分支並將 master 重置為以前的狀態。 來自提交的分支 new_calculator (id 3) 來自提交的分支 new_form (id a2) 分支 rollback_master 提交 (id m1)

我為所有分支都試過這個git checkout m1 git checkout -b rollback_master git push origin rollback_master

當我將 rollback_master 拉入 master 時,我看不到任何變化。 沒什么好合並的。 我也試過在 rollback_master 分支上git reset --hard m1沒什么可提交的。

為了將您的 master 恢復到較舊的提交/分支,您需要git reset

如果您想將例如分支current重置為older_branch ,請執行

  1. 切換到current分支: git checkout current (可能在克隆你的 repo 或隱藏未提交的更改之后)
  2. 可選如果要保留currentolder_branch之間的所有提交,請創建一個新分支作為備份: git branch backup
  3. 將現在檢出的分支current git reset older_brancholder_branchgit reset older_branch

這樣做,您簽出的文件不會被更改,因此您結束的狀態將是您current分支已重置為older_branch ,但您簽出的文件仍然是重置前的current版本。 您可能還想用older_branch文件替換已檢出的文件。 為此,請執行git reset --hard

順便說一句:您基本上可以重置為任何內容,它不必是分支名稱 - 您也可以使用提交的哈希代替。

但是,如果您處理遠程存儲庫並且您已經將current信息推送到遠程存儲庫,這將變得更加棘手 - 由於與其他貢獻者的潛在沖突,許多遠程存儲庫將不允許修改現有歷史記錄。 還有另一種選擇,雖然:您可以讓GIT中創建一個提交恢復舊的提交,因此,歷史將不會改變,但你還是結了current包含的確切狀態older_branch 這個恢復提交可以安全地推送到遠程。 查看git revert

Git 並不是真正關於分支 這真的是關於commits 分支名稱只是我們(和 Git)用來查找提交的標簽。

您繪制的日志輸出:

 /| merge branch A into master (commit id a.into.m)
| | commit (id a2) branch A
| | commit (id 3) branch master
| | commit (id a1) branch A
 \| new branch A commit (id a0)
  | commit 2 master (id m2)
  | commit 1 master (id m1)

有點接近git log --decorate --oneline --graph繪制的 ASCII 藝術圖,但缺少用於標記提交的小星號:

*   4291fbf Merge dotfiles installer
|\  
| * 4f5b84b dotfiles: add --tar option
| * 8a45ff6 dotfiles: refuse to install over special files
| * e750c89 dotfiles: reshuffle work-to-do code
| * 36aa02c dotfiles: s/--install-to/--homedir/
| * 93c597c dotfiles: simplify FileInfo
| * 4771f69 dotfiles: manipulate home dir .foorc etc
|/  
* 6c9a43a add prototype Dot-files

請注意,每個提交也有一個唯一的哈希 ID(此處已縮短)。 這些哈希 ID 是提交的真實名稱。

分支名稱僅充當指針,指向這些提交。 對於 StackOverflow 帖子,我喜歡橫向而不是縱向地繪制這些東西。 我也喜歡給他們一個大寫字母,而不是大而難看的哈希 ID。 結果是:

...--F--G--H   <-- branch1
            \
             I--J   <-- branch2

這里的想法是每個分支名稱都指向一個提交。 提交本身也指向(向后)一步,因此J指向II指向HH指向G ,依此類推。

Git 中“在”分支上的提交是您可以通過從分支名稱開始,跟隨其箭頭指向其提交,然后從該提交向后工作來獲得的提交。 因此,從某種意義上說,這些提交包含在分支中更好,因為通過H提交在或包含在兩個分支中,而提交IJ當前僅包含branch2

現在,有關這些提交和分支名稱的信息包括:

  • 任何提交都不能更改。 最多,您可以將一些現有的提交復制到一個新的和改進的版本中,這將獲得一個新的和不同的哈希 ID。

  • 正如我們剛剛所說的,包含在某個分支中的一組提交是通過從分支指向的地方開始,然后從一個提交到另一個提交,向后一步一步地獲得。 名稱本身只包含我們所說的應該是分支中最后一次提交的任何提交的提交哈希 ID。

  • 分支名稱因此移動 他們的正常進程是向前的:我們從一些提交開始,就像上面的那些,然后添加一個新的提交。 例如,如果我們git checkout branch1 ,我們會得到提交H 如果我們進行新的提交,它會獲得一個新的 ID,我們將其稱為KIJ正在使用中):

     ...--F--G--H---K <-- branch1 (HEAD) \\ I--J <-- branch2

    (我們可以使用單詞HEAD ,將它附加到這樣的某個分支上,以記住我們在哪個分支上。)

  • 正如我們剛剛看到的,正常的運動是附加。 讓我們將branch2合並到branch1 不用擔心 Git 如何做到這一點,讓我們繪制結果:

     ...--F--G--H---K--L <-- branch1 (HEAD) \\ / I--J <-- branch2

    新提交L是一個合並提交,它有兩個父級(或兩個以上,但我們不會在這里看到)。

回到你的問題

由於分支名稱僅指向特定的提交,我們可以添加更多名稱和/或強制名稱向通常不會移動的方向移動:

 ...--F--G--H---K--L   <-- branch1 (HEAD), master
             \    /
              I--J   <-- branch2

讓我們強制名稱master指向提交K而不是L

$ git branch -f master <hash-of-K>

並得出結果:

               K     <-- master
              / \_
             /    \
 ...--F--G--H------L   <-- branch1 (HEAD)
             \    /
              I--J   <-- branch2

提交方面實際上沒有任何變化,但標簽已經移動。

master朝着“錯誤”的方向前進。 如果我們將我們的 Git 連接到其他一些 Git,並且他們的master指向提交L ,並且我們要求他們像我們的一樣移動他們的 master 指向提交K ,他們通常會說no 他們會說不的原因是這個分支標簽的移動方向錯誤。 對此的 Git 術語是它不是快進.

我們可以使用git push --force命令他們像這樣移動他們的master 他們可能會也可能不會服從。 我們或他們的存儲庫的其他克隆也可能有一個master指向“提前”提交K ,例如,指向L ,我們必須說服他們以相同的方式移動。

所以:

我正在嘗試創建它的多個分支並將 master 重置為以前的狀態。 來自提交的分支 new_calculator (id 3) 來自提交的分支 new_form (id a2) 分支 rollback_master 提交 (id m1)

可以在您自己的 Git 存儲庫中執行此操作,您可以在其中控制一切。 只需更改您的標簽並根據需要制作新標簽。 所有現有的提交都保持原樣; 只有標簽移動。 有關如何執行此操作的更多詳細信息,請參閱Michael Beer 的回答(使用git reset和/或git branch -f )。

您不一定能夠更改其他人的Git 存儲庫。 這包括您放在 GitHub 上的您自己的 Git 存儲庫的任何副本。 畢竟,那是 GitHub 的存儲庫,而不是你的:他們只是讓你控制它,由他們決定給你多少控制權。

如果您擁有它以便 GitHub 給您完全控制權,並且沒有其他人擁有您需要擔心的 Git 存儲庫,您可以git push --force將您的更新更新到您的 GitHub 存儲庫(又名“他們持有的 GitHub 存儲庫”)為你”)。

但是,如果沒有,您通常應該做的是向存儲庫添加提交,以便新提交以這種“向前移動”的方式擴展現有的提交鏈。 新的提交也只是記錄了事物的新狀態。 各地的 Git 都明白向前推進的概念,而新的提交又引用了現有的提交,所以所有以前提交的文件仍然在以前提交的版本中。

當我將 rollback_master 的 pull request 拉入 master 時......

拉取請求不是 Git 的一部分。 它們是由 GitHub 等服務提供的擴展。

當您發出拉取請求時,您將標簽(實際標簽取決於服務)附加到服務器上存在的某個存儲庫中的某個提交。 與所有提交一樣,該提交具有一些哈希 ID。 然后,服務器將拉取請求(帶有內部附加標簽)傳遞給他們管理其他存儲庫的任何人 - 您從中分叉的那個人。 (服務器已在某處記錄了該存儲庫。在某些情況下,您選擇此存儲庫作為“其他”存儲庫;那么此人就是擁有此存儲庫的人。)該人獲得一個用戶界面,允許他們:

  • 在 GitHub 服務器上運行稍微修改過的git merge ,或者
  • 在 GitHub 服務器上運行稍微修改過的git rebase ,或者
  • 在 GitHub 服務器上運行稍微修改過的git merge --squash

這些步驟都不包括git push --force “以任意方式移動標簽”操作。 所有這些通常或專門創建一個新的提交,添加到現有的提交。 因此,通過拉取請求完成的所有操作必然與將標簽向后移動的git push --force類型非常不同。

換句話說,如果你沿着那條路走,“你不能從這里到達那里”。 :-)

暫無
暫無

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

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