首页 > 解决方案 > 为什么提交一个文件,但所有文件都在 RStudio 中提交?

问题描述

我在 Github 上有一个包含一些文件的存储库,我在 RStudio 服务器上也有一个包含一些文件的文件夹。我对某些文件进行了一些更改,但我只想使用命令提交一个文件(test.Rmd)

git init
git add test.Rmd
git commit -m "Adding some plots" 
git push

而不是仅提交此文件,而是提交文件 test.Rmd 所在文件夹中的所有文件。为什么会这样?我尝试对不同文件夹中的另一个文件执行完全相同的操作,并且提交工作正常。在此之前,似乎我已经做了一些事情,比如

git init
git add .

这就是为什么它在目录中添加所有文件的原因?

使用git status结果

状态

我现在怀疑问题是取消添加这些大文件?我想我提交了所有文件的所有更改,而不知道我git push在最后使用时做了。

标签: gitfilerstudiopushcommit

解决方案


你的问题充其量是模棱两可的,并且包含一些不好的假设,所以这个答案很长。

关于 Git 提交的一些背景知识和git init

Git 中的所有提交总是包含所有文件。这就是 Git 本身的工作方式。

运行git init将:

  • 在当前工作目录中创建一个新的空 Git 存储库,或者
  • 重新初始化现有的 Git 存储库,无论它在哪里。

如果 Git 发现您在某个现有的 Git 存储库中,您将获得第二种行为 - 重新初始化现有的 Git 存储库。的输出git init告诉你它做了哪一个:

$ git init
Initialized empty Git repository in [path, redacted]
$ git init
Reinitialized existing Git repository in [path, redacted]

除了一些几乎肯定不适用于您使用 Git 的特殊情况外,“重新初始化”变体实际上并没有做任何事情:您现有的存储库保持不变。

git init创建一个新的、完全空的存储库时,没有提交,因此还没有分支。因此,您进行的一次提交是有史以来的第一次提交。这个提交有点特别:它是一个根提交,没有历史。它包含你告诉 Git 让它包含的任何文件,使用git add.

但是,在此之后,您将拥有一个包含现有提交的现有 Git 存储库。这包括您使用git clone将一些现有存储库(例如,从 GitHub)复制到您自己的机器(例如,您的笔记本电脑)上的新 Git 存储库的情况。您将告诉 Git检查某个特定的提交(通常是某个分支名称的提示提交),这意味着 Git 将使用该提交中的所有文件填充其暂存区域和您的工作

随后,您将编辑一些文件,甚至可能创建一些新文件。然后,您git add在这些文件中的一个或多个上运行。如果你正在git add处理一个已经存在于 Git 暂存区的文件,Git 会从它的暂存区扔掉旧的副本,并用你的工作树制作的新副本覆盖暂存区的副本。或者,如果你git add是一个全新的文件,Git 会将文件复制它的暂存区,作为一个新文件。

在所有这些情况下,暂存区域中的所有现有文件都保留在那里。 接下来git commit获取Git 暂存区域中的所有文件,并从中制作快照。

一个具体的例子

假设您有一个现有的存储库,其中主分支(无论它的名称是什么:GitHub 现在鼓励人们使用main,而旧的存储库倾向于使用master)在其最近的提交中有十个文件。您git clone将此存储库添加到您的笔记本电脑,因此您的笔记本电脑 Git 软件(“您的 Git”)会检查最后一次提交,将十个文件提取到 Git 的暂存区和您的工作树中。

您现在更改了工作树中十个文件中的五个git add,但只在五个更新文件中的一个上运行。这意味着您的 Git 暂存区中有十个文件:九个文件与当前提交中的一个匹配,一个与工作树中的更新文件匹配。四个暂存区文件不同于它们的四个工作树对应文件;其余六个暂存区文件与它们的工作树对应文件匹配。

如果您现在运行git commit -m haaaaaands,您将获得一个包含十个文件的新提交,这些文件与它们现在出现在暂存区域中的完全相同。您的工作树中仍然拥有所有更新的工作树文件,但暂存区副本仍然与先前提交的副本匹配,因此新提交的副本与旧提交的副本匹配,除了您运行的一个文件git add

您刚刚进行的新提交成为当前提交,现在是当前分支上笔记本电脑存储库中的最新提交。您现在可以使用git push将此提交发送到 GitHub 存储库;如果并且当您最终这样做时,他们收到的提交将逐位匹配您的 Git 存储在笔记本电脑存储库中的提交。它将有 9 个文件匹配一个文件不匹配的情况;他们获得的提交将以先前的提交作为其父提交;等等。

需要了解的事情git status

