如何在交互式变基期间取消暂存文件(从旧提交中删除文件)?

2023-12-22

我读过Pro Git关于交互式变基以更改多个提交的书。所以我正在做git rebase HEAD~3,改了我想修改的一项edit。然后我可以通过更改消息git commit --amend。并通过添加一个文件git add file3之后git commit --amend开始输出“3 个文件已更改”而不是之前的“2​​ 个文件”。但如何删除文件呢?为什么两者都没有git restore --staged file1 nor git reset HEAD file1作品? (输出git commit --amend仍然是“3 个文件已更改”并且输出为git log --patch仍然显示file1对于具有最近修改的提交消息的提交)。

我使用过网络搜索并阅读什么是“git Restore”命令以及“git Restore”和“git Reset”之间的区别是什么? https://stackoverflow.com/questions/58003030/what-is-the-git-restore-command-and-what-is-the-difference-between-git-restor(如上所示,尝试了两者restore and reset), 从 Git 提交中删除文件 https://stackoverflow.com/questions/12481639/remove-files-from-git-commit(谈论最后一次提交),删除旧提交中提交的文件 https://stackoverflow.com/questions/68374103/remove-file-commited-in-old-commit(据我了解git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch <path_to_file>" HEAD从分支中的所有提交中删除文件)。

也有没有被赞成的答案删除旧的、已推送的 git commit 中对文件的更改? https://stackoverflow.com/questions/58795826/remove-changes-to-file-in-old-already-pushed-git-commit with

git rebase -i HEAD~3 //Change "pick" to "edit" for commit to change
git reset HEAD^ -- F2 //reset F2 to previous version in staging area  

是否要删除文件需要重置整个提交?在变基期间无法删除单个文件?


当你谈论删除文件在交互式变基期间,您可能意味着以下两件事之一:

  • 制作文件匹配之前的提交。有些人会称之为删除对文件的更改,并且由于有些人想到了提交as变化,有些人会将其缩短为删除文件.

  • 从字面上删除该文件,以便您新的和改进的提交omits文件。

两者都相对容易做到。在我说如何做之前,我先介绍一些背景知识。

背景

要了解您在做什么及其原因,拥有正确的 Git 提交思维模型会有所帮助:

  • 每个提交都有一个唯一的编号(hash ID or OID,其中 OID 代表对象 ID)。提交的编号是 Git 真正找到提交的方式:分支机构名称其实并不重要。

  • 所有 Git 提交(实际上,所有 Git 内部对象)都是完全只读的。你不能change一个提交,所以这不是git rebase确实如此。

  • 每个提交都存储两件事:(a) 的快照all文件,以及 (b) 一些元数据。

提交快照中的文件经过压缩和 Git 化,重要的是,去重(在提交内和跨提交)当某个文件的内容与其他文件的内容匹配时。因此,大多数提交主要包含大多数先前提交的所有文件这一事实不会导致存储库膨胀:这些重复的文件仅存储一次。只要您不在 Git 中存储大型、不可压缩的二进制文件,Git 就会以无形的方式很好地处理这一切(如果您这样做,那么 Git 处理此问题的能力很差,并且存储库会膨胀并变得无法使用)。但这也意味着承诺don't store changes.

The metadata提交中记录了诸如提交人、提交时间和原因(他们的日志消息)之类的内容,但它也记录了对 Git 内部操作至关重要的数据:每个提交都存储一个列表父提交哈希 ID。该列表通常只有一个元素长,为每个提交提供一个父项。父提交也有一个快照,为了将提交转换为更改(用于查看目的),Git 提取两个快照并查看哪些文件发生了更改。由于重复数据删除,Git 可以缩短这一过程,根本不需要提取相同的文件;然后它只需要为以下文件提出一个更改方案:don't两次提交中的匹配。这就是你所看到的git show or git log -p:提交的(单个)父级快照与提交快照的差异。

因为提交是只读的,只有 Git 本身可以read对于他们,我们实际上并没有处理或处理这些提交。相反,当我们选择一些提交来使用时,我们就有了 Gitextract提交(例如解压缩或解压缩某些存档)到工作区域,Git 将其称为我们的工作区域工作树 or 工作树。当这些工作树文件被提取时fromGit,他们实际上不是in当你完成工作时,完全可以使用 Git。

