【问题标题】:How to commit only part of files?如何只提交部分文件?
【发布时间】:2021-05-04 21:31:12
【问题描述】:

如上所说

git - How do I commit only some files? - Stack Overflow

我们可以使用

 git commit [--only] a b c -m "only part of files"

但是在下面的例子中:

$ mkdir t
$ cd t
$ git init
Initialized empty Git repository in /mnt/c/test/git-test/t/.git/
$ touch a b
$ git add .
$ git commit a -m a
[master (root-commit) c7939f9] a
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a
$ git commit b -m b
[master cf4514a] b
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 b
$ git status
On branch master
nothing to commit, working tree clean
$ ls
a  b

我尝试仅将文件 b 提交到第二次提交中,但失败了。 (在工作树和工作树中使用 a、b 和 clean。这意味着两个文件都已提交。)

那么如何真正提交部分文件呢?

即使git add 单个文件也不起作用:

$ mkdir t
$ cd t
$ git init
Initialized empty Git repository in /mnt/c/test/git-test/t/.git/
$ touch a b
$ git add a
$ git commit --only a -m "a"
[master (root-commit) 04383c9] a
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a
$ git rm --cached -r .
rm 'a'
$ git add b
$ git commit --only b -m "b"
[master d518916] b
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 b
$ git checkout -f head~
Note: switching to 'head~'.

...
HEAD is now at 04383c9 a
$ ls
a
$ git checkout -f master
Previous HEAD position was 04383c9 a
Switched to branch 'master'
$ ls
a  b

文件 a 仍在第二次提交中。

背景: 假设我有一个包含许多文件的文件夹,并且我想将文件集 A 提交到第一个提交(即第一个提交仅包含文件集 A),将 B 设置到第二个提交,... 我为什么这样做:只是出于好奇。

【问题讨论】:

  • 使用git commit -p ./fiepath 命令。在这里查看更多git-scm.com/docs/git-commit#Documentation/git-commit.txt--p
  • 在提交前使用git add -pi。运行命令后使用? 阅读帮助。
  • @NomanGul 它不起作用。它只是提交了所有暂存文件。
  • @caramba 我认为git add -pi 是选择要暂存的更改块,而不是文件选择。例如,当您有一些空白文件时,它会显示“没有更改”。
  • 我认为你误解了一些东西。在您的第二个示例中,您尚未提交删除 a,因此它在第二次提交中仍然存在。你想达到什么目的,目的是什么?我认为这会更容易解释这一点(或意识到你实际上是正确的:p)

标签: git commit


【解决方案1】:

解决方案非常简单:添加到您感兴趣的仅暂存文件中。 例如add . 表示 - 添加以暂存当前目录中的所有文件。反而 转到带有a 的文件夹并输入添加a

【讨论】:

  • 我尝试了git rm --cached,但也失败了。您现在可以在问题中看到它
  • 首先:在 m 之后添加 " 引号以提交消息。其次,尝试使用附加标志-o 相同,这意味着only
  • --only 是给出文件时的默认选项。尽管如此,我还是尝试了您的描述并更新了我的问题。你试过成功了吗?
  • 是的,我暂存了两个文件,然后我使用选项-o (--only) 提交了第一个文件,我无法提交下一个文件:On branch main Your branch is ahead of 'origin/main' by 1 commit. (use "git push" to publish your local commits),这是预期的行为
  • 好吧,我可以成功提交第一个文件。关键是您将第一个文件提交到第一个提交中,然后将第二个文件提交到第二个提交中。两次提交完成后,使用 -f 签出到第二次提交。您应该会发现第一个文件仍然存在。
【解决方案2】:

git commit [--only] path 仅将更改提交到指定文件。

所以你从一个空的仓库开始。您暂存了两个新文件:ab。现在你有两件事上演了(你可以用git status确认):

  • 新文件a
  • 新文件b

你说

git commit a -m a

如果您此时运行git status,您会看到确实只有对a 的更改被提交; a 现在已提交,b 仍作为“新文件”暂存。那你说

git commit b -m b

仅提交 ba 与上一次提交保持不变

你可以再次确认每次提交只影响你指定的文件

git log --name-status

这会告诉你第一次提交只添加了a,第二次只添加了b

听起来你想要一个命令来创建一个只包含指定文件的提交。为此,您不仅需要将更改 提交到要添加的任何新文件,还需要提交要删除的任何旧文件。这就是您第二次尝试失败的原因:您成功创建了一个仅包含您想要的文件的暂存区域,但随后您告诉git 不要将更改应用于新文件以外的任何内容。如果不是

git commit --only b -m "b"

你只是说

git commit -m "b"

它会做你想做的事。

