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更改,没有A更改)合并到master中

注意你对“只有我的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