撤消 git 中的更改(不重写历史记录)

发布于 2024-07-15 04:34:57 字数 367 浏览 7 评论 0原文

我对脚本进行了更改并提交了它。 然后我做了一些其他更改,并将它们推送到远程存储库等。

然后我意识到我提到的第一个更改是愚蠢的,并且想要撤消它。我可以“取消应用”该提交,而不手动复制/粘贴差异吗?

举个例子:我有两个文件,a.pyb.py

Commit 1:
I delete a function in a.py

Commit 2:
I change a few lines in b.py

Commit 3:
I change the docstring in a.py

我可以撤消该函数删除,并使其显示为“commit 4”(而不是删除提交1)

I made a change in a script and committed it. Then I made a few other changes, and pushed them to a remote repository and such.

Then I realised that first change I mentioned was stupid, and want to undo it.. Can I "unapply" that commit, without manually copy/pasting the diff?

As an example: I have two files, a.py and b.py:

Commit 1:
I delete a function in a.py

Commit 2:
I change a few lines in b.py

Commit 3:
I change the docstring in a.py

Can I undo that function deletion, and make it appear as "commit 4" (rather than deleting commit 1)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

凉世弥音 2024-07-22 04:34:57

是的,您可以使用 git revert 来实现此目的。 有关详细信息,请参阅 git 手册部分

要点是你可以说:

git revert 4f4k2a

其中 4f4k2a 是你想要撤消的提交的 id,它会尝试撤消它。

Yes, you can use git revert for this. See the git manual section on this for more information.

The gist is that you can say:

git revert 4f4k2a

Where 4f4k2a is the id of the commit you'd like to undo, and it will try to undo it.

三五鸿雁 2024-07-22 04:34:57

只是一条评论:

git revert aCommit

是否恢复所有提交(如“提交的所有文件部分”):
它计算一个反向补丁,将其应用于 HEAD 并提交。

所以这里有两个问题(第一个问题很容易解决):

  • 它总是提交,所以你可能需要添加 -no-commit 选项:“git revert --no-commit aCommit":当连续恢复多个提交对索引的影响时,这非常有用。
  • 它不适用于特定文件(如果您的 a.py 是包含 1000 个您可能不想恢复的其他更改的提交的一部分,该怎么办)?
    为此,如果您想提取另一个提交中的特定文件,您应该看到 git-checkout,特别是 git checkout语法(但这并不完全是您在本例中需要的)

Easy Git (Elijah Newren) 试图为 Git 邮件列表; 但没有取得多大成功:

人们有时想要“恢复更改”。

现在,这可能是:

  • 32 版和 29 版之前的更改,
  • 可能是自上次提交以来的所有更改,
  • 可能是自 3 次提交以来的更改,或者
  • 它可能只是一个特定的提交。
  • 用户可能希望将此类还原子集到特定文件

例如还原记录在这里,但我不确定它是当前发行版的一部分,例如)

但这一切最终都归结为“恢复更改”。

eg revert --since HEAD~3  # Undo all changes since HEAD~3
eg revert --in HEAD~8     # much like git revert HEAD~8, but nocommit by default
eg revert --since HEAD foo.py  # Undo changes to foo.py since last commit
eg revert foo.py               # Same as above
eg revert --in trial~7 bar.c baz.  # Undo changes made in trial~7 to bar.[ch]

这些类型的“恢复数据”是否真的如此不同,以至于需要不同的命令,或者简单的恢复命令不支持其中一些操作?
当然,大多数用户大多数时候可能会使用“eg revert FILE1 FILE2...
形式,但我没有看到支持额外功能的坏处。

另外...有什么基本的东西可以阻止核心 git 采用这种行为吗?

以利亚

注意:默认情况下提交对于通用的 revert 命令没有意义,并且“git revert REVISION”会错误提示(告诉用户添加--in 标志)。


假设您在 50 个已提交的文件中发现了 20 个文件,您意识到旧的提交 X 引入了不应该发生的更改。
一些管道是有序的。
您需要的是一种列出您需要恢复的所有特定文件的方法
(如“取消提交 X 中所做的更改,同时保留所有后续更改”),
然后,对于它们中的每一个:

git-merge-file -p a.py X X^

这里的问题是恢复丢失的函数,而不删除您可能想要保留的 a.py 中的所有后续更改。
该技术有时被称为“负合并”。

由于 git merge-file; <基本文件> <其他文件>意思
将从 的所有更改合并到 中;,您可以通过说要合并所有更改来恢复已删除的函数。)

  • 从:X(函数已被删除的位置)
  • 到:X^(X 之前的上一次提交) ,该函数仍然存在)

注意:'-p' 参数允许您首先查看更改,而无需对当前文件执行任何操作。 当您确定后,删除该选项。

注意git merge-file没那么简单:您不能像这样引用文件的早期版本。
(您会一遍又一遍地收到令人沮丧的消息:错误:无法统计 X
您必须:

git cat-file blob a.py > tmp/ori # current file before any modification
git cat-file blob HEAD~2:a.py > tmp/X # file with the function deleted
git cat-file blob HEAD~3:a.py > tmp/F # file with the function which was still there

git merge-file a.py tmp/X tmp/F # basically a RCS-style merge
                                 # note the inversed commit order: X as based, then F
                                 # that is why is is a "negative merge"
diff -u a.py tmp/ori # eyeball the merge result
git add a.py 
git commit -m "function restored" # and any other changes made from X are preserved!

如果要在先前提交中对大量文件执行此操作...需要编写一些脚本;)

