有没有办法与策略“我们的"合并没有产生新的提交? [英] Is there a way to merge with Strategy "ours" without producing a new commit?

查看:133
本文介绍了有没有办法与策略“我们的"合并没有产生新的提交?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要做的是关闭一个分支,并使另一个分支的尖端(最后一次提交)看起来像与该分支的合并,而无需实际更改其内容.我尝试过

git merge -s ours other_branch --squash

但没有任何反应(在我阅读了南瓜的实际作用之后,这才有意义)

I.E.在命令之前

  * other_branch
 / 
*---* HEAD

命令后的预期结果

  * other_branch
 / \
*---* HEAD

注意:这里的内容是指提交的东西:元数据会随着我要实现的操作实际上是在提交中添加一个父对象而发生变化;我知道这正在改变历史,至少会影响阴影.

解决方案

为什么的解释:

git reset --soft $(git log --format=%B -n 1 | \
    git commit-tree HEAD^{tree} -p HEAD^ -p other_branch)

有效.我会介绍一下,但是首先...

一个字面答案

尽管如此,对您的问题的字面回答是否".由于可以删除主题行,因此我引用它:

是否可以在不产生新提交的情况下与策略我们的"合并?

Git中的 merge 一词可以指两件事:合并动作或先前合并动作的结果.其中第一个是动词,要合并; ;第二个是形容词,是合并提交,甚至是名词,是合并.合并提交是指有两个父母(或更多,但我们实际上只关心两个)的提交.

运行git merge --squash告诉Git执行合并(即,做动词部分),但是最后,在进行新提交时,进行普通的非合并提交,即,抑制形容词合并 1 的意义在于,当您进行一系列提交时,例如,分支"在某种意义上不是" ",而是指向特定对象的名称".提交"(请参见分支"到底是什么意思?)

运行git merge -s ours告诉Git执行伪造的合并动作-即 prentend 进行动词合并,但实际上没有做任何事情-结果是合并-作为形容词/名词提交.由于动词形式已消失,因此剩下的唯一内容就是后效应.这就是为什么在这里使用--squash没用的原因:您建议同时消除动词名词,而且一无所有.


1 出于特殊原因,git merge --squash也具有设置--no-commit参数的副作用,因此您必须在末尾手动运行git commit.请注意,也可以使用--no-commit运行真正的合并(进行合并提交),这也使您可以手动运行git commit.否则它可能由于冲突而停止,您必须解决该冲突,从而使您手动完成提交.最新版本的Git已添加git merge --continue,以使您感觉不太尴尬,但它仅运行git commit.


Git有--amend,但它并不能使我们到达那里

该图显示了您想要的内容(例如,如果Git支持的话,可能会拼写为git commit --amend --add-parent),但并不能产生关键的洞察力,实际上,git commit --amend很小,甚至很大,谎言,因为它没有更改提交,它只是做出了一个 new 提交,该提交使用了不寻常的父哈希.

正常的git commit进程执行一堆步骤,就好像它们一次完成一样,因此它们要么全部正确完成,要么什么都没有发生(或者至少看起来没有发生).这些步骤是:

  1. 获取当前提交的哈希ID(git rev-parse HEAD).称为 P :现有的HEAD将是新提交的父级. (如果要进行合并,则MERGE_HEAD也将存在,并且git commit会读取它以获取更多的父级.不过,这只是为了完成合并.)
  2. 写入当前索引以创建一个tree对象(git write-tree).将此称为树哈希ID T . (这可能与先前的提交不是同一棵树.)
  3. 获取您的姓名和电子邮件地址以及当前时间作为提交者.通常,也可以将它们用作作者(您可以覆盖它们).
  4. 获取提交消息.
  5. 用所有这些信息(tree T parent P authorcommitter)写出一个新的提交,如步骤3所示. ,以及在步骤4中获得的提交消息.结果是一个新的提交哈希 C .
  6. new 哈希 C 写入当前分支名称,以便git rev-parse HEAD现在生成 C .

