简体   繁体   English

如何判断壁球之后git分支是否已完全合并

[英]How to tell if git branch has been fully merged after squash

When merging branches with --squash , git does no longer seem to be able to determine whether a branch has been fully merged. 将分支与--squash合并时,git似乎不再能够确定分支是否已完全合并。 How can I quickly check this with existing git commands? 如何使用现有的git命令快速检查这一点?

To reproduce, create a new git repository: 要重现,请创建一个新的git存储库:

$ mkdir tmp
$ cd tmp
$ git init
$ echo "bla" > ans
$ git add .
$ git commit -m "First commit"

Create branch new-branch with commits: 使用提交创建分支new-branch

$ git checkout -b new-branch
$ echo "blabla" >> ans
$ echo "blupp" > zwa
$ git add .
$ git commit -m "Commit on new-branch"

Create branch another-branch with commits: 使用提交创建another-branch

$ git checkout master
$ git checkout -b another-branch
$ echo "test" >> ans
$ echo "three" > dra
$ git add .
$ git commit -m "Commit on another-branch"

Merge: 合并:

$ git checkout master
$ git merge --squash new-branch
$ git commit -m "Squash new-branch"
$ git merge --squash another-branch
$ git mergetool
$ git commit -m "Squash another-branch"
$ git clean -f

Both branches have been merged. 两个分支都已合并。 I should be able to delete them now, right? 我现在应该可以删除它们,对不对? Unfortunately, no: 抱歉不行:

$ git branch -d new-branch
> error: The branch 'new-branch' is not fully merged.
> If you are sure you want to delete it, run 'git branch -D new-branch'.
$ git branch -d another-branch
> error: The branch 'another-branch' is not fully merged.
> If you are sure you want to delete it, run 'git branch -D another-branch'.

I could use option -D , but usually someone else merges my stuff, and I want to check whether the merge was successful. 可以使用选项-D ,但是通常其他人会合并我的东西,并且我想检查合并是否成功。 Trying to use git diff : 尝试使用git diff

$ git diff new-branch
$ git diff ..new-branch
$ git diff ...new-branch
$ git diff new-branch..
$ git diff new-branch...

All the statements above yield a non-empty diff. 上面所有的语句都产生一个非空的差异。 Another attempt: 另一尝试:

$ git branch --merged
> * master

The two branches do not appear in the list of merged branches, so this didn't work either. 这两个分支未出现在合并分支的列表中,因此这也不起作用。

How can I tell whether the branches have been merged? 如何判断分支机构是否已合并?

You cannot get what you want. 您无法得到想要的东西。 You can get several things that may or may not be good enough. 可能会得到几件可能不够好的东西。 In particular, if you use a plain git merge (with --no-ff if/when required), Git records the commits . 特别是,如果您使用纯git merge (如果需要,则使用--no-ff ),Git会记录提交 Git isn't really concerned with files, as it's about commits . Git实际上并不关心文件,因为它与commit有关。 Git isn't really very much concerned about branches either: it's still all about commits . Git也不是非常在乎分支 :它仍然是关于commit的

Illustration, with graph-drawings 插图,带有图形绘图

Using your example, creating a new empty repository and stopping at this point: 使用您的示例,创建一个新的空存储库并在此时停止:

 $ git commit -m "First commit" 

you now have a repository with exactly one commit in it. 您现在有了一个仅包含一次提交的存储库。 That one commit has some big ugly hash ID, but I'll just use the letter A to stand in for it. 那个提交有一个很大的难看的哈希ID,但是我只用字母A来代替它。 The repository now has one named branch, master , which holds the hash ID of this one commit A , so we can draw it like this: 现在,存储库有一个命名分支master ,它保存此提交A的哈希ID,因此我们可以像这样绘制它:

A   <-- master (HEAD)

Now we run your second series of commands: 现在,我们运行您的第二系列命令:

 $ git checkout -b new-branch 

At this point we have: 至此,我们有:

A   <-- new-branch (HEAD), master
 $ echo "blabla" >> ans $ echo "blupp" > zwa $ git add . $ git commit -m "Commit on new-branch" 

This creates new commit B , dragging the name new-branch forward: 这将创建新的提交B ,将名称new-branch向前拖动:

A   <-- master
 \
  B   <--  new-branch (HEAD)

