我阅读了很多关于这个主题的文章,但我仍然不确定如何继续。
我的申请在过去的15年中有所增长;到目前为止,源代码已使用其他源控制系统进行管理。我打算迁移到Git并打算使用像here所述的分支模型。
这些分支需要从我们当前的系统迁移到Git:
Current system | Git
---------------------------
Dev | master
v1 | release/v1
v1是一个遗留版本,现在需要维护,但在某些时候会被弃用。它与Dev非常不同,永远不会完全合并。
我想做的是:
Dev
代码签入master
release/v1
并用当前v1
文件替换所有文件,然后将它们推送到服务器现在我不清楚这一部分:我希望能够在将来将release/v1
到master
的特定更改合并到仍然足够相似的应用程序部分,即修复v1
中的错误。为了能够做到这一点,我需要进行从release/v1
到master
的初始上游合并,其中忽略所有差异,因此master
保持原样并且仅在该点之后发生变化考虑到了。
git merge -Xtheirs
会成为这种情况的方法吗?
谢谢, 扬
编辑:
我想我可能找到了我的解决方案 - 不确定它是否优雅,但它似乎导致了正确的状态:
master
release/v1
release/v1
的内容替换为以前SCM中的文件master
git merge -s ours release/v1
答案 0 :(得分:0)
从另一个SCM系统移植历史记录非常麻烦。幸运的是,由于你从两个固定且易于理解的点开始,并希望在它们之间绘制一个有意和渐进的路径,所以有一种比使用merge命令更简单的方法。
首先按照您的描述建立分支master
和v1
。然后,一次将v1
更改为master
一个文件。
鉴于src/someFile.cpp
和master
中存在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}}指向C
。 B
指向B
,A
作为根提交,无处可指。
现在,如果我们决定添加 new 提交,我们将从以下内容开始:
A
这会将我们的HEAD添加到名称$ git checkout master
。然后我们对文件和master
以及git add
大惊小怪,以进行新的提交git commit
。 D
的父级是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
更改为指向master
。 B
的父母是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
,然后追踪B
至A
,搜索历史记录。两个分支上的提交D
。事实上,它是最新的这样的提交(最新的概念在这里是相当愚蠢但我怀疑你知道这意味着什么),所以提交A
是合并基础提交A
和A
。
C
与D
,以查看{em>在A
上发生了哪些更改,因为我们最后一次同步。它将比较D
与branch-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
,这会导致D
和E
两者;我们有共同的承诺:它现在是C
。 Git会比较D
与D
,看看他们做了什么,比较D
与F
,看看我们做了什么,结合这些变化,将它们应用到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的另一个名称 - 因此,如果要添加带注释标记带来的额外数据,请选择带注释的标记。
(与注释标签合并时还有另一个区别,但我现在暂时跳过它。)