首页 > 解决方案 > 在我“压缩并合并”我的代码后,我的旧 git 用户名会保留为作者吗?

问题描述

我有这个问题。

我有一个代码,我做了大约 30 次提交......

在提交 28 时,我意识到我输入了错误的用户名,所以我更新了它git config --global user.name

我试图避免所有以前的提交,我想知道如果我squash and merge从他们的 UI 中使用 github.com,我之前的 30 次提交是否应该是一个,然后应该是我更正的用户名的地址?

如果有人使用注释命令怎么办?它会显示我的用户名有错别字还是我的最终用户名?

标签: git

解决方案


TL;博士

正如 Tim Biegeleisen 所指出的,Git 提交的作者和提交者是提交本身的一部分,现有的错误地址提交不会改变。但是,您可以停止使用它们,这就是git merge --squash启用的原因。因此答案是:是的,这可以解决问题不,这不会解决问题,这取决于您如何看待它以及您在提交期间和之后所做的事情。git merge --squash

基本上,在 squash-merge 之后,您只需删除进行这 30 次提交的分支。但是,您可能必须从两个地方删除它:您机器上的 Git,以及 GitHub 上的 Git(大概是您的,但也不是——毕竟,GitHub 是控制它的人) .

你提到:

从他们的 UI 使用 github.com

当您从命令行(在 Windows 或 Linux 或 MacOS 或其他)使用 Git 时,您通常首先从某个地方克隆存储库:

$ git clone https://github.com/path/to/repo.git
$ cd repo

例如(尽管我更喜欢ssh://git@github.com/...URL)。现在有两个Git 存储库:一个在 GitHub 上,一个在您自己的机器上本地。

压缩、获取、推送和一般的多个 Git 存储库

一般来说,这里有两件事要知道:如何git merge --squash工作,以及这两个不同的 Git 如何通信和共享提交。特别是,无论您如何进行,这都有两个部分:您将首先进行一个新的提交,然后您将使用或转移git pushgit fetch的提交(可能还有旧的提交,或者不) 在两个独立的 Git 之间。

您使用 Git 在您的机器上所做的任何提交都使用在计算机上所做的设置——但如果您使用 GitHub 上的 Web 界面,并单击那里的各种按钮,您正在使用他们的机器在他们的Git中进行提交存储库,它将使用他们为您存储的设置。因此,如果您转到具有侧面下拉箭头的按钮,可让您在mergerebase-and-merge和之间进行选择squash-and-merge,选择这三个中的任何一个,然后单击它,他们的Git 会在他们的Git 存储库和您设置的所有设置中进行新的提交在您自己的计算机上的 Git 中是无关紧要的。

您可以git merge --squash在自己的机器上执行,然后使用git push,以便您在本地进行提交,然后才将它们发送其他 Git。是否以及何时这样做取决于您。

有关进行任何新提交的一些背景知识

您首先选择一些分支名称和一些提示提交,或者通过显式运行:

git checkout somebranch

或者隐含地因为您在之前的某个时候这样做了,所以无论如何,在这个特定的存储库中,您现在位于分支上somebranch,根据定义,它有一些最新的或提示的提交:

...--o--o--o  <-- somebranch (HEAD)

每个提交都有一些大而丑陋的哈希 ID,这意味着特定的提交:任何 Git 存储库都可以立即判断它是否有那个提交,如果它确实有那个哈希 ID 的提交,它就有那个确切的提交。宇宙中所有的 Git 都同意:包含这些内容的提交——那个快照、那个用户名和电子邮件地址、那些日期戳、那个日志消息等等——将具有哈希 ID,并且没有其他提交将具有哈希 ID。哈希 ID。

该名称somebranch在其中包含该提交的哈希 ID,您可以执行以下操作:

git rev-parse somebranch

查看该哈希 ID。您还将在输出中看到该哈希 ID 或其缩写git log版本git log --oneline。存储在该分支名称中的哈希 ID 是 Git 如何知道您当前的分支somebranch以该特定提交结束的方式。

该提交内部是其直接前任或提交的哈希 ID。这就是 Git 从提示提交到前一步提交的方式。在 parent 内部是当前提交的祖父母的哈希 ID,因此 Git 可以从 parent 走到祖父母。祖父母有另一个哈希 ID,所以 Git 可以走到曾祖父母那里,依此类推。这个哈希 ID 链允许 Git 找到所有提交,从头开始并向后工作。

因此,如果我们有一些以 hash 结尾的提交链H

... <-grandparent <-parent <-H   <-- branch-name

然后我们去进行一个新的提交,Git实际进行新提交的方式是将提交快照打包,加上作者姓名和日志消息等,将其父哈希设置为H,并将其写出来。因为这个提交的内容是唯一的——父哈希H,加上一个时间戳,Git 添加了一个时间戳,告诉你进行新提交时的确切时间,确保它是唯一的——新提交获得一个新的唯一哈希ID。让我们称之为I

... <-parent <-H <-I

现在 Git 所要做的就是将新提交的哈希 ID 写入I分支名称,这样提交I就是提示。

换句话说,Git 从每个分支的末尾开始,然后向后工作。所以这种链允许 Git 找到包含在这个分支中的所有提交。一些提交可能只包含在其他一些分支中:

       B--C   <-- master
      /
...--A
      \
       D--...--G--H--I   <-- somebranch (HEAD)

在这里,有一堆提交(例如,大约 30 个提交)on somebranch,其中两个提交on master,许多提交都在两个分支上。(如果算上7 个提交的空间;好吧,让我们假设在和:-)D-E-F-G-H-I-J之间有一堆新字母)。提交时通过有错误的作者姓名提交并有正确的作者姓名。我们现在的计划是完全停止使用all through 。DHDHIJDH

