如何配置合并策略? [英] How to configure a merge strategy?

查看:112
本文介绍了如何配置合并策略?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想合并具有不同配置文件的2个分支,以便即使没有冲突,配置文件仍然保持不变。正如我从此处此处,我需要配置自定义合并策略而不是合并驱动程序(我已配置合并驱动程序,并且在合并冲突时正常工作)



我试图按照



我在我的项目根目录(放置我的.gitattributes的地方)用这段代码创建了git-merge-mystrategy.sh :

  merge-file -q --ours$ 2$ 1$ 3; 

in .gitattributes我有:

  pom.xml merge = our 

当我运行时:

  git merge --strategy mystrategy develop 

我得到:

 无法找到合并策略'mystrategy'。 
可用的策略是:章鱼我们的递归解析子树。

我可能会错过 :在你的路径中创建一个可执行的git-merge-mystrategy

。 >

您可以给我多一点详细介绍如何创建自定义合并策略? 作为爱德华汤姆森在他的回答中说,你猜,你必须把 git-merge-mystrategy.sh 转换为Git可以通过 $ PATH 找到的文件。该文件必须是可执行文件(Unix / Linux上的 chmod + x ,我不知道在Windows上是什么),并通过名称找到该文件。git-merge- mystrategy ,如果你运行它为 git merge -s mystrategy 。 (你可以运行 git merge -s mystrategy.sh 来搜索 $ PATH 用于 git- )



更大的问题是,正确的合并策略是不是 易于编写。这个:
$ b


  merge-file -q --ours$ 2$ 1$ 3 ; 


不会起作用。您的策略将通过由 git merge 计算的特定参数进行调用,但没有一个是文件名。我将在这里用一个非功能性的合并策略脚本来说明这一点。



如果您运行:

  git merge --strategy mystrategy develop 

您的脚本将被调用四个(!)参数。如果你运行:

  git merge -s mystrategy -Xa -Xkcd = 12 --find-renames = 35 develop 

您的脚本将获得七个参数:

  $ cat〜/ scripts / git-merge-silly 
#! / bin / sh

echoI got $#arguments ...
for $ do
printf%s\\\
$ i
完成
读取垃圾
$ chmod + x〜/ scripts / git-merge-silly
$ git merge -s silly -Xa -Xkcd = 12 -X find-renames = 35 b1
我有7个参数...
--a
--kcd = 12
- 查找 - 重命名= 35
4f95ecf496de9dfe175e7ed4dd97972adf0ca625
-
HEAD
439e327bc8f8e78d74b27ae89f433451eec09111

