Git:使用两个不同的分支

时间:2018-03-08 14:33:51

标签: git

我阅读了很多关于这个主题的文章,但我仍然不确定如何继续。

我的申请在过去的15年中有所增长;到目前为止,源代码已使用其他源控制系统进行管理。我打算迁移到Git并打算使用像here所述的分支模型。

这些分支需要从我们当前的系统迁移到Git:

Current system | Git
---------------------------
Dev            | master
v1             | release/v1

v1是一个遗留版本,现在需要维护,但在某些时候会被弃用。它与Dev非常不同,永远不会完全合并。

我想做的是:

  • 初始化存储库
  • Dev代码签入master
  • 切换到分支release/v1并用当前v1文件替换所有文件,然后将它们推送到服务器

现在我不清楚这一部分:我希望能够在将来将release/v1master的特定更改合并到仍然足够相似的应用程序部分,即修复v1中的错误。为了能够做到这一点,我需要进行从release/v1master的初始上游合并,其中忽略所有差异,因此master保持原样并且仅在该点之后发生变化考虑到了。

git merge -Xtheirs会成为这种情况的方法吗?

谢谢, 扬

编辑:

我想我可能找到了我的解决方案 - 不确定它是否优雅,但它似乎导致了正确的状态:

  • master
  • 创建回购并签入文件
  • 分行release/v1
  • release/v1的内容替换为以前SCM中的文件
  • 结帐master
  • 合并策略我们的git merge -s ours release/v1

2 个答案:

答案 0 :(得分:0)

从另一个SCM系统移植历史记录非常麻烦。幸运的是,由于你从两个固定且易于理解的点开始,并希望在它们之间绘制一个有意和渐进的路径,所以有一种比使用merge命令更简单的方法。

首先按照您的描述建立分支masterv1。然后,一次将v1更改为master一个文件。

鉴于src/someFile.cppmaster中存在release/v1,请先查看master然后ask Git to bring in the other branch's version

从存储库的根目录执行git checkout release/v1 -- src/someFile.cpp。语法git checkout branch1 -- fileA告诉git将fileA带入branch1的当前分支,用相同的名称覆盖任何现有文件。

历史照常保存;每次执行此分支间检出命令时,它都会进行必须提交的更改。您可以为多个文件执行此操作。然后立即全部提交。

答案 1 :(得分:0)

以不同的方式考虑创建您的Git存储库。首先创建现有的v1旧版本作为首次提交。任何Git存储库中的第一个提交都有一个有趣的属性。

假设您有一个现有的工作存储库,让我们从分支如何增长开始。想象一下只有一个命名分支的小型存储库 - master - 和三个提交。提交的实际名称是大丑陋的哈希ID,但我们在这里使用单个大写字母。 (我们想象的存储库只能容纳26次提交!)

让我们按照Git的方式绘制提交。每个提交都记住,作为其,是前一次提交的哈希ID。因为我们有三个提交,第三个提交是C,提交C会记住提交B作为其父级:

  <-B <-C

当然B也有父母,并且记得A是其父母:

A <-B <-C

但提交A的父提交是什么?答案是:没有。它不能拥有父母。这是第一次提交!它是无父母的。它是sui generis。 Git称之为 root commit

根提交是所有操作开始的地方 - 但是Git向后工作 ,所以它确实在所有操作结束的地方。 Git记住提交C的哈希ID的方法是将其存储在名称master中。真正的哈希ID是大的,丑陋的,看似随机的:除了把它们写下来之外,没有好的方法来记住它们,所以Git将它们写在像master这样的分支名称中。

提交自己是永久的,只读的和不腐败的。 1 一旦你提交了提交,你就可以永远不会改变它。因此,作为父项连接到每个提交中的哈希ID是不可更改的,并且我们不需要将箭头绘制为箭头。但是,分支名称中的哈希ID是高度可变的!因此,让我们继续绘制箭头:

A--B--C   <-- master

我们说名称master 指向提交C,并且因为C记录了B的哈希值,{ {1}}指向CB指向BA作为根提交,无处可指。

现在,如果我们决定添加 new 提交,我们将从以下内容开始:

A

这会将我们的HEAD添加到名称$ git checkout master 。然后我们对文件和master以及git add大惊小怪,以进行新的提交git commitD的父级是D

C

A--B--C <-- master (HEAD) \ D 的最后一步(好的,差不多最后一步)是Git将git commit的哈希ID写入D所附加的名称。因此,HEAD现在指向master,而不是D

C

我们因此创建了一个新提交并将其添加到我们的A--B--C \ D <-- master (HEAD) 分支。

1 &#34;永远&#34; part只是大部分都是真的:一些外部名称(例如分支名称)无法到达的提交最终被垃圾收集和删除。其余部分得到保证:您无法更改任何现有的Git对象,当Git注意到该对象的 name 不再与该对象的加密校验和匹配时,它将检测(并抱怨)任何损坏。 (名称​​是加密校验和,因此如果它们不匹配,则会发生损坏。)

