dig*_*doo 7 git merge git-squash
当使用 合并分支时--squash,git 似乎不再能够确定分支是否已完全合并。如何使用现有的 git 命令快速检查这一点?
要重现,请创建一个新的 git 存储库:
$ mkdir tmp
$ cd tmp
$ git init
$ echo "bla" > ans
$ git add .
$ git commit -m "First commit"
Run Code Online (Sandbox Code Playgroud)
创建new-branch带有提交的分支:
$ git checkout -b new-branch
$ echo "blabla" >> ans
$ echo "blupp" > zwa
$ git add .
$ git commit -m "Commit on new-branch"
Run Code Online (Sandbox Code Playgroud)
创建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"
Run Code Online (Sandbox Code Playgroud)
合并:
$ 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
Run Code Online (Sandbox Code Playgroud)
两个分支已合并。我现在应该可以删除它们了,对吗?很不幸的是,不行:
$ 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'.
Run Code Online (Sandbox Code Playgroud)
我可以使用 option -D,但通常其他人会合并我的东西,我想检查合并是否成功。尝试使用git diff:
$ git diff new-branch
$ git diff ..new-branch
$ git diff ...new-branch
$ git diff new-branch..
$ git diff new-branch...
Run Code Online (Sandbox Code Playgroud)
上面的所有语句都会产生一个非空差异。另一种尝试:
$ git branch --merged
> * master
Run Code Online (Sandbox Code Playgroud)
这两个分支没有出现在合并分支列表中,因此这也不起作用。
如何判断分支是否已合并?
你无法得到你想要的东西。您可以获得一些可能足够好也可能不够好的东西。特别是,如果您使用普通格式git merge(带有--no-ffif/when required),Git 会记录提交。Git 并不真正关心文件,因为它关心的是提交。Git 也不太关心分支:它仍然与提交有关。
使用您的示例,创建一个新的空存储库并在此时停止:
\n\n\n\n\nRun Code Online (Sandbox Code Playgroud)\n$ git commit -m "First commit"\n
您现在拥有一个存储库,其中只有一个提交。该提交有一些又大又难看的哈希 ID,但我将使用该字母A来代替它。存储库现在有一个名为分支 ,master它保存了这一提交的哈希 ID A,因此我们可以这样绘制它:
A <-- master (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n\n现在我们运行第二组命令:
\n\n\n\n\nRun Code Online (Sandbox Code Playgroud)\n$ git checkout -b new-branch\n
此时我们有:
\n\nA <-- new-branch (HEAD), master\nRun Code Online (Sandbox Code Playgroud)\n\n\n\n\nRun Code Online (Sandbox Code Playgroud)\n$ echo "blabla" >> ans\n$ echo "blupp" > zwa\n$ git add .\n$ git commit -m "Commit on new-branch"\n
这将创建新的提交B,并将名称new-branch向前拖动:
A <-- master\n \\\n B <-- new-branch (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n\n现在我们使用您的第三组命令(我将稍微删减其中一个):
\n\n\n\n\nRun Code Online (Sandbox Code Playgroud)\n$ git checkout -b another-branch master\n
这会切换回 commit A,创建一个指向它的新分支名称,附加HEAD到新分支,然后给我们留下:
A <-- another-branch (HEAD), master\n \\\n B <-- new-branch\nRun Code Online (Sandbox Code Playgroud)\n\n\n\n\nRun Code Online (Sandbox Code Playgroud)\n$ echo "test" >> ans\n$ echo "three" > dra\n$ git add .\n$ git commit -m "Commit on another-branch"\n
这将创建第三次提交C,并将名称another-branch向前拖动:
C <-- another-branch (HEAD)\n /\nA <-- master\n \\\n B <-- new-branch\nRun Code Online (Sandbox Code Playgroud)\n\n现在我们来面对最困难的部分,这两个merge命令。我们从以下开始:
\n\n\nRun Code Online (Sandbox Code Playgroud)\n$ git checkout master\n
这会将提交的内容提取A到索引/暂存区域和工作树中,并将名称附加HEAD到名称master:
C <-- another-branch\n /\nA <-- master (HEAD)\n \\\n B <-- new-branch\nRun Code Online (Sandbox Code Playgroud)\n\n\n\n\nRun Code Online (Sandbox Code Playgroud)\n$ git merge --squash new-branch\n
这会执行一个合并操作,其中包括比较提交A与提交A(当然没有显示任何差异),然后是提交A与提交B(显示一些差异)。在这两个差异中发现的更改被组合起来\xe2\x80\x94,没有任何冲突,因为一组更改是“不执行任何操作”\xe2\x80\x94,并且 Git 停止进行新的提交,因此我们需要:
\n\n\nRun Code Online (Sandbox Code Playgroud)\n$ git commit -m "Squash new-branch"\n
这确实进行了新的提交。我称之为D:
C <-- another-branch\n /\nA---D <-- master (HEAD)\n \\\n B <-- new-branch\nRun Code Online (Sandbox Code Playgroud)\n\n请注意, commit与 commitD没有向后看的联系B;它只记住现有提交的哈希 ID A。您是否使用过:
git merge --no-ff new-branch\nRun Code Online (Sandbox Code Playgroud)\n\n为了使D,我们有一条连接线(应该是箭头,但箭头字体并不总是在每个浏览器上都能正常工作)从 到 向右D向下B:
A---D\n \\ /\n B\nRun Code Online (Sandbox Code Playgroud)\n\n但我们不这样做。
\n\n下一个:
\n\n\n\n\nRun Code Online (Sandbox Code Playgroud)\n$ git merge --squash another-branch\n
这次的合并操作包括比较 commit A(合并基础)和 commit D,以查看我们更改了什么,然后比较 commitA与 commit C(的尖端提交another-branch),以查看它们更改了什么。合并实际上是冲突的\xe2\x80\x94我们都更改了文件ans,在文件末尾的行中\xe2\x80\x94所以大多数形式的合并将在此处停止并出现冲突。因此你需要:
\n\n\nRun Code Online (Sandbox Code Playgroud)\n$ git mergetool\n
为了解决冲突,我们可以在 shell 中使用以下命令来解决:
\n\n$ cat << END > ans\nbla\nblabla\ntest\nEND\n$ git add ans\nRun Code Online (Sandbox Code Playgroud)\n\n(您可以在此处文档部分中的 之前替换任何您想要的合并结果END)。这里的最后一步是提交合并。由于它是挤压合并,而不是真正的合并,因此我们再次没有指向 commit 的向后链接C,即使我们使用commitC进行合并:
\n\n\nRun Code Online (Sandbox Code Playgroud)\n$ git commit -m "Squash another-branch"\n
这会产生新的 commit/snapshot E,所以让我们画一下:
C <-- another-branch\n /\nA---D--E <-- master (HEAD)\n \\\n B <-- new-branch\nRun Code Online (Sandbox Code Playgroud)\n\n请注意,新提交E没有返回到 的连接C。使用名称master,Git 从 commit 开始E,返回到D,然后返回到A。提交A没有父\xe2\x80\x94,这是我们所做的第一个提交,毕竟\xe2\x80\x94,所以操作在此时停止。提交B并C在此过程中未找到。
git branch -d失败如果我们不使用git mergetool,我们不需要git clean清理它的垃圾文件,所以我将跳过它并继续:
\n\n\nRun Code Online (Sandbox Code Playgroud)\n$ git branch -d new-branch\n> error: The branch \'new-branch\' is not fully merged.\n> If you are sure you want to delete it, run \'git branch -D new-branch\'.\n
这告诉您的是,从提交E\xe2\x80\x94 到您现在所在的位置\xe2\x80\x94,无法找到 commit B。这是真的; 我们不会B在步行中找到从E回到根源的地方。事实上,名称new-branch是我们找到 commit 的唯一B方法。(记住,B代表一些我们永远无法猜测的随机哈希 ID。)如果我们删除name new-branch,我们将丢失 commit B。
由于 Git 的核心就是提交,因此丢失提交会很糟糕。Git 不会丢弃该名称,从而丢失提交,除非您强制执行。
\n\ncommit 也是如此,只能通过 nameC查找。该命令将拒绝删除它,因为 commit不是当前 commit 的祖先。another-branchgit branch -dCE
如果我们第一次使用常规的git merge\xe2\x80\x94 --no-ff,否则 Git 会用快进而不是合并 \xe2\x80\x94 来作弊,此时我们将得到以下图表:
C___ <-- another-branch\n / \\\nA---D--E <-- master (HEAD)\n \\ /\n B <-- new-branch\nRun Code Online (Sandbox Code Playgroud)\n\n现在,删除名称new-branch和的每个请求another-branch都是“安全的”,因为从提交开始E,Git 可以返回到提交D 和 C。从 开始D,Git 可以返回到提交A 和 B。提交B和C因此位于分支 上master,以及分别位于分支new-branch和上another-branch。删除该名称 是安全的,new-branch因为提交B受到 on 的保护master。删除该名称 是安全的,another-branch因为提交C受到 on 的保护master。
从根本上来说,使用git merge --squash是一种告诉 Git 的方式:我将丢弃一些提交/历史记录。 如果我们有:
...--A--B--C--D--E <-- branch1 (HEAD)\n \\\n F--G--H <-- branch2\nRun Code Online (Sandbox Code Playgroud)\n\n然后我们运行git merge --squash branch2,我们将在当前分支上进行新的提交,这是将我们在\xe2\x80\x94上所做的操作与到\ xe2\x80\的差异相branch1结合的结果x94他们在 上做了什么。成功进行新提交后:CEbranch1CHbranch2
...--A--B--C--D--E--FGH <-- branch1 (HEAD)\n \\\n F--G--H <-- branch2\nRun Code Online (Sandbox Code Playgroud)\n\n唯一明智的做法branch2就是删除它。Git 不会立即删除它,因为我们可能对其某些提交有其他计划\xe2\x80\x94例如,也许我们想在其他G分支中挑选或H作为新提交\xe2\x80\x94但是最终我们应该消灭它。但是我们新的组合提交不记得提交的哈希值,因此删除该名称将丢失提交、、和;因此要求我们强制删除。FGHHbranch2 FGHgit branch
请注意,如果在 之外 还有一些额外的提交H,则可以通过其他名称找到:
...--A--B--C--D--E--FGH <-- branch1 (HEAD)\n \\\n F--G--H <-- branch2\n \\\n I--J <-- branch3\nRun Code Online (Sandbox Code Playgroud)\n\n这个附加名称将使提交保持J活动状态,并将J保留I哪个保留H哪个保留G哪个保留F。在这里,从\xe2\x80\x94git branch -d branch2仍然会失败,commit 不是提交\xe2\x80\x94 的祖先,但从 运行时会成功。branch1HFGHbranch3
随着时间的推移,何时必须强制删除分支名称的精确定义已经发生了一些变化。Git 过去只使用当前提交 ( HEAD) 和分支提示来决定删除是否安全。现在,如果分支有上游设置,它还会考虑分支的上游设置。如果分支提示提交是分支上游提示提交的祖先,但不是当前提交的祖先,Git 现在会删除该分支并发出警告。提交是安全的\xe2\x80\x94它们受到上游名称的保护,至少目前是这样\xe2\x80\x94但是Git不确定你真的打算这样做,所以它会打印一条警告,包括存储在分支名称中的哈希 ID,以允许您使用该哈希 ID 恢复分支名称。
小智 -1
您的分支已成功合并。您收到以下消息 错误:分支“new-branch”未完全合并。 因为代码还没有推送到远程分支。请在 gitbranch -d new-branch 命令正常运行后推送代码,不会出现任何错误。
-d 选项代表 --delete,只有当您已经将本地分支推送并与远程分支合并时,它才会删除本地分支。-D 选项代表 --delete --force,它会删除分支,无论其推送和合并状态如何,所以要小心使用这个选项!
| 归档时间: |
|
| 查看次数: |
1981 次 |
| 最近记录: |