【问题标题】:the modified file pulled through git pull origin master is not shown as changed even though git log confirms the pull即使 git log 确认拉取,通过 git pull origin master 拉取的修改文件也不会显示为已更改
【发布时间】:2021-05-10 07:35:33
【问题描述】:

我有一个分支,想使用 $ git pull origin master 来 git 对 master 所做的更改 在我这样做之后,拉动真的没有显示任何合并的 PR 被拉动并说它已经更新了。但是,git log 会显示最后合并的 PR。

那么我怎样才能在这个分支上获得最新的更改(合并的 PR)?

在 master 分支上执行 $ git pull origin master 会显示合并的 PR 被拉取。

我该如何解决这个问题?由于我在 Github 页面上合并了它的 PR,并且能够使用 git pull origin master 并将其拉到 master 的 README.md 没有被拉入这个新分支。

$ git branch
  dataprocessing
  master
* toyota

在分行时:

$ git merge master
Already up to date.

$ git branch -vv
  dataprocessing dcaa9f9 Merge pull request #122 from XYZaiXYZ/toyota
  master         dcaa9f9 [origin/master] Merge pull request #122 from XYZaiXYZ/toyota
* toyota         dcaa9f9 [origin/toyota: ahead 1] Merge pull request #122 from XYZaiXYZ/toyota

此外,以下不会产生任何结果:

$ git diff origin master

这是我在本地分支 toyota 的 README.md 中看到的:

这是我在 GitHub PR 的 README.md 中看到的,我合并了:

这是我在 GitHub 网站上浏览到实际的 README.md 时看到的:

这就是我在 $ git checkout master 中看到的,正如你在 master 中看到的那样,在拉取更新后 README.md 没有改变:

$ git checkout toyota
Switched to branch 'toyota'
Your branch is ahead of 'origin/toyota' by 1 commit.
  (use "git push" to publish your local commits)

$ git merge origin master
Already up to date.

$ git log README.md 
commit ac7cXXXX (origin/toyota)
Author: Mona Jalal <mona@XYZ>
Date:   Fri Feb 5 22:40:32 2021 +0000

    fixed two typos in the README.md

$ git pull origin master
From ssh://github.com/XYZaiXYZ/vision
 * branch            master     -> FETCH_HEAD
Already up to date.

我已经合并了 #122 PR 来掌握自己,当我进入 git repo 时我看到了这个:

$ git checkout master
$ git log
commit dcaa9XYZ (HEAD -> master, origin/master, origin/HEAD, toyota, dataprocessing)
Merge: 3b29485 ac7c61e
Author: Mona Jalal <76495162+XYZ@users.noreply.github.com>
Date:   Fri Feb 5 17:44:36 2021 -0500

    Merge pull request #122 from XYZaiXYZ/toyota
    
    fixed two typos in the README.md

我还在测试目录中做了 git clone 的 repo,我可以看到更改显示在这个新的克隆中

【问题讨论】:

  • 你是本地的master吗?使用 git branch -vv 检查您当前的分支,其中显示了本地分支和远程分支的映射详细信息。
  • 我在当地的丰田分公司
  • @MohanaRao 将git branch -vv 的结果添加到我的帖子末尾
  • 因为您在不同的分支上,所以您看不到 master 的 PR 提交。您看到的主要是您在用于创建 PR 的 toyota 分支上创建的提交。尝试git checkout mastergit pull origin 来查看 PR 提交。
  • 您在本地 master 上的 git log 中看到了什么(git checkout mastergit log)?

标签: git github pull-request git-pull


【解决方案1】:

git diff origin master 没有结果意味着您的分支与 origin/master 相同。所以你已经从原点拉出主分支,你的分支与主分支是最新的。

此外,git merge master 合并 master 上的更改,如果这些更改已在本地提交。如果 master 上的更改是在远程提交的,则需要执行 git merge origin master 来拉取 master。

