Vad*_*ick 1 git branch rebase feature-branch
我正在实现一个新功能,为此我创建了一个新分支,并对其进行了一些提交,然后推送。我现在希望继续在 master 中以挂起/未提交更改的形式处理此功能。待定,因为在我提交之前它需要更多的工作。我该怎么做呢?
当我最终提交时,master 的修订历史应该只显示一次提交,就好像我从未创建过另一个分支或进行过中间提交。我该怎么做呢?
手动方法是创建第二个 git 工作区,在其中打开 master 并在另一个中打开分支,然后复制粘贴每个修改过的文件。有没有自动化的方法来做到这一点?
首先,我会给出命令,然后是他们真正在做什么以及为什么这会实现你想要的。我假设在下面只是调用了功能分支branch-git merge如果它有其他名称,请更改给定的名称。
git status # and make sure everything is committed on your branch
# do any extra commits here if required
git checkout master # change the current branch/commit, index, and work-tree
git merge --squash --no-commit branch
Run Code Online (Sandbox Code Playgroud)
(无论如何,在git merge,--squash暗示--no-commit,所以你可以在这里省略第二个标志参数。为了说明,我更多地包含了它。此外,--squash意味着在索引和工作树中进行合并操作,但不要进行合并提交。)
根据您的工具,您可能想要做git reset(默认为提交时--mixed重置HEAD)将HEAD提交复制回索引,以便普通git diff显示所有内容,并将git status所有内容显示为未暂存提交。
重要的是要注意,这里并没有挂起更改这样的事情。Git 根本不存储更改。Git 存储的是commits,每次提交都是所有文件的完整快照。当你问 Git发生了什么变化时,你必须告诉 Git 有两个提交,或者至少,提交了两棵树满的文件。Git 然后从这两个输入中找出快照 A 和 B 的不同之处。然后 Git 会告诉您这些差异——但 A 和 B 仍然是完整快照。在某些方面,这些都无关紧要——您可以随时查看更改,并在需要时查看快照。但是,如果您的心智模型与 Git 的实际工作相匹配,那么您需要解决的谜团就会减少:您自己的工作会更容易。最重要的方式是记住:为了看到变化或差异,您必须提供两个输入。 有时,这些输入之一是隐含的。有时,两者都是隐含的!
最后一个是简单的git diffor git diff --cached/ 的情况git diff --staged:Git 将索引与工作树(plain git diff)或HEAD对索引的提交(--stagedor --cached)进行比较。该选项(如果有)确定两个输入。也就是说,Git 从这个列表中提取了三样东西中的两样:
HEAD承诺,这是一个真正的承诺,包含的所有文件的特殊的Git只,冷冻/只读形成;手头有这两样东西,Git 会比较它们并告诉你有什么不同。该git status命令运行时,作为其工作的一部分,双方这两个git diffS(通过某些选项始终设置,如--name-status和--find-renames=50%),并总结了结果:从输出git diff --staged的变化上演提交,并从输出git diff的变化不会上演承诺。
您也可以手动运行git diff HEAD. 这将选择HEAD和 您的工作树作为两个输入:您HEAD在参数中明确命名了其中一个,, 另一个是隐含的。它将冻结的HEAD内容与未冻结的、正常格式的、工作树内容进行比较,并向您展示差异。再次,Git 正在比较完整的快照:第二个是实时工作树,由差异过程快照。
(旁白:在我看来,你想做的事情可能是错误的做法。通过尽早和经常提交,你通常可以让你自己的工作更容易。做 diffs 是一个好主意,但你可以从base-commit to tip-commit,即跨提交跨度评估整体结果。然后您可以使用交互式 rebase,或者——我更喜欢的方法——附加分支,将较小的提交重建为更合理的一系列提交。但其中大部分是个人偏好。听起来您的 Xcode 工具可以比它做得更好;您使用它的方式并没有错,它只是非常有限。)
我开始写一篇更长的文章,但它失控了,所以我只想说:查看我的一些其他 StackOverflow 答案,了解 Git 如何从索引而不是工作树中进行提交,以及这些提交如何更新分支HEAD所附的名称。
Git 的合并本身也是一个相当大的话题。但是,可以说合并是通过索引来实现其合并结果,工作树起次要作用,主要针对有合并冲突的情况。
一个真正的合并——也就是说,一个git merge进行完整的三向合并,最终会进行一个类型为合并提交的新提交——使用两个父项进行新提交。这两个父项是计算合并结果的三个输入中的两个。第三个输入由其他两个隐含;它是合并基础。Git 使用提交图自行查找合并基础。然后 Git 将合并基础--ours与--theirs分支提示进行比较,就像使用两个git diff --find-renames命令:一个从 base 到我们的,一个从 base 到他们的。
忽略诸如添加、删除或重命名文件之类的问题,这三个提交的比较会生成谁更改了哪些文件的列表。如果我们更改了一些他们没有更改的文件,或者他们更改了一些我们没有更改的文件,合并这些很容易:Git 可以只获取我们的文件或他们的文件。如果我们都没有更改文件,Git 可以采用文件的三个版本中的任何一个(合并基础、我们的或他们的)。
对于困难的情况——我们都更改了一些文件——Git 以合并基础开始,并将我们的更改和他们的更改组合起来,并将组合的更改应用于合并基础副本。如果我们的更改和它们的更改触及不同的行,则合并很简单,Git 会宣布结果成功(即使在现实中没有任何意义)。如果我们和他们都更改了相同的行,Git 会声明合并冲突。
合并冲突的情况是最混乱的。在这里,Git 将所有三个输入文件都保留在索引中,并将冲突的合并写入工作树。你的工作,作为人类运行git merge,是想出正确的合并,不管你喜欢。您可以使用文件的三个索引副本,或工作树副本,或所有这些。您自己解决合并问题,然后使用git add将正确的结果写入索引,用一个合并的副本替换三个未合并的副本。这解决了这个冲突;一旦您解决了所有冲突,您就可以完成合并。
如果您git merge使用--no-commit,Git 将尽可能多地自行合并——这可能是全部——然后停止,就像合并冲突一样。如果没有冲突,这会使索引和工作树都处于准备提交状态。(如果存在冲突,则该--no-commit选项无效:Git 已经要停止了,留下了混乱,并且仍然停止,留下了混乱等待您修复。)
但是,在所有这些情况下,Git 都会在.git目录中留下一个文件,告诉 Git下一次提交——无论是来自git merge --continue还是——git commit应该有两个父项。这使新提交成为合并提交,它将两个分支联系在一起:
I--J [master pointed to J when we started]
/ \
...--F--G--H M <-- master (HEAD)
\ /
K--L <-- branch
Run Code Online (Sandbox Code Playgroud)
那不是你想要的。因此,我们使用git merge --squash, 它告诉 Git:像往常一样进行合并工作,但不是进行合并提交,而是安排下一次提交为普通提交。 也就是说,如果 Git 要进行新的 commit M,它将如下所示:
I--J--M <-- master (HEAD)
/
...--F--G--H
\
K--L <-- branch
Run Code Online (Sandbox Code Playgroud)
没有特别好的理由,--squash即使合并顺利也禁止提交。所以之后git merge --squash,索引和工作树被更新了,但是你可以对工作树做更多的改变,用于git add将更新的文件复制到索引中,只有当你完成后,才用于git commit进行新的提交。
(对于更简单的合并,Git 将默认执行快进,也会--squash禁止快进模式,就像通过git merge --no-ff. 所以--squash,没有--no-ff和没有--no-commit,就足够了。)
最后git reset,如果使用和需要,只需将HEAD提交复制回索引。也就是说,您可能希望您的工具将HEAD提交与工作树进行比较,就好像您正在运行git diff HEAD. 如果该工具将索引与工作树进行比较,您可以通过HEAD在--squash操作更新索引后取消更新索引以匹配来获得相同的效果。
| 归档时间: |
|
| 查看次数: |
877 次 |
| 最近记录: |