仅恢复推送提交的单个文件

2024-01-05

以下是推送的提交历史记录。

Commit      Changed Files
Commit 1|  (File a, File b, File c)
Commit 2|  (File a, File b, File c)
Commit 3|  (File a, File b, File c)
Commit 4|  (File a, File b, File c)
Commit 5|  (File a, File b, File c)

我想恢复发生的更改File b of Commit 3.

但是...我想在提交 4 和 5 中保留对此文件发生的更改。


在 Git 中,每次提交都会保存一个snapshot——也就是说,状态every文件——而不是一组更改。

然而,每一次提交——好吧,almost每个提交 - 也有一个parent(上一个)提交。如果你问 Git,例如,提交中发生了什么a123456?,Git 所做的就是找到parent of a123456, 提炼that快照,然后提取a123456本身,然后比较两者。无论是不同的 in a123456,这就是 Git 会告诉你的。

由于每次提交都是完整的快照,因此很容易恢复to特定提交中特定文件的特定版本。你只需告诉 Git:给我获取文件b.ext来自提交a123456,例如,现在您拥有文件的版本b.ext来自提交a123456。这就是您似乎要问的问题,因此链接的问题和当前的答案(截至我输入此内容时)提供的内容。不过,您编辑了您的问题,以要求一些完全不同的东西。

多一点背景

我现在必须猜测您的五次提交中每一次的实际哈希 ID。 (每个提交都有一个唯一的哈希 ID。哈希 ID——由字母和数字组成的丑陋的大字符串——是提交的“真实名称”。这个哈希 ID 永远不会改变;使用它总是能让你that提交,只要该提交存在。)但是它们是由字母和数字组成的丑陋的大字符串,所以不要猜测,比如说,8858448bb49332d353febc078ce4a3abcc962efe,我会调用你的“commit 1”D,你的“提交2”E, 等等。

由于大多数提交都有一个父提交,这让 Git 从较新的提交向后跳转到较旧的提交,让我们用这些向后箭头将它们排列成一行:

... <-D <-E <-F <-G <-H   <--master

A 分店名称 like master实际上只是保存了latest提交该分支。我们说这个名字指着提交,因为它具有允许 Git 检索提交的哈希 ID。所以master指着H. But H哈希 ID 为G, so H指向其父级G; G哈希 ID 为F;等等。这就是 Git 向你展示提交的方式H首先:你问 Git哪个提交是master?它说H。你要求 Git 向你展示H和 Git 摘录both G and H并对它们进行比较,并告诉您发生了什么变化H.

你所要求的

我想恢复提交 3 的文件 b 发生的更改[F]。但我想要在提交 4 和 5 中对该文件进行的更改[G and H].

请注意,此版本的文件可能不会出现在任何提交中。如果我们采用提交中出现的文件E(您的提交 2),我们得到一个没有更改的F,但它没有变化G and H添加到其中。如果我们继续do添加来自的更改G对它而言,这可能与in G;如果我们添加以下更改H之后,这可能与现在的不一样in H.

显然,这会有点困难。

Git 提供git revert这样做,但它做得太多了

The git revert命令旨在执行此类操作,但它是在提交范围的基础. What git revert实际上,是找出某些提交中发生了什么变化,然后(尝试)undo just 那些变化.

这是一个相当好的思考方式git revert:它通过将提交与其父提交进行比较,将提交(快照)转换为一组更改,就像查看提交的所有其他 Git 命令一样。所以对于提交F,它将与 commit 进行比较E,查找文件的更改a, b, and c。然后——这是第一个棘手的地方——它反向应用这些更改给你的current犯罪。也就是说,因为你实际上正在提交H, git revert可以获取所有三个文件中的任何内容 -a, b, and c——并且(尝试)undo到底做了什么to他们在提交E.

(实际上有点复杂,因为这个“撤消更改”的想法还考虑到了other自提交以来所做的更改F,使用 Git 的三向合并机制。)

反向应用某个特定提交的所有更改后,git revert现在做了一个new犯罪。所以如果你做了一个git revert <hash of F>你会得到一个new提交,我们可以称之为I:

...--D--E--F--G--H--I   <-- master

其中F对所有三个文件的更改都被撤销,产生了可能不在其中的三个版本any较早的提交。但这太过分了:你只想F的更改为b退出。

因此,解决方案是少做一点,或者做太多,然后修复它

我们已经描述了git revert行动为:找到更改,然后反向应用相同的更改。我们可以使用一些 Git 命令自行手动完成此操作。让我们从git diff或简写版本,git show: 这两个都转快照进入改变。

  • With git diff,我们将 Git 指向父级E和孩子F并询问 Git:这两者有什么区别?Git 提取文件,比较它们,并向我们展示发生了什么变化。

  • With git show,我们指向Git提交F; Git 找到父级E自己,提取文件并进行比较,并向我们展示发生了什么变化(以日志消息为前缀)。那是,git show commit总数是git log(仅针对该一次提交)然后是git diff(从该提交的父级到该提交)。