这如何适用于您的情况

所以,假设我们在你的v1旧版本中进行了第一次提交 - 我们的#34;提交master&#34; -out。我将省略所有Git命令,因为你可能有这些命令:

A

现在让我们创建一个新的名称,比如A <-- master (HEAD) ,*也指向提交branch-v1。我们使用简单的命令执行此操作:

A

给了我们这个:

$ git branch branch-v1

现在我们删除工作树和索引中的每个文件:

A   <-- master (HEAD), branch-v1

并复制开发系统中的所有文件,例如:

$ git rm -r .

然后$ ssh development-system 'cd some/path; tar cf - .' | tar xf - 全部和git add

git commit

这会使我们的新提交$ git add . $ git commit -m 'import development version' ,并将名称B更改为指向masterB的父母是B。我们来画吧:

A

这个图绘制非常重要。提交A <-- branch-v1 \ B <-- master (HEAD) 位于分支A上,但它位于branch-v1上。在Git中,提交可以一次在多个分支上

时间在

上游行

假设它现在是第二天(周,无论如何),并且有一个v1的补丁。您现在可以在任何Git存储库中克隆这个存储库(如果您愿意,可以在此存储库中),请查看master分支:

branch-v1

我们在主线上做了更多提交(也许它已经被多个分支名称所拥有,等等,但提交A <-- branch-v1 (HEAD) \ B--...--C <-- master A肯定仍然存在在,形状像这样)。现在让我们添加并提交更新的v1代码:

B

如果我们希望在快照A--D <-- branch-v1 (HEAD) \ B--...--C <-- master A之间进行更改,那么它现在大部分都是微不足道的(具体取决于完全不同的{{ 1}}来自D),使用C。运行:

A

将告诉Git在两个分支git merge(我们刚刚附加了我们的HEAD)和$ git checkout master $ git merge branch-v1 之间找到最新的共享/公共提交。因此,Git会在master返回branch-v1后返回C,然后追踪BA,搜索历史记录。两个分支上的提交D 。事实上,它是最新的这样的提交(最新的概念在这里是相当愚蠢但我怀疑你知道这意味着什么),所以提交A合并基础提交AA

因此,Git将比较CD,以查看{em>在A上发生了哪些更改,因为我们最后一次同步。它将比较Dbranch-v1,以查看A我们的变化。然后Git将这两组更改结合起来,并在我们当前的分支C上创建一个新的合并提交(请记住,我们的HEAD现已附加到master):

master

合并提交链接回先前的master,即A----------D <-- branch-v1 \ \ B--...--C--E <-- master (HEAD) ,作为其第一个父级,并链接到我们合并的提交(HEAD)作为其第二个父级。 (如果你想要的话,这个父母编号在以后很重要。)

如果对C进行了更多更改,我们可以将其与另一个D合并。我们从这开始,以某种方式获取提交v1

git merge

再次运行F,找到最近的常见提交:它会查看A----------D--F <-- branch-v1 \ \ B--...--C--E <-- master (HEAD) ,这会导致git merge branch-v1;它会查看F,这会导致DE两者;我们有共同的承诺:它现在是C。 Git会比较DD,看看他们做了什么,比较DF,看看我们做了什么,结合这些变化,将它们应用到D& #39; s源,并进行新的合并提交E

D

您的分支流模型使所有这些变得复杂(出于各种原因,主要是:-)好的)但基本原理保持不变。

我们将v1作为第一次提交的原因是为了确保提交G,我们的根提交,包含v1的源,在 all < / em>分支机构。这样,以后发生的任何事情都可以与v1进行比较。

标记

您可以通过执行图遍历在存储库中找到根提交:当没有更多图表要遍历时,您已达到根提交。只要图表只有一个根(正常情况),那就是 根。但是Git确实允许你创建更多的root提交,如果你合并独立的存储库,你也会获得额外的根。在任何情况下,将图形转移到查找根是一种痛苦 - 所以Git提供标记以及分支名称。

正如我们所看到的,分支名称只是标识一个特定的提交 - 但随着时间的推移名称​​移动,因为我们向分支添加了更多提交。标记名称与分支名称的作用相同,但与分支名称不同,标记名称​​不要移动。因此,一旦我们在A----------D--F <-- branch-v1 \ \ \ B--...--C--E--G <-- master (HEAD) 分支上创建了根提交,我们就可以标记提交:

A

使字符串v1-branch表示特定提交

标签可以很简单(&#34;轻量级标签&#34;)并且只是直接识别提交,或者是想象一下(#34;带注释的标签&#34;)。花哨的一个携带额外的数据,然后识别提交。任一标记的使用基本相同 - $ git tag v1.0 是原始哈希ID的另一个名称 - 因此,如果要添加带注释标记带来的额外数据,请选择带注释的标记。

(与注释标签合并时还有另一个区别,但我现在暂时跳过它。)