Py学习  »  Git

git pull如何管理提交历史?

Rufat Imanov • 3 年前 • 1466 次点击  

假设我克隆了一个远程存储库,到目前为止它有1个commit=> A .然后,我向我的本地分支机构做出了两项承诺,因此它变成了=> A - B - C .然而,我的同事同时向他们当地的分支机构提交了另外两份提交,因此他们的提交历史记录变成=> A - D - E .然后他们将其推送到远程存储库。

然后我意识到我想推动我的改变,但是 git push 告诉我远程存储库就在我前面。所以,我知道 git pull .

我的问题是,现在我跟踪远程跟踪分支的本地分支看起来像什么?我知道会有合并冲突,但我真正的问题是:提交历史会是什么样子?

更具体地说,假设我修复了冲突,并且现在就实施了冲突,我的实施历史会是这样的吗 A - D - E - F A - B - C - D - E - F ? git中的提交历史是非线性的吗?

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/133730
文章 [ 3 ]  |  最新文章 3 年前
Jdeep
Reply   •   1 楼
Jdeep    4 年前

提交历史不一定是线性的 .假设你的朋友对某个文件进行了更改并将其推送。所以遥远的历史看起来像 A - D - E .如果您进行了其他更改,使您的提交历史记录 A - B - C ,然后,如果存在冲突,并且修复了这些冲突并将提交推送到远程,那么远程历史将如下所示:

  /--D---E-\
 A          P
  \--B---C-/

在这里 P

eftshift0
Reply   •   2 楼
eftshift0    4 年前

是的,如果这是一个正常的拉,将有一个合并与2个父分支。。。。比如说,你会有两个平行的分支。请用鼠标查看结果 git log --all --graph .作为旁白:冲突不是强制性的。它们的出现有很多原因,但当你合并的时候,它不是一个给定的。您可能会经常进行无冲突的合并。

torek
Reply   •   3 楼
torek    4 年前

最短的答案(不是100%准确,但非常接近)是 git pull 管理好历史。什么 git pull 对您来说,它的作用是运行两个Git命令,作为初学者,我建议您单独运行:

  • 第一 git pull 表演 git fetch .这个命令非常简单明了(但有些曲折)。它从其他存储库中获取新的提交:Git调用其他Git、Git和他们的Git exchange提交哈希ID,从中,Git发现你需要从他们那里获取哪些提交(以及相关文件),这样你就可以通过互联网以合理的最小数据量完成所有提交。

  • 一旦完成了, git pull 运行第二个Git命令。这就是大部分复杂性所在。(这些第二个命令往往有很多选项、模式和功能,所以它几乎就像运行十几个命令中的一个。)

这个 选择 第二个Git命令是你的,但是当你使用 git pull ,你被迫 制作 在你有机会看到什么之前 git fetch 行。我认为这是不好的(大写B不好,但不是粗体或斜体不好,所以只有中等程度的不好)。一旦你经常使用Git,并且知道fetch是如何工作的,也许更重要的是,你已经发现某些同事、同事或朋友是如何使用Git的——这些都会影响到什么 git fetch will doit可以在获取提交之前安全地决定如何集成获取的提交。但在早期,我认为这有点太过分了。 1.


1. 总是有可能 打开 第二个命令所做的事情,但你需要了解第二个命令的所有内容。作为初学者,您可能甚至没有意识到这里有两个不同的命令。你肯定不知道如何撤销每个命令的每个模式的每个效果。


之后你就有了正确的设置 git fetch

假设我克隆了一个远程存储库,到目前为止它有1个commit=> A .然后,我向我的本地分支机构做出了两项承诺,因此它变成了=> A - B - C .然而,我的同事同时向他们当地的分支机构提交了另外两份提交,因此他们的提交历史记录变成=> A - D - E .然后他们将[这个]推送到[共享的远程]存储库。

当他们把你揍一顿 git push 向共享(第三)存储库“wins”提交 在里面 共享的第三个存储库现在拥有 A-D-E 表格:

A--D--E   <-- main

(这里的分支名称并不那么重要,但我使用的是 main 因为GitHub现在使用它作为默认值,而您提到 在标签中。)

什么 git fetch 这一步让你明白了 D E .你已经承诺了 A. ,并且在提交后不能更改任何提交。 2. 所以你只需要 D-E ,它会像这样出现在您的存储库中:

  B--C   <-- main
 /
A
 \
  D--E   <-- origin/main

