Git 与x27之间的差异;重基主控';和';重新基址——到主';从主分支派生的分支
鉴于以下分支结构:Git 与x27之间的差异;重基主控';和';重新基址——到主';从主分支派生的分支,git,git-rebase,Git,Git Rebase,鉴于以下分支结构: *------*---* Master \ *---*--*------* A \ *-----*-----* B (HEAD) o---o---o---o---o master | \
*------*---*
Master \
*---*--*------*
A \
*-----*-----*
B (HEAD)
o---o---o---o---o master
| \
| o'--o'--o' B
\
x---x---x---x---x A
如果我想将我的B更改(和仅我的B更改,无A更改)合并到master中,这两组命令之间的区别是什么
>(B) git rebase master
>(B) git checkout master
>(master) git merge B
我主要感兴趣的是,如果我使用第一种方法,分支A的代码是否可以成为master。区别: 第一盘
- (B)
git重新基址主机
*---*---* [master] \ *---*---*---* [A] \ *---*---* [B](HEAD)
*---*---*-- [master](HEAD) |\ | *---*---*---* [A] | *---*---* [B]
B
分支以来,master
分支中没有新的提交
- (B)
git签出主机
*---*---* [master](HEAD) \ *---*---*---* [A] \ *---*---* [B]
- (主)
git合并B
*---*---*-----------------------* [Master](HEAD) \ / *---*---*---* [A] / \ / *---*---* [B]
*---*---*----------------------* [master](HEAD) |\ / | *---*---*---* [A] / | / *---*--------------* [B]
- (B)
git-rebase——在主A B上
*---*---*-- [master] |\ | *---*---*---* [A] | *---*---* [B](HEAD)
- (B)
git签出主机
*---*---* [master] \ *---*---*---* [A] \ *---*---* [B](HEAD)
*---*---*-- [master](HEAD) |\ | *---*---*---* [A] | *---*---* [B]
- (主)
git合并B
*---*---*-----------------------* [Master](HEAD) \ / *---*---*---* [A] / \ / *---*---* [B]
*---*---*----------------------* [master](HEAD) |\ / | *---*---*---* [A] / | / *---*--------------* [B]
B
分支是(在最终合并之前):
在第二组中,您的B分支是:
*---*---*
|
|
|
*---*---* [B]
如果我理解正确,您只需要不在分支中的B提交。因此,在合并之前,第二个集合是您的正确选择。在任何给定操作之前,您的存储库如下所示
o---o---o---o---o master
\
x---x---x---x---x A
\
o---o---o B
在标准重基(不含——主控
)后,结构将:
o---o---o---o---o master
| \
| x'--x'--x'--x'--x'--o'--o'--o' B
\
x---x---x---x---x A
…其中x'
是从A
分支提交的。(注意它们现在是如何在分支B
的底部复制的)
相反,将--to master
重基创建为以下更简洁的结构:
*------*---*
Master \
*---*--*------*
A \
*-----*-----*
B (HEAD)
o---o---o---o---o master
| \
| o'--o'--o' B
\
x---x---x---x---x A
你可以自己试试看。您可以创建本地git存储库来使用:
#! /bin/bash
set -e
mkdir repo
cd repo
git init
touch file
git add file
git commit -m 'init'
echo a > file0
git add file0
git commit -m 'added a to file'
git checkout -b A
echo b >> fileA
git add fileA
git commit -m 'b added to file'
echo c >> fileA
git add fileA
git commit -m 'c added to file'
git checkout -b B
echo x >> fileB
git add fileB
git commit -m 'x added to file'
echo y >> fileB
git add fileB
git commit -m 'y added to file'
cd ..
git clone repo rebase
cd rebase
git checkout master
git checkout A
git checkout B
git rebase master
cd ..
git clone repo onto
cd onto
git checkout master
git checkout A
git checkout B
git rebase --onto master A B
cd ..
diff <(cd rebase; git log --graph --all) <(cd onto; git log --graph --all)
#/bin/bash
set-e
mkdir回购
cd回购
初始化
触摸文件
git添加文件
git提交-m'init'
回显>文件0
git添加文件0
git commit-m“已将添加到文件”
git签出-b A
回声b>>文件A
git添加文件a
git提交-m'b已添加到文件'
echo c>>文件A
git添加文件a
git提交-m'c已添加到文件'
git签出-b
echo x>>文件B
git添加文件b
git提交-m'x已添加到文件'
回声y>>文件B
git添加文件b
git提交-m'y已添加到文件'
光盘
git克隆回购再基础
cd再基
切换到主分支
git签出A
git签出B
git重基主控器
光盘
git克隆回购到
光盘
切换到主分支
git签出A
git签出B
git重新基址——到主A B上
光盘
在我回答问题之前,请耐心等我一会儿。前面的一个答案是正确的,但是还有标签和其他相对较小(但可能会混淆)的问题,所以我想从分支图形和分支标签开始。此外,来自其他系统的人,甚至可能是刚刚接触修订控制和git的人,通常认为分支是“开发线”,而不是“历史痕迹”(git将分支实现为后者,而不是前者,因此提交不一定是在任何特定的“开发线”上)
首先,绘制图形的方式存在一个小问题:
*------*---*
Master \
*---*--*------*
A \
*-----*-----*
B (HEAD)
这是完全相同的图表,但标签的绘制方式不同,并且添加了一些箭头(我已经对提交节点进行了编号,以便在下面使用):
这只是强调,重要的不是水平线的提交,而是父/子关系。分支标签指向一个开始提交,然后(至少按照这些图形的绘制方式)我们向左移动,也可以根据需要向上或向下移动,以找到父提交
当您重新设置提交的基础时,实际上是在复制这些提交
Git永远无法更改任何提交
任何提交(或者git存储库中的任何对象)都有一个“真实名称”,这就是它的SHA-1:例如,在git日志中看到的40个十六进制数字字符串,如9f317ce…
。SHA-1是对象内容的加密校验和。内容包括作者和提交者(姓名和电子邮件)、时间戳、源树和父提交列表。commit#7的父级总是commit#5。如果您制作了commit#7的几乎完全相同的副本,但将其父级设置为commit#2而不是commit#5,则会得到具有不同ID的不同commit。(此时我已经用完了个位数,通常我使用单大写字母来表示提交ID,但对于名为A
和B
的分支,我认为这会让人困惑。因此我将在下面调用#7,#7a的副本。)
git rebase的作用是什么
当您要求git重新设置提交链(如上面的提交#7-8-9)的基础时,它必须复制它们,至少如果它们要移动到任何地方(如果它们不移动,可以将原始文件保留在原位)。它默认从当前签出的分支复制提交,因此git-rebase
只需要两条额外的信息:
- 它应该复制哪个提交
- 副本应该放在哪里?也就是说,第一次复制提交的目标父ID是什么?(其他提交只需指向第一次复制、第二次复制,依此类推。)
当您运行git-rebase
时,您让git从一条信息中找出这两部分。当您使用--on
时,您可以分别告诉git这两部分:您仍然提供上游
,但它不从
计算目标,它只计算从(顺便说一句,我认为
不是一个好名字,但它是rebase使用的,我没有更好的方法,所以让我们在这里继续。rebase调用target
,但我认为target是一个更好的名字。)
让我们先来看看这两个选项。它们都假设
(B) git log --graph --oneline --decorate A B master
* 5a84c72 (A) C6
| * 9a90b7c (HEAD -> B) C9
| * 2968483 C8
| * 187c9c8 C7
|/
* 769014a C5
* 6b8147c C4
* 9166c60 C3
* 0aaf90b (master) C2
* 8c46dcd C1
* 4d74b57 C0
#!/bin/bash
commit () {
for i in $(seq $1 $2); do
echo article $i > $i
git add $i
git commit -m C$i
done
}
git init
commit 0 2
git checkout -b A
commit 3 6
git checkout -b B HEAD~
commit 7 9
(B) git rebase master
Current branch B is up to date.
(B) git checkout master
Switched to branch 'master'
(master) git merge B
Updating 0aaf90b..9a90b7c
Fast-forward
<... snipped diffstat ...>
(master) git log --graph --oneline --decorate A B master
* 5a84c72 (A) C6
| * 9a90b7c (HEAD -> master, B) C9
| * 2968483 C8
| * 187c9c8 C7
|/
* 769014a C5
* 6b8147c C4
* 9166c60 C3
* 0aaf90b C2
* 8c46dcd C1
* 4d74b57 C0
(B) git rebase --onto master A B
First, rewinding head to replay your work on top of it...
Applying: C7
Applying: C8
Applying: C9
(B) log --graph --oneline --decorate A B master
* 7c0e241 (HEAD -> B) C9
* 40b105d C8
* 5b0bda1 C7
| * 5a84c72 (A) C6
| * 769014a C5
| * 6b8147c C4
| * 9166c60 C3
|/
* 0aaf90b (master) C2
* 8c46dcd C1
* 4d74b57 C0
git log --graph --oneline --decorate A B master 9a90b7c
* 7c0e241 (HEAD -> master, B) C9
* 40b105d C8
* 5b0bda1 C7
| * 5a84c72 (A) C6
| | * 9a90b7c C9 <- NOTE: This is what B used to be
| | * 2968483 C8
| | * 187c9c8 C7
| |/
| * 769014a C5
| * 6b8147c C4
| * 9166c60 C3
|/
* 0aaf90b C2
* 8c46dcd C1
* 4d74b57 C0