首先,git status告诉您有关当前分支的信息。它会说类似on branch main. 这是您的 Git 告诉您您的笔记本电脑存储库已main作为当前分支。您的 Git 也可能会告诉您您“领先”和/或“落后”其他名称,例如origin/main:这使用完全本地存储在笔记本电脑上的信息。此信息可能已过时,具体取决于其他 Git 存储库在 GitHub 上或其他任何地方的活跃程度。

接下来,如果您没有处于冲突合并的中间——如果是,其余的会变得更加复杂——该git status命令运行两个比较:

  • 首先,它将当前提交中的文件与暂存区中的文件进行比较。其中一些文件通常会完全匹配,因为自从它们从某个提交中提取后,您没有对它们做任何事情。对于这些文件,你的 Git 什么也没说。

    暂存区域中的其他文件将与您当前的提交git add匹配,因为例如您在它们上运行。在这种情况下,你的 Git 会说这些文件是为提交暂存的。这仅仅意味着暂存区副本在某种程度上与当前提交的副本不同。

    请注意,暂存区域中的某些文件可能是新文件。也就是说,这些文件在当前提交中根本不存在。对于这些文件,Git 会说这些是“新文件”。

  • 列出了“暂存待提交”的文件,或者没有找到任何要列出的文件,您的 Git 现在继续将暂存区域中的文件与工作树中的文件进行比较。和以前一样,某些文件可能匹配。其他文件可能会有所不同,甚至工作树中的某些文件在暂存区域中根本没有对应文件:与以前一样是新文件。

    不过,这一次,您的 Git 只会告诉您有关已更改文件的信息,并表示此类文件不会暂存以进行提交。它确实也收集了每个文件的列表,但将它们推迟到下一部分。

  • 列出所有“未暂存以供提交”的文件后,您的 Git 会继续告诉您有关未跟踪文件的信息。这些是工作树中不在Git 暂存区中的任何文件。换句话说,这些是“新”文件。

    这些的奇怪之处在于它们是如何被分离出来的,作为一个单独的类别进入“未跟踪”。这样做的原因是 Git 作者希望这里不应该报告大量未跟踪文件。特别是 Git 是为与创建“目标文件”和其他“构建工件”的编译器一起工作而构建的,尽管它们可能很重要,但不应添加到提交中并因此永久保存。1

在这个程度上,Git 有一个排除工具,via.gitignore和其他排除文件。在这里,您列出了 Git 应该关闭的文件。它不应该抱怨这些未跟踪的文件是未跟踪的。此外,当这些文件未被跟踪时,您可以使用 en-massegit add操作,例如git add .,添加所有未跟踪的文件......除了那些标记为“忽略”的文件。

令人误解的.gitignore是它不会忽略任何被跟踪的文件。这里的tracked一词是根据untracked的定义的对立来定义的。未跟踪文件是存在于工作树中但不存在于 Git 索引中的文件。跟踪文件是 Git 索引中的文件,无论它是否存在于 Git 索引中。跟踪的文件永远不会被忽略。

良好的文件维护.gitignore使 Git 使用起来更加愉快:git status告诉你有用的东西;只添加正确的东西。git add .


1这样做的原因是,构建工件(至少在理想情况下)完全可以从原始来源重现。我们只想保存原件,而不是派生的工作产品。这至少可以节省大量的空间和时间以及以后的人工工作。请注意,这里有很多“理想”和“潜力”。这些事情并不总是按计划进行,有时保存一切实际上是合理的。不过,Git 在这方面并不是那么出色,因此您可能不想为此目的使用Git 。


“所有文件始终提交”的可能来源

如果你运行git add .,你是在告诉 Git:扫描我当前的工作目录,找到所有更新的文件和所有新的文件和任何删除的文件,并git add在每个文件上使用来更新你的暂存区副本.gitignore此处唯一的例外是未跟踪的文件或其他排除文件中列出的文件。

如果您运行git add *,则行为在某种程度上取决于您的命令行解释器:Unix 风格的 CLI(如 bash 或 zsh)让shell扩展*,而 MS-DOS 风格的 CLI(如 CMD.EXE)将文字星号传递*给 Git , 然后扩展*. 我不会在这里详细介绍差异的所有细节,但这往往会根据许多细节进行大量或全部文件的整体添加。

如果运行git add -u,则告诉 Git 查找更新的文件并添加它们。

你可以有一个预提交钩子。Git 中的钩子相当复杂,但一些软件安装程序不仅会为您安装 Git,还会设置某种自动钩子创建。(这是一种重新初始化 Git 存储库可能会产生影响的设置,尽管要这样做,安装程序必须将这些挂钩放入 Git“模板”中,这似乎很少使用。)根据您的运行方式,预提交挂钩可以为您运行,即使不希望它运行。git commitgit add

如果你运行git commit -a,你实际上是在告诉 Git 运行:

git add -u
git commit

这里有一个与预提交挂钩的交互,因此两个命令序列并不完全相同,但这可能是您问题的根源。


推荐阅读