使用git merge --squash

让我们看看究竟是如何git merge --squash工作的。本质上,它与常规合并所做的事情相同,只是在最后。常规合并从查找哪个提交是最佳共享提交开始,即两个分支上的最佳提交。这个最佳共享提交是两个分支的合并基础

我们检查一个分支,然后在我们的git merge --squash命令中命名另一个:

$ git checkout master                   # note: this attaches HEAD to master
$ git merge --squash somebranch

Git 检查构成此存储库中历史记录的提交链,以找到最佳共享提交。在我们的例子中,随着返回提交的历史和返回master提交A的历史,提交是最好的共享提交。(之前的所有提交也都是共享的:它们只是不如.)somebranchAA AA

接下来,Git 将合并基础中的快照(在 commitA中)与我们当前的分支提示 commit 中的快照进行比较C

git diff --find-renames <hash-of-A> <hash-of-C>   # what we changed in master

找出“他们”(实际上,我们)在另一个分支中更改的内容也是如此:

git diff --find-renames <hash-of-A> <hash-of-I>   # what they changed

然后 Git 会尽力将这两组更改组合起来,将组合的更改应用到来自 commit 的快照A。如果一切顺利,这个组合就是要使用的快照。如果进展不顺利,Git 将因合并冲突而停止,并从用户那里获得帮助以修复并完成它。

(更准确地说,你的Git 会这样做。GitHub 上基于 Web 的 Git 只会说对不起,我不能这样做,甚至根本不让你开始合并——他们在使合并按钮可点击之前先进行检查。它与普通的 Git 有点不同,因为它无法让您与之交互以解决冲突。GitHub 的人确实有一个桌面客户端,但我从未使用过它;我不知道它是否是一个包装器命令行 Git,或者更高级的东西。)

我们在这里假设一切顺利,或者您已修复所有冲突并运行git merge --continuegit commit完成合并。Git 现在做出一个提交,而不是通常的一个父级,两个父级:

       B--C--------------J   <-- master (HEAD)
      /                 /
...--A                 /
      \               /
       D--...--G--H--I   <-- somebranch

新提交进入分支master,因为HEAD附加到master(由于我们的git checkout master上述)。新的提交J指向 commit C,即作为master;提示的提交 但它也指向了I提交,即仍然是提示的提交somebranch。提交CI现在都 on master,因为这个额外的第二个父级导致返回 commit I

使用git merge --squash时,主要区别在于新提交J 没有第二个父项:

       B--C---------------J   <-- master (HEAD)
      /
...--A
      \
       D--...--G--H--I   <-- somebranch

其他一切都完全相同: 的快照J、作者和提交者(由 Git 实际提交的任何一个设置J)、时间戳(同样)和提交的父哈希 ID C。但是 new commit 没有第二个父母J

如果您希望提交D消失I,您现在只需删除分支somebranch名称 somebranch是您的 Git 查找这些提交的方式:它首先从名称中提取哈希 ID ,I然后somebranch使用Ifind HHto findG等等,一直到您到达A. 名字somebranch会让你到达A,名字也会master。因此,删除名称 somebranch会使提交DI视图中消失。最终——通常是在 30 天左右之后的某个时间——你的 Git 将垃圾收集这些提交。

但是,您再次提到“使用 [the] github.com ... UI”。如果你打算这样做,你必须先将提交发送DGitHub ,或者你可能已经发送了它们。I

git fetch使用and传输提交git push

您可以随时让一个 Git 将自己连接到另一个 Git,然后从另一个 Git 获取 ( fetch) 或发送 ( push) 提交。然后,从您自己计算机的 Git 中,您使用 获取 GitHub-Git 的提交git fetch,然后使用 将提交发送该 Git git push

实际的传输协议有很多细微之处,但一开始很简单:你的 Git 和他们的 Git 进行对话,其中一个告诉对方它可以发送的提交的哈希 ID,另一个说:我已经有那个了,或者哦,我要那个。所以,假设有:

...--A
      \
       D--...--G--H--I   <-- somebranch (HEAD)

他们有:

       B--C   <-- master (HEAD)
      /
...--A

(不同HEAD的 s 是因为这是两个不同的 Git,毕竟)。如果您使用 将您的 Git 连接到他们的git push origin somebranchGit,您的 Git 将枚举您的提交哈希 ID IH等返回到A. 当你的 Git 达到 的哈希 ID 时A,他们会说:好的,停止,我有那个! 然后,您的 Git 将打包提交DH发送过来。

