二进制文件如何在git上运行

时间:2017-05-17 16:47:38

标签: git git-merge

我正在使用git管理这个LaTeX项目,其中我有几个分支,我使用master作为分支,我得到所有更改(在项目结束时它将是最终的发布)。有时当我在分支下编译我的项目时,获取pdf,然后当我将该分支与master合并时,我得到合并冲突(master版本的pdf和branch之间版本的pdf)。其他一些时候,两个版本无缝合并。我在做什么导致一个又一个情况?如何确保两个版本合并而不会发生冲突?

2 个答案:

答案 0 :(得分:4)

通常认为任何可以从源构建的内容都而不是置于版本控制之下。也就是说,它应该列在.gitigore文件中。

这有几个原因;

  1. 它会生成大量额外数据(可以轻松复制)以存储在仓库中。
  2. 您可能会发现二进制文件的合并冲突。二进制文件通常无法以有意义的方式合并。你可以,但是选择其中一个替换另一个。请参阅ourstheirs合并策略。
  3. 如果源也已合并,则无论如何都必须在之后创建新的二进制文件。否则二进制文件与源不一致。
  4. 对于LaTeX存储库,我的.gitignore至少包含:

    *.aux
    *.bbl
    *.blg
    *.fdb_latexmk
    *.fls
    *.idx
    *.ilg
    *.ind
    *.lof
    *.log
    *.lot
    *.out
    *.toc
    

    (我正在使用latexmk来构建LaTeX文档。)

答案 1 :(得分:2)

作为crashmstr says in a comment,二进制文件根本不会合并。但是,您应该了解git merge:它并不总是合并文件。事实上,除了作为副作用之外,它并没有永远真正合并文件。它有时(并不总是)合并提交。当它这样做时,其中一些有时需要它来合并文件。

正如其他人在评论中所说的那样,"编译"文件(处理 希望用版本控制系统管理的文件的程序的输出 - 这些的现代术语似乎是构建工件,尽管artifact has a more general definition)通常不应该在Git中提交。

git merge branch做什么

当您运行git merge时,您:

  • 正在进行某些提交,通常是分支的提示(通过git checkout branch-name):此提交是由HEAD命名的提交(尝试git rev-parse HEAD以查看哈希ID,并且git symbolic-ref HEAD了解Git如何从HEAD)找到您当前的分支名称;
  • 提供另一个分支的名称,或者解析为另一个提交的任何其他标识符(尝试git rev-parse branch-name以查看其工作原理)。

然后,merge命令运行合并策略(默认情况下为-s recursive)。有一些特殊的策略可以做不同的事情,但默认的策略是通过提交图(也称为指向非循环图的 DAG )来获取两个提交哈希和grub。找到合并基础。您可以使用git log --graphgit log --all --decorate --oneline --graph查看此图表,其中" A DOG"是一个有用的助记符,记住All Decorate Oneline Graph选项。粗略地说,合并基础是"图表中的两条线从HEAD和其他提交开始,首先再次聚集在一起。"

我们可以在StackOverflow上看起来更好的方式绘制这个图形(实际上有很多方法可以绘制它):

       C--D--E   <-- branch1
      /
...--B
      \
       F--G--H   <-- branch2

其中每个大写字母代表一个提交。这里,两个分支的两个提示是提交EH,它们的合并基础是提交B

合并(作为动词)提交EH,Git基本上会运行git diff B E(以查看自branch1以来发生的变化基础提交)然后是第二个git diff B H(以查看branch2中的更改)。如果这两行中的不同的文件发生了变化,那么合并结果很简单:我们只需要在两行中更改哪些文件,以及基础B中所有未更改的文件,以及把它们堆在一起。

如果EH 两者都对一个特定的文件进行了更改,那么 {{1必须将这些更改合并(合并)到该文件。如果文件是二进制文件,Git将 - 至少在默认情况下 - 立即放弃并声明冲突。对于您的PDF文件就是这种情况:如果它与 git merge E不同,那么{{1} ,Git将声明合并冲突并让你解析文件。

在任何情况下,一旦解决了所有冲突,H通常会进行新的合并提交。这是 a 合并:合并作为名词。合并提交是一个包含两个父项的提交,我们可以将其绘制为:

B

请注意,这次我已经取消了分支名称。新提交git merge是相同的(就提交文件而言),无论我们移动到哪个分支 name 指向它。但是,移动的分支名称是我们运行 C--D--E / \ ...--B I \ / F--G--H 时所处的分支名称。因此,如果我们在I,结果是:

git merge

但如果我们在branch1,则结果为:

       C--D--E
      /       \
...--B         I   <-- branch1
      \       /
       F--G--H   <-- branch2

换句话说,新的提交是以通常的方式进行的:无论我们现在现在名称的分支都会被更改,以便它指向到新的提交。新提交本身 - 提交branch2,在我们的案例中指向上一次提交,对于合并提交,指向另一个提交。

作为一个微妙但重要的观点,新提交的第一个父级是当时 C--D--E <-- branch1 / \ ...--B I <-- branch2 \ / F--G--H 提交的父级。因此,虽然合并I内容不依赖于我们所在的分支,但第一个父确实如此。如果我们稍后使用HEAD,那么在查看提交历史记录时,我们只会关注第一个父级。由于这是我们所在的分支,这意味着我们会根据需要返回Igit log --first-parent

E未合并时

上述图纸故意只涵盖四种可能情况中的一种。

假设代替:

H

之类,我们有:

git merge

现在合并基础提交 C <-- branch1 / ...--B \ D <-- branch2 C <-- branch1 (HEAD) / ...--B <-- branch2 的提示提交。我们已经B了 - 这就是为什么它被标记为branch2 - 但branch1没有任何内容可以合并。{1}}在这种情况下,(HEAD)说&#34;已经是最新的&#34;什么都不做。

或者,假设我们改为:

branch2

在这种情况下,git merge C <-- branch2 / ...--B <-- branch1 (HEAD) 的合并基础再次提交branch1,但branch2 提前 {{ 1}}。 Git可以,默认,跳过合并并执行它所谓的快进。它将更改名称B,使其直接指向提交branch2,并检出提交branch 1,并提供:

branch1

这是&#34;快进合并&#34; (当你正在共享&#34;上游&#34;存储库(例如GitHub上的存储库)与其他工​​作和推送的存储库。如果你们中的一个人做了一些工作和推动,而另一个人没有做出新的提交并进行了一次获取和合并,那么Git看到从上游获得的新提交是&#34;快速前进的&#34;并且这样做而不是进行真正的合并。

你可以用C打败这个。一些工作流程要求这样做。

还有最后一种可能的情况,但这种情况非常罕见:根本没有没有合并基础。如果组合两个单独的存储库,或使用C启动新的独立提交子图,则会发生这种情况。在这里,我们可以将整个图形绘制为:

       C   <-- branch2, branch1 (HEAD)
      /
...--B

如果您要求Git合并提交git merge --no-ffgit checkout --orphan,结果取决于您的Git版本。较早版本的Git尝试使用Git's semi-secret empty tree作为基础树合并这两个图表,根据A--B--...--G--H <-- branch1 (HEAD) I--J--...--O--P <-- branch2 H的内容,这可能会有效,也可能无效。但是从Git版本2.9.0开始,Git默认开始拒绝这些,需要P。 (如果您提供该标志,那么合并就像以前一样,使用空树作为基础。)