Now we use your third series of commands (I'll trim one down a bit): 现在,我们使用您的第三系列命令(我将略微减少一些):

 $ git checkout -b another-branch master 

This switches back to commit A , creates a new branch name pointing to it, attaches HEAD to the new branch, and leaves us with: 这将切换回提交A ,创建指向它的新分支名称,将HEAD附加到新分支,并为我们提供:

A   <-- another-branch (HEAD), master
 \
  B   <--  new-branch
 $ echo "test" >> ans $ echo "three" > dra $ git add . $ git commit -m "Commit on another-branch" 

This creates third commit C , dragging the name another-branch forward to it: 这将创建第三个提交C ,将名称another-branch向前拖动到它:

  C   <-- another-branch (HEAD)
 /
A   <-- master
 \
  B   <--  new-branch

Now we take on the hard part, the two merge commands. 现在我们来讨论两个merge命令。 We start with: 我们从开始:

 $ git checkout master 

This extracts the contents of commit A into the index/staging-area and your work-tree, and attaches the name HEAD to the name master : 这会将提交A的内容提取到索引/临时区域和您的工作树中,并将名称HEAD附加到名称master

  C   <-- another-branch
 /
A   <-- master (HEAD)
 \
  B   <--  new-branch
 $ git merge --squash new-branch 

This does a merge operation that consists of diffing commit A vs commit A (which shows no differences of course) and then commit A vs commit B (which shows some differences). 这会执行合并操作,其中包括将提交A与提交A (这没有表现出任何差异),然后将提交A与提交B (显示出一些差异)。 The changes discovered in these two diffs get combined—without any conflict, since one set of changes is "do nothing"—and Git stops short of making a new commit, so we need: 在这两个差异中发现的更改将合并在一起-不会发生任何冲突,因为一组更改是“什么都不做”-并且Git停止进行新的提交,因此我们需要:

 $ git commit -m "Squash new-branch" 

which does make the new commit. 确实会进行新的提交。 I'll call this D : 我称这个为D

  C   <-- another-branch
 /
A---D   <-- master (HEAD)
 \
  B   <--  new-branch

Note that commit D has no backwards-looking connection to commit B ; 注意,提交D与提交B 没有向后连接; it remembers only the hash ID of existing commit A . 记住现有提交A的哈希ID。 Had you used: 您曾经使用过:

git merge --no-ff new-branch

to make D , we'd have a connecting line (should be an arrow but arrow fonts don't always work right on every browser) going down-and-right from D to B : 要创建D ,我们需要从DB的连接线(应该是箭头,但箭头字体不一定总是在每种浏览器上都正常工作):

A---D
 \ /
  B

but we don't. 但是我们没有。

Next: 下一个:

 $ git merge --squash another-branch 

This time the merge operation consists of diffing commit A (the merge base) with commit D , to see what we changed, then diffing commit A vs commit C (the tip commit of another-branch ), to see what they changed. 这次合并操作包括将提交A (合并基数)与提交D ,以查看我们所做的更改,然后将提交A与提交Canother-branch的尖端提交),以查看它们的更改。 The merge is actually conflicted—we both changed the file ans , in lines that abut at the end of the file—so most forms of merge will stop here with a conflict. 合并实际上是冲突的-我们都更改了文件ans ,并在文件末尾邻接了ans行-因此大多数合并形式都将因冲突而在此处停止。 Hence you needed: 因此,您需要:

 $ git mergetool 

to resolve the conflict, though we could do that in shell with: 解决冲突,尽管我们可以使用以下命令在Shell中进行操作:

$ cat << END > ans
bla
blabla
test
END
$ git add ans

(you can substitute whatever you want for the merge result in the here-document section before the END ). (您可以在END之前的here-document部分中用所需的任何内容代替合并结果)。 The last step here is to commit the merge. 这里的最后一步是提交合并。 Since it is a squash-merge, rather than a real merge, once again we do not have a backwards link to commit C , even though we used commit C to do the merge: 由于这一个挤压合并,而不是真正的合并,因此即使我们使用 commit C进行合并,我们也没有向后链接到commit C

 $ git commit -m "Squash another-branch" 

This makes new commit/snapshot E , so let's draw that: 这将产生新的commit / snapshot E ,因此我们来画一下:

  C   <-- another-branch
 /
A---D--E   <-- master (HEAD)
 \
  B   <--  new-branch

Note that new commit E has no connection back to C . 请注意,新提交EC没有连接。 Using the name master , Git starts at commit E , walks back to D , then walks back to A . 使用名称master ,Git从提交E开始,返回到D ,然后回到A Commit A has no parent—it's the very first commit we ever made, after all—so the action stops at this point. 提交A 没有父级-毕竟这是我们做过的第一个提交-因此操作到此为止。 Commits B and C are not found in this process. 在此过程中找不到提交BC

