git:壁球/修复较早的提交
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/204461/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
git: squash/fixup earlier commit
提问by elmarco
Suppose you have:
假设你有:
A-B-C
Now your build/test fails. The fix should be merged in A. My current work-flow is like this:
现在您的构建/测试失败。该修复程序应合并到 A 中。我当前的工作流程是这样的:
$ git commit -m "fixA"
A-B-C-fixA
$ git rebase -i A~1
And squash fixA in A, result in:
并在 A 中压缩 fixA,导致:
A'-B-C
Is there a command to do something like:
是否有命令可以执行以下操作:
A-B-C + (index with fix for A)
$ git commit -supperdupper A
Result:
结果:
A'-B-C
采纳答案by Jo Liss
If you're just looking for the easy solution for fixing up earlier commits, read the question! It explains it all. But since Elmarco was asking for a slick way, here we go:
如果您只是在寻找修复早期提交的简单解决方案,请阅读问题!它解释了这一切。但既然 Elmarco 要求一种巧妙的方式,我们就开始吧:
As of Git 1.7.0, there is an --autosquash
option for rebase
, which does what you want. There is also the --fixup
and --squash
options for commit
to make things easier. With some aliasing you can probably even get the whole thing into a single command.
从 Git 1.7.0 开始, 有一个--autosquash
选项rebase
,可以满足您的需求。还有--fixup
和--squash
选项commit
可以让事情变得更容易。通过一些别名,您甚至可以将整个事情整合到一个命令中。
I'd suggest upgrading to the newest Git for maximum awesomeness:
我建议升级到最新的 Git 以获得最大的效果:
git/Documentation/RelNotes $ grep -i -A1 autosquash\\|fixup *
1.7.0.txt: * "git rebase -i" learned new action "fixup" that squashes the change
1.7.0.txt- but does not affect existing log message.
--
1.7.0.txt: * "git rebase -i" also learned --autosquash option that is useful
1.7.0.txt: together with the new "fixup" action.
1.7.0.txt-
--
1.7.3.txt: * "git rebase -i" peeks into rebase.autosquash configuration and acts as
1.7.3.txt: if you gave --autosquash from the command line.
1.7.3.txt-
--
1.7.4.txt: * "git commit" learned --fixup and --squash options to help later invocation
1.7.4.txt- of the interactive rebase.
--
1.7.4.txt: * "git rebase --autosquash" can use SHA-1 object names to name which
1.7.4.txt: commit to fix up (e.g. "fixup! e83c5163").
1.7.4.txt-
回答by bkeepers
I created some aliasesto make it easier to use the git commit --fixup
and git commit --squash
commands added in git 1.7. Add these to your ~/.gitconfig
:
我创建了一些别名,以便更轻松地使用git 1.7 中添加的git commit --fixup
和git commit --squash
命令。将这些添加到您的~/.gitconfig
:
[alias]
fixup = !sh -c 'REV=$(git rev-parse ) && git commit --fixup $@ && git rebase -i --autosquash $REV^' -
squash = !sh -c 'REV=$(git rev-parse ) && git commit --squash $@ && git rebase -i --autosquash $REV^' -
Usage:
用法:
$ git commit -am 'bad commit'
$ git commit -am 'good commit'
$ git add . # Stage changes to correct the bad commit
$ git fixup HEAD^ # HEAD^ can be replaced by the SHA of the bad commit
The bad commit can be several commits back.
错误的提交可以是多次提交。
回答by Mika Eloranta
My current git work flow is so --fixup
/--squash
intensive, that I wrote a new git-fixup
command that handles most of the annoying bits automatically:
我当前的 git 工作流程非常--fixup
/--squash
密集,以至于我编写了一个新git-fixup
命令来自动处理大部分烦人的部分:
git fixup
shows the modified files grouped under that latest commits that touch the same filesgit fixup -a
commits all those changes as--fixup
changes with their corresponding "parent" commitsgit fixup -r
does an automaticgit rebase --autosquash
for all the fixup commits
git fixup
显示在触及相同文件的最新提交下分组的修改文件git fixup -a
将所有这些更改提交为--fixup
具有相应“父”提交的更改git fixup -r
git rebase --autosquash
为所有修复提交自动执行
A lot of changes are such that just the three commands above are enough to get the job done, no copy-pasting of commit-id's or reading thru the git log
to find the right --fixup
targets.
很多变化是这样的,仅仅上面的三个命令就足以完成工作,没有提交 ID 的复制粘贴或通过阅读git log
来找到正确的--fixup
目标。
回答by lanoxx
If you want to squash the last two commits then you have to invoke
如果你想压缩最后两个提交,那么你必须调用
git rebase --interactive <3rd last commit>
You then need to pick the last commit and squash the second-to-last commit. You cannot squash the topmost commit of a history.
然后,您需要选择最后一个提交并压缩倒数第二个提交。您无法压缩历史记录的最高提交。
回答by Martin OConnor
What you are doing is dangerous if you are sharing the branch on which you are making changes with other people. In your case, you are rewritingcommit A and rebasing B and C on top of the new A, which is a completely new object. Anyone who had already pulled the old A into their repositories could end up corrupting their repository as the history in your repository will have "magically" changed. Their git would have no way of knowing that the history has been rewritten.
如果您与其他人共享您正在其上进行更改的分支,那么您正在做的事情是危险的。在您的情况下,您正在重写提交 A 并在新的 A 之上重新设置 B 和 C,这是一个全新的对象。任何已经将旧的 A 拉入他们的存储库的人最终都可能损坏他们的存储库,因为您的存储库中的历史将“神奇地”改变。他们的 git 无法知道历史已被改写。
Because of this fact, the Git developers have intentionally not made this easy to do as you must be aware of the consequences of doing such an operation.
由于这个事实,Git 开发人员故意不让这件事变得容易,因为您必须了解执行此类操作的后果。
回答by Oktalist
I think the root of the problem is that git (and version control generally) forces you to think in terms of sequences of changes, but a changeset or feature-branch or whatever you call a cohesive group of related changes is in general not logically sequential. The order in which the code was written is incidental and not necessarily related to the order in which it should be read.
我认为问题的根源在于 git(以及通常的版本控制)迫使您根据更改的顺序进行思考,但是更改集或功能分支或任何您称之为相关更改的内聚组的内容通常在逻辑上不是顺序的. 编写代码的顺序是偶然的,不一定与应该阅读的顺序有关。
I don't have a solution to that, but I have written a Perl scriptto help automate the process of rewriting history. It's similar to the Python script of @MikaEloranta which I hadn't seen when I wrote it.
我没有解决方案,但我编写了一个Perl 脚本来帮助自动化重写历史的过程。它类似于我写它时没有看到的@MikaEloranta 的 Python 脚本。
commit --fixup
and rebase --autosquash
are great, but they don't do enough. When I have a sequence of commits A-B-C
and I write some more changes in my working tree which belong in one or more of those existing commits, I have to manually look at the history, decide which changes belong in which commits, stage them and create the fixup!
commits. But git already has access to enough information to be able to do all that for me.
commit --fixup
并且rebase --autosquash
很棒,但他们做得还不够。当我有一系列提交A-B-C
并且我在我的工作树中写了更多属于一个或多个现有提交的更改时,我必须手动查看历史记录,确定哪些更改属于哪些提交,暂存它们并创建fixup!
提交。但是 git 已经可以访问足够的信息来为我做这一切。
For each hunk in git diff
the script uses git blame
to find the commit that last touched the relevant lines, and calls git commit --fixup
to write the appropriate fixup!
commits, essentially doing the same thing I was doing manually before.
对于git diff
脚本中的每个大块,git blame
用于查找最后触及相关行的提交,并调用git commit --fixup
编写适当的fixup!
提交,本质上与我之前手动执行的操作相同。
If the script can't resolve a hunk to a single, unambiguous commit, it will report it as a failed hunk and you'll have to fall back to the manual approach for that one. If you changed a line twice in two separate commits, the script will resolve a change on that line to the most recent of those commits, which might not always be the correct resolution. IMHO in a "normal form" feature branch you shouldn't be changing a line twice in two different commits, each commit should be presenting the final version of the lines that it touches, to help the reviewer(s). However, it can happen in a bugfix branch, to contrive an example the line foo(bar());
could be touched by commit A (rename foo
to fox
) and commit B (rename bar
to baz
).
如果脚本无法将大块解析为单个明确的提交,它将报告为失败的大块,您将不得不回退到该手动方法。如果您在两个单独的提交中两次更改了一行,脚本会将在该行上的更改解析为这些提交中的最近一次,这可能并不总是正确的解析。恕我直言,在“正常形式”功能分支中,您不应该在两次不同的提交中两次更改一行,每次提交都应该显示它所涉及的行的最终版本,以帮助审阅者。但是,它可能发生在错误修复分支中,设计一个示例foo(bar());
,提交 A(重命名foo
为fox
)和提交 B(重命名bar
为baz
)可以触及该行。
If you find the script useful, please feel free to improve and iterate on it and maybe one day we'll get such a feature in git
proper. I'd love to see a tool that can understand how a merge conflict should be resolved when it has been introduced by an interactive rebase.
如果您发现该脚本有用,请随时对其进行改进和迭代,也许有一天我们会git
适当地获得这样的功能。我很想看到一个工具,它可以理解在交互式 rebase 引入时应该如何解决合并冲突。