【讨论】:

  • 请查看原帖中的更新。如您所见,Github.com 中合并的 PR 文件与本地分支和 master 分支都不同
  • 请检查原始帖子中的最新更新,这些更新反映了您编辑中请求的项目
  • 好的,这意味着您需要执行git pull origin master 将远程更改拉到本地。它也会自动将其合并到您当前的分支中。
  • 这就是我最初所做的,但没有成功
  • 我明白了。想不出其他原因!
【解决方案2】:

让我们首先在这里考虑一些明确的项目/确凿的事实:

  1. Git 不是关于文件,而是关于提交

  2. 提交被编号,例如,dcaa9f9(在git branch -vv 输出中看到)或ac7cXXXX(在您的git log 输出中看到)。这些数字(十六进制)是 hash IDs,因此它们没有任何合理的顺序,对 人类 不是很有用,但它们是 Git 真正访问每个提交的方式。

  3. 哈希 ID 实际上是提交的内容的加密校验和,这使得每个提交的所有部分都完全只读。提交一旦完成,就不会发生任何变化。所以一般来说,我们只需将 new 提交添加到存储库,这就是 Git 存储历史记录的方式。提交历史记录。

  4. 提交存储文件,但不作为更改。每个提交都存储了 每个 文件的完整快照——或者更准确地说,是 Git 知道的每个文件,当时有人运行 git commit 进行该提交。 (这些是 已跟踪 文件:未跟踪文件是在您将进行的下一次提交中不是的文件。)

  5. 提交也存储元数据。这包括有关提交的人员、时间和原因的信息(日志消息)。在此元数据中,Git 在每次提交中存储一些哈希 ID。这些是您(或任何人)进行提交时存在的提交 ID,因此它们必然是 较早提交的哈希 ID。通常,大多数提交都只存储一个哈希 ID:之前的提交,从该提交中进行。其余大部分提交是合并提交,其中存储了两个哈希 ID:上一个提交和被合并的提交。

元数据中的哈希 ID(Git 将其称为相关提交的提交)将提交本身形成 DAG。对于一个简单的提交链——最常见的事情——我们将绘制这个 DAG 片段(“DAGlet”),如下所示:

... <-F <-G <-H

其中H 是链中最后 次提交的哈希ID。然后,因为懒惰,我们会草率地处理箭头,这让我们可以绘制多个分支和合并的 DAGlet:

          I--J
         /    \
...--G--H      M--N   <-- main
         \    /
          K--L   <-- feature2

例如。右侧的名称,自动总是指向链中的最后提交,是我们的分支名称。上图中带字母的节点是我们的提交,它永久存储文件。

Git 通过比较存储的文件向您显示更改。选择任意两个提交。例如,选择一对父/子,例如 G-HH-IM-N 或其他。这些提交中的每一个都有每个文件的完整快照。也许H 中的快照有一个与G 中的文件不同的文件,以及一个根本不在G 中的文件。然后GH比较将显示一个更改的文件和一个添加的文件

请注意,要将提交与其父级(单数)进行比较,我们必须只有 一个 父级。除了合并提交M 之外,这对上述所有提交都非常有用。它有两个父母。如果你让 Git 向你展示 M 中发生了什么变化,它应该比较 J-vs-M 还是 L-vs-M

如果两者都能做到,那就太好了。事实上,一些 Git 命令 do 两者兼而有之,但他们对此有点奇怪。然而,git log 命令默认不会与任何一个进行比较。这很快就会成为一个问题。

同时,关于存储在提交中的文件还有一件事需要注意。它们不是作为文件存储,而是作为特殊的、只读的、仅 Git 的、压缩和去重的实体(Git 将这些 blob 对象 内部虽然你通常不需要关心细节)。您自己的程序实际上无法使用这些,因此为了使提交有用,Git 必须将该提交提取到工作区中。