Why git branch -d fails 为什么git branch -d失败

If we don't use git mergetool , we don't need git clean to clean up its junk files, so I'll skip over that and proceed to: 如果我们不使用git mergetool ,则不需要git clean来清理其垃圾文件,因此我将跳过它并继续执行以下操作:

 $ git branch -d new-branch > error: The branch 'new-branch' is not fully merged. > If you are sure you want to delete it, run 'git branch -D new-branch'. 

What this is telling you is that from commit E —where you are now—there's no way to find commit B . 这告诉您的是,从提交E现在所在的位置)无法找到提交B That's true; 确实如此; we will not find B in a walk from E back to the root. E到根的步行过程中,我们不会找到B In fact, the name new-branch is the only way we have to find commit B . 实际上,名称new-branch是我们必须找到提交B唯一方法。 (Remember, B stands in for some random-looking hash ID that we would never be able to guess.) If we do delete the name new-branch , we will lose commit B . (请记住, B代表一些我们永远无法猜到的随机哈希值ID。)如果确实删除名称new-branch ,则将丢失提交B

Since Git is all about commits, losing a commit would be bad. 由于Git完全是关于提交的,因此丢失提交将是不好的。 Git won't discard the name, and hence lose the commit, unless you force it. Git不会丢弃名称,因此会丢失提交,除非您强行执行。

The same goes for commit C , find-able only through the name another-branch . 提交C只能通过名称another-branch The git branch -d command will refuse to delete it as commit C is not an ancestor of current commit E . git branch -d命令将拒绝删除它,因为提交C不是当前提交E的祖先。

If we'd used regular git merge —with --no-ff the first time, since otherwise Git would have cheated with a fast-forward instead of a merge—we'd have, at this point, this graph: 如果我们第一次使用常规的git merge ---- --no-ff ,因为否则Git会用快进而不是合并来作弊-此时,我们将有这张图:

  C___  <-- another-branch
 /    \
A---D--E   <-- master (HEAD)
 \ /
  B   <--  new-branch

Now each request, to delete the names new-branch and another-branch , would be "safe", because starting at commit E , Git can walk back to commits D and C . 现在,每个删除名称new-branchanother-branch请求都是“安全的”,因为从提交E开始,Git可以返回到提交D C From D , Git can walk back to commits A and B . D出发,Git可以返回到提交A B Commits B and C are therefore on branch master , as well as being on branches new-branch and another-branch respectively. 因此,提交BC在分支master ,分别在new-branchanother-branch上。 It's safe to delete the name new-branch as commit B is protected by being on master . 删除名称 new-branch是安全的,因为提交Bmaster保护。 It's safe to delete the name another-branch as commit C is protected by being on master . 删除名称 another-branch是安全的,因为commit Cmaster保护。

Commits are history 承诺就是历史

Fundamentally, using git merge --squash is a way to tell Git: I'm going to throw away some commits / history. 从根本上讲,使用git merge --squash是告诉Git的一种方式: 我将丢弃一些提交/历史记录。 If we have: 如果我们有:

...--A--B--C--D--E   <-- branch1 (HEAD)
            \
             F--G--H   <-- branch2

and we run git merge --squash branch2 , we'll make a new commit on the current branch branch1 that is the result of combining the diff from C to E —what we did on branch1 —with the diff from C to H —what they did on branch2 . 我们运行git merge --squash branch2 ,我们就当前分支 commit branch1是从DIFF相结合的结果CE -什么我们做了branch1 -with的差异从CH -什么他们branch2做了。 After successfully making this new commit: 成功进行此新提交后:

...--A--B--C--D--E--FGH   <-- branch1 (HEAD)
            \
             F--G--H   <-- branch2

the only sensible thing to do with branch2 is to delete it. branch2 唯一明智的事情是删除它。 Git doesn't immediately delete it because we might have some other plans for some of its commits—for instance, maybe we want to cherry-pick G or H as a new commit in some other branch—but eventually we should kill it off. Git不会立即删除它,因为我们可能会对它的某些提交有其他计划-例如,也许我们想在其他分支中将GH樱桃拣选为新的提交-但最终我们应该杀死它。 But our new FGH combined commit doesn't remember the hash of commit H , so deleting the name branch2 will lose commits F , G , and H ; 但是我们新的FGH组合提交不能记住提交H的哈希,因此删除名称branch2 丢失提交FGH ; so git branch requires that we force this deletion. 因此git branch要求我们强制执行此删除操作。