名字 origin/main 是你的Git的吗 远程跟踪名称 ,这是你的Git从他们的Git中创建的 树枝 名称 主要的 .您的Git会获取每个Git的分支名称并对其进行更改,以生成这些远程跟踪名称。因为远程追踪的名字不是 树枝 姓名,有什么变化吗 git fetch 让他们——处理其他Git存储库中发生的任何事情——不会影响任何 你的 树枝。因此,跑步总是安全的 git fetch . 3.

我画了承诺 A. 强调这只是一个承诺,由两条开发线共享。还有一些关于 树枝 是一个 发展路线 那不是吗 起源/主要 A. 树枝 差不多吧?这是“分支”的模糊定义, 4. 但事实证明,它在一瞬间是有用的。


2. 注意 git commit --amend 例如,实际上 改变 承诺。相反,它制造了一个 提交,并让您使用该提交,而不是您正在使用的其他提交。现在,您有两个几乎相同的提交,其中一个被推到一边并被忽略。

3. 可以 设置 git fetch ,或者给它一些理由,让它做“不安全”的事情,但这很难。通常的简单方法是创建镜像克隆,但镜像克隆是自动生成的 --bare 而且一个赤裸裸的克隆人不会让你在里面做任何工作。(镜像克隆只适用于特殊情况,不适用于普通的日常工作。)

4. Git对 树枝 故意 脆弱而模糊,小心地说 分支机构名称 相反分支名称定义明确,没有这种哲学上的模糊性。A. 远程跟踪名称 显然不同于 分支机构名称 ,尽管这两种名称都让Git找到提交,而提交本身形成了我们(人类)喜欢的“分支”。所以从这个意义上说, 起源/主要 是找到分支的名称。这不是一个好主意 树枝 名称:内部拼写为 refs/remotes/origin/main ,其中分支名称必须以开头 refs/heads/ 这个 树枝 名称 主要的 拼写正确 refs/heads/main 内部。另见 What exactly do we mean by "branch"?


第二条命令:你的选择 git merge git rebase

这个 第二 命令 git pull 跑步是大部分真实动作发生的地方。这不是 合并分支 git rebase . 5. 这些解决了你和你的朋友之间的分歧 git fetch .每一个都使用不同的方法。

从根本上讲,合并比重新调整基期简单。这是因为rebase由 抄袭 提交,就像通过运行 git cherry-pick 一些形式的 git rebase 字面上使用 吉特樱桃采摘 其他人则使用近似值,每个樱桃采摘本身就是一种合并。这意味着,当你重新设定三次提交的基准时,你将执行三次合并。rebase执行的复制之后是一个又一个内部Git操作,而许多形式的 合并分支 一步到位。


5. 从技术上讲 git pull 能跑吗 git checkout 在一种特殊情况下,但这种情况不适用于这里。


合并

从根本上说,合并是关于合并工作。

请注意,当我们遇到像上面所画的情况时,我们必须合并工作,其中有一些共同的起点(提交) A. )然后是发散的工作。然而,在有些情况下,“合并工作”并不重要:

A   <-- theirs
 \
  B--C   <-- ours

在这里,“他们”——不管他们是谁——实际上并不是 做什么工作 ,所以要“结合”你的工作和他们的工作,你可以让Git切换到你的最新提交:

A--B--C   <-- (combined successfully)

Git将这种“组合”称为 快进操作 ,什么时候 合并分支 是吗,Git称之为 快进合并 .一般来说,如果 合并分支 可以 做一个快进合并,它 做一个。如果没有,它将进行全面合并。

完全合并会发现 合并基 正在进行的共享提交 两个分支 ,故意使用松散的 树枝 我在前面提到过,并将该特定提交中的快照与两个分支提示提交中的快照进行了比较。这使Git能够了解“我们改变了什么”以及“他们改变了什么”:

B--C<——主要的
/
A.
\
D--E<——起源/主要

区别于 A. C 显示什么 我们 我们的两次承诺发生了变化。区别于 A. E 显示什么 他们 换了 他们的 两次犯罪。

Git然后尝试组合并应用 两组变化 到提交中的快照 A. .如果Git认为这一切进展顺利,Git将继续并根据结果制作一个新的快照——“一个新的提交”。通过获取我们的更改并添加他们的更改(或者,等效地,获取他们的更改并添加我们的更改),Git的合并提交将具有以下快照:?对的结合体这里的问号是因为Git只是使用简单的逐行规则。结果可能是 从另一个意义上说,要正确:按照Git的规则,这是正确的。

