.gitattributes:merge=我们的策略与快进合并

2023-12-12

如果我处于这样的 git 情况:

* da6a750 (A) Further in A, okay for merging back into master
*   bf27b58 Merge branch 'master' into A
|\  
| * 86294d1 (HEAD -> master) Development on master
* | abe6b8a Welcome to branch A
|/  
* 589517c First commit

On the master分支,三个文件:

./development:

development on master
initial

./specific:

master branch

./.gitattributes:

specific merge=ours

On A分支,还有三个文件:

./development:

development on master
initial
development in A
further in A, okay for merging into master

./specific:

Welcome to branch A

./.gitattributes:

specific merge=ours

当我合并的时候master into A产生提交bf27b58,我很高兴的是./specific文件没有被改变。因为我需要将它保留在分支上A.

但是,我现在想进一步合并A变成master以这样的方式,在新生成的提交中,./specific文件将与提交中的相同86294d1.


我的猜测是specific merge=ours可以保证这一点,但事实似乎并非如此。我尝试过运行:

$ git merge A
Updating 86294d1..da6a750
Fast-forward
 development | 2 ++
 specific    | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

..没有成功。快进合并已被触发,我得到了A and master两者都指向da6a750。换句话说,我有一个master提交显示Welcome to branch A in the ./specific file:

* da6a750 (HEAD -> master, A) Further in A, okay for merging into master
*   bf27b58 Merge branch 'master' into A
|\  
| * 86294d1 Development on master
* | abe6b8a Welcome to branch A
|/  
* 589517c First commit

..这不是我想要的。


相反,我尝试运行:

$ git merge --no-ff A
Merge made by the 'recursive' strategy.
 development | 2 ++
 specific    | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

..也没有成功:一个新的提交02c9c10产生同样的问题:

*   02c9c10 (HEAD -> master) Merge branch 'A'
|\  
| * da6a750 (A) Further in A, okay for merging into master
| *   bf27b58 Merge branch 'master' into A
| |\  
| |/  
|/|   
* | 86294d1 Development on master
| * abe6b8a Welcome to branch A
|/  
* 589517c First commit

..这不是我想要的,因为02c9c10(master):./specific显示Welcome to branch A.

(so 02c9c10 and da6a750完全相同,我猜它们只是由于提交消息而散列不同)。


怎么会specific merge=ours本例中似乎没有考虑到?
我如何让它工作而不需要手动git merge --no-ff --no-commit && git checkout master specific && git commit?


TL;DR:它实际上并不是快进本身

您的问题归结为:“为什么 Git 不遵守我的自定义合并方向?”事实上,这个问题可能会发生在any合并,并且any自定义合并驱动程序。事实上,这次合并can仅作为快进操作来完成保证您(根据您的具体情况)会遇到问题。

原因归结为以下事实:any custom .gitattributes合并驱动程序,包括merge=ours, 被调用only当 Git 认为有“需要合并的东西”时。在您意识到 Git 需要什么才能拥有这样的信念之前,这似乎并没有那么糟糕。

侧边栏:合并策略

这里值得一提的是,作为侧边栏,Git 的-s strategy论证git merge。这些策略接管整个过程,包括“找到合并基础”步骤 - 以及之后的所有内容 - 因此可以做自己的事情,其中​​包括ignoring .gitattributes完全。显然,如果策略忽略了你的.gitattributes,在那里设置自定义合并驱动程序或模式不会有帮助。

因此,我们只关注-s策略do使用合并基础和 Git 调用的两个heads(我们将标记为“我们的”和“他们的”),以及do use .gitattributes。 Git 内置了三个——recursive, resolve, and subtree- 但它们在这里的工作方式都是相同的,关于合并的内容以及自定义合并驱动程序会发生什么。 (另外两个内置合并策略,ours and octopus,要么根本不理会合并基础和“他们的”,要么——对于octopus——有两个以上的头,所以没有“我们的”和“他们的”的明确概念。)

