tar*_*ahf 0 git merge commit rebase squash
在某些情况下,我在功能分支上执行 git pull 操作,最终得到多个烦人的“合并提交”,我可以理解它们为什么会发生,但我想将它们合并起来,使其看起来像正常提交。
我尝试使用git rebase -i --rebase-merges HEAD~4但无法弄清楚如何压缩合并提交。
我做了进一步的研究,经过大量的挖掘,我能够执行以下操作,使用 rebase 将不需要的合并提交合并到正常提交中,然后在需要时压缩它们:
git checkout feature
git pull # will create merge commits
git checkout featur_backup # to create a backup
git switch --orphan emty_commit
git commit -m "First empty commit for the feature branch" --allow-empty
git switch feature
git rebase empty_commit
git rebase -i --root # this allows you to squash commits
git branch -D empty_commit
Run Code Online (Sandbox Code Playgroud)
有没有更好的方法来合并合并提交?
笔记:
\xc3\x94rel\ 的答案有一个秘诀。你可能不想要origin/master直接\xe2\x80\x94你想要什么取决于你,一旦你读完并正在研究这个解释的部分\xe2\x80\,你可能希望考虑这个并进行实验x94但是 rebase确实有效。
\n\n我还是不明白你提到的 git rebase 是如何工作的?我的问题是,当我在功能分支上执行 git pull 时,它最终会进行合并提交,那么它将如何
\ngit rebase origin/master工作?
这里的关键是同时理解很多事情。(Git 经常出现这种情况。)您需要知道:
\ngit fetch运作;git merge运作;git pull意味着run git fetch,然后运行git merge或其他第二个命令(您一直在使用git merge);和git rebase运作。这是很多东西!我们不能指望涵盖所有内容,即使是在我著名的长答案之一中也是如此,因此我们将快速讨论几个关键项目。
\n1或您选择的其他副词。
\n一次提交:
\n被编号:它有一个唯一的哈希ID。该数字意味着commit不仅在您的存储库中,而且在每个存储库中,甚至是所有没有您提交的 Git 存储库中。(这是为 Git 提供支持的主要深层魔力。)
\n是只读的:任何提交的任何部分都不能更改。这是魔法编号系统所要求的。
\n包含两件事:所有文件的完整快照(以特殊的 Git 化形式间接存储,其中它们被压缩和去重复,因此当新提交重新使用几乎所有文件时,这是一件好事先前的提交:这意味着它们不占用空间),加上元数据:有关提交的信息。
\n元数据包含您的姓名、电子邮件地址以及提交的日期和时间等内容。出于 Git 自身的目的,Git 在任何一次提交的元数据中存储先前提交哈希 ID 的列表。大多数提交\xe2\x80\x94“普通”提交,我们倾向于称其为\xe2\x80\x94,这里只有一个哈希ID。这将普通提交形成一个简单的链,只不过连接提交的箭头是向后而不是向前。人们喜欢将箭头视为前进,但这在 Git 中行不通,因为提交是严格只读的。
\n这意味着,给定一行中的提交字符串,每个提交都有自己的哈希 ID,我们可以绘制该提交字符串,较新的提交向右,如下所示:
\n... <-F <-G <-H\nRun Code Online (Sandbox Code Playgroud)\n这里H代表链中最后一次提交的哈希ID。提交H包含每个文件的完整快照以及一些元数据。在H\ 的元数据中,Git 存储了一些早期提交的哈希 ID G,我们(和 Git)将其称为提交的父级H。所以 commitH 指向更早的 commit G。
当然,G它也是一个提交,因此它有一个快照和元数据,并且该元数据保存了一些更早的父级的哈希ID F。但这F也是一个提交,所以F也指向向后。这会永远持续下去,或者更确切地说,直到我们到达第一个提交,其中 \xe2\x80\x94 是第一个 \xe2\x80\x94 没有父级(可以说是一种奇怪的“处女诞生”;Git 调用它是根提交\xe2\x80\x89)。
提交全部存储在一个大数据库中(“所有 Git 对象”,包括提交对象\xe2\x80\x94,其他类型的对象主要支持提交,即树和 blob 对象,以及带注释的标记对象)。该数据库是一个简单的键值存储,其中键是哈希 ID。因此 Git 需要一个哈希 ID 来查找提交。
\n所有这一切都有两个重要的含义:
\n提交不存储差异。我们得到 diffs\xe2\x80\x94,通过让 Git比较相邻的提交,我们将提交视为更改\xe2\x80\x94 。我们选择一些父/子对,例如和,并让 Git 比较两个快照。Git 使用的重复数据删除技巧使 Git 可以轻松地立即丢弃所有完全相同的文件,因此 Git 只需要找出两个实际上不同的文件中发生了什么变化。这通常不会花费太长时间,因此即使 Git 仅存储了快照,也可以显示“补丁”。GHgit showgit log -p
Git 可以使用每个提交中存储的父项来找到除最后一个提交之外的每个提交。我们必须告诉 Git commit 的哈希 ID H。从那里开始,Git 完全自行向后工作。但我们必须在这里提供一个原始哈希 ID,这对人类来说是可怕的:哈希 ID看起来是随机的,而且没有办法找出来。你必须记住它们,或者写下来,或者其他什么。
为了解决最后一个问题,Git 提供了一个单独的键值数据库,该数据库以名称为键:分支名称、标记名称和许多其他类型的名称。在此数据库中,与任何给定名称关联的值都是一个哈希 ID。你只得到一个哈希ID\xe2\x80\x94,而不是两个、三个或多个,只有一个\xe2\x80\x94,但这就是我们所需要的,因为我们只需要存储最新提交的哈希ID ,例如,犯罪H。
由于我们说提交H“指向”其父级G,因此我们同样说分支名称指向分支中的最后一个提交。Git 对此的术语是Htip commit,我们可以将它添加到我们的绘图中,如下所示:
...--G--H <-- main # or master or whatever\nRun Code Online (Sandbox Code Playgroud)\n当我们“位于”某个分支时,我们将一个特殊名称 \xe2\x80\x94 HEAD\xe2\x80\x94 附加到该分支名称。(Git 实际上只是将分支的名称存储在.git命名的文件HEAD中,至少对于主工作树来说是这样,尽管您不应该依赖于此,以防未来版本的 Git 出现更好/更奇特的版本这意味着如果我们有多个分支名称,所有分支名称都指向提交H\xe2\x80\x94 这在 Git\xe2\x80\x94 中是完全正常的事情,那么当我们添加提交时,只有分支HEAD-名称已更新。我们可以从以下开始:
...--G--H <-- feature (HEAD), main, zorg\nRun Code Online (Sandbox Code Playgroud)\n然后进行一个新的提交\xe2\x80\x94,它会获得一些新的、唯一的哈希 ID,但我们将其称为“提交I”\xe2\x80\x94,并且该git commit命令使 Git 将新的哈希 ID 存储在当前分支中name,这样我们就得到:
...--G--H <-- main, zorg\n \\\n I <-- feature (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n特殊名称HEAD仍附加到当前分支名称。feature现在选择该名称I作为其提示提交;I向后指向H,因为这H 是feature我们制作 时的提示I;等等H都G没有改变(它们必须是,因为它们都是只读的)。下一次提交再次更新当前分支名称:
...--G--H <-- main, zorg\n \\\n I--J <-- feature (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n新提交J指向I,它又指向H,依此类推。 I一旦我们完成就无法更改:任何提交的任何部分都不能更改。请注意,虽然有些人将提交称为I-J“什么feature”,但事实上,所有提交都在feature:只是提交到并包括H也在其他分支上,而仅I-J在at此时此刻。feature
git fetch,或者,提交是通用的,但分支名称不是当我们克隆一个存储库时,我们复制它的所有提交2,而不复制它的任何分支名称。我们不是复制它的分支名称,而是获取它们的每个(另一个存储库的)分支名称并将其更改为远程跟踪名称:例如,他们main成为 our ,origin/main他们feature成为 our 。origin/feature然后,我们的 Git 将在新存储库中创建一个分支名称,其中包含所有提交和这些修改后的名称。新的分支名称将匹配其分支名称之一,并选择相同的提示提交作为其名称,因此我们的图片可能如下所示:
...--G--H <-- main (HEAD), origin/main\n \\\n I--J <-- origin/feature\nRun Code Online (Sandbox Code Playgroud)\n取决于我们运行git clone创建Git 存储库时他们的Git 存储库中的内容。
我们现在也可以创建自己的feature名称,并切换到它:
...--G--H <-- main, origin/main\n \\\n I--J <-- feature (HEAD), origin/feature\nRun Code Online (Sandbox Code Playgroud)\n如果我们做出新的提交,它们就会添加到我们的 feature. 我们对他们的 feature记忆仍然指向我们要做出的承诺J:
...--G--H <-- main, origin/main\n \\\n I--J <-- origin/feature\n \\\n K--L <-- feature (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n它们看起来完全像分支,因为它们完全像分支,这取决于我们所说的分支的含义(另请参阅“分支”到底是什么意思?)。在某种程度上,“分支”意味着“通过从某个名称开始并向后工作找到的提交集”,远程跟踪名称可以很好地作为分支。(但它也不是一个分支,因为你不能到达git switch它。那么它是一个分支吗?这取决于你所说的分支的意思。分支这个词的问题就是为什么你在说“分支”时必须小心\xe2 \x80\x94你可能知道你的意思,但其他人会吗?你确定你知道你的意思吗?人类的认知是松散的,草率的措辞可能会得出不好的结论。)
无论如何,您的远程跟踪名称反映了其分支名称,截至您的 Git 软件上次调用其 Git 存储库并向其询问这些分支名称时为止。但如果已经过去了几个月、几天、几小时、甚至几秒钟,那么他们的存储库可能已经发生了变化。要重新同步,您运行git fetch:
因此,在 之后 git fetch,如果他们提供了新的提交,您可能会:
...--G--H <-- main, origin/main\n \\\n I--J----N--O <-- origin/feature\n \\\n K--L <-- feature (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n正如您所记得的那样,您feature现在落后于他们,因为您的承诺并不在您身上。您的提交也领先于他们:您的. 但“背后”可能是一个问题。featureorigin/featureNOfeaturefeaturefeatureK-L
2嗯,全部或大部分:我们不会在这里进行细微的区分。请注意,这有点过于笼统。
\ngit merge运作让我们退后一步,考虑一下您自己的个人存储库。假设您有两个分支名称br1和br2,排列如下:
I--J <-- br1\n /\n...--G--H\n \\\n K--L <-- br2\nRun Code Online (Sandbox Code Playgroud)\n您想用来git merge将这两个分支绑在一起。您选择其中之一并切换到它,例如,git switch br1或git checkout br1:
I--J <-- br1 (HEAD)\n /\n...--G--H\n \\\n K--L <-- br2\nRun Code Online (Sandbox Code Playgroud)\n然后你就跑git merge br2。最终结果是:
I--J\n / \\\n...--G--H M <-- br1 (HEAD)\n \\ /\n K--L <-- br2\nRun Code Online (Sandbox Code Playgroud)\n之后您可以随意删除该名称 ,因为现在可以从 commit 中找到br2它的提示提交。您不必删除该名称,但您可以:分支名称的要点是能够找到一些提示提交,只有当您计划再次使用该提交或添加到它,或两者兼而有之。LM
为了进行提交M,Git 必须组合工作。这种组合工作技巧需要使用git diff两次,并且要实现该工作需要在两个分支上找到一些合适的提交。在这种特殊情况下,很容易看出提交H同时在br1和上br2 ,并且是最合适的提交:Git 将其称为两个分支提示提交的合并基础。所以 Git 现在运行:
git diff --find-renames <hash-of-H> <hash-of-J> # what we changed on br1\ngit diff --find-renames <hash-of-H> <hash-of-L> # what they changed on br2\nRun Code Online (Sandbox Code Playgroud)\n然后合并代码组合两组更改,将组合的更改应用到提交H\xe2\x80\x94这将它们的更改添加到我们的更改中,或者将我们的更改添加到他们的更改中,具体取决于您想要如何查看它\xe2\ x80\x94,如果一切顺利,git merge则继续进行合并提交 M。
它的特别之处M在于它有两个父母。 这是唯一的特别之处M!它与任何其他提交在其他方面都一样:它具有所有文件的快照,并且具有元数据。快照包含合并库中由组合更改修改的文件。元数据像往常一样包含您的姓名和电子邮件地址,以及像往常一样的当前日期和时间,并且\xe2\x80\x94这是唯一的特殊部分\xe2\x80\x94两个父哈希ID的列表,而不仅仅是一。
这使得M找到两个父提交,这就是我们可以删除 的原因br2。但我们不必删除br2。br2如果我们愿意,我们可以继续做出更多承诺:
I--J\n / \\\n...--G--H M <-- br1\n \\ /\n K--L---N--O <-- br2 (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n如果我们现在切换回br1并再次运行git merge br2,我们将得到另一个合并提交:
I--J\n / \\\n...--G--H M------P <-- br1 (HEAD)\n \\ / /\n K--L---N--O <-- br2\nRun Code Online (Sandbox Code Playgroud)\n这次将提交两个差异的合并基础(这不太明显),并且两个提示提交将是和,这是进入新合并提交的文件的源。最终结果是 Git 不必单独考虑或中的更改,也不必从 开始:而是从 开始。更多的重复工作和合并会导致更多相同的结果:LMOPI-JKHL
...--M-----P-----T <-- br1 (HEAD)\n / / /\n...--L--N--O--R--S <-- br2\nRun Code Online (Sandbox Code Playgroud)\n制作的合并基础T是O,两个提示提交是P和S。
简而言之git merge:我们组合自某个共同起点以来的更改,通过检查提交到提交的连接发现,并与两个父级进行新的合并提交,这样,如果存在下一个合并,则下一个合并可以从我们停下来的地方开始。
git pull一旦您正确理解了提交、git fetch和git merge,默认的git pull\xe2\x80\x94 基本上只是运行git fetch,然后git merge按该顺序 \xe2\x80\x94 很简单。当然git pull,如果没有很多额外的选项和功能,就不会是现在的样子,但如果您还没有使用过它们,我们可以到此为止。
我总是建议 Git 新手避免, git pull因为:
Fails太强了,这就是为什么我把它放在引号中,但是 和 都git merge可以git rebase停在中间。合并比变基简单得多(我们稍后会看到),因此它很可能是更好的默认值,但无论哪种方式,您都必须知道如何在失败后进行清理,否则您就会遇到麻烦\xe2 \x80\x94并且不幸的是(并且出于不好的原因),清理方法有所不同,因此您需要知道您正在使用哪一种。如果您是 Git 新手,您甚至不知道您正在运行哪一个:您的默认值取决于您的配置,如果您正在与一个团队合作(这很常见),他们可能会问您将默认值设置为使用git rebase.
如果您运行git fetch,然后单独运行第二个命令,您将更好地了解自己在做什么,从而更好地了解如何获取帮助。这并不是真的更容易\xe2\x80\x94我曾经有一位同事,他将这称为在\xe2\x80\x94周围推动“硬”,但是......它在某种程度上更容易。(也许它只是提供更多信息,但它似乎确实有帮助。)
git rebase我们经常发现,在使用 Git 时,我们迄今为止所做的一些提交是……嗯,它们还可以,但并不是我们真正想要的。但我们这里有一个大问题:提交实际上是不可能改变的。一旦制作完成,它们就一成不变。
\n但是:如果我们可以将一些提交复制到一些新的和改进的提交呢?假设我们有:
\n...--G--H <-- main\n \\\n I--J--K <-- feature (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n我们已经完成了我们的工作feature,但我们刚刚注意到 中有一个错误I,因此我们再次提交来修复它:
...--G--H <-- main\n \\\n I--J--K--L <-- feature (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n现在,我们唯一的 理由L就是修复I. I如果我们一开始就没有把这个 bug 放进去,那就太好了。我们可以做到这一点。我们无法更改commit I,但我们可以在 \xe2\x80\x94let 中创建一个新的组合提交,并将其称为“commit ” \ xe2\x80\x94,之后是:ILILH
IL <-- temporary-branch (HEAD)\n /\n...--G--H <-- main\n \\\n I--J--K--L <-- feature\nRun Code Online (Sandbox Code Playgroud)\n我们通过创建一个新的分支名称 来实现这一点temporary-branch,它指向提交H(与 相同main),然后通过任何方式进行新的提交(稍后我们将回到“通过任何方式”)。然后我们进行另一个新的提交J,这是J所做的重复,但应用于IL. 我们将调用这个新的提交,J\'因为它与以下内容非常相似J:
IL-J\' <-- temporary-branch (HEAD)\n /\n...--G--H <-- main\n \\\n I--J--K--L <-- feature\nRun Code Online (Sandbox Code Playgroud)\n最后,我们复制K到一个新的K\':
IL-J\'-K\' <-- temporary-branch (HEAD)\n /\n...--G--H <-- main\n \\\n I--J--K--L <-- feature\nRun Code Online (Sandbox Code Playgroud)\n我们的临时分支现在保存了我们希望做出的三个提交。
\n现在,等一下:不久前,我们观察到我们通过使用分支名称来查找提示提交来查找提交。每个分支名称仅包含一个哈希 ID。如果我们强制名称feature选择 commitK\'而不是 commit会发生什么L?我们会得到这个:
IL-J\'-K\' <-- feature, temporary-branch (HEAD)\n /\n...--G--H <-- main\n \\\n I--J--K--L [abandoned]\nRun Code Online (Sandbox Code Playgroud)\n提交I-J-K-L仍然存在,但如果没有用于查找它们的名称,我们将永远不会看到它们。我们现在可以再次附加并完全删除临时分支名称:HEADfeature
IL-J\'-K\' <-- feature (HEAD)\n /\n...--G--H <-- main\n \\\n I--J--K--L [abandoned]\nRun Code Online (Sandbox Code Playgroud)\n看起来我们从一开始就做对了一切。
\n让我们考虑另一种情况,我们I-J-K完美地进行了提交,但其他人已经加入并添加了一个新的内容L到我们想要的main. 我们得到自己的main更新:
...--G--H--L <-- main\n \\\n I--J--K <-- feature (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n现在我们不喜欢的一件事I-J-K是他们紧随其后H:如果他们紧随其后L,他们就会完美。我们可以使用完全相同的技巧,创建一个临时分支,复制我们喜欢的三个提交,并使名称feature指向最终的提示提交:
I\'-J\'-K\' <-- feature (HEAD)\n /\n...--G--H--L <-- main\n \\\n I--J--K [abandoned]\nRun Code Online (Sandbox Code Playgroud)\n废弃的提交从视图中消失(尽管它们仍然存在于存储库中),并且似乎我们在L存在后才开始工作。
在这两种情况下,我们正在做的事情都相对简单,尽管它会产生很多后果:我们正在复制\ xe2\x80\x94 并在它们正式生效之前进行一些更改或更改\xe2\x80\x94一些现有的提交是某种新的和改进的提交。Git 提供了一个主要的“强大工具”命令来执行此操作,即git rebase.
最基本的工作形式是不对每次提交更改的git rebase内容进行任何更改。例如,我们将其用于简单的-on-变为-on-操作。每个“复制一个提交”步骤都是通过一个简单的命令完成的。3I-J-KHI\'-J\'-K\'Lgit cherry-pick
但有一个问题:cherry-pick 实际上无法复制合并提交。如果您尝试这样做,您会收到一条错误消息,告诉您必须提供该-m选项。此选项告诉git cherry-pick假装提交是普通的单父提交,而不是合并。4git rebase这个问题 最初的解决方案是完全忽略合并提交。
也就是说,如果我们有:
\n I--J--M <-- feature (HEAD)\n / /\n...--G--H--K--L <-- main\nRun Code Online (Sandbox Code Playgroud)\n我们运行git rebase main,完全git rebase 忽略提交。M它复制只是提交I-J,将副本放在后面L:
I--J--M [abandoned]\n / /\n...--G--H--K--L <-- main\n \\\n I\'-J\' <-- feature (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n事实证明,这往往正是我们想要的。
\n如果这不是我们想要的,那么我们可以使用 new-in-Git-2.18git rebase --rebase-merges选项。这根本不会尝试复制合并。相反,它复制导致合并的提交,然后git merge重新运行以进行新的合并提交,然后根据需要继续复制更多提交。但由于我们想要删除合并,因此我们可以忽略这个新奇的选项。
我们还有git rebase --interactive,它提供了进行压缩和修复操作以及移动提交的能力。pick说明书中的每个命令git rebase --interactive对应于一个单独的git cherry-pick命令。将其更改为squash意味着之前的操作应该执行cherry-pick但尚未提交,而不是执行正常的cherry-pick-and-commit,然后应该添加此提交,然后我们将提交。
所有这些奇特的选项都只是:添加到基本想法的奇特选项,我们将复制一些提交。那么,从根本上来说,git rebase需要了解两件事:
默认情况下,该git rebase命令巧妙地将这两个问题合二为一。我们首先观察到,当我们运行时git merge,我们总是合并到当前分支中。类似地,当我们运行时git rebase,我们总是从当前分支复制提交。5 仅仅说“好吧,我们想要当前分支上的提交”的问题是当前分支上可能有数百甚至数千个提交。当前分支一直追溯到第一次提交!我们不想复制所有这些提交。我们想要复制这些提交的选定子集。
例如,在上面的示例中,我们要复制的选定提交子集是I-J-M,但它M是合并,因此我们毕竟不想复制它。剩下的I-J就是提交复制。
Git 有一个时髦的语法 ,X..Y这意味着所有提交都可以从 到达Y但不能从 到达X。这种“可从”概念是您应该了解的关于 Git 的另一件事:请参阅Think Like (a) Git等。在我们的例子中,main..feature生成完全正确的提交列表:I、J,然后M。6
由于我们总是要从当前分支复制提交,因此git rebase不需要您写下名称feature。它只需要您输入名称main,以便它可以构建main..feature自身并获取要复制的提交列表。 \ xe2\x80\x94 这是聪明的部分\xe2\x80\x94这个名称main也选择了正确的位置来放置副本! 我们要I\'-J\'在commit之后进行L,名称main选择commit L。
所以我们只需运行:
\ngit rebase main\nRun Code Online (Sandbox Code Playgroud)\n或者也许git rebase -i main是为了获得互动多样性,Git 知道:
main..feature,和main这就是我们所需要的!
\n有时这不太有效。对于这些情况,git rebase有--onto. 我们跑:
git rebase --onto <place> <what-not-to-copy>\nRun Code Online (Sandbox Code Playgroud)\n复制的提交列表用于*what-not-to-copy..HEAD确定不复制的内容。也就是说,对于X的部分,我们没有说的X..Y部分就是 的部分。然后放置副本的位置就是部分,这就是我们运行的方式。--ontoX--ontogit rebase
3现代 Git 也是如此。在旧版本中,一些变基使用了cherry-pick,有些则没有,而在非常古老的 Git 中,所有变基都没有。显然,挑选比其他方法效果更好,这是这里要记住的想法。
\n4其自身的参数-m是用于此假装操作的父数字1,并且大多数情况下它只是常量,原因我们不会介绍,因为我们这里没有空间。
5有一个变体git rebase使它git switch首先运行,以切换到其他分支。您需要知道的是,这git switch就像您将其编写为单独的命令一样。然后,在这工作之后git switch(假设它确实工作),git rebase使用当前分支工作。当整个事情完成后,你会到达你切换到的分支,就像你git switch自己运行一样。
由于“假装您首先运行了一个单独的命令”方面,我建议避免这种情况,至少在您真正熟悉其他所有内容之前。它有点类似于git pull:如果您没有意识到这与运行单独的命令相同,您可能会惊讶地发现这git switch br2 && git rebase --onto x y z会让您处于分支z而不是br2。你的git switch br2有点毫无意义,因为这相当于git switch br2 && git switch z && git rebase --onto x y.
6实际上,就像 Git 中的其他所有内容一样,它以错误的顺序生成它们:向后。对于挑选,我们需要它们按照 Git 的错误顺序进行转发。所以rebase代码--reverse在内部使用。它还使用--topo-orderand --no-merges,但由于篇幅原因我们不会讨论这些细节。
git rebase有时您不应该使用git rebase. 这些主要归结为 rebase 通过复制来工作的事实。请记住,git fetch它本身也通过复制提交来工作,并且git clone通过复制提交来工作。我们有我们的分支名称,他们\xe2\x80\x94其他一些Git存储库\xe2\x80\x94有他们的分支名称,我们复制一些提交并调整我们的分支名称以找到新副本。
我们不能 强迫其他人调整他们的分支名称以指向新复制的提交而不是原始提交。我们可以用 来(礼貌地)询问他们git push,我们可以用 来命令他们git push --force,但他们甚至可以拒绝强硬的git push。而且,即使我们让另一个 Git 存储库将其分支名称切换为指向新的和改进的提交,如果还有其他克隆仍然具有旧的和糟糕的提交,并将哈希 ID 存储在分支中,该怎么办?名字?
因此,如果其他人正在使用旧的提交,我们通常不应该使用变基来改进提交。我们可以做到,如果我们得到所有相关“其他人”的预先批准,那也没关系。但是,如果我们对从未让其他人看到的提交进行变基,那么我们就保证是安全的,因为在这种情况下,没有其他人使用旧的提交。
\n一般来说,如果我们没有习惯将git push这些提交发送给其他人,那么重新设置我们自己的分支名称和提交的基础是“安全的”。即使我们使用了git push这种方式,如果我们确定没有其他人依赖于旧的提交哈希 ID,它仍然是安全的。如果有人确实依赖这些,如果我们提前与其他人做出安排,那就更安全了。
剩下的只是(仅仅是?)管理:跟踪谁有哪些提交。令人遗憾的是,Git 在这方面没有做得更好,但 Git 的提交永久不可变的想法关闭了很多可能性。(我认为 Mercurial 的“evolve”扩展利用了一些插入到其提交中的可变位:他们定义了一种提交格式,其中哈希 ID 不覆盖提交的每一个位。还有一些其他分布式数据库此处提供了一些技术,但 Git 也不使用这些技术。)
\n| 归档时间: |
|
| 查看次数: |
2704 次 |
| 最近记录: |