我正在使用git管理这个LaTeX项目,其中我有几个分支,我使用master
作为分支,我得到所有更改(在项目结束时它将是最终的发布)。有时当我在分支下编译我的项目时,获取pdf,然后当我将该分支与master
合并时,我得到合并冲突(master
版本的pdf和branch
之间版本的pdf)。其他一些时候,两个版本无缝合并。我在做什么导致一个又一个情况?如何确保两个版本合并而不会发生冲突?
答案 0 :(得分:4)
通常认为任何可以从源构建的内容都而不是置于版本控制之下。也就是说,它应该列在.gitigore
文件中。
这有几个原因;
ours
或theirs
合并策略。对于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 --graph
或git log --all --decorate --oneline --graph
查看此图表,其中" A DOG"是一个有用的助记符,记住All Decorate Oneline Graph选项。粗略地说,合并基础是"图表中的两条线从HEAD和其他提交开始,首先再次聚集在一起。"
我们可以在StackOverflow上看起来更好的方式绘制这个图形(实际上有很多方法可以绘制它):
C--D--E <-- branch1
/
...--B
\
F--G--H <-- branch2
其中每个大写字母代表一个提交。这里,两个分支的两个提示是提交E
和H
,它们的合并基础是提交B
。
要合并(作为动词)提交E
和H
,Git基本上会运行git diff B E
(以查看自branch1
以来发生的变化基础提交)然后是第二个git diff B H
(以查看branch2
中的更改)。如果这两行中的不同的文件发生了变化,那么合并结果很简单:我们只需要在两行中更改哪些文件,以及基础B
中所有未更改的文件,以及把它们堆在一起。
如果E
和H
两者都对一个特定的文件进行了更改,那么则 {{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
,那么在查看提交历史记录时,我们只会关注第一个父级。由于这是我们所在的分支,这意味着我们会根据需要返回I
或git 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-ff
和git 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
。 (如果您提供该标志,那么合并就像以前一样,使用空树作为基础。)