最后,您的 Git 添加了一个最终请求:请设置您的分支名称somebranch以记住哈希 ID I他们的 Git 还没有somebranch,所以他们对此很好并做到了,你的 Git 和他们的 Git 完成了,你断开了连接。

您也可以在此之前或之后运行git fetch origin. 在这里,您的 Git 调用了他们的 Git,但这次他们的 Git 是发送者。他们会给你发送提交哈希C,说:master有这个哈希,你有提交C吗? 如果你不这样做,他们也会提供Band then A,你确实有。

如果你运行git fetch origin master,你的 Git 将只接受他们有的提交,master而你根本没有,但是使用,你的 Git 将从他们的任何git fetch origin分支名称中接受你没有的任何提交:他们将列出所有其中,连同提交哈希 ID,您的 Git 可以并且将会贪婪地要求所有这些。

无论如何,在此过程结束时,没有单独的礼貌请求:您的 Git 只是说:好的,我现在知道他们master识别了 commit C,所以我将更新my origin/master以使其识别 commit C 所以你在你的存储库中得到了这个:

       B--C   <-- origin/master
      /
...--A   <-- master
      \
       D--...--G--H--I   <-- somebranch (HEAD)

你自己master的现在在他们的后面有两个提交,所以master你现在可以运行:

$ git checkout master

这将您移动HEAD到您的master

       B--C   <-- origin/master
      /
...--A   <-- master (HEAD)
      \
       D--...--G--H--I   <-- somebranch

然后运行:

git merge origin/master

(或git merge --ff-only origin/master)。这注意到 yourmaster和 your之间的共同合并基础origin/master是 commit - the Atip of -master所以它执行 Git 所谓的快进操作,而不是真正的合并:它只是直接检查提交C,然后将你移到master那里:

       B--C   <-- master (HEAD), origin/master
      /
...--A
      \
       D--...--G--H--I   <-- somebranch

请注意,git fetch更新您的origin/*名称以匹配他们的 Git,同时git push以以下形式的礼貌请求结束:请将您的实际分支名称设置为这些特定的提交。这意味着您始终可以运行git fetch:它根本不会影响您的分支。但是你有时无法实现一个平原git push,因为他们有时会拒绝改变他们的分支。(我不打算在这里详细介绍,因为这已经很长了,但这就是git push --force目的:它将礼貌的请求变为命令。他们不必服从命令,但他们可能会。 )

关于git pull

git pull命令在 Git 中有点奇怪。它的作用是运行git fetch,然后运行第二个 Git 命令。第二个命令通常是git merge. 您可以告诉它使用git rebase,或者一次性使用,或者一般使用。我更喜欢避免 git pull并分别运行这两个命令,因为这样我可以在选择要运行的任何命令之前查看git fetch获取的内容。如果您让git pull运行第二个命令,则必须先选择要使用的第二个命令,然后才能知道git fetch实际获取的内容。

但是很多人使用git pull,所以你应该知道它的作用。在这种情况下,它可能是一个陷阱,正如我们将看到的。

假设你在 GitHub 上 squash-merge

为了在 GitHub 上进行squash-merge,您首先必须将所有somebranch提交发送给他们:

$ git push origin somebranch

他们现在有:

       B--C   <-- master (HEAD)
      /
...--A
      \
       D--...--G--H--I   <-- somebranch

在他们的 Git 中。现在,您使用 GitHub Web 界面发出拉取请求,然后转到 clicky 按钮并选择squash-and-merge并单击它:

       B--C----------J   <-- master (HEAD), origin/master
      /
...--A
      \
       D--...--G--H--I   <-- somebranch

您现在需要做的是删除 somebranch无论是在 GitHub 上还是在您自己的 Git 存储库中。如果你这样做了,你就很好了:30 个(或许多)提交,现在仍然是,只有在somebranch(在两个 Gits 中)不再可见。(旁注:GitHub 倾向于立即删除他们的副本,而您的 Git 会等待 30 天或更长时间。)

但是,如果您或任何人不小心与合并 ,您现在会得到一个新的合并提交:somebranchmaster

       B--C----------J
      /               \
...--A                 K
      \               /
       D--...--G--H--I

请注意,任何人都J可以在创建后和D-through-I消失/不可见之前的任何时间犯此错误。由于后面跟着git pull运行,使用.很容易犯这个错误。git fetchgit mergegit pull

具体来说,如果你独自一人somebranch运行git pull origin master,你会得到这个错误。幸运的是,这个很容易修复。你现在有这个:

       B--C----------J    <-- master
      /               \
...--A                 K   <-- somebranch (HEAD)
      \               /
       D--...--G--H--I

你仍然只需要删除分支somebranch,现在提交DK不可见的,最终会消失:

$ git checkout master
$ git branch -D somebranch

checkout是因为你不能删除你所在的分支;(大写的-DDelete,实际上是)是强制删除的方式,即使git branch通常会抱怨您将失去提交D-through- K(这当然是想法)。


推荐阅读