簡體   English   中英

如何在另一個分支中獲取一個分支的更改

[英]How to get changes of one branch in another branch

我正在開發作為父分支的開發分支,並在分支 a1 上創建了一些東西,添加了完全獨立的新功能並僅推送到 a1。 現在我有另一個任務創建了一個 bew 分支 b1 但是一旦我切換到 dev 我沒有得到 a1 的代碼所以如果我創建一個新的分支 b1,我需要 a1 中存在的一些文件或者它們都很好。 如何做到這一點?

  1. 我應該結帳到 dev 並提取 a1 並將 a1 合並到 dev 然后結帳 yo b1 並且更改會在那里嗎?
  2. 我應該結帳到 b1 並拉取 a1 然后合並它。 這是正確的方法嗎,如果不是,請提出建議。 它是什么?

還有什么時候合並真正起作用?

如果我理解正確,您需要在分支 a1 中更改分支 b1 嗎? 如果是這樣,您需要對 b1 -> a1 進行合並請求(或將 b1 拉入 a1 並直接合並)。 完成 a1 上的所有工作后,您可以將所有更改從 a1 合並回 dev 分支。

(旁注:你用標記了你的問題。不要那樣做。選擇一個或另一個——無論你使用的是哪一個——如果它適用的話。一旦你 go 發出拉取請求或合並請求。)

我正在開發作為父分支的開發分支......

你從一個糟糕的(不正確的)假設開始。 在 Git 中沒有這樣的東西,作為父分支 實際上,分支這個詞一開始並沒有任何特定的含義(請參閱“分支”到底是什么意思? ),但無論您選擇哪種含義,它們都沒有使用修飾語“父母”。

Git 擁有並且是關於提交的內容。 提交確實有父提交,所以你在那里狀態很好。 不過,Git 中的分支名稱只是選擇了一個特定的提交。

...並在分支 a1 上創建了一些東西,添加了完全獨立的新功能

我相信你的意思是:

  1. 您有一系列以當前可區分的提交結束的提交。 這個特殊的(目前)提交的名稱為dev選擇它。

  2. 您創建了一個新的分支名稱a1 ,它標識了與dev相同的提交。

  3. 然后,您在“打開”分支a1時創建了一個或多個新提交。 這些新提交實現了您想要的功能。

並只推到 a1。

事情在這里變得更加復雜。 這些名稱 — deva1在您自己的存儲庫中是本地的 它們只您自己的存儲庫中有意義。

他們標識的提交具有通用唯一標識符 (UUID)。 這些名稱在每個Git 存儲庫中都有效。 Git 將這些 UUID稱為 hash ID ,或更正式地稱為 object ID或 OID。

當您使用git push時,您 select 您自己的存儲庫中的一些提交(或一組提交)您希望移交給其他一些 Git 存儲庫。 您將這些提交發送另一個存儲庫,然后讓他們的Git 軟件在他們的存儲庫中設置一個名稱(例如分支或標簽名稱),以通過該名稱找到此提交。 您在他們的存儲庫中設置了一個名稱,因為沒有人喜歡輸入 UUID。 它們對人類來說太大太丑了。

人類(作為人類)在另一個存儲庫中使用他們在自己的存儲庫中使用的相同名稱是很自然的。 對於人類來說,重要的是要意識到這就是您正在做的事情:另一個Git 存儲庫中的名稱a1不同的名稱,即使您自己的a1和他們的a1都拼寫為a1 這里真正通用的一件事——一直在所有存儲庫中都有效——是原始 hash ID。 但是,通過在您的存儲庫和某個遙遠的其他存儲庫中設置相同的簡短、人類可讀的名稱,您可以假裝名稱a1是通用的。 不是——它只在有限的時間內有效,與 hash ID 不同。

暫時這些都是一回事,但請記住以后再說。 讓我們繼續您的直接問題:

現在我有另一個任務創建了一個新的分支 b1 但是一旦我切換到 dev 我沒有得到 a1 的代碼

你在這里混淆了三個不同的名字 是時候停下來,存儲庫中正在發生的事情有一個良好的、扎實的心理畫面。 (請記住,這是您的存儲庫。它不是其他任何人的存儲庫,而是的。)

每個 Git 提交都有一個唯一的hash ID ,例如e188ec3a735ae52a0d0d3c22f9df6b29fa613b1e If someone gives you such a hash ID, you either have that commit with that hash ID , or you have no Git object at all with that hash ID. This is how one Git repository can give copies of stuff to another Git repository when needed: the sending Git tells the receiving Git some hash IDs, and the receiving Git tells the sending Git which of those hash IDs it needs. 其余的一切——盡管還有更多——只是優化。

但是:提交中到底是什么? 要找出答案,我們可以看一個:

$ git cat-file -p e188ec3a735ae52a0d0d3c22f9df6b29fa613b1e | sed 's/@/ /'
tree 23abcae16bcca7bf541eb62d2a0b2c1ae7f5a2a9
parent 21dd13e025aaada474fae6014f1b14799e37bedf
parent a0feb8611d4c0b2b5d954efe4e98207f62223436
author Junio C Hamano <gitster pobox.com> 1663097028 -0700
committer Junio C Hamano <gitster pobox.com> 1663097028 -0700

Sync with 'maint'

(如果您點擊此提交的 GitHub 副本的鏈接,則將上述內容與您看到的內容進行比較)。

這就是這一次提交的全部內容。 它僅包含元數據:有關此特定提交的信息,例如 Junio Hamano 所做的事實。 但請注意tree線:每個提交都需要恰好具有這些tree線之一,並且該行給出了與此特定提交相關的所有文件的完整快照的原始 hash ID。

所以一個提交——如果你的 Git 有它——提供了tree object,它提供了那個提交的快照 這些是您使用git switchgit checkout出到select后將看到的所有文件。

(這tree很大:

$ git cat-file -p 23abcae16bcca7bf541eb62d2a0b2c1ae7f5a2a9 | head
100644 blob 4860bebd32f8d3f34c2382f097ac50c0b972d3a0    .cirrus.yml
100644 blob c592dda681fecfaa6bf64fb3f539eafaf4123ed8    .clang-format
100644 blob f9d819623d832113014dd5d5366e8ee44ac9666a    .editorconfig
100644 blob b0044cf272fec9b987e99c600d6a95bc357261c3    .gitattributes
040000 tree 700f96ea68cfa31767d24659c4981083f65fd276    .github
100644 blob 80b530bbed2c80814ac74956d329d277d85bba86    .gitignore
100644 blob cbeebdab7a5e2c6afec338c3534930f569c90f63    .gitmodules
100644 blob 07db36a9bb949c4c911d07baeb1c4c10c13bec4c    .mailmap
100644 blob 5ba86d68459e61f87dae1332c7f2402860b4280c    .tsan-suppressions
100644 blob 0215b1fd4c05e668f37973549384aae24dcc65cf    CODE_OF_CONDUCT.md

這持續了將近 500 行:在這棵樹 object 中有 496 個頂級條目,其中包含更多的樹對象,因此快照總共包含大約 4200 個文件。 但是,除了其中一個文件之外,所有這些文件實際上都是從以前的提交中重復使用的,因此提交本身只需要幾百個字節,盡管有 4200 個文件!)

所以,這就是提交的意義和作用:

  • 它是一個編號實體(使用唯一的hash ID作為其編號);
  • 它包含元數據,例如誰制作了它以及何時制作的;
  • 它擁有每個文件的完整快照,但以一種間接且節省空間的方式。

但是,元數據不僅僅是由誰(以及何時)制作的:請參閱上面的parent行。 這個特定的提交,在 Git 本身的 Git 存儲庫中,是具有兩個父級的合並提交 合並提交有點不尋常:大多數提交都是普通提交,只有一個父提交。

看到parent之后的大丑數字。 你能猜出這是什么? 您可能可以:這是另一個 hash ID。 此父編號存儲在每個提交的元數據中,提供 Git 中的父/子關系。 這些關系是在提交之間,而不是在分支之間。

如上所述,分支名稱只是指定一個特定提交的一種方式。 這是一個我們(人類)因為一些奇怪的、人類類型的原因而特別感興趣的提交。 在這種特殊情況下,這很有趣,因為它是最新的提交。

事實上,分支名稱的定義,在 Git 中,是一個包含一個提交 hash ID 的名稱,該提交就是分支的提示提交 分支的提示提交是該分支的最新提交。 這個屬性——作為最新的提交——實際上並不是提交本身的一部分:它只是一個臨時的東西:那個提交是最新的,直到我們說另一個提交是最新的。

讓我們看一個例子

例如,讓我們以您自己的分支名稱deva1為例。 在某些時候,有一些帶有 hash ID 的提交,這是您自己的dev上的最新提交。 我不知道該提交的 hash ID 是什么(是a123456...b987654...還是deadcabbeeffad或什么?)所以我只是將其稱為提交H ,對於 Hash。 我會這樣畫它,目前還沒有明顯的原因:

            <-H

H伸出的那個小箭頭指向左(向后),表示提交H的元數據中的parent行。 parent行包含一些較早提交的 hash ID。 我也不知道之前提交的 hash ID,所以我將其稱為G ,即H之前的字母,然后我將把該提交畫在:

        <-G <-H

提交G當然也是一個普通的提交,所以它向后指向一些更早的提交,所以我現在把它畫出來:

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