一个合并底座和两个头部

所以,现在我们已经确定了具有以下功能的内置合并:合并基地提交和两个head提交,我们可以看看 Git 以它预编程的 Gitty 方式来思考意味着什么要合并的东西.

The two heads更容易定义。其中之一,我们称之为“我们的”,只是HEAD本身。另一个是我们传递给任何参数git merge:

git merge A

意思是“我们的”是HEAD“他们的”是由A.

这是你的git log --all --decorate --oneline --graph再次输出(顺便说一句,感谢您将其包括在内 - 这对于大多数合并至关重要!):

* da6a750 (A) Further in A, okay for merging back into master
*   bf27b58 Merge branch 'master' into A
|\  
| * 86294d1 (HEAD -> master) Development on master
* | abe6b8a Welcome to branch A
|/  
* 589517c First commit

所以我们可以说两个头都承诺了86294d1 (HEAD or master或者只是“我们的”)并承诺da6a750 (A或者只是“他们的”)。

The 合并基地是他们在图表历史中首先共享的任何提交,即从两个头开始,如果需要的话在历史中向后工作,直到找到他们共同的提交,您可以reach从两个头。所以我们从da6a750,向后退一步得到bf27b58,然后向后退一步,得到两者86294d1 and abe6b8a。同时,我们从86294d1而且...哦,看我们已经达成了共同承诺! :-)

自合并基地以来is两个头之一,通常我们要么快进,要么抱怨没有什么可以合并。由于合并基础是“我们的”头,因此在这两个选项中,Git 会选择快进操作。使用--no-ff告诉 Git:不要选择那个,毕竟继续进行完整的合并。

现在,事实是合并基础is“我们的”承诺保证我们会遇到您的问题,但事实上,即使合并基础不是“我们的”提交,我们也可能会遇到您的问题。让我们看看一次提交里面有什么,在下一个层次上,Git 在两者上都工作时需要什么和做什么git diff and git merge——但首先,让我们想想什么git merge is supposed to do.

合并的目标是合并工作

一般来说,跑步时的想法git merge是我们想要做两组工作——事情we did on our分支进入our承诺,以及“他们”所做的事情,无论他们是谁their分支进入their提交——并产生一个两全其美的新提交:这需要我们所做的任何好东西,plus他们做过的任何好事。

如果我们水平绘制图表而不是垂直绘制图表,旧的提交在左侧,新的提交在右侧,我们可以这样画:

          o--o--o--...--H   <-- ours
         /
...--o--B
         \
          o-----...-----T   <-- theirs

其中每个o是一个提交,也是B, H, and T。犯罪B is the 合并基地,该图中的两个叉子在“过去”(向左)方向重新汇合。H是我们的(HEAD)承诺并且T是他们分支的头/尖端提交。那么,我们如何才能将我们的工作与他们的工作结合起来呢?

Git 的答案是运行两个git diffs:

git diff B H     # find out what we did
git diff B T     # find out what they did

那么就可以combine这两个差异:

  • 无论我们在何处添加某些内容(某些文本行)到某些文件,Git 都应该使最终结果在这些文件中包含这些添加的行。无论我们在某些文件中删除了某些文本行,它都应该使最终结果删除这些行。

    Because git diff将差异表示为“删除这个并添加那个”(即使对于以下差异)change这个到那个),涵盖了一切git diff says.

  • 同样,无论他们在哪里添加行,Git 都应该使最终结果包含添加的行。无论他们在哪里删除行,Git 都应该使最终结果具有相同的删除。

  • 处理一个非常常见的情况,如果我们和他们做了完全一样更改 - 删除相同的原始行,和/或添加相同的替换 - Git 仅需要one copy这个的。

  • 当然,如果有一个地方我们都以不同的方式触及了相同的界限,Git 就会举起它的隐喻之手,感叹道“OY合租!”,并声明合并冲突。

    (正是这些合并冲突让我们最头疼,所以大部分扭曲的旋钮Git 为我们提供了旨在以某种方式处理这些冲突的方法。这大多是正确的.gitattributes也合并属性——尽管这与我们这里的问题没有直接关系。)