Just a comment:

git revert aCommit

does revert the all commit (as in "all the files part of the commit" ):
it computes a reverse patch, applies it on HEAD and commit.

So two problems here (the first one is easily solved):

  • it does always commit, so you may want to add -no-commit option: "git revert --no-commit aCommit": this is useful when reverting more than one commits' effect to your index in a row.
  • it does not apply for a specific file (what if you a.py was part of a commit with 1000 other changes that you may not want to revert) ?
    For that, if you want to extract specific files as they were in another commit, you should see git-checkout, specifically the git checkout <commit> <filename> syntax (that is not exactly what you need in this case though)

Easy Git (Elijah Newren) tried to bring a more "complete revert" to the Git Mailing list; but without much success:

People occasionally want to "revert changes".

Now, this may be:

  • the changes between 32 and 29 revisions ago,
  • it might be all changes since the last commit,
  • it could be the changes since 3 commits ago, or
  • it could be just one specific commit.
  • The user may want to subset such reversions to just specific files,

(eg revert is documented here, but I am not sure it is part of the current distribution of eg though)

but it all boils down to "reverting changes" in the end.

eg revert --since HEAD~3  # Undo all changes since HEAD~3
eg revert --in HEAD~8     # much like git revert HEAD~8, but nocommit by default
eg revert --since HEAD foo.py  # Undo changes to foo.py since last commit
eg revert foo.py               # Same as above
eg revert --in trial~7 bar.c baz.  # Undo changes made in trial~7 to bar.[ch]

Are these kinds of "reverting data" really so different that there should need to be different commands, or that some of these operations shouldn't be supported by the simple revert command?
Sure, most users most of the time will probably use the "eg revert FILE1 FILE2..."
form, but I didn't see the harm in supporting the extra capabilities.

Also...is there anything fundamental that would keep core git from adopting such behavior?

Elijah

Note: commits by default don't make sense for the generalized revert command, and "git revert REVISION" would error out with instructions (telling the user to add the --in flag).


Lets say you have, out of 50 committed, 20 files you realize that old commit X introduced changes that should not have taken place.
A little plumbing is in order.
What you need is a way to list all the specific files you need to revert
(as in "to cancel changes made in commit X while keeping all subsequent changes"),
and then, for each of them:

git-merge-file -p a.py X X^

The issue here is to recover the lost function without obliterating all subsequent changes in a.py you might want to keep.
That technique is sometime called "negative merging".

Since git merge-file <current-file> <base-file> <other-file> means:
incorporates all changes that lead from the <base-file> to <other-file> into <current-file>, you can restore the deleted function by saying you want to incorporate all changes.)

  • from: X (where the function has been deleted)
  • to: X^ (the previous commit before X, where the function was still there)

Note: the '-p' argument which allows you to review first the changes without doing anything on the current file. When you are sure, remove that option.

Note: the git merge-file is not that simple: you can not reference previous versions of the file just like that.
(you would have over and over the frustrating message: error: Could not stat X)
You have to:

git cat-file blob a.py > tmp/ori # current file before any modification
git cat-file blob HEAD~2:a.py > tmp/X # file with the function deleted
git cat-file blob HEAD~3:a.py > tmp/F # file with the function which was still there

git merge-file a.py tmp/X tmp/F # basically a RCS-style merge
                                 # note the inversed commit order: X as based, then F
                                 # that is why is is a "negative merge"
diff -u a.py tmp/ori # eyeball the merge result
git add a.py 
git commit -m "function restored" # and any other changes made from X are preserved!

If this is to be done for a large number of files within a previous commit... some scripting is in order ;)

吝吻 2024-07-22 04:34:57

要将更改仅恢复到提交中的一个文件,如 VonC 指出,我会签出分支(主干或主干或其他),然后签出我想要恢复的文件版本并将其视为新提交:

$ git checkout trunk
$ git checkout 4f4k2a^ a.py
$ git add a.py
$ git diff              #verify I'm only changing what I want; edit as needed
$ git commit -m 'recover function deleted from a.py in 4f4k2a'

可能有一个管道命令可以直接执行此操作,但如果我知道的话我就不会使用它。 这并不是说我不相信 Git,而是我不相信自己——我不会相信,如果不查看该提交中的文件以及此后的更改内容,我就知道自己知道什么。 一旦我查看,通过编辑 diff 来构建新的提交会更容易。 也许这只是个人的工作方式。

To revert the changes to only one file within a commit, as VonC pointed out, I would checkout the branch (master or trunk or whatever) and then checkout the version of the file I wanted to revert and treat it as a new commit:

$ git checkout trunk
$ git checkout 4f4k2a^ a.py
$ git add a.py
$ git diff              #verify I'm only changing what I want; edit as needed
$ git commit -m 'recover function deleted from a.py in 4f4k2a'

There's probably a plumbing command that would do this directly, but I wouldn't use it if I knew it. It's not that I don't trust Git, but I don't trust myself -- I wouldn't trust that I knew without looking what was changed in that file in that commit and since then. And once I look, it's easier to just build a new commit by editing the diff. Perhaps that's just personal workstyle.

忆梦 2024-07-22 04:34:57

Have a look at this git revert question.
There seems to be a issue reverting older commits if not in a consecutive sequence including the most recent commit.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文