Note that if there is some additional commit beyond H , find-able through some other name: 需要注意的是,如果有一些额外的承诺超出了 H ,发现,能够通过一些其他的名字:

...--A--B--C--D--E--FGH   <-- branch1 (HEAD)
            \
             F--G--H   <-- branch2
                    \
                     I--J   <-- branch3

this additional name will keep commit J alive, and J will keep I which keeps H which keeps G which keeps F . 这个附加名称将使提交J保持活动状态,并且J将使I保持H ,保持H保持G保持F Here, git branch -d branch2 will still fail from branch1 —commit H is not an ancestor of commit FGH —but will succeed when run from branch3 . 在这里, git branch -d branch2仍然会从branch1失败— commit H不是commit FGH的祖先—但是从branch3运行时将成功。

The precise definition of when a branch-name delete must be forced has evolved a bit over time. 随着时间的推移,关于何时必须强制删除分支名称的确切定义有所发展。 Git used to just use the current commit ( HEAD ) and the branch-tip to decide if a deletion was safe. Git过去仅使用当前提交( HEAD )和分支提示来确定删除是否安全。 Now it also considers the branch's upstream setting, if the branch has one. 现在,如果分支有一个上游设置,它还将考虑分支的上游设置。 If the branch tip commit is an ancestor of the branch's upstream's tip commit, but not of the current commit, Git now deletes the branch with a warning. 如果分支提示提交是分支上游的提示提交的祖先,而不是当前提交的祖先,则Git现在将删除警告并删除分支。 The commits are safe—they're protected by the upstream name, at least for the moment—but Git isn't sure you really meant to do this, so it prints a warning including the hash ID that was stored in the branch name, to allow you to restore the branch name using that hash ID. 提交是安全的-至少在当前情况下,它们受到上游名称的保护-但Git不确定您确实打算这样做,因此它会打印警告,其中包括存储在分支名称中的哈希ID,允许您使用该哈希ID还原分支名称。

Your branches were successfully merged. 您的分支已成功合并。 You are getting below message error: The branch 'new-branch' is not fully merged. 您收到以下消息错误:分支“ new-branch”未完全合并。 Because code is not pushed to remote branch yet. 因为代码尚未推送到远程分支。 Please push the code after that git branch -d new-branch command will work without any error. 请在该git branch -d new-branch命令正常运行后推送代码。

The -d option stands for --delete, which would delete the local branch, only if you have already pushed and merged it with your remote branches. -d选项代表--delete,仅当您已经将其推送并与远程分支合并后,它才会删除本地分支。 The -D option stands for --delete --force, which deletes the branch regardless of its push and merge status, so be careful using this one! -D选项代表--delete --force,它将删除分支而不管其推送和合并状态如何,因此请谨慎使用该分支!

You can print only the merged branches with: 您只能使用以下命令打印合并的分支:

git branch --merged

So if you see your branch there when you are on the master branch, it means it is merged. 因此,当您在master分支上时,如果在那里看到分支,则表示该分支已合并。

The reason why you don't see it is because that is what "--squash" is for. 之所以看不到它,是因为那是“-壁球”的作用。 It does not merge at all, it includes the changes and then you commit. 它根本不合并,包括更改,然后您提交。 It is confusing but there is actually no merge. 令人困惑,但实际上没有合并。 Everything happens as if you typed the changes that happened to be similar in another branch. 一切都会发生,就像您在另一个分支中键入恰好类似的更改一样。

The good thing to note though is that "mergetool" works anyway. 值得一提的是,“ mergetool”仍然有效。 I didn't know that. 我不知道

Now it would perfectly work with "diff", but the problem is that you included 2 branches, and with a conflict on top of it. 现在,它可以完美地与“ diff”一起使用,但是问题在于您包括了两个分支,并且在它上面有一个冲突。 So you will not get a result. 所以你不会得到结果。

I also understand you are looking for an answer and my advice would be to clone your master branch somewhere else, do a real merge of these branches, and then do a diff between your real working repo and your clone. 我也理解您正在寻找答案,我的建议是将您的master分支克隆到其他位置,对这些分支进行真正的合并,然后在实际的工作库和克隆之间进行区分。

Does that make sense? 那有意义吗? Then you'll know for sure. 然后您将确定。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM