主頁 > 資料庫 > 如何使分支B與分支A具有相同的代碼?

如何使分支B與分支A具有相同的代碼?

2021-11-10 14:26:20 資料庫

分支 A 的代碼比分支 B 少。我想將分支 A 合并到 B 中,這樣 B 最終的代碼就會更少,并且本質上與 A 具有完全相同的代碼。類似于撤消多次提交。問題是我必須通過合并請求來做到這一點。我不能直接推送到 B,它必須通過 A(功能分支)。

拉取請求應該是什么樣子的?當我嘗試將 A 合并到 B 時,它沒有檢測到任何差異 - 為什么會這樣?如果我翻轉拉取請求(B 到 A),它會顯示 B 有但 A 沒有的所有更改。

uj5u.com熱心網友回復:

TL; 博士

你想要一個新的提交,其快照是一個犯。然后你可以從這個做一個 PR。使用普通的 Git 工具進行這個新提交是很棘手的,但是通過繞過來實作它很容易。不過,我會把它留到很長的部分。

我們需要在這里區分拉取請求——GitHub 添加的東西,1超過 Git 所做的事情——和 Git 自己做的事情。一旦我們這樣做了,事情就會變得更清楚一些,盡管因為這是 Git,它們可能仍然相當不清楚。

Git 真的是關于commits 的Git 與檔案無關,盡管提交包含檔案。Git 也與分支無關,盡管我們(和 Git)使用分支名稱來查找提交。所以 Git 是關于commits 的這意味著我們需要確切地知道提交是什么以及為我們做什么:

  • 每個提交都有編號然而,這些數字又大又丑又隨機,用十六進制表示,例如,e9e5ba39a78c8f5057262d49e261b42a8660d5b9我們稱這些哈希 ID(或者有時更正式地稱為物件 ID或 OID)。不知道將來的提交將具有什么哈希 ID。但是,一旦進行了提交,哈希 ID 將參考提交,而不會在任何地方進行任何其他提交。2 這允許兩個不同的 Git 存盤庫通過比較提交編號來查看它們是否具有相同的提交。(我們不會在這里使用該屬性,但這很重要。)

  • 每個提交存盤兩件事:

    • 提交具有每個檔案完整快照(盡管這些是壓縮的——有時是非常壓縮的——并且通過用于制作提交編號的相同型別的加密技巧,去重復)。

    • 提交也有一些元資料:關于提交本身的資訊,例如誰做出的,什么時候做出的。在這個提交資料中,每個提交都存盤了一個先前提交哈希 ID的串列,通常正好是一個元素長。單個先前提交的哈希 ID 是此提交父級

這個我的父母是弗蘭克,弗蘭克是倒鉤的東西將提交粘合到他們的祖先鏈中。當我們使用普通 時git merge,Git 使用祖先鏈來確定要合并的內容。不過,我們不希望這里進行正常的合并。同時,同樣的父級內容是 Git 如何將提交(快照)轉換為“更改”:找出“我”中發生的更改,如果我的父項已提交feedcab(不能是frank,其中有太多非十六進制字母一)和我 commit ee1f00d,Git比較這兩個提交中的快照。什么都一樣,沒變。不同的檔案確實發生了變化,Git 通過玩一種Spot the Difference來弄清楚游戲——其中發生什么變化并產生了一個配方:feedcab對這個檔案版本執行此操作,您將獲得該ee1f00d版本。

現在,實際上沒有人使用原始提交編號來查找提交。您最近一次提交的提交編號是多少?你知道嗎?在乎嗎? 可能不是:您只需使用mainmasterdevelop或某個名稱來查找它。

這是它的作業原理。假設我們有一個很小的存盤庫,其中只有三個提交。讓我們稱它們為A, B, and C(而不是使用它們真實的哈希 ID,它們又大又丑,反正我們也不認識它們)。這三個提交看起來像這樣:

A <-B <-C   <--main

CommitC是我們最新的。它有一個快照(所有檔案的完整副本)和元資料。它的元資料列出了早期提交的原始哈希 ID B:我們說它C 指向 B. B同時,Commit有一個快照和一些元資料,并且它B的元資料指向A. A有一個快照和元資料,因為A是第一次提交,它的元資料根本沒有列出父級。這是一個孤兒,有點(所有提交都是處女分娩,有點 - 好吧,我們不要再走這條路了)。所以這是動作停止的地方,這就是我們如何知道只有三個提交。

But we find commit C by name: the name main points to C (holds the raw hash ID of C), just like C points to B.

To make a new commit, we check out main, so that C is our current commit. We change stuff, add new files, remove old files, whatever, and use git add and then git commit to make a new snapshot. The new snapshot gets a new random-looking hash ID, but we'll just call it D. D points back to C:

A <-B <-C   <--main
         \
          D

and now git commit pulls off its clever trick: it writes D's hash ID into the name main:

A--B--C--D   <-- main

Now main points to D instead of C, and there are now four commits.

Because people use names, not numbers, to find commits, we can go back to some old commit by throwing out our access to the newer commits. We force a name, like main, to point to some older commit, like C or B, and forget that D exists. That's what git reset is about. That's presumably not what you want here though, especially because Git and GitHub like to add new commits, not take them away. A pull request in particular won't let you take a commit away.

No, what you want instead is to make a new commit whose snapshot matches some old commit.


1If you're not using GitHub, perhaps you are using some other site that also adds Pull Requests. This gets a bit tricky since each site that adds them, does it their own way. GitLab, for instance, have something similar but call them Merge Requests (rather a better name, I think).

2This depends on some cryptographic tricks that will eventually fail. The size—the big-and-ugly-ness of the hash ID—pushes the failure off as long as we need, although now it's a bit too small and they're going to get even bigger and uglier soon.


Normal merges

In normal everyday Git usage, we make branch names, and we use those branch names to add commits. I already showed a really simple example. Let's get a little more complicated. We'll start with a small repository, as before:

...--G--H   <-- br1 (HEAD)

I've added the HEAD notation here to indicate that this is the name of the branch we have checked out. Let's now add another branch name, br2, that also selects commit H right now:

...--G--H   <-- br1 (HEAD), br2

Since we're using commit H via the name br1, any new commits we make now update only the name br1. Let's make two new commits:

          I--J   <-- br1 (HEAD)
         /
...--G--H   <-- br2

Now let's check out commit H again, with git switch br2:

          I--J   <-- br1
         /
...--G--H   <-- br2 (HEAD)

and make two more commits:

          I--J   <-- br1
         /
...--G--H
         \
          K--L   <-- br2 (HEAD)

We can now run git checkout br1 and then git merge br2, or just run git merge br1 now. Let's do the former: the snapshot we get in the end is the same either way, but other things change a bit, so we have to pick one.

Either way, Git now has to perform a real merge (not a fast-forward fake merge, but a real one). To perform a merge, Git needs to figure out what we changed on br1, and what they (ok, we, but not for the moment) changed on br2. That means Git has to figure out where we both started—and if we just look at the drawing, it's pretty clear: we both started from commit H. We made "our" changes and committed (several times) and got the snapshot that is in J.

The difference from H to J:

git diff --find-renames <hash-of-H> <hash-of-J>

tells Git what we changed on br1.

A similar difference:

git diff --find-renames <hash-of-H> <hash-of-L>

tells Git what they changed on br2. (Note that Git is using the commits here: the branch names, br1 and br2, just served to find the commits. Git then used the history—as recorded in the parents in each commit—to find the best shared starting-point commit H.)

To perform the merge itself, Git now combines the two diff listings. Where we changed some file and they didn't, Git uses our changes. Where they changed a file and we didn't, Git uses their changes. Where we both changed the same file, Git has to combine those changes.

If we both made the exact same change, that's fine. If we touched different lines, that's fine too—although there's an edge case here: if our changes abut, Git declares a merge conflict; but if they overlap exactly, with the same changes, that's OK). If all goes well, so that there are no merge conflicts while combining changes, Git can apply the combined changes to the snapshot from H. This keeps our changes and adds theirs—or, equivalently, keeps their changes and adds ours. Where our changes overlap exactly, Git keeps just one copy of the changes.

The resulting snapshot—H plus both sets of changes—goes into our new merge commit. There's one thing that is special about this new merge commit though. Instead of just the one normal parent, which in this case—on branch br1—would be J, it gets two parents:

          I--J
         /    \
...--G--H      M   <-- br1 (HEAD)
         \    /
          K--L   <-- br2

As always, Git updates the current branch name to point to the new merge commit M. The merge is now complete.

git merge -s ours

Let's draw what you want. You are starting with this:

          o--o--...--R   <-- br-A
         /
...--o--*
         \
          o--o--...--L   <-- br-B (HEAD)

You would like to git merge br-A, but keep the snapshot from the commit L at the tip of br-B.

To accomplish what you want in raw Git, you would run:

git switch br-B
git merge -s ours br-A

Git would now find the merge base * (or not bother, really), then ... completely ignore their changes, and make a new merge commit M, on the current branch:

          o--o--...--R   <-- br-A
         /            \
...--o--*              \
         \              \
          o--o--...--L---M   <-- br-B (HEAD)

where merge commit M has L and R as its two parents, but uses commit L as the snapshot.

That's easy, in raw Git. But GitHub won't do this! How do we get GitHub to deliver this kind of result?

We have to trick GitHub a bit

Suppose, for argument sake, that we were to git switch br-A—i.e., check out commit R—and then make a new commit whose snapshot is that from commit L? That is, we make:

          o--...--R--L'  <-- br-A (HEAD)
         /
...--o--*
         \
          o--o--...--L   <-- br-B

Commit L' has a different hash ID from commit L, and has different metadata—we made it just now, with our name and email and date and time and so on, and its parent is R—but has the same snapshot as commit L.

If we had Git do a normal merge here, Git would:

git diff --find-renames <hash-of-*> <hash-of-L>
git diff --find-renames <hash-of-*> <hash-of-L'>

to get the two diffs that Git needs to combine. These diffs would show exactly the same changes.

A normal merge will combine these changes by taking one copy of all of the changes. So that's just what we want! The final merge result will be:

          o--...--R--L'  <-- br-A
         /            \
...--o--*              M   <-- br-B (HEAD)
         \            /
          o--o--...--L

where I've drawn this in the other style (with M in the middle) for no particular reason. The snapshot in M will match both commits L and L', and branch br-B will end at the new commit, with no changes to any files, but with a new commit on the end.

We can easily make commit L' in Git, and then raise a Pull Request on GitHub by sending commits up through L' on our br-A branch. The PR will merge smoothly, by "changing" nothing at all in br-B, just adding the new merge commit M. So—except for the extra L' commit—we get the same effect as with git merge -s ours run on branch br-B.

Doing this the hard way

The hard way to get snapshot L' added to branch br-A is this:

git switch br-A
git rm -r .                         # from the top level
git restore -SW --source br-B -- .
git commit -C br-B

for instance. The first step puts us on br-A with commit R checked out. The second one—git rm -r .—removes all files from Git's index / staging-area, and the corresponding files from our working tree. The git restore puts all files back but takes them from --source br-B or commit L, and last step, git commit -C br-B, makes a new commit using the message from commit L. (With -C you can edit this.)

This works fine, it's just a bit slow. To go faster, we can use either of two tricks. Here's the first one, which is probably the one I would actually use:

git switch br-A
git read-tree -u --reset br-B
git commit -C br-B

這消除了有利于 的洗掉和恢復git read-tree,它可以一舉完成。(您可以使用-m而不是,--reset但需要兩個標志之一,這git read-tree是一個棘手的命令,我不喜歡使用太多,所以我從不記得使用哪個:幸運的是,這里無關緊要。)

或者,我們可以這樣做:

git switch br-B      # so that we are not on br-A
git branch -f br-A $(git log --no-walk --format=%B br-B | git commit-tree -F - -p br-A br-B^{tree})

如果我沒有打錯字。但是,這使您沒有機會編輯提交訊息。你需要查不出來br-B,直接,你只需要確保,要么你不 br-A,或者你使用git merge --ff-only來進行提交后向前移動。

如果 GitHub 可以做一個就好了 git merge -s ours

但它不能,所以就是這樣。

uj5u.com熱心網友回復:

測驗變基 A 功能分支(包括清理過的代碼) B 您的開發人員

第一次保存你的開發

git checkout B git add git commit -am "blabla my dev"

然后更新A

git checkout A git pull A

然后在 A 的基礎上重新設定 B

git checkout B git rebase A

此時您可能需要處理一些沖突

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/354942.html

標籤:混帐 github git提交

上一篇:Git添加不起作用,推送已洗掉的檔案

下一篇:為什么在執行“gitbranch-a”時仍然顯示git中已洗掉的遠程分支?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • GPU虛擬機創建時間深度優化

    **?桔妹導讀:**GPU虛擬機實體創建速度慢是公有云面臨的普遍問題,由于通常情況下創建虛擬機屬于低頻操作而未引起業界的重視,實際生產中還是存在對GPU實體創建時間有苛刻要求的業務場景。本文將介紹滴滴云在解決該問題時的思路、方法、并展示最終的優化成果。 從公有云服務商那里購買過虛擬主機的資深用戶,一 ......

    uj5u.com 2020-09-10 06:09:13 more
  • 可編程網卡芯片在滴滴云網路的應用實踐

    **?桔妹導讀:**隨著云規模不斷擴大以及業務層面對延遲、帶寬的要求越來越高,采用DPDK 加速網路報文處理的方式在橫向縱向擴展都出現了局限性。可編程芯片成為業界熱點。本文主要講述了可編程網卡芯片在滴滴云網路中的應用實踐,遇到的問題、帶來的收益以及開源社區貢獻。 #1. 資料中心面臨的問題 隨著滴滴 ......

    uj5u.com 2020-09-10 06:10:21 more
  • 滴滴資料通道服務演進之路

    **?桔妹導讀:**滴滴資料通道引擎承載著全公司的資料同步,為下游實時和離線場景提供了必不可少的源資料。隨著任務量的不斷增加,資料通道的整體架構也隨之發生改變。本文介紹了滴滴資料通道的發展歷程,遇到的問題以及今后的規劃。 #1. 背景 資料,對于任何一家互聯網公司來說都是非常重要的資產,公司的大資料 ......

    uj5u.com 2020-09-10 06:11:05 more
  • 滴滴AI Labs斬獲國際機器翻譯大賽中譯英方向世界第三

    **桔妹導讀:**深耕人工智能領域,致力于探索AI讓出行更美好的滴滴AI Labs再次斬獲國際大獎,這次獲獎的專案是什么呢?一起來看看詳細報道吧! 近日,由國際計算語言學協會ACL(The Association for Computational Linguistics)舉辦的世界最具影響力的機器 ......

    uj5u.com 2020-09-10 06:11:29 more
  • MPP (Massively Parallel Processing)大規模并行處理

    1、什么是mpp? MPP (Massively Parallel Processing),即大規模并行處理,在資料庫非共享集群中,每個節點都有獨立的磁盤存盤系統和記憶體系統,業務資料根據資料庫模型和應用特點劃分到各個節點上,每臺資料節點通過專用網路或者商業通用網路互相連接,彼此協同計算,作為整體提供 ......

    uj5u.com 2020-09-10 06:11:41 more
  • 滴滴資料倉庫指標體系建設實踐

    **桔妹導讀:**指標體系是什么?如何使用OSM模型和AARRR模型搭建指標體系?如何統一流程、規范化、工具化管理指標體系?本文會對建設的方法論結合滴滴資料指標體系建設實踐進行解答分析。 #1. 什么是指標體系 ##1.1 指標體系定義 指標體系是將零散單點的具有相互聯系的指標,系統化的組織起來,通 ......

    uj5u.com 2020-09-10 06:12:52 more
  • 單表千萬行資料庫 LIKE 搜索優化手記

    我們經常在資料庫中使用 LIKE 運算子來完成對資料的模糊搜索,LIKE 運算子用于在 WHERE 子句中搜索列中的指定模式。 如果需要查找客戶表中所有姓氏是“張”的資料,可以使用下面的 SQL 陳述句: SELECT * FROM Customer WHERE Name LIKE '張%' 如果需要 ......

    uj5u.com 2020-09-10 06:13:25 more
  • 滴滴Ceph分布式存盤系統優化之鎖優化

    **桔妹導讀:**Ceph是國際知名的開源分布式存盤系統,在工業界和學術界都有著重要的影響。Ceph的架構和演算法設計發表在國際系統領域頂級會議OSDI、SOSP、SC等上。Ceph社區得到Red Hat、SUSE、Intel等大公司的大力支持。Ceph是國際云計算領域應用最廣泛的開源分布式存盤系統, ......

    uj5u.com 2020-09-10 06:14:51 more
  • es~通過ElasticsearchTemplate進行聚合~嵌套聚合

    之前寫過《es~通過ElasticsearchTemplate進行聚合操作》的文章,這一次主要寫一個嵌套的聚合,例如先對sex集合,再對desc聚合,最后再對age求和,共三層嵌套。 Aggregations的部分特性類似于SQL語言中的group by,avg,sum等函式,Aggregation ......

    uj5u.com 2020-09-10 06:14:59 more
  • 爬蟲日志監控 -- Elastc Stack(ELK)部署

    傻瓜式部署,只需替換IP與用戶 導讀: 現ELK四大組件分別為:Elasticsearch(核心)、logstash(處理)、filebeat(采集)、kibana(可視化) 下載均在https://www.elastic.co/cn/downloads/下tar包,各組件版本最好一致,配合fdm會 ......

    uj5u.com 2020-09-10 06:15:05 more
最新发布
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:33:24 more
  • MySQL中binlog備份腳本分享

    關于MySQL的二進制日志(binlog),我們都知道二進制日志(binlog)非常重要,尤其當你需要point to point災難恢復的時侯,所以我們要對其進行備份。關于二進制日志(binlog)的備份,可以基于flush logs方式先切換binlog,然后拷貝&壓縮到到遠程服務器或本地服務器 ......

    uj5u.com 2023-04-20 08:28:06 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:27:27 more
  • 快取與資料庫雙寫一致性幾種策略分析

    本文將對幾種快取與資料庫保證資料一致性的使用方式進行分析。為保證高并發性能,以下分析場景不考慮執行的原子性及加鎖等強一致性要求的場景,僅追求最終一致性。 ......

    uj5u.com 2023-04-20 08:26:48 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:26:35 more
  • 云時代,MySQL到ClickHouse資料同步產品對比推薦

    ClickHouse 在執行分析查詢時的速度優勢很好的彌補了MySQL的不足,但是對于很多開發者和DBA來說,如何將MySQL穩定、高效、簡單的同步到 ClickHouse 卻很困難。本文對比了 NineData、MaterializeMySQL(ClickHouse自帶)、Bifrost 三款產品... ......

    uj5u.com 2023-04-20 08:26:29 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:25:13 more
  • Redis 報”OutOfDirectMemoryError“(堆外記憶體溢位)

    Redis 報錯“OutOfDirectMemoryError(堆外記憶體溢位) ”問題如下: 一、報錯資訊: 使用 Redis 的業務介面 ,產生 OutOfDirectMemoryError(堆外記憶體溢位),如圖: 格式化后的報錯資訊: { "timestamp": "2023-04-17 22: ......

    uj5u.com 2023-04-20 08:24:54 more
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:24:03 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:23:11 more