使用git commit --amend在第1步更改该过程,而不是将HEAD作为父提交,Git 读取当前提交的父哈希(如果您使用'--amend-进行合并),并在第5步中使用它们.

效果是将当前提交搁置一旁:

...--o--o--*   <-- master (HEAD)

成为:

          *   [the commit that was HEAD before]
         /
...--o--o--@   <-- master (HEAD)

您想要让Git做些什么.

为什么(和如何)shell命令起作用

Git的commit-tree命令产生新的提交对象.就像上面的六步提交序列的步骤5一样.但是它还没有树,也没有预先准备好的预先计算的父提交哈希,因此将其作为命令行参数:

git commit-tree tree-hash -p parent-hash-1 -p parent-hash-2

在这种情况下

.我们想要的 tree-hash 就像git merge -s ours一样,是当前提交具有​​的相同树.我们可以使用HEAD^{tree}为该树命名,这在中进行了描述gitrevisions文档.我们要从 current 提交的父级开始的两个父级哈希. (我们可以假设只有一个这样的父级.)再次,gitrevisions语法为我们提供了一种编写方法:我们可以使用parent^1parent~1,或者从任何一个表达式中省略1.我们想要的另一个父哈希是other_branch指向的提交,因此我们可以将其命名.这给了我们:

git commit-tree HEAD^{tree} -p HEAD^ -p other_branch

此命令从其标准输入中读取提交 message .如果要保留当前提交中的提交消息,可以使用git log提取该消息:--format=%B告诉git log通过将其主题和正文打印为文本来显示每个提交,然后-n 1告诉git log仅显示一个提交.默认情况下,git log所显示的第一个提交是HEAD提交.因此,这给了我们:

git log --format=%B -n 1 |

part-将git log的标准输出传递到git commit-tree的标准输入.

git commit-tree进行提交后的操作是将其哈希ID打印到其自己的标准输出中.因此,如果我们仅自己运行该管道,就会看到打印了新的提交哈希,但不会在任何地方存储.我们需要做的就是更改当前分支名称(无论是什么名称)以指向新的提交.并且git reset --soft commit-hash会这样做,因此:

git reset --soft $(...)

$(...)构造是最后一位:shell将此表示运行给定命令,捕获其标准输出,然后将该标准输出文本视为git reset --soft的命令参数.由于只有一个输出字(即新提交的哈希),因此它将在新提交ID上运行git reset --soft.

What I need to do is close a branch and make the tip (last commit) of another branch looking like a merge with that branch without actually change its content. I tried

git merge -s ours other_branch --squash

but nothing happened (which made sense after I read what squash actually does)

I.E. before command

  * other_branch
 / 
*---* HEAD

expected result after command

  * other_branch
 / \
*---* HEAD

Note: by content here I mean committed stuff: metadata would change as the operation I want to achieve is actually adding one more parent to the commit; I'm aware this is changing history and would affect at least the shasum.

解决方案

PetSerAl's comment has the (or "a", at least) command you can use to achieve what you want. The only thing missing is an explanation as to why:

git reset --soft $(git log --format=%B -n 1 | \
    git commit-tree HEAD^{tree} -p HEAD^ -p other_branch)

works. I'll get to that in a bit, but first...

A literal answer

The literal answer to your question as asked, though, is "no". Since subject lines can be edited away, let me quote it:

Is there a way to merge with Strategy "ours" without producing a new commit?

The word merge, in Git, can refer to two things: the act of merging, or the result of a previous act-of-merging. The first of these is a verb, to merge; the second is an adjective, a merge commit, or even a noun, a merge. A merge commit is a commit with two parents (or more, but we only really care about two).

Running git merge --squash tells Git to perform the merge—i.e., do the verb part—but at the end, when making a new commit, make an ordinary, non-merge commit, i.e., suppress the merge-as-adjective effect.1 The point of this is that when you have a series of commits, e.g., a "branch" in the sense that's not just "a name pointing to a specific commit" (see What exactly do we mean by "branch"?)

Running git merge -s ours tells Git to perform a faked-up act of merging—that is, prentend to do merge-as-a-verb, but not really do anything—resulting in a merge-as-an-adjective/noun commit. Since the verb form is gone, the only thing remaining is the after-effect. This is why using --squash is useless here: you propose to eliminate both the verb and noun, and there's nothing left.


1For no particularly good reason, git merge --squash has the side effect of setting the --no-commit argument as well, so that you have to manually run git commit at the end. Note that a real merge (that makes a merge commit) can also be run with --no-commit, making you run git commit manually as well; or it can stop due to conflicts, which you must resolve, making you finish the commit manually. The latest versions of Git have added git merge --continue to make this feel less awkward, but it just runs git commit.


Git has --amend but it doesn't quite get us there

The diagram shows what you want—which, if Git supported it, might be spelled git commit --amend --add-parent, for instance—but doesn't produce the key insight, and in fact git commit --amend is a small, or perhaps huge, lie, as it doesn't change a commit, it just makes a new commit that uses an unusual parent hash.

The normal git commit process executes a bunch of steps as if they were done all at once, so that either they all finish correctly, or nothing happens (or seems to, at least). The steps are:

  1. Obtain the current commit's hash ID (git rev-parse HEAD). Call this P: the existing HEAD will be the new commit's parent. (If you're concluding a merge, MERGE_HEAD will also exist and git commit reads it to get additional parents. That's just for finishing merges, though.)
  2. Write the current index to make a tree object (git write-tree). Call this T, the tree hash ID. (This may or may not be the same tree as some previous commit.)
  3. Obtain your name and email address and the current time as committer. Normally, use these as author too (you can override them).
  4. Obtain a commit message.
  5. Write out a new commit with all of this information (tree T, parent P, author and committer as obtained in step 3, and commit message obtained in step 4. The result is a new commit hash C.
  6. Write the new hash C into the current branch name, so that git rev-parse HEAD now produces C.

Using git commit --amend changes the procedure right at step 1: instead of getting HEAD as the parent commit, Git reads the current commit's parent hashes (there may be more than one, if you're --amend-ing a merge), and uses those in step 5.

The effect is to shove the current commit aside:

...--o--o--*   <-- master (HEAD)

becomes:

          *   [the commit that was HEAD before]
         /
...--o--o--@   <-- master (HEAD)

What you want to get Git to do is a bit different.

Why (and how) the shell command works

Git's commit-tree command produces new commit objects. It's like step 5 of the six-step commit sequence above. But it hasn't made a tree, and it does not have precomputed parent commit hashes ready to go, so it takes those as command line arguments:

git commit-tree tree-hash -p parent-hash-1 -p parent-hash-2

in this case. The tree-hash we want is, like git merge -s ours, the same tree that the current commit has. We can name that tree using HEAD^{tree}, which is described in the gitrevisions documentation. The two parent hashes we want start with the parent of the current commit. (We can assume there's only one such parent.) Again, gitrevisions syntax gives us a way to write this: we can use parent^1, or parent~1, or leave out the 1 from either of those expressions. The other parent hash we want is the commit to which other_branch points, so we can just name that. That gives us:

git commit-tree HEAD^{tree} -p HEAD^ -p other_branch

This command reads the commit message from its standard input. If we want to retain the commit message from the current commit, we can extract it with git log: --format=%B tells git log to show each commit by printing its subject-and-body as text, and -n 1 tells git log to show only one commit. The first commit that git log shows is, by default, the HEAD commit. So this gives us the:

git log --format=%B -n 1 |

part—we pipe this git log's standard output to git commit-tree's standard input.

What git commit-tree does after making the commit is to print its hash ID to its own standard output. Hence if we just ran this pipeline by itself, we'd see the new commit hash printed, but we would not store it anywhere. What we need to do is change the current branch name—whatever that is—to point to the new commit; and git reset --soft commit-hash will do that, hence:

git reset --soft $(...)

The $(...) construct is the last bit: the shell takes this to mean run the given command, capturing its standard output, then treat that standard output text as command arguments to git reset --soft. Since there's only one output-word—the new commit's hash—this runs git reset --soft on the new commit ID.

这篇关于有没有办法与策略“我们的"合并没有产生新的提交?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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