现在,所有这些组合需要大量工作,因此要让 Git 运行起来fast,有一个捷径。

原始提交里面有什么git merge to git diff

我们可以查看任何提交对象,或者实际上任何 Git 对象,git cat-file -p:

$ git cat-file -p HEAD
tree 5bc304073b94505cd3f6716829c4cec5a7474762
parent 29257c2c82dca881c4cc65765392a32e46264fbe
author Chris Torek <[email protected]> 1490287144 -0700
committer Chris Torek <[email protected]> 1490297185 -0700

insert early footnote on Git branch creation

In the "about version control" chapter section that introduces

(我在这里剪掉了其余的部分)。

这里更有趣的部分实际上是tree,让我们看看其中的一些:

$ git cat-file -p 5bc304073b94505cd3f6716829c4cec5a7474762
100644 blob 8d1519c435c4da5a65228785fa7ba7033fe011ff    .gitignore
100644 blob 66c9d22a735ee9d8da7f7ed49599583aa642842f    Makefile
100644 blob c9c824fa6668e45976c4fe8a10e4d5c25e272f0c    about.tex
100644 blob 1757109f5aa921ecf9a8051180c25f09e1496c07    aboutvc.tex

(我再次在这里剪掉了东西)。

每个的原始哈希 IDblob对象(即存储的文件版本)告诉 Git哪个版本与此提交一起进行。 (更准确地说,这是这个文件的版本tree对象,但是这个tree与此提交一致,因此它相当于同一件事。)

Git can, and in fact has to, extract these blob hash IDs for each of the three commits—the merge base, "ours", and "theirs". The hash IDs are how it will be able to diff the old and new versions of files like aboutvc.tex (in my case) or specific (in yours). But there is an interesting thing about these hash IDs: they're based entirely on the contents of the object.1 If two files in two different commits are exactly, completely, 100% bit-for-bit identical, they have the same hash and are stored in the repository just once. This means that no matter how many commits have a copy of that particular version of that file, there's only one copy stored in the database.


1In fact, they are cryptographic hashes of the object contents, including the little type-and-size header Git sticks on the front of each object. That header is why the now-famous SHA-1 hash collision is not an immediate problem for Git.


相同的哈希 => 问题

这种快速哈希比较——事实是same散列的意思是“该文件的相同版本”——意味着git diff and git merge可以立即轻松地知道有不用找了到某些文件,从基地到我们的,或基地到他们的......而这正是merge=ours出错。 Git 会比较基础与我们的、基础与他们的。一对有same哈希。一对有一个不同的 hash.

此时,Git 简单地assumes无论合并策略或旋钮设置如何,正确的答案都是.gitattributes,就是从有一个头的地方取出文件不同的哈希。对于大多数文件,在大多数情况下,这就是正确答案。但是如果我们定义了一个自定义合并驱动程序,或者设置merge=ours,这可能是wrong answer.

当不同的一个头是“他们的”,并且自定义合并方向是“保留我们的”时,这是错误的答案。无论选择哪个提交作为合并基础都是如此,但是当合并基础是HEAD——是我们的承诺——那么all与我们的基础的差异中的哈希值是相同的,结果是always“他们的文件版本”。

那其实就是why首先,快进是可能的:最终的合并树始终只是他们的树。实际上,Gitignores所有自定义方向.gitattributes。即使您强制进行真正的合并而不是快进非合并“合并”,这仍然是正确的。

也许 Git 应该检查自定义合并驱动程序或merge=ours指令,并禁用此快捷方式,至少对于真正的(非快进)合并。但事实并非如此,因此你会遇到这个问题。你会also对于其他情况也存在此问题,其中需要进行真正的合并,但文件仅在基础与它们的比较中被修改。