Because commits are read-only, git rebase can't fix any bad commit, nor does git commit --amend change a commit. Instead, Git makes use of the fact that humans—unlike Git itself—never1 find a commit by hash ID. Instead, we use branch names. A branch name simply holds the hash ID of the last commit we want to claim to be "part of the branch". That commit then holds, in its metadata, the hash ID of the previous commit, which holds in its metadata the hash ID of another even-earlier commit, and so on. This produces a simple backwards-looking chain:

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

其中分支名称保存了该分支的哈希 IDlast commit H在链条中,一切都从那里开始向后进行。

当我们以正常的日常方式添加提交时,Git 会创建一个新的(只读)提交I谁的父母是H,将其添加到链中,并写入新提交的哈希 IDI进入分支名称:

...--G--H--I   <-- branch (HEAD)

“修改”提交H, Git 只是写入新的提交I与提交G作为其父级,而不是提交H, 导致:

       H
      /
...--G--I   <-- branch (HEAD)

Commit H仍然存在,但除非我们记住了它的哈希 ID,否则我们永远不会see再来一次。 (Git可以,只要 Git 能找到它的哈希 ID。)

变基简单地包括超过一个新的和改进的提交。如果我们有:

...--F--G--H   <-- main
         \
          I--J   <-- feature (HEAD)

我们想要一个修订版I出现after H我们进行新的快照和元数据提交,而不是之前/并行I'(否则看起来像I,除了我们拿起H的快照作为我们的“基础”并“重新添加”我们的更改I,因此“re-base”-ingI):

             I'  <-- HEAD [detached]
            /
...--F--G--H   <-- main
         \
          I--J   <-- feature

然后我们重复此操作以进行提交J to get J':

             I'-J'  <-- HEAD [detached]
            /
...--F--G--H   <-- main
         \
          I--J   <-- feature

一旦我们复制了all对新的和改进的提交,我们让 Git 移动了name feature指向最后复制的提交:

             I'-J'  <-- feature (HEAD)
            /
...--F--G--H   <-- main
         \
          I--J   [abandoned]

原来的提交仍然存在;我们就是找不到他们。


1(insert Gilbert & Sullivan HMS Pinafore routine here)


交互式变基

Interactive rebase uses the same process as non-interactive rebase,2 but lets us stop and make adjustments. To do that, Git provides us with an instruction sheet. It contains, initially, a series of pick commands for each commit that we will copy. These instruct Git to run git cherry-pick, which is the step that copies a commit, like I to I' above.

改变pick to edit让 Git 进行优先选择,但随后会停止在 detached-HEAD 模式下。请注意,这里我们正在复制I to I'并将其放置在与以前相同的物理位置,而不是将其移动到提交后H:

          I'  <-- HEAD [detached]
         /
...--G--H   <-- main
         \
          I--J   <-- feature

现在我们处于这种状态,我们可以使用git commit --amend使完后还有犯罪,I"。在此提交中,我们可以存储任何我们喜欢的快照,并使用任何我们喜欢的提交消息。的父母I"H,与父级相同I and I'.

The snapshot进入新提交的内容与任何新 Git 提交具有相同的来源:它来自 Gitindex AKA https://www.merriam-webster.com/dictionary/aka 暂存区。当前包含提交的所有文件I',它匹配提交中的所有文件I(因此,不会使用任何空间,因为它们都是已经预先去重复的重复项)。这些文件的 Git 化副本也位于您的工作树中。因此您可以修改或删除工作树中的文件并运行git add:

vim foo.py
git add foo.py

or:

rm foo.py
git add foo.py

The git add步骤告诉 Git 让索引副本与工作树副本匹配,通过读取、压缩和去重文件,或者在删除之后foo.pyremoving索引完全复制。或者:

git rm foo.py

结合了rm and git add变成一步。不管怎样,我们已经安排了正确的(更新或删除的)文件在 Git 的索引中,所以我们现在运行git commit --amend,就像你所做的那样:

git commit --amend

这推动了提交I'让开,留下提交I"指向H:

         I'  [abandoned]
        /
        | I"  <-- HEAD [detached]
        |/
...--G--H   <-- main
         \
          I--J   <-- feature

Running git rebase --continue告诉变基代码继续执行指令表中的下一条指令:另一个pick, or edit, or reword, 管他呢。一旦遵循了最后一条指令,rebase 将像以前一样拉动分支名称:

         I'  [abandoned]
        /
        | I"-J'  <-- feature (HEAD)
        |/
...--G--H   <-- main
         \
          I--J   [abandoned]

(废弃的提交会保留一段时间——在通常的设置中,默认情况下至少为 30 天——然后 Git 最终注意到它们已经闲置了足够长的时间,删除了使它们保持活动状态的引用日志条目,并清除了它们是真的。不过,在那之前,您可以轻松取回原件。请注意,特殊名称ORIG_HEAD还记得提交J一段时间,直到你做了其他让 Git 覆盖的事情ORIG_HEAD与另一个哈希 ID。成功变基后,如果您不喜欢结果,ORIG_HEAD与 reflog 条目一样有效branch@{1}.)


2In older versions of Git, there were numerous technical differences. In modern Git, these are largely gone now, though you can still invoke them on purpose if you really want to. I will also elide a number of optimizations Git normally uses for the kind of interactive rebase you'll be doing, that do make things better for Git but don't change the final outcome here.


我们现在可以看到什么git reset or git restore will do

为什么两者都没有git restore --staged file1 nor git reset HEAD file1 works?

Both git reset and git restore将从某处读取文件的内容并将该文件的内容写入某处。这git reset命令本身非常复杂,所以最好坚持使用更新的、更有针对性的(更有限的)git restore在我看来,但任何一种都可以:我们只需要知道这里的几件事。

git reset HEAD^ -- F2 //reset F2 to previous version in staging area

在这里,我们使用的是git reset, not git restore,以其恢复一个文件的操作模式。如果我们使用:

git reset HEAD -- file1

我们告诉 Git:阅读 Git 化的副本file1来自指定的提交HEAD。如果我们使用:

git reset HEAD^ -- F2

我们告诉 Git:阅读 Git 化的副本F2来自指定的提交HEAD^.

在这两种情况下,从指定的提交读取指定的文件后,git reset将(Git 化、预先去重的)内容写入索引/暂存区域,准备进入新的提交。暂存区中的文件名与所选提交中的文件名相同(file1 or F2). The 工作树副本文件的内容在这里没有改变!这是不可取的,因为它使你很难看到你在做什么,但因为 Git 实际上并不是using此时的工作树副本,它并不完全是harmful现在也是。

Using git checkout更好:

git checkout HEAD^ -- F2

这种形式的git checkout——这,就像git reset,极其复杂,这就是为什么git checkout被分成git switch and git restore在 Git 2.23 中——从指定的提交读取文件并将其写入bothGit 的索引and你的工作树。这使得查看您所做的事情变得更加容易,因为工作树副本现在很明显。

如果您的目标是复制F2在新的I"提交与提交中的副本匹配H, these HEAD^命令的形式就可以解决问题。原因是HEAD当前名称提交I', 提交的副本I. I'的父母是H,因此检索文件的副本F2 (or file1) 来自提交H将恢复索引和工作树版本以匹配in H,现在你所做的承诺git commit --amend—the I"提交——其中包含该文件的相同副本(已消除重复)。

如果你的目标是真正remove F2完全,以便提交I"没有文件F2无论如何,git rm F2 (or git rm -- F2以避免名为的文件出现问题--cached,例如)会这样做。

如果我们想做F2匹配中的副本H,但是使用git restore为了避免与过于复杂的签出命令相关的错误,我们运行:

git restore -SW --source=HEAD^ -- F2

例如。这与git checkout:我们指定HEAD^作为文件的来源,-S (--staged) 告诉git restore将文件写入暂存区,并且-W (--worktree) 告诉git restore将文件写入我们的工作树。