(此时脚本正在读取变量垃圾来自stdin,所以一切都暂停,我可以打击 CTRL + C 来中断它并停止伪造合并。 p>

以下是参数,以及它们的含义:


  • - 不管 :这通过一个扩展的策略选项, -X C>。正如你所看到的,没有审查这些参数:它们实际上只是任意字符串( -Xkcd 选项是什么意思?)。
  • - 之前的散列或某些情况下的多个散列:这是合并基础,或者合并基础(如果存在多个)
  • - :这将合并基数与字符串分开,以指定要合并哪些头。

  • HEAD :这总是下一个参数;它被编译成 git merge 。这是本地头,以及像 -s我们(或 -s他们的 >,如果它存在的话)它有一些额外的意义,但是对于大多数合并策略,您应该将它与其余参数相同处理。

  • 一个或多个散列ID:这些是Git称之为远程主管被合并。在这种情况下,运行 git merge ... develop ,Git已经将开发解析为特定的提交ID您的分支的提示提交名为开发,并且该提交是远程头文件。



作为合并策略,您的工作现在是:


  • 查找自从合并基础到每个各种元首(HEAD加每个遥控器)

  • 结合这些更改,将生成的组合文件写入索引和工作树

  • 退出表示您的成功或失败状态的状态。



只要引用 git merge source here : / p>


此时,我们需要真正的合并。无论我们使用什么策略
,它都会在索引上运行,可能会影响
工作树,并且在干净地解决时,索引中会有所需的
树 - 这意味着索引必须在
与头部提交同步。这些策略负责
以确保这一点。


稍后


当冲突是
还有待解决时,后端以1退出,其中2时不退出
处理给定的合并。


< blockquote>

如果您在命令行上提供了多个 -s 选项, git merge 代码会(在现代Git中)为你运行 git stash ,然后执行 git reset --hard git stash在每个策略尝试之间应用,直到一个通过以0状态退出而完全成功 - 在这种情况下Git接受其结果 - 或者它的策略用完尝试。在已经用完策略的情况下,如果其中一些退出1而不是2,Git选择产生最好结果的那个,其中最好是有点奇怪的措施:已更改文件的最小数字总数(与HEAD提交)和未合并的文件被认为是最好的结果。 (在我看来,也许没有合并的文件在这里应该被加权得更重)。然后它会在又一次硬复位后重新运行最佳合并。

总之所有这一切都意味着你在合并战略中需要做很多工作。在继续之前确保索引和工作树处于明显可恢复状态并拒绝合并(如果不是请提交或存储更改)是明智的做法。同样,如果有多个合并基础,您可以完全使用 exit 2 拒绝尝试,但是除此之外,您必须对多个合并基础进行一些操作。 ( resolve )策略会选择其中一个并忽略其余部分,这是一个有效的选项,尽管并不总是最好的选择。递归 strategy合并合并基础,并使用结果提交作为新的合并基础。)

然后,对于每个头部 - 您自己的常规 HEAD 头部加上每个远程头部 - 您必须找到所做的更改。通常的递归和解析策略会拒绝运行,除非有两个头,默认情况下运行 git diff ,重命名检测设置为50%,允许 -X 参数来改变它,以便在与基数比较时检测每个头中的文件创建,删除和重命名。然后,他们将这些文件合并到一个高层次 - 或者不能合并它们,留下索引中的高层次冲突 - 以及在每个文件头中标识的每个文件对,然后使用 git merge-file



在您的策略中,您需要完成所有这些工作,除 外,您可以按照您希望的方式使用 git merge-file -q --ours

I want to merge 2 branches with different configuration files, so that configuration files stayed untouched even if there is no conflict. As I understood from here and here, I need to configure custom merge strategy not merge driver (I had configured merge driver and was working fine when I had conflicted merges)

I am trying to set up my merge strategy with guidance of this.

I created git-merge-mystrategy.sh in my projects root directory (the place where my .gitattributes is placed) with this code in it:

merge-file -q --ours "$2" "$1" "$3";

in .gitattributes I have:

pom.xml merge=ours

When I run:

git merge --strategy mystrategy develop

I get:

Could not find merge strategy 'mystrategy'.
Available strategies are: octopus ours recursive resolve subtree.

I am possibly missing this part from here: create an executable git-merge-mystrategy in your path.

Could you please give me a bit more detailes how to create custom merge strategy?

解决方案

As Edward Thomson said in his answer, and you guessed, you have to put that git-merge-mystrategy.sh into a file that Git can find via $PATH. That file must be executable (chmod +x on Unix/Linux, I have no idea what on Windows) and found via the name git-merge-mystrategy if you run it as git merge -s mystrategy. (You could run git merge -s mystrategy.sh to search $PATH for git-merge-mystrategy.sh.)

The larger problem is that a correct merge strategy is not easy to write. This:

merge-file -q --ours "$2" "$1" "$3";

is not going to work. Your strategy will be invoked with particular arguments computed by git merge, but none are filenames. I'll illustrate this with a non-functional merge strategy script here.

If you run:

git merge --strategy mystrategy develop

your script will be invoked with four (!) arguments. If you run:

git merge -s mystrategy -Xa -Xkcd=12 --find-renames=35 develop

your script will get seven arguments:

$ cat ~/scripts/git-merge-silly
#! /bin/sh

echo "I got $# arguments..."
for i do
    printf "%s\n" "$i"
done
read junk
$ chmod +x ~/scripts/git-merge-silly
$ git merge -s silly -Xa -Xkcd=12 -X find-renames=35 b1
I got 7 arguments...
--a
--kcd=12
--find-renames=35
4f95ecf496de9dfe175e7ed4dd97972adf0ca625
--
HEAD
439e327bc8f8e78d74b27ae89f433451eec09111

(at this point the script is reading the variable junk from stdin, so everything pauses, and I can hit CTRL+C to interrupt it and stop the bogus merge).

Here is what the argument are, and what they mean:

  • --whatever: this passes through an extended strategy option, -X whatever. As you can see, there is no vetting of these arguments: they are really just arbitrary strings (what would the -Xkcd option mean?).
  • The hash, or in some cases multiple hashes, before the --: this is the merge base, or the merge bases if there are more than one, from the heads to be merged.
  • The --: this separates the merge bases from the strings specifying which heads are to be merged.
  • HEAD: this is always the next argument; it's compiled into git merge. This is the local head and for a strategy like -s ours (or -s theirs, if it existed) it has a bit of extra significance, but for most merge strategies, you should treat it the same as the remaining arguments.
  • One or more hash IDs: these are what Git calls the remote heads to be merged. In this case, having run git merge ... develop, Git has resolved develop to a specific commit ID that is the tip commit of your branch named develop, and that commit is the remote HEAD.

As a merge strategy, your job is now to:

  • find the changes since the merge base(s) to each of the various heads (HEAD plus each of the remotes)
  • combine those changes, writing the resulting combined files to the index and work-tree
  • exit with a status indicating your success or failure status.

It's perhaps simplest to just quote the git merge source here:

At this point, we need a real merge. No matter what strategy we use, it would operate on the index, possibly affecting the working tree, and when resolved cleanly, have the desired tree in the index -- this means that the index must be in sync with the head commit. The strategies are responsible to ensure this.

and, just a bit later:

The backend exits with 1 when conflicts are left to be resolved, with 2 when it does not handle the given merge at all.

If you supply multiple -s options on the command line, the git merge code will (in modern Git) run git stash for you and then do a git reset --hard and git stash apply between each strategy-attempt, until one either succeeds completely by exiting with 0 status—in which case Git takes its result—or it runs out of strategies to attempt. Having run out of strategies, if some of them exited 1 instead of 2, Git chooses the one that produced the "best" result, where "best" is a somewhat odd measure: the smallest numerical sum of changed files (vs the HEAD commit) and unmerged files is considered the "best" result. (It seems to me that perhaps unmerged files should be more heavily weighted, here.) It will then re-run that best merge after yet another hard-reset.

Anyway, the point of all this is that you have a lot of work to do in a merge strategy. It's wise to make sure the index and work-tree are in a sensibly recoverable state, before proceeding, and to reject the merge (with "please commit or stash your changes") if not. Simialrly, if there are multiple merge bases, you can reject the attempt entirely using exit 2, but otherwise you must do something about the multiple merge bases. (The resolve strategy chooses one of them and ignores the rest, which is a valid option, though not always the best one. The recursive strategy merges the merge bases, and uses the resulting commit as the new merge base.)

Then, for each head—your own regular HEAD head plus each remote head—you must find the changes. The usual recursive and resolve strategies, which refuse to run unless there are exactly two heads, run git diff with rename detection set to 50% by default, allowing -X arguments to change this, so as to detect file creation, deletion, and renaming in each head when compared with the base. They then merge these at a high level—or fail to merge them, leaving the high-level conflict in the index—and for each file-pair identified in base-to-each-head, merge those files at a lower level using git merge-file.

In your strategy, you would need to do all of this same work, except that for one particular file, you would use git merge-file -q --ours in the way you want here.

这篇关于如何配置合并策略?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