因此,您在使用 Git 存储库时看到和使用的所有文件毕竟不在存储库中。它们位于您的工作树工作树中。这些不在 Git 中。它们最多从Git 中提取。未来的git commit 也不会使用这些文件:Git 从 Git 调用的不同位置构建新的提交,分别是 index暂存区,或者——很少有这些天——缓存

当您选择某个特定的提交时——例如通过检查一个分支,使用git checkout master——Git 通过提取该提交的文件来工作。 Git 使用包含提交的哈希 ID 的分支名称来查找提交。如提交中所见,文件的原始副本进入 Git 的索引(它们仍然被删除重复,因此它们在索引中几乎不占用空间)进入您的工作树(它们被扩展回可用的文件,这确实占用空间)。

然后我们处理/处理我们的文件——那些在Git中不是的文件——因为这些是有用的文件。当我们完成对它们的处理后,我们必须至少在其中一些上运行git add。只要我们小心确保 Git 不会自动添加我们不想希望在下一次提交中包含的未跟踪文件。或者,我们可以只在我们更改的那些上运行git add。这样做是为了告诉 Git:为我们实际添加的每个文件,使索引/暂存区域副本与我的工作树副本匹配。 Git 现在将压缩它们,通过检查存储在存储库中 in 任何位置的每个现有文件来删除它们,并更新索引/暂存区域以引用正确的文件内容,准备进入下一个提交。

这意味着索引/暂存区域充当您提议的下一次提交的存储空间。它总是有所有文件在里面,只是大多数时候,这些文件中的大多数——甚至所有文件——匹配当前提交中的文件。

当我们进行 new 提交时,Git 会简单地打包当时在其索引中的所有文件,添加适当的元数据——包括 current 的哈希 ID em> commit,正如我们之前在运行 git checkout 时选择的分支名称所找到的那样——并将所有这些内容写出来以进行新的提交。新提交获得一个新的、随机的哈希 ID,保证1 不同于所有现有的哈希 ID。新的提交对象进入由哈希 ID 索引的所有对象的数据库。然后 Git 将新的哈希 ID 存储到分支名称中,以便名称挑选出 latest 提交。

随着不变量的恢复——当前的分支名称拥有当前的hash ID,并且我们可以通过跟随父节点一次一个地找到所有较早的提交链接——Git 已准备好进行更多工作。请注意,commit 是由 Git 索引中的任何内容 生成的。工作树中的文件无关紧要。


1什么pigeonhole principleCollisions never happen!


你所看到的

让我们从git branch -vv 输出开始:

$ git branch -vv
  dataprocessing dcaa9f9 Merge pull request #122 from XYZaiXYZ/toyota
  master         dcaa9f9 [origin/master] Merge pull request #122 from XYZaiXYZ/toyota
* toyota         dcaa9f9 [origin/toyota: ahead 1] Merge pull request #122 from XYZaiXYZ/toyota