请注意,在所有情况下,我们的目标都是使索引包含正确的文件 as git commit --amend将从 Git 的索引中创建新的快照。作为人类,我们通常应该同时更新这些文件的工作树副本,因为我们不能see索引(暂存区)副本,但我们can在我们喜欢的任何编辑器或文件查看器中查看工作树副本。

我们还必须记住,如果当我们跑步时git status,Git 将运行两个git diff --name-status为我们运营:

  • 人们将比较HEADcommit 与 Git 的索引。但是HEAD提交就是提交I',不提交H!所以我们不必对此过于关注。
  • 另一个将比较 Git 的索引和我们的工作树。理想情况下,该 diff 应该为空,以便我们在工作树中查看与 Git 将在下一次提交中使用的文件相同的文件。

The reset --soft option

我们还有一件事can这样做,我自己实际上从未这样做过:而不是git commit --amend,我们可以开始整个修改过程git reset --soft。也就是说,我们开始git rebase -i, 换一个pick编辑时,写出说明表,然后开始变基。我们现在处于这样的状态:

          I'  <-- HEAD [detached]
         /
...--G--H   <-- main
         \
          I--J   <-- feature

The git reset --soft命令让我们移动分离的HEAD不改变eitherGit 的索引or我们的工作树。跑步git reset --soft HEAD^产生这个:

          I'  [abandoned]
         /
...--G--H   <-- main, HEAD [detached]
         \
          I--J   <-- feature

也就是我们放弃commitI' 马上。我们在 Git 的索引/暂存区和工作树中拥有我们想要的大部分内容。通过放弃I'完全,我们现在已经安排好了as if我们从未做出承诺I根本:当前提交 is now H, not I.

我们现在可以git restore -SW --source HEAD -- file1,如果这就是我们想要的。事实上,与-S, --source HEAD是默认值,所以我们可以将其缩短为:

git restore -SW -- file1

这将复制提交的file1来自提交H——我们现在调整后的HEAD——对于 Git 的索引和我们的工作树,丢弃任何changes我们已经提交了I. Now git status and git diff --cached给我们与第一次进行此提交时得到的结果相同的结果。

