我有这个问题。
我有一个代码,我做了大约 30 次提交......
在提交 28 时,我意识到我输入了错误的用户名,所以我更新了它git config --global user.name
我试图避免所有以前的提交,我想知道如果我squash and merge
从他们的 UI 中使用 github.com,我之前的 30 次提交是否应该是一个,然后应该是我更正的用户名的地址?
如果有人使用注释命令怎么办?它会显示我的用户名有错别字还是我的最终用户名?
我有这个问题。
我有一个代码,我做了大约 30 次提交......
在提交 28 时,我意识到我输入了错误的用户名,所以我更新了它git config --global user.name
我试图避免所有以前的提交,我想知道如果我squash and merge
从他们的 UI 中使用 github.com,我之前的 30 次提交是否应该是一个,然后应该是我更正的用户名的地址?
如果有人使用注释命令怎么办?它会显示我的用户名有错别字还是我的最终用户名?
正如 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 merge --squash
工作,以及这两个不同的 Git 如何通信和共享提交。特别是,无论您如何进行,这都有两个部分:您将首先进行一个新的提交,然后您将使用或转移git push
新git fetch
的提交(可能还有旧的提交,或者不) 在两个独立的 Git 之间。
您使用 Git 在您的机器上所做的任何提交都使用您在计算机上所做的设置——但如果您使用 GitHub 上的 Web 界面,并单击那里的各种按钮,您正在使用他们的机器在他们的Git中进行提交存储库,它将使用他们为您存储的设置。因此,如果您转到具有侧面下拉箭头的按钮,可让您在merge、rebase-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 。D
H
D
H
I
J
D
H
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
的历史,提交是最好的共享提交。(之前的所有提交也都是共享的:它们只是不如.)somebranch
A
A
A
A
接下来,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 --continue
或git 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
。提交C
和I
现在都 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
使用I
find H
、H
to findG
等等,一直到您到达A
. 名字somebranch
会让你到达A
,名字也会master
。因此,删除名称 somebranch
会使提交D
从I
视图中消失。最终——通常是在 30 天左右之后的某个时间——你的 Git 将垃圾收集这些提交。
但是,您再次提到“使用 [the] github.com ... UI”。如果你打算这样做,你必须先将提交发送到D
GitHub ,或者你可能已经发送了它们。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 somebranch
Git,您的 Git 将枚举您的提交哈希 ID I
、H
等返回到A
. 当你的 Git 达到 的哈希 ID 时A
,他们会说:好的,停止,我有那个! 然后,您的 Git 将打包提交D
并H
发送过来。
最后,您的 Git 添加了一个最终请求:请设置您的分支名称somebranch
以记住哈希 ID I
。他们的 Git 还没有somebranch
,所以他们对此很好并做到了,你的 Git 和他们的 Git 完成了,你断开了连接。
您也可以在此之前或之后运行git fetch origin
. 在这里,您的 Git 调用了他们的 Git,但这次他们的 Git 是发送者。他们会给你发送提交哈希C
,说:我master
有这个哈希,你有提交C
吗? 如果你不这样做,他们也会提供B
and 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 A
tip 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,您首先必须将所有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 天或更长时间。)
但是,如果您或任何人不小心与合并 ,您现在会得到一个新的合并提交:somebranch
master
B--C----------J
/ \
...--A K
\ /
D--...--G--H--I
请注意,任何人都J
可以在创建后和D
-through-I
消失/不可见之前的任何时间犯此错误。由于后面跟着git pull
运行,使用.很容易犯这个错误。git fetch
git merge
git pull
具体来说,如果你独自一人somebranch
运行git pull origin master
,你会得到这个错误。幸运的是,这个很容易修复。你现在有这个:
B--C----------J <-- master
/ \
...--A K <-- somebranch (HEAD)
\ /
D--...--G--H--I
你仍然只需要删除分支somebranch
,现在提交D
是K
不可见的,最终会消失:
$ git checkout master
$ git branch -D somebranch
这checkout
是因为你不能删除你所在的分支;(大写的-D
Delete,实际上是)是强制删除的方式,即使git branch
通常会抱怨您将失去提交D
-through- K
(这当然是想法)。
与 Git 提交关联的作者姓名是提交历史的一部分,因此,仅更改您的用户名 via不会git config
改变过去已经存在的任何提交。它只会影响你在未来做出的承诺。因此,这意味着搜索在您之前的用户名下进行的提交的人必须在搜索中使用之前的用户名。
不建议这样做,但如果您真的想重写一个或多个分支的历史以更改作者,请阅读如何更改提交作者以进行特定提交?.