【讨论】:

  • 有效!我仍然对提交更改感到困惑。我曾经将提交作为快照,但不是差异内容。所以我希望commit 命令只是拍摄索引的快照并存储它。您能否提供一些有关提交机制的文档?
  • kakakali,你说得对,你在找出其中的含义时遇到了麻烦。 git add 在路径中添加新版本的内容,索引列出了您最后签出/提交的内容,并由您修改(使用git addgit rm 以及其他更深奥的,例如批量工作命令)。带有路径的git commit 会忽略您使用这些命令所做的任何分阶段更改,并且仅使用列出的路径的当前工作树版本来替换当前提示(即HEAD 提交)那里的任何内容,但无论 other 快照中的路径保持不变。
  • @kakakali 好吧,提交 是一个快照。提交的行为是它被编码的任何内容,并且正如文档所指定的那样,在这种情况下 - 即当您为提交命令提供路径时 - 它被编码为构造一个仅在这些路径中与先前提交不同的快照。 (git-scm.com/docs/git-commit) 有几个命令将提交视为他们自己和他们的父母之间的区别——想想 rebase 是如何工作的。这是 git 可以更符合术语的情况,当然不是唯一的,但它就是这样
  • 是的,我现在明白了。 commit --only 的文档实际上是:通过获取指定路径的更新工作树内容进行提交(从当前提交)...
【解决方案3】:

添加到Mark Adelsberger's answer 和地址your comment here

我曾经将提交作为快照而不是差异内容。所以我希望提交命令只是拍摄索引的快照并存储它。

是正确的。但是,当你使用git commit --only 时,Git 实现这一点的方式很复杂。 (它也没有很好的记录。)

我通常谈论“索引”/暂存区/缓存。 Git 确实有一个特殊的索引,“the”索引,虽然它实际上是每个工作树的:如果你运行git worktree add,你不仅会得到一个新的工作树,而且还有一个新的索引(和新的@987654326 @,以及其他特定于工作树的引用,例如 git bisect)。但是 Git 能够处理额外的临时索引文件,这就是 git commit --onlygit commit --include 所做的。

让我们再看看你的设置:

$ mkdir t
$ cd t
$ git init
Initialized empty Git repository in /mnt/c/test/git-test/t/.git/
$ touch a b
$ git add .

此时,“the”索引(.git/index 中的主要索引)包含两个文件。他们在这里:

$ git ls-files --stage
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       a
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       b

但是,现在您运行 git commit a -m a,创建初始提交(根提交,没有父提交)。这个命令——git commit --only a,或多或少——由:

  1. 创建一个新的临时索引,.git/index<em>digits</em>
  2. 从当前提交初始化那个索引;1
  3. 运行相当于GIT_INDEX_FILE=.git/index<em>digits</em> add a;
  4. 运行相当于cp .git/index .git/index.<em>moredigits</em> 来创建临时索引;
  5. 运行相当于GIT_INDEX_FILE=.git/index.<em>moredigits</em> add a;2
  6. 从第一个临时索引构建提交,就像git commit 通常从主索引构建提交一样;3
  7. 通过将 second 临时索引重命名为 .git/index 来完成提交,使其成为主索引。

这是做什么的:

  • 为包含HEAD 提交和--only 文件的提交创建并使用临时索引。如果新提交失败(尽管在您的情况下它成功),主索引不会受到干扰。
  • 创建并设置第二个临时索引,以便在提交成功时使用。
  • 使用第一个临时索引尝试提交。

如果提交成功,第一个临时索引将被丢弃,第二个临时索引成为主索引(通过rename 操作,使其全部是原子的)。如果提交失败,则删除两个临时索引文件。

这意味着在 成功 git commit --only 之后,主索引会更新,就像您在 --only 文件上运行 git add 一样。在一个失败之后——提交可能由于预提交钩子或你删除提交消息而失败,例如——就像你根本没有运行过git commit --only一样。

(在您的情况下,由于您在运行git commit --only a 之前没有修改文件a,因此您无法区分其中一些情况。)

当您继续运行 git commit --only b 时,这些步骤会重复,但使用文件 b 而不是文件 a


1当前没有提交,因为您还没有创建任何提交,所以这被视为一种特殊情况:Git 将其创建为空索引。

2这个git add 最终无效,因为名为@9​​87654354@ 的文件仍然是空的。如果此时您在工作树中修改了名为 a 的文件,它会更新第二个临时索引。

3由于 Git 没有使用文件 .git/index 来构建这个新的提交,任何 假定索引名为 .git/index 的预提交钩子都会做错事。请注意,添加工作树后,添加的工作树的主索引也有不同的名称(.git/worktrees/&lt;name&gt;/index,如果我没记错的话)。

【讨论】:

  • 完美答案,帮了我很多:)!你能告诉我在哪里可以找到类似的 Git 详细信息吗?
  • 如果你指的是关于git commit 的东西,我只知道它是如何工作的,因为我查看了git commit 源代码。如果您指的是一般的 Git,那么我现在所知道的最好的一本书是 Pro Git book,但我真的认为任何一种资源都不够。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-21
  • 2011-11-06
  • 2011-12-11
  • 2013-05-20
  • 2015-10-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多