Git 将显示的变化本质上是:指示:他们告诉我们,如果我们从以下文件开始E,删除一些行,并插入一些其他行,我们将得到其中的文件F。所以我们只需要reversediff,这很容易。事实上,我们有两种方法可以做到这一点:我们可以将哈希 ID 与git diff,或者我们可以使用-R标记为任一git diff or git show。然后我们会收到实质上说的说明:如果您从以下文件开始F,并应用这些说明,您将从中获取文件E.

当然,这些说明会告诉我们进行更改三个全部 files, a, b, and c。但现在我们可以脱掉三个文件中两个的说明,只留下我们的说明want.

同样,有多种方法可以做到这一点。显而易见的方法是将所有指令保存在一个文件中,然后编辑该文件:

git show -R hash-of-F > /tmp/instructions

(然后编辑/tmp/instructions)。不过,还有一种更简单的方法,那就是告诉 Git:只需要显示特定文件的说明。我们关心的文件是b, so:

git show -R hash-of-F -- b > /tmp/instructions

如果您检查说明文件,它现在应该描述如何获取其中的内容F并且不改变b让它看起来像里面的东西E反而。

现在我们只需要让 Git 应用这些指令,除了代替提交的文件F,我们将使用来自current commit H,它已经位于我们的工作树中,准备进行修补。应用补丁的 Git 命令(一组有关如何更改某些文件集的说明)是git apply, so:

git apply < /tmp/instructions

应该可以解决问题。 (但请注意,这将fail如果说明说要换行b随后被提交更改G or H。这是哪里git revert更聪明,因为它可以完成整个“合并”的事情。)

成功应用说明后,我们可以查看文件,确保它看起来正确,然后使用git add and git commit照常。

(旁注:您可以使用以下方法一次性完成这一切:

git show -R hash -- b | git apply

And, git apply有它自己的-R or --reverse也有标志,所以你可以这样拼写:

git show hash -- b | git apply -R

它做同样的事情。还有额外的git apply标志,包括-3 / --3way这将让它做更奇特的事情,就像git revert does.)

“做太多,然后撤回一些”的方法

处理这个问题的另一种相对简单的方法是让git revert完成其所有工作。当然,这将撤销对other您不想撤回的文件。但我们在顶部展示了获得它是多么容易any文件来自any犯罪。那么假设我们让 Git 撤消all的变化F:

git revert hash-of-F

这使得新的提交I退出一切 in F:

...--D--E--F--G--H--I   <-- master

现在是微不足道的git checkout两个文件a and c from commit H:

git checkout hash-of-H -- a c

然后进行新的提交J:

...--D--E--F--G--H--I--J   <-- master

文件b同时I and J是我们想要的方式,文件a and c in J是我们想要的方式——它们与文件匹配a and c in H——所以我们现在已经基本完成了,除了烦人的额外提交I.

我们可以摆脱I有几个方面:

  • Use git commit --amend制作时J: 这个推I通过提交来摆脱困境J使用提交H as J的父母。犯罪I仍然存在,只是被遗弃了。最终(大约一个月后)它会过期并真正消失。

    如果我们这样做,提交图将如下所示:

                       I   [abandoned]
                      /
    ...--D--E--F--G--H--J   <-- master
    
  • Or, git revert has a -n告诉 Git 的标志:进行恢复,但不提交结果。(这也可以使用脏索引和/或工作树进行恢复,但如果您确保从干净的提交签出开始H你不需要担心这意味着什么。)在这里我们将从H, 恢复F,然后告诉 Git获取文件a and c从提交返回H:

    git revert -n hash-of-F
    git checkout HEAD -- a c
    git commit

    既然我们已经提交了H当我们这样做时,我们可以使用名称HEAD参考副本a and c正在提交的H.

(Git 就是 Git,还有六种其他方法可以做到这一点;我只是使用我们在这里一般说明的那些方法。)

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