這里的每個提交都有元數據和快照,通過從您的dev分支上的最新提交開始,我可以讓 Git 向后工作並找到也在您的dev分支上的每個較早的提交 你可以很容易地做同樣的事情:例如,只需運行git log dev ,或git switch dev然后git log 無論哪種方式, Git 都會向您顯示H的元數據,然后后退一步提交G並向您顯示G的名稱和 email 地址和日志消息。 然后git log將后退一步以提交F並向您顯示提交的元數據,然后后退一步,依此類推,永遠 - 或者更確切地說,直到它向后用完步驟。

要讓這一切發生,我們需要做的就是知道提交H的 hash ID。 但是我不知道提交H的 hash ID,那我該怎么辦? 我要做的是使用你告訴我的名字dev 通過使用您的dev人員,我可以找到 hash ID H

我們說您的dev人員指向提交H ,我現在可以畫出來:

...--G--H   <-- dev

我(故意)變得懶惰並且不費心將從提交到父級的連接繪制為這里的箭頭,但我確實將伸出分支名稱的箭頭繪制為箭頭 - 一個更長的箭頭 - 因為這些箭頭move

當您創建一個新名稱a1時,您將名稱命名為a1 select commit H ,就像您的名稱dev選擇 commit H一樣:

...--G--H   <-- a1, dev

所有這些提交現在都在兩個分支上,而不僅僅是一個。 提交Ha1dev最新提交。 H本身內部沒有任何變化:它的快照和元數據與往常一樣。 改變的是我們添加了另一個名稱a1 ,並將其箭頭指向H

If we now use git switch or git checkout to select the name a1 , this tells Git that we'd like all the files from commit H . 名稱a1指向H ,所以這就是 Git 得到我們想要H文件的想法的地方。 為了表明我們“在”分支a1上,我將它畫成這樣:

...--G--H   <-- a1 (HEAD), dev

您現在對一些文件和git addgit commit進行一些更改以進行新的提交 這個新的提交獲得了一個新的、隨機的(但唯一的,實際上根本不是隨機的)hash ID,沒有 Git 軟件可以再次用於任何其他提交。 1我將這里稱為“commit I ”,現在將其繪制:

...--G--H   <-- dev
         \
          I   <-- a1 (HEAD)

名稱a1現在指向提交I 其他名稱沒有改變; 沒有提交改變; 2但名稱a1現在選擇提交I並且提交I因此是分支a1上的最后一個提交。

這是根據定義! 如果我們運行git reset --hard HEAD^丟棄提交I ,會發生什么是H存儲a1 ID H回到名稱分支a1中,所以提交的提交實際上沒有任何事情發生在I自己身上。 只是如果我們沒有找到它的名稱,我們將不得不自己想出原始的 hash ID。 快,Git 存儲庫中用於 Git 的 hash ID 是什么。 你已經記住了,不是嗎? 好吧,沒有。 如果沒有找到它的名稱,我無法告訴您 hash ID 是什么。

所以這就是我們有分支名稱的原因:找到該分支“上”的最后一次提交。 這就是提交的含義:它是元數據——提交的時間、日志消息,對於 Git、其父級或父級至關重要——加上一個快照,可以永久保存所有文件,或者至少與提交本身一樣長繼續存在。 3


1 Git 的這一部分是非常神奇的,或者至少是數學上的,而且也有嚴重的缺陷,因為這個技巧一天會停止工作。 但只要那一天在我們都死去很久之前不會發生,沒有人會擔心太多。

2 Git 使用的神奇編號方案(提交內容的加密 hash)要求任何提交的任何部分都不得更改。

3當我們使用git reset或類似方法從某個分支的末尾彈出一些提交時,這些提交會繼續存在一段時間 在 GitHub 上,該時間量是“永遠”的,但在本地存儲庫中,我們通常允許 Git 在 30 到 90 天左右后清除未使用的提交。


回到你的基本問題

但是一旦我切換到開發,我就沒有得到 a1 的代碼

Git 存儲庫中的名稱dev表示“我的dev分支上的最新提交”。 該提交有一個快照,這就是您在git switch dev時獲得的快照。

如果您不想要該快照,則只有兩個選擇:

  • 不要使用名稱dev ,或
  • 使名稱dev select 成為不同的提交。

我們使用這些名稱來查找感興趣的特定提交,並且您可能希望保留自己的dev人員,以便它仍然可以找到提交H (無論H的真實 hash ID 是什么)。 如果是這種情況,您不想使用第二個選項,只留下第一個選項:使用不同的名稱。

現在,您說要從最新a1快照中的文件開始 這樣做的方法是使用名稱a1創建新分支,以便新分支選擇相同的 commit ,如下所示:

...--G--H   <-- dev
         \
          I   <-- a1 (HEAD), b1

您現在可以git switch b1並且您將使用所有 commit I的文件從 commit I切換到... commit I ,使用所有 commit I的文件。 這根本沒有改變,Git 將巧妙地不改變任何文件。 4您現在可以自己更改文件,然后像往常一樣git addgit commit

