二进制文件如何在 git 上工作

mrb*_*chi 1 git git-merge

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

Rol*_*ith 5

人们普遍认为良好的做法是,任何可以从源代码构建的东西都不会受到修订控制。也就是说,它应该列在一个.gitigore文件中。

有几个原因;

  1. 它会生成大量额外数据(可以轻松复制)以存储在存储库中。
  2. 正如您所发现的,您可能会在二进制文件上遇到合并冲突。二进制文件通常无法以有意义的方式合并。不过,您可以选择其中之一来替换另一个。请参阅ourstheirs合并策略。
  3. 如果源也被合并,那么无论如何您都必须创建一个新的二进制文件。否则二进制文件与源不一致。

对于 LaTeX 存储库,我的.gitignore至少包含:

*.aux
*.bbl
*.blg
*.fdb_latexmk
*.fls
*.idx
*.ilg
*.ind
*.lof
*.log
*.lot
*.out
*.toc
Run Code Online (Sandbox Code Playgroud)

(我用于latexmk构建 LaTeX 文档。)


tor*_*rek 5

正如crashmstr 在评论中所说,二进制文件根本不会合并。但是,您应该了解一些事情git merge:它并不总是合并文件。事实上,它不会真正合并文件,除非有副作用。它有时(并非总是)合并提交。当它这样做时,其中一些有时需要它来合并文件。

\n\n

正如其他人到目前为止在评论中所说的那样,“编译”文件(处理您确实想要使用版本控制系统管理的文件的程序的输出\ xe2\x80\x94这些文件的现代术语似乎是构建工件(尽管工件有更一般的定义)通常不应该在 Git 中提交。

\n\n

什么是git merge branch

\n\n

当你跑步时git merge,你:

\n\n
    \n
  • 位于某个提交上,通常是分支的尖端(通过):该提交是由 命名的(尝试查看哈希 ID,并查看 Git 如何从 中找到当前分支名称);git checkout branch-nameHEADgit rev-parse HEADgit symbolic-ref HEADHEAD
  • \n
  • 提供另一个分支的名称,或解析为另一个提交的任何其他标识符(尝试看看这是如何工作的)。git rev-parse branch-name
  • \n
\n\n

然后,合并命令运行合并策略-s recursive默认情况下为 )。有一些特殊的策略可以执行不同的操作,但默认策略会通过提交(也称为有向无环图的DAG )获取两个提交哈希和 grub,以找到合并基础。您可以使用git log --graph或查看此图表git log --all --decorate --oneline --graph,其中“A DOG”是一个有用的助记符,可以记住“所有装饰单线图表”选项。粗略地说,合并基础是“图中的两条线,从你的 HEAD 和其他提交开始,首先再次聚集在一起。”

\n\n

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

\n\n
       C--D--E   <-- branch1\n      /\n...--B\n      \\\n       F--G--H   <-- branch2\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n\n

但是,如果E两者H 都对一个特定文件进行了更改,则必须合并(合并) 对该文件git merge的这些更改。如果文件是二进制的,Git 至少默认情况下会\xe2\x80\x94\xe2\x80\x94 立即放弃并声明冲突。您的 PDF 文件就是这种情况:如果 和 vs两者都不同,Git 将声明合并冲突并让您解决该文件。 E HB

\n\n

无论如何,一旦解决了所有冲突,git merge通常会进行新的合并提交。这是合并:作为名词合并。合并提交是具有两个父级的提交,我们可以将其绘制为:

\n\n
       C--D--E\n      /       \\\n...--B         I\n      \\       /\n       F--G--H\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意,这次我省略了分支名称。新的提交I是相同的(就提交的文件而言),无论我们移动到哪个分支名称来指向它。不过,移动的分支名称是我们运行时所在的分支名称git merge。因此,如果我们在 上branch1,结果是:

\n\n
       C--D--E\n      /       \\\n...--B         I   <-- branch1\n      \\       /\n       F--G--H   <-- branch2\n
Run Code Online (Sandbox Code Playgroud)\n\n

但如果我们在branch2,结果是:

\n\n
       C--D--E   <-- branch1\n      /       \\\n...--B         I   <-- branch2\n      \\       /\n       F--G--H\n
Run Code Online (Sandbox Code Playgroud)\n\n

换句话说,新的提交以通常的方式进行:无论我们现在在哪个分支该分支名称都会更改,以便它指向新的提交。新提交本身\xe2\x80\x94commit I,在我们的例子中\xe2\x80\x94指向上一个提交,对于合并提交,指向另一个提交。

\n\n

作为一个微妙但重要的一点,新提交的第一个父级是HEAD当时的提交。因此,虽然合并的内容I不取决于我们所在的分支,但第一个父级却取决于。如果我们稍后使用, ,我们在查看提交历史记录时git log --first-parent将仅遵循第一个父级。因为那是我们所在的分支,这意味着我们将返回到EH根据需要

\n\n

什么时候git merge不合并

\n\n

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

\n\n

假设代替:

\n\n
       C   <-- branch1\n      /\n...--B\n      \\\n       D   <-- branch2\n
Run Code Online (Sandbox Code Playgroud)\n\n

或类似的,我们有:

\n\n
       C   <-- branch1 (HEAD)\n      /\n...--B    <-- branch2\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在合并基础提交B 的尖端提交branch2。我们位于branch1\xe2\x80\x94,这就是为什么它被标记为(HEAD)\xe2\x80\x94,但没有任何内容可以branch2合并。在这种情况下,git merge显示“已经是最新的”并且不执行任何操作。

\n\n
\n\n

或者,假设我们有这个:

\n\n
       C   <-- branch2\n      /\n...--B    <-- branch1 (HEAD)\n
Run Code Online (Sandbox Code Playgroud)\n\n

branch1在这种情况下,和 的合并基础同样branch2是 commit B,但branch2位于之前 branch 1。Git 可以(默认情况下)跳过合并并执行所谓的快进操作。它将更改名称,branch1以便它直接指向 commit C,并检查 commit C,给出:

\n\n
       C   <-- branch2, branch1 (HEAD)\n      /\n...--B\n
Run Code Online (Sandbox Code Playgroud)\n\n

当您与也在其中工作和推送的其他人共享“上游”存储库(例如 GitHub 上的存储库)时,这种“快进合并”(根本不是合并)经常发生。如果你们中的一个人做了一些工作并进行了推送,而另一个人没有进行任何新的提交并进行了获取和合并,Git 会发现从上游获得的新提交是“可快进的”,并且会执行此操作而不是执行此操作真正的合并。

\n\n

你可以用 来击败它git merge --no-ff。有些工作流程需要这样做。

\n\n
\n\n

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

\n\n
A--B--...--G--H   <-- branch1 (HEAD)\n\nI--J--...--O--P   <-- branch2\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您要求 Git 合并提交HP,结果取决于您的 Git 版本。旧版本的 Git 尝试使用Git 的半秘密空树H作为基础树来合并这两个图,这可能会也可能不会起作用,具体取决于和的内容P。然而,从 Git 版本 2.9.0 开始,Git 开始默认拒绝这些,要求--allow-unrelated-histories. (如果您提供该标志,则合并会像以前一样继续,使用空树作为基础。)

\n