仅恢复推送提交的单个文件 的相关文章

  • git 忽略行结尾

    我知道已经有人问过类似的问题 但我仍然无法让它发挥作用 我的项目在使用不同操作系统的人之间共享 而我使用的是 OSX 另外 并不是每个人都使用 git 有时我最终不得不提交其他人的更改 有时 git 会突然说有待处理的更改 查看文件 它们看
  • 将各种提交合并为一而不合并

    是否可以加入各种提交 这是我的情况 我的应用程序在 OSX 10 6 和 10 7 中运行 我已经修复了 10 6 的一些内容 然后提交了 更改为 10 7 并再次修复修复程序 以便它们兼容 然后再次提交 然后返回到 10 6 并再次检查并
  • git 查找胖提交

    是否可以获取有关每次提交中的更改浪费了多少空间的信息 以便我可以找到添加了大文件或大量文件的提交 这一切都是为了尝试减少 git repo 的大小 变基并可能过滤提交 你可以这样做 git ls tree r t l full name H
  • 如何使用 vim 作为“git log”编辑器?

    当我跑步时git log 编辑器到底是什么git log正在使用 Also 无论如何我可以使用吗vim作为我的默认编辑器git log 如果我想搜索 git 日志 最好的方法是什么 现在我正在做类似的事情 git log grep bla
  • Git 日志历史记录

    对于版本控制来说 重要的一件事是知道谁做了什么更改 如果某些内容发生了变化 而我不知道为什么要进行更改 我会查看历史并询问进行更改的人 当我探索 git 时 让我对这个功能有点紧张的一件事是它似乎很容易伪造 是什么阻止我将同事姓名 电子邮件
  • Mercurial日志接口

    我一直在浏览 hginit com 上的教程 但我发现了一个相当不方便的方面hg log特征 基本上 当我输入它时 我会在顶部看到最新的更改 在底部看到最旧的更改 但这很烦人 因为大多数时候 您希望看到最新的修订版 所以 假设我有 100
  • Git 合并删除文件

    这是第二次发生这种情况 当我进行合并时 我后来意识到正在合并的分支中的一些文件不再位于正在合并的分支中 最新的例子是我们有一个功能分支 我一直在合并主开发分支中的更改 合并后我们丢失了很多文件 并且它们现在不存在于功能分支中 为什么会出现这
  • egit:设置gitignore忽略所有eclipse项目文件

    我在 github 上有一个项目 我想从中删除所有与 eclipse 相关的文件 并允许克隆它的人使用他们想要的任何 ide 这是该项目 https github com vedi0boy Archipelo https github co
  • 禁止 Gerrit 推送到 refs/for/master

    我已经更新了所有项目昨天获得访问权限 人们说他们今天早上无法将更改推送到存储库 当我恢复访问权限后 他们仍然无法将更改推送到存储库 只能clone工作正常 在将更改推送到远程存储库时 它表示 remote rejected HEAD gt
  • 使用 git filter-branch 然后 git push --force 清除大文件后,远程(Gitlab)上的 Git 存储库大小仍然很大

    我已按照 Gitlab 文档中有关 使用 Git 减少存储库大小 的说明进行操作 通过从历史记录中清除大文件 https docs gitlab com ee user project repository reducing the rep
  • 使用 Git 部署时压缩 JS/CSS 文件

    我对 git 有点陌生 另外 这是我第一个自动化部署过程的项目 到目前为止 能够做到这一点真是太幸福了git push dev并上传文件 复制配置文件等 现在 当我推送到我的开发服务器时 我想缩小 JS CSS 文件 我正在考虑在服务器上安
  • 无法从 Git 扩展向 GitHub 进行身份验证?

    我只是在 Visual Studio 2010 中设置 Git 扩展 但无法真正使其工作 我无法进行身份验证 在 Git Extensions 中 如果我选择 Plugins gt GitHub 我可以添加用户 密码 API 令牌并选择 S
  • github Diff 截断错误

    在 github 中发出拉取请求并审查更改时 我们收到了 Diff Truncated 错误 如下所示 任何人都可以帮助解决这个问题 拉取请求可能会触发以下提到的限制之一GitHub 支持 https stackoverflow com a
  • 在功能分支上运行测试

    我有一个构建配置 其中包含连接到 git 分支的测试 VCS 根dev 3 个构建步骤和 1 个触发器 这些是我的构建步骤 构建测试 运行测试 构建和部署 我想为分支运行所有这些构建步骤dev但只有其中两个 构建和运行测试 用于分支匹配fe
  • 返回到 Github Desktop 中的上一个提交

    我正在尝试使用 GitHub Desktop 即 GUI 应用程序 而不是命令行 返回到先前的提交 在同一分支上 我认为这是一个核心功能 因为它是首先使用源代码控制的主要原因 我可以看到可以恢复提交 但这并不是我真正想要的 因为它创建了一个
  • 使用 SourceTree 克隆存储库

    有人可以给我一个简单的使用 SourceTree 克隆存储库的快速演练吗 在书签中 我单击克隆存储库 对于源路径 我粘贴如下所示的 URL 电子邮件受保护 cdn cgi l email protection 客户端 应用程序名称 ios
  • Azure git 部署 - 第二个程序集中缺少引用

    我正在尝试将 Bitbucket 部署设置到 Azure 网站 我成功链接了 Bitbucket 和 Azure 但是当我推送到 Bitbucket 时 我在 Azure 站点上收到以下错误 如果我单击 查看日志 它会显示以下编译错误 D
  • git 从存储中删除文件

    我有一个藏匿处 里面有一堆文件 但由于文件冲突 我无法应用我的存储 我已经在我的存储中发现了有问题的文件 我想将其删除 如何从存储中删除单个文件而不破坏整个文件 存储是一次提交 或者实际上是两次甚至有时是三次提交 并且您无法更改提交 那么
  • Git rebase --继续而不打开编辑器

    调用时git rebase continue在正常的变基冲突之后 编辑器 GIT EDITOR 打开并要求修改提交消息 因为提交消息可能包含前导 所以这可能会失败 export GIT EDITOR true git rebase cont
  • 将主分支的提交合并到另一个分支,但不合并两个分支

    我有 git 存储库和一个主分支 我决定开发新功能 并且创建了新分支 new branch 我已经在 new branch 中创建了一些提交 但我还没有完成新功能 我决定修复 master 分支中的一些错误 因此我切换到 master 分支

随机推荐