...--G--H   <-- dev
         \
          I   <-- a1
           \
            J--K   <-- b1 (HEAD)

例如(一旦你又做了兩次提交)。


4如果您忘記先切換分支,您會發現在開始工作,您可以利用 Git 的這種聰明來創建新的分支。 例如,如果你不小心繼續分支a1 ——甚至做了一些新的提交——你可以創建新的分支b1指向當前提交,然后使用git resetgit branch -f名稱a1強制為 Z99938941118犯罪。

但這是稍微先進的 Git 工作,用於修復錯誤。 現在,您需要知道的主要事情是分支名稱選擇提交,並且 Git 在更改分支名稱而不更改提交時很聰明。


“但是分支不會有錯誤的父母嗎?”

否:分支機構沒有父母:鑒於:

...--G--H   <-- dev
         \
          I   <-- a1
           \
            J--K   <-- b1 (HEAD)

根本沒有分支關系。 名稱dev選擇提交H 這就是它的作用,這就是它的用途。 名稱a1選擇提交I 名稱b1選擇提交K 根本沒有“名稱b1的父母”。

(分支名稱確實有一個可選的上游設置。 dev上游可能是origin/deva1的上游將是origin/a1b1的上游將是origin/b1 。但這完全是另外一回事:它不是“父分支”。沒有父分支這樣的東西。)

有父提交和子提交:提交IH的子級,這意味着HH(注意不是a )父級。 JI的孩子,這意味着IJ的父母。

一旦某個提交存在,該提交的任何部分都無法更改。 提交的級是提交本身的一部分,所以這永遠不會改變。 J的父母總是I在這里。 但是我們可以隨時創建新的分支名稱,然后創建新的提交,因此我們可以根據需要創建一個新名稱br3

...--G--H   <-- dev, br3
         \
          I   <-- a1
           \
            J--K   <-- b1 (HEAD)

然后切換到br3

...--G--H   <-- dev, br3 (HEAD)
         \
          I   <-- a1
           \
            J--K   <-- b1

我們現在有來自提交H的文件。 如果我們更改一些文件並提交,我們會得到:

          L   <-- br3 (HEAD)
         /
...--G--H   <-- dev
         \
          I   <-- a1
           \
            J--K   <-- b1

提交H現在有兩個孩子, IL 但是I仍然只有一個父級H ,並且會永遠存在,因為I現在存在,並且一旦做出承諾,就永遠無法改變。

如果我需要更改提交怎么辦?

你不能——但請注意,我們通過names找到提交。 假設提交J中有錯誤,並且您希望修復該錯誤。 讓我們再做一個新的提交並將其命名為J'並將其父級設為I ,使用臨時分支名稱fixup

...--G--H   <-- dev
         \
          I   <-- a1
          |\
          | J'  <-- fixup (HEAD)
           \
            J--K   <-- b1

現在讓我們將提交K的效果復制到一個新的提交,否則它就像K一樣,但有J'作為它的父:

...--G--H   <-- dev
         \
          I   <-- a1
          |\
          | J'-K'  <-- fixup (HEAD)
           \
            J--K   <-- b1

現在讓我們強制 Git 讓名稱b1指向K' ,然后切換到分支b1

...--G--H   <-- dev
         \
          I   <-- a1
          |\
          | J'-K'  <-- b1 (HEAD), fixup
           \
            J--K   ???

請注意,提交K不再有名稱 我們找不到提交K 提交K是我們找到提交J的方式,我們找不到K ,所以我們也找不到J 但是名稱b1現在找到了K' ,它找到了J' ,它找到了I ,就像J找到了I一樣。 如果我們刪除臨時分支名稱,我們會得到:

...--G--H   <-- dev
         \
          I   <-- a1
          |\
          | J'-K'  <-- b1 (HEAD)
           \
            J--K   ???

只要我們忘記了提交JK的實際 hash ID 是什么,看起來我們確實更改了提交J 我們沒有——提交是不可變的——但我們做了一些“同樣好”的事情。

(您不必擔心這一點,但您應該記住它。)\

這就是你的答案:通過添加剛剛過去的分支a1來工作

而不是從分支dev的尖端提交開始你的新分支b1 ,只需從分支a1的尖端提交開始你的新分支b1

還有什么時候合並真正起作用?

可能永遠不會。 如果您特別使用 GitHub 並且您提出了pull request ,則對該 pull request 進行操作的人可能會 select 執行除MERGE之外的操作。 如果您特別使用 GitLab 並且您提出了合並請求,則對該合並請求進行操作的人可能會 select 執行合並以外的操作。

你應該把它留到以后:現在,如果你需要你在a1中所做的工作來開始處理b1 ,你需要從你在a1中停止的地方開始,而不是從dev中的最后一次提交開始。

暫無
暫無

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

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