无论如何,新的 合并提交 Git现在将链接回 二者都 我们目前的承诺 C 他们的承诺 E :

  B--C
 /    \
A      F   <-- main
 \    /
  D--E   <-- origin/main

我们的分支机构名称, 主要的 ,现在选择新的合并提交 F .注意 F 有一个快照,就像任何普通的提交一样,还有一个日志消息和作者等等,就像任何普通的提交一样。这个 只有 有什么特别的吗 F 是不是指的不是 上一次提交时,它指向两个。

然而,这会产生巨大的后果,因为Git 发现 commits是从某个名称(通常是分支名称)开始的,尽管任何类型的名称都可以使用它来定位 最后的 承诺,然后追随 全部的 反向链接到 全部的 上一次提交。所以从 F ,Git倒退到两者 C E “同时”。 6.


6. 由于这不太可能,Git必须使用某种近似值。Git的某些部分使用广度优先搜索算法,而其他部分则使用各种技巧。


重新定基

从根本上说,重定基期是指做出一些“还行,但还不够好”的承诺 抄袭 然后,他们会做出(据推测)更好的新的和改进的承诺 放弃原件,取而代之的是新的和改进的副本 .

这样做有几个问题:

  • Git“喜欢”添加新提交。它“不喜欢”抛弃旧的承诺。Rebase迫使Git放弃旧版本,转而使用新的和改进的版本,就目前而言,这很好,但是。。。

  • 我们将提交从一个Git存储库发送到另一个Git存储库。一旦它们被复制——一旦马走出谷仓并被克隆——摧毁其中一些是没有好处的。如果我们有新的和改进的替代品,我们必须 每一个 吉特,那是 副本 原作的一部分会被挑选出来,换成新的和改进的替代品。这意味着我们需要强制其他Git放弃一些现有的提交。

一条始终有效的简单规则是: 只有你从未放弃的承诺。 这是因为如果你只有一个副本,你的新的和改进的替代品就不需要得到任何副本 另外 我真想把旧的扔掉。没有其他Git存储库!但它太简单了,至少对于许多GitHub工作流来说是如此。

更复杂的处理方法是: 只有您和这些存储库的所有其他用户事先同意的替换提交才能被替换。 如果其他用户注意到了,他们至少会注意到替换件并将其捡起来。

不谈所有细节,什么 git rebase 是不是:

  • 列出要复制的提交(散列ID);
  • 使用Git的 分离头 避免设立临时分支机构的模式;
  • 检查目标提交副本的目标;
  • 使用一个接一个地复制要复制的提交 吉特樱桃采摘 或类似的;最后呢
  • 移动分支名称以指向上次复制的提交。

在这种情况下,您可以将现有的两个提交重新设置(复制)为两个新的和改进的提交:

  B--C   <-- main
 /
A      B'-C'  <-- HEAD
 \    /
  D--E   <-- origin/main

哪里 B' C' 这些是 B C .中的快照 B' 是通过制造 变化 到中的快照 E ; 将要做出的改变是 比较 A. B .中的快照 C' 是相似的,但是通过从 B C .

一旦拷贝完成,Git就会把旧的剥离掉 主要的 给旧标签 C 提交并粘贴到新的 C' 承诺:

  B--C   [abandoned]
 /
A      B'-C'  <-- main (HEAD)
 \    /
  D--E   <-- origin/main

原著 B C commit仍然存在一段时间,但是没有一个简单的方法来实现 发现 他们,你就是不知道 看见 他们再也没有了。如果您没有仔细记下原始文件的真正哈希ID B C ,你会认为他们的新的和改进的替代品以某种神奇的方式 改变 B C 在正确的位置但他们没有:他们是 完全新的 ,旧的承诺仍然存在。旧的承诺很简单 不用 。一段时间后,defaultGit将至少30天视为垃圾,并最终使用“垃圾收集”它们 git gc (哪个Git会自动为您运行,通过 git gc --auto 从各种Git命令中分离出来,而无需执行任何操作)。

如果一切顺利,重定基调的承诺“保留你工作的本质”,让它看起来像你开始工作一样 之后 你看到你的同事要做什么了。不过,复制的提交中的日期和时间戳更为复杂:

  • 这个 作者日期 是您最初编写提交时保存的。
  • 这个 提交日期 是您上次使用rebase复制它们的时间。

您可以重复地重新设置提交的基础,并且作者时间戳在每个副本中保持不变。要查看这两个时间戳,请使用 git log --pretty=fuller 例如。