这里有大量的信息。我们有三个分支名称。所有三个名称都标识相同的提交,其哈希 ID 以 dcaa9f9 开头(实际哈希 ID 更长,但至少 4 个字符的任何唯一初始缩写就足够了,所以 dcaa9f9 在这里很好,并且我们可能只需要dcaa 就可以逃脱惩罚。

我们有两个远程跟踪名称:这些是我们的 Git 存储库对其他 Git 存储库的分支名称的记忆。这些被设置为相应(本地)分支名称的 upstreammaster 链接到 origin/master 作为 master 的上游,toyota 链接到 origin/toyota 作为 它的上游。

我们在这里看不到存储在远程跟踪名称中的哈希 ID,但 git branch -vv 确实做了一些特别的事情,我们在第三行看到:ahead 1。这意味着我们有一个提交我们的(本地)分支toyota,它不在他们的toyota 分支上。 origin Git 存储库也有一个 toyota 分支,但它们的 toyota 存储的哈希 ID 不是 dcaa9f9。我不知道它是什么,但我知道,从 ahead 1 文本中,dcaa9f9 将此提交作为其父级,或者可能作为其父级之一,复数,如果 dcaa9f9 是合并提交.

最后,我们还为每个提交获取每个提交消息的主题行。由于我们获得了 3 次相同的提交,因此每次都获得相同的主题行。我们得到的主题是Merge pull request #122 from ...。这是 GitHub 将生成的(可怕的,但至少是标准化的)消息,例如,当您使用他们的 Web 界面执行合并时。所以dcaa9f9 几乎可以肯定是一个合并提交,有两个父提交。我们的origin/toyota,代表我们Git 对origintoyota 的记忆,指向这个合并提交的父节点之一。

因此,如果我们要画这个,我们可以画成:

...--I--J   <-- origin/toyota
         \
          M   <-- dataprocessing, master, toyota (HEAD), origin/master
         /
...--K--L

用字母M 代表提交dcaa9f9。我不知道任何其他提交的哈希 ID(除了Jac7c 开头),但我们在这里并不需要它们。

你还提到:

在分行时:

$ git merge master
Already up to date.

现在,这并不奇怪。 git merge 命令:

  • 使用您的 current 提交(Mdcaa9f9)通过您当前的分支名称找到(通过特殊名称 HEAD 找到,这就是它在上图中所做的事情) ;
  • 将定位另一个提交的内容作为参数:这里,master。然后它找到提交;和
  • 然后使用我们绘制的提交图来查找合并基础,即最佳共享的共同祖先提交。

您要求合并的提交是dcaa9f9。那 当前的提交。因此,最好的共享提交是dcaa9f9 本身。该提交 当前提交,因此没有必要甚至可能的合并。合并命令说Already up to date. 并退出。

$ git diff origin master

[不打印]:这也不足为奇,尽管我们需要学习一个新的 Git 技巧。 git diff 命令采用 两个 提交说明符。2您提供的两个是 originmaster

现在,origin 实际上是一个远程,而不是远程跟踪名称。在 Git 中,遥控器是一个短名称,它存储一些东西以便于访问,并启用其他一些东西。大多数人感兴趣的它存储的主要内容是 URL。这是当 Git 运行 git fetch(或 git pull,运行 git fetch)时 Git 将使用的 URL。它启用的“其他东西”是远程跟踪名称,例如origin/masterorigin/toyota

The gitrevisions documentation 描述了将masterorigin/master 等名称转换为哈希ID 的六步过程。按照文档链接,如果需要向下滚动一点,并通读六个编号的步骤。我不会在这里全部引用它们,但特别看一下最后一个:六步中的第六步。它谈到寻找refs/remotes/<em>name</em>/HEAD。这将存在于您的存储库中,几乎可以肯定它是 Git 所说的 符号引用origin/master3

最后,所有这些加起来就是您要求git difforigin/master 解析为一个哈希ID——它确实如此,并得到dcaa9f9——然后解析master再次到哈希 ID:dcaa9f9。然后 Git 会尽职尽责地将 dcaa9f9 中的快照与 dcaa9f9 中的快照进行比较。当然,每个文件都匹配。

最后,还是在本节中:

$ git log README.md 
commit ac7cXXXX (origin/toyota)
Author: Mona Jalal <mona@XYZ>
Date:   Fri Feb 5 22:40:32 2021 +0000

    fixed two typos in the README.md

在这里,您可能会遇到git log 的“功能”(通常是错误功能)。

当您运行 git log 时,它的工作原理是:

  • 从您选择的一个或多个提交开始:如果您没有选择一个或多个开始提交,它将从当前提交开始(像往常一样通过HEAD)。

    git log 代码将这些提交哈希 ID 放入优先级队列中。这是因为它一次只能处理一个提交。然而,当使用HEAD 时,它只选择一个提交,首先只有一个条目队列中。

  • 遍历提交图,一次一步。这部分可能变得相当棘手。

提交图遍历使用优先级队列如下:

  1. 将最前面的条目从队列中移除。 (如果队列为空,我们就完成了:退出。)
  2. 决定是否打印有关此提交的任何内容。如果是这样,请打印有关它的内容。
  3. 决定是否访问此提交的父级或父级。如果这是一个普通的单亲提交,我们将访问(单)父母(当然--no-walk 下除外)。但是,如果这是一个合并提交,请根据任何有效的历史简化选择要访问的父级。
  4. 将任何要访问的父提交按优先级顺序推送到优先级队列中。 (省略任何已经访问过的父级。)

这里的棘手部分在第 3 步:决定要访问合并提交的哪些父项。这里的棘手部分也在第 2 步:决定是否打印有关此提交的任何内容。

我们首先访问commit M,因为这是队列中的一个commit:

  • 由于M 一个合并提交,git log 是惰性的,至少最初不会尝试将其与任何其父级进行比较。它只是决定不打印提交M,因为在不检查之后,文件README.md 似乎没有改变,因为Git 懒得检查。因此,即使 M JL 相比更改为 README.md,也不会在此处打印。

  • 由于M 是一个合并,我们检查历史简化。这是开启的!它被打开是因为我们有一个路径规范:README.md。所以现在我们检查M 是否是git log 在根据提供的路径规范剥离树之后对任何父级调用“TREESAME”的内容。所以现在我们实际上检查M的父母JL是否具有与M相同的README.md

    如果这两个父母中的一个确实有相同的README.md,那就是这个特定的git log 将遵循的那个。显然,提交 J (ac7c...) 与提交 M 具有相同的 README.md 文件。提交Jorigin/toyota 标识的那个,正如我们在提交的哈希ID 之后的括号中看到的那样。 (这来自 --decorate 选项,在现代 Git 中默认为“on”。)

所以,由于提交 J 具有相同的 README.mdgit log 访问 M 打印它,并将提交 J 放入队列中以进行下一步,但将提交L 放入队列中。这就是 Git 所说的历史简化

Git 现在访问提交 J,因为它是队列中唯一的提交。提交J 作为其单亲,提交I——所以git log 确实费心比较IJ,特别是看看README.md 在这对之间是否发生了变化的提交。确实如此,所以git log 确实 打印提交L。这就是我们如何知道 (a) 合并在其历史简化过程中选择了 J,并且 (b) 提交 J 的哈希 ID 以 ac7c 开头——这是您在报价中留下的。

由于JI 作为其父级,因此这是进入队列的提交。由于队列是空的,它现在只有一个提交,git log 继续查看提交I。这将重复,直到 git log 用完提交,或者您厌倦了阅读它的输出。


2git diff 命令有点花哨,所以它可以不带任何、1、2,甚至在某些情况下甚至更多的提交说明符。它还可以采用路径名和其他参数。不过,git diff 这个特殊的form 需要两个提交说明符。

3origin/HEAD 中存储的值通常在您进行克隆时由git clone 设置。您可以使用git remote 及其set-head 子命令更改它。 git clone 所做的初始设置取决于您要克隆的 Git 存储库设置为 its HEAD。对于 GitHub,这通常是 master,或者,自从最近的切换,main,尽管任何 GitHub 存储库的管理员都可以设置他们喜欢的任何内容。


到目前为止的总结

  • Git 关于提交。始终寻找提交哈希 ID,因为它们是 Git 真正关心的。如果两个哈希 ID 匹配,那就是 相同的提交
  • 提交存储快照。您看到和使用的是来自快照的文件,充其量是。
  • Git 使用 提交图。使用 git log --graph 在 Git 中查看它(使用 --oneline--decorate 通常很好:记住这里的“DOG”,装饰 Oneline Graph;现代 Git 默认启用装饰)。如果您觉得有用,请考虑使用图形查看器。请注意,某些图形查看器比其他查看器更好。另见Pretty git branch graphs
  • git log 命令谎言。这是故意的,而且通常是一件好事。 Git 存储库中唯一的历史记录存储库中的提交。我们经常希望看到“文件历史记录”。这不存在——但git log 可以伪造一个,通过选择性地对我们撒谎。但是,如果我们试图弄清楚为什么某些更改会丢失,那么这种选择性的谎言就会成为阻碍。 (这不是您的实际问题,但值得记住。)

你的实际问题

我还在测试目录中做了 git clone 回购,我可以看到 [在新克隆中正确的 README.md]

这意味着您在该新克隆中签出的提交在文件中具有正确的内容。 Git 将提交的文件复制到 Git 的索引,然后复制到您的工作树。新克隆中的工作树副本向您展示了索引副本中的内容,该副本来自已提交的副本。

如果您现有的工作树副本不匹配,那只是意味着......您的工作树副本不匹配。就这样。您的工作树副本是您的。你可以用它做任何你喜欢的事情。您可以将其打印出来、将打印输出揉成一个球、将其点燃等。您可以删除文件或对其进行加密。您对工作树副本所做的任何操作都不会影响 Git 的 副本:这些副本安全地存储在提交中,只读,永远不变。

您可以通过更改您的工作树复制并运行git add README.md。这使得 Git 使其索引副本与您的工作树副本匹配,现在未来的 git commit 将保存文件的 this 版本。

或者,如果您只是希望您的工作树副本被删除并替换为从现有 Git 提交中提取的副本,或者从现在出现的 Git 索引中提取的副本,您也可以这样做.有不止一种方法可以做到这一点。在最现代的 Git 版本(2.23 或更高版本)中,最好的方法是使用新的git restore 命令。

git restore 命令是 Git 人员用来分解git checkout 命令的两个命令之一。问题是git checkout 太强大了。它做了太多不同的事情。所以他们把它分成git switch,它做了大约一半的事情,和git restore,它做了另一半。

要从HEAD-commit 副本恢复工作树文件,您可以使用:

git restore --source HEAD --staged --worktree -- README.md

例如。 (这是完整拼写的版本;允许简写,但我将在此处跳过它,因为这个答案已经很长了)。

如果您没有此版本的 Git(2.23 或更高版本),您可以通过以下方式实现上述目标:

git checkout HEAD -- README.md

这实际上在 Git 2.23 或更高版本中仍然有效,因此您也可以在最现代的 Git 版本中使用这种形式(已经是简写形式)。

请注意,这些清除您在工作树中拥有的README.md 版本。 Git 将无法取回任何尚未提交的版本。 要从某个历史提交中取回版本 - 而不是从当前或 HEAD 提交中取回 - 只需替换源部分, HEAD,带有该提交的原始哈希 ID,或任何可以让 Git 找到该哈希 ID 的拼写:再次查看 the gitrevisions documentation

git checkout 被拆分的原因是 git switch 操作集是“安全”的:Git 将检查您是否正在销毁未保存的工作,并告诉您,除非您强制使用--force 进行操作。git restore 集合是“不安全的”:他们假设你知道你在告诉 Git 清除我的工作,然后就去做。将两者放在一个前端下, git checkout,是灾难的根源:人们知道git checkout 是安全的……它是安全的,直到它不是。)

【讨论】:

    【解决方案3】:

    @mona-jalal,我非常感谢提供所有细节。正如您能够在新克隆中看到 README 内容一样,至少,我们知道提交存在于 repo 中并且它是完整的。不知何故,您的本地副本纵横交错。我知道移动训练数据很困难。您可以尝试的事情很少,但它可能比通过将训练数据移动到新目录来使用新克隆的副本更复杂。

    一切顺利!

    【讨论】:

      猜你喜欢
      • 2012-01-31
      • 2011-02-22
      • 1970-01-01
      • 2020-07-27
      • 2019-03-07
      • 1970-01-01
      • 1970-01-01
      • 2011-08-12
      相关资源
      最近更新 更多