(如果edit的模式rebase -i一直都是自动完成的,但它没有,现在改变它已经太晚了。)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在交互式变基期间取消暂存文件(从旧提交中删除文件)? 的相关文章

  • 当 .gitattributes 中的 EOL 设置为 CRLF 时,Git diff 认为行结尾为 LF

    当我恢复对带有 Windows 行结尾的文件的更改并且 gitattributes 将 EOL 定义为 CRLF 时 git 认为行结尾已更改为 LR 即使十六进制编辑器显示 CRLF 仅当 gitattributes 定义 EOL 字符时
  • 将bitbucket发布到数字海洋

    我本质上是试图使用 bitbucket 来理解 git 的概念 我一直在通过修改本地帐户和 bitbucket 帐户之间的文件来练习版本控制 事实证明这很有帮助 现在我正在尝试弄清楚如何将文件从 bitbucket 或者我猜是 GitHub
  • 自定义 SSH 端口上的 Git

    我的 VPS 提供商建议我将 SSH 端口保留为他们默认分配的自定义端口号 不是 22 问题是 虽然我知道我可以在创建远程配置时提供端口号 但在进行 Git 克隆时似乎无法提供相同的操作 我在用gitolite https wiki arc
  • Android 存储库初始化失败

    我想我非常仔细地遵循该网站的说明 http source android com source downloading html http source android com source downloading html 但是当我尝试这
  • Git 在哪里存储标签?

    Git 在哪里存储标签 我执行 git tag v0 1 0 v0 10 0 v0 11 0 但目录 git refs tags是空的 Git 将这些标签存储在哪里 谢谢 它们也可以存储在 git packed refs
  • Git 更改丢失 - 为什么?

    我们的开发团队正在使用 git 最近我们至少两次丢失了文件更改 我们正在使用私人 Github 存储库 在当前情况下 我们可以返回 Github 上的日志并查看我对文件所做的一些更新 后来 另一位团队成员更改了文件的不同部分 它似乎破坏了我
  • `git push` -- 没有输出,什么也没有发生

    touch test git add test git commit m test git push u origin master 这奏效了 该文件已上传到存储库 rm test cp R website website git rm t
  • 使用 gitignore 嵌套存储库。

    我想嵌套 2 个 git 存储库 我一直在阅读子模块 有一段时间我认为它很棒 我想我可能想要其他东西 这是我的情况 首先 我想我应该提到我的所有服务器都托管网站 并以 staging domain com 和 domain com 实时 模
  • Eclipse Git 关键字扩展

    每次我检查 git hub 服务器的源代码时 我都需要更新源代码修订关键字 version date 等 你可能知道 Git 中的主要问题是你无法使用以下命令修改文件 提交后有关提交的信息 因为 Git 首先对文件进行校验 基本上我想要实现
  • VSTS:在构建过期的情况下自动变基/合并和重新排队构建验证门

    我们最近对 PR 上的构建验证门进行了更改 这样 如果另一个提交在当前 PR 完成之前进入主分支 则构建会 立即 过期 看here https stackoverflow com questions 49418800 vsts invali
  • 是否可以检测 http git 远程是智能还是愚蠢?

    我正在我的应用程序中实现一个选项来使用 depth 1制作 git repo 的最小功能克隆 我刚刚意识到愚蠢的 http 传输不支持 depth 我想自动检测 http 远程是愚蠢的还是聪明的 这样我就可以省略 depth与哑 http
  • 为 RHEL 6 安装/构建 git-svn

    我无权访问 RHEL6 存储库 那么在 RedHat Enterprise Linux 6 上构建和 或安装 git svn 工具的最佳方法是什么 通过卸载现有的 yum擦除git 并从源安装最新的来设法安装git和git svn 1 7
  • 使用BFG时可以指定多个文件吗?

    我正在尝试删除通配符无法覆盖的多种类型的文件 我尝试使用多个 delete files 但它不接受它 还尝试将文件全部放在 delete files 之后 但它也不接受它 有没有一种方法可以将它们全部放在一个命令中 如果没有 那么我必须运行
  • Git 认为我每次进行小更改时都在重写我的一个文件

    我有一个中等大小的 Java 文件 每次我对一个文件 BuildTable java 进行更改时 Git 都会将其报告为巨大的更改 即使只是一两行 BuildTable java 大约有 200 行 本次提交中的更改仅更改了一行 git d
  • git 错误:无法处理 https

    当我尝试使用 git clone 时https xxx https xxx我收到以下错误我不处理协议 https 有人可以帮我吗 完整消息 dementrock dementrock A8Se git 克隆https git innosta
  • 删除并在另一个文件夹中重新创建后保留文件的 Git 历史记录

    我有以下场景 删除了提交 1 中名为 src GetData cs 的文件 在提交 5 中创建了一个名为 src Get GetDataNew cs 的文件 在提交 7 中将 2 中的文件重命名为 src Get GetData cs 1
  • 在推送后检索孤立的提交对象 --force

    Doing push force总是有点冒险 这里有一个例子 说明它如何产生一些问题 例如远程丢失修订版本 假设 有一个人Bob已更新远程master分支来自B to C 还有另外一个人Mike还没有获取此更新并且HEAD of his m
  • 如何修改 git add 来处理已删除的文件?

    我从 git 存储库中删除了一些文件 现在 根据状态查看 Changes not staged for commit deleted project war favicon ico deleted project war index htm
  • 如何在多个不同的分支上工作,以便我可以在它们之间轻松切换?

    有没有办法在 GIT 中处理同一个文件但不同的功能 分支 我确信有办法 但最简单的方法是什么 我不想隐藏我的更改 因为这很麻烦 借助 SVN 我能够将 2 个独立的分支作为 2 个不同的实体进行工作 无需任何干预 并且可以轻松在两者之间切换
  • 如何在 Visual Studio 2013 中使用 Git 的外部 diff 工具?

    我找到了这个帖子 http architects dzone com articles how configure diff and merge这解释了如何让 Visual Studio 2013 在比较 Git 中的文件时使用内置 dif

随机推荐