最后一个侧边栏:不要对配置文件执行此操作

人们经常想用这个merge=ours以确保配置文件存储在分支上的内容保持在该分支上的方式。这几乎总是错误的总体策略:相反,配置文件应该是完全省略来自版本控制,或者至少来自这个特定存储库的版本控制。而不是承诺,例如config.ini or config.php,提交一个config.ini.sample or config.default.php或一些这样的。将此配置复制到“真实配置”,或者如果“真实”配置丢失或不完整,则将其读取为辅助策略。

这为您提供了一种一般版本配置(示例和/或默认配置)的方法,无需版本控制具体运行时间某人使用的配置this存储库作为他们运行软件/应用程序本身的地方。如果用户希望对她进行版本控制特别的配置,她可以将其存储在单独的存储库中,并替换config.ini带有(例如)到的符号链接../myconfigs/fooapp.ini,这是她对配置进行版本控制的地方。

(类似的技巧是从$HOME/.gitconfig or /usr/local/etc/fooapp.ini。也就是说,首先单独存储配置。再说一次,如果你想要或需要某种default配置,您可以将该版本与软件一起保留,但是用户自己的配置是独立的,根本不在您自己的版本控制之下。)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

.gitattributes:merge=我们的策略与快进合并 的相关文章

随机推荐

  • 表格大小与页面布局

    我在 Oracle Linux Server 6 3 版上使用 PostgreSQL 9 2 根据存储布局文档 页面布局包含 页头数据 24 字节 n 项 索引项 表项 的点数 AKA ItemIdData 4 字节 可用空间 n 个项目
  • Java - PaintComponent 中的 MouseListener 操作事件

    这里我有一个代码 它使用paintComponent在鼠标单击的位置上绘制一个矩形 我可以获得输出消息 但与图形和 draw 相关的任何内容都不起作用 Code import java awt import java awt event i
  • 仅解码 URL 非 ascii 字符

    现在我正在研究维基百科 在很多文章中 我注意到一些网址 例如 https www google com search q 26 E0 B8 89 E0 B8 B1 E0 B8 99 都很长 示例 URL 可以替换为 https www go
  • WPF UserControl 不继承父 DataContext

    我正在尝试开发一个可重用的用户控件 但遇到了绑定问题 我创建了一个较小的应用程序来测试它 但无法解决它 或者至少无法理解为什么它没有按照我的预期工作 代码如下 我期望的是我放在 MainWindow xaml 上的 TestUserCont
  • 转换颜色以模仿灰度打印

    读书时这个问题 我开始思考是否可以转换颜色来模仿普通的灰度打印机 假设您的屏幕已校准 找到一个可认可的近似值可以节省纸张 例如 如何转换这些颜色 看看在纸上是否可以区分浅蓝色和深蓝色和红色 temp lt rgb2hsv 239 138 9
  • 使用accepts_nested_attributes_for创建新记录或更新现有记录

    阅读重大更新以获取最新信息 嘿大家 我在 Rails 应用程序中有一个多对多关系 涉及三个表 用户表 兴趣表和连接 user interests 表 该表也有一个评级值 以便用户可以对他们的每个兴趣进行评级1 10 级 我基本上是在寻找一种
  • EF Core 5.0 - 更新 ASP.NET Core Web API 中的多对多实体

    EF Core 5 0 引入了多对多关系 我陷入了如何通过我的 asp net api 更新它们的困境 对于一对一和一对多关系 有一个约定 只需添加属性名称后跟 ID public class Blog public int BlogId
  • 找不到或无法加载程序集 mscorlib.dll

    首先 我见过这个问题 虽然问题看起来相似 但其实并不相同 我正在运行一个精简的单声道 没有使用 4 5 配置文件构建 configure with profile4 yes with profile4 5 no 我有一个针对 NET 4 0
  • Groupby 与 TimeGrouper“向后”

    我有一个DataFrame包含时间序列 rng pd date range 2016 06 01 periods 24 7 freq H ones pd Series 1 24 7 rng rdf pd DataFrame a ones 最
  • 从透明形式的图像中删除轮廓

    我正在尝试将窗口形式转换为透明 并使其仅显示一个对象 但它在我的对象周围仍然有一条线 描边 它并不像我想要的那么完美 如何取出线条 笔画 附上图片对比一下 这是我的代码 private void Form1 Load object send
  • 动态分配 UI 权限 WPF [重复]

    这个问题在这里已经有答案了 可能的重复 在代码隐藏中设置 WPF UI 权限 我开始使用 WPF 并希望创建一个应用程序 该应用程序将根据用户 AD 及其角色 自定义 显示 隐藏控件 我设法通过使用继承 MarkupExpension 和
  • 如何将 Eclipse 和 Eclipse 项目指向 Eclipse 中的新 JRE 版本? (不使用JAVA_HOME)

    设置场景 我的系统上有两个版本的 Java 有 32 位版本和 64 位版本 我拥有的另一个版本是 64 位版本的 Eclipse Java EE MyEclipse 32位版本 指的是32位版本的JDK 我现在尝试将 Eclipse 32
  • 当使用浏览器返回时,如何保留 jquery 删除的一些输入文本?

    我对以下页面有一些错误 http jsbin com agedu 带有一些注释的源代码 http jsbin com agedu edit 问题是 当输入内容并执行查询以显示搜索结果时 如果我返回浏览器中的搜索页面 Firefox 3 5
  • rdtscp、rdtsc 之间的区别:内存和 cpuid/rdtsc?

    假设我们尝试使用 tsc 进行性能监控 并且我们希望防止指令重新排序 这些是我们的选择 1 rdtscp是一个序列化调用 它可以防止对 rdtscp 调用进行重新排序 asm volatile rdtscp serializing read
  • Java:如何在抛出异常后继续读取文件

    所以我的教授给我们分配了一个项目 我们必须从文本文件中获取命令并使用它们来驱动程序的流程 这些命令 例如起飞 着陆 装载货物 卸载货物等 旨在模拟类似飞机的物体 有时这些命令执行起来没有意义 例如在飞机飞行时装载货物 因此 为了防止发生类似
  • 是否可以在不使用 EDT 的情况下在 Java Swing 中执行主动渲染?

    我正在考虑使用缓冲策略以及 Javadoc 中描述的以下技术 Main loop while done Prepare for rendering the next frame Render single frame do The foll
  • 我可以使用 Java API 将图像文件存储在 firebase 中吗?

    有没有什么方法可以使用Java api将图像文件存储在firebase中以用于android应用程序 我已阅读此主题我可以使用 Java API 将图像文件存储在 firebase 中吗但仍然没有答案 我知道有一个官方 api 名为 fir
  • TFrecord 比原始 JPEG 图像占用更多空间

    我正在尝试将 Jpeg 图像集转换为 TFrecords 但 TFrecord 文件占用的空间几乎是图像集的 5 倍 经过大量谷歌搜索后 我了解到当 JPEG 被写入 TFrecords 时 它们就不再是 JPEG 了 但是我还没有遇到这个
  • 如何子类化 matplotlib 的图形类?

    我正在尝试向我的图形添加一些自定义行为和属性 但我无法决定有效的 和Pythonic 方法 我的第一个冲动是简单地子类化matplotlib figure Figure但我不知道如何实现这一点 我通常创建新的图形并用类似的东西开始我的绘图
  • .gitattributes:merge=我们的策略与快进合并

    如果我处于这样的 git 情况 da6a750 A Further in A okay for merging back into master bf27b58 Merge branch master into A 86294d1 HEAD