[英]git: squash/fixup earlier commit
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
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. 从Git 1.7.0开始,
rebase
有一个--autosquash
选项, --autosquash
您的需求。 There is also the --fixup
and --squash
options for commit
to make things easier. 还有
--fixup
和--squash
选项用于commit
以使事情更容易。 With some aliasing you can probably even get the whole thing into a single command. 通过一些别名,您甚至可以将整个事情整合到一个命令中。
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-
I created some aliases to make it easier to use the git commit --fixup
and git commit --squash
commands added in git 1.7. 我创建了一些别名 ,以便更容易使用在git 1.7中添加的
git commit --fixup
和git commit --squash
命令。 Add these to your ~/.gitconfig
: 将这些添加到
~/.gitconfig
:
[alias]
fixup = !sh -c 'REV=$(git rev-parse $1) && git commit --fixup $@ && git rebase -i --autosquash $REV^' -
squash = !sh -c 'REV=$(git rev-parse $1) && 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. 糟糕的提交可以是几次提交。
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 files git fixup
显示在触摸相同文件的最新提交下分组的已修改文件 git fixup -a
commits all those changes as --fixup
changes with their corresponding "parent" commits git fixup -a
将所有这些更改提交为--fixup
随其相应的“父”提交而更改 git fixup -r
does an automatic git rebase --autosquash
for all the fixup commits git fixup -r
为所有fixup提交执行自动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. 很多变化是这样的,只有上面的三个命令足以完成工作,没有复制粘贴commit-id或通过
git log
读取以找到正确的--fixup
目标。
Source: https://github.com/ohmu/git-crust 资料来源: https : //github.com/ohmu/git-crust
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.
你不能压缩历史的最高提交。
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 rewriting commit A and rebasing B and C on top of the new A, which is a completely new object.
在您的情况下,您将重写提交A并在新A的顶部重新定位B和C,这是一个全新的对象。 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.
任何已经将旧A拉入其存储库的人最终可能会破坏他们的存储库,因为存储库中的历史记录会“神奇地”改变。 Their git would have no way of knowing that the history has been rewritten.
他们的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开发人员故意不会这么做,因为您必须意识到执行此类操作的后果。
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. 我认为问题的根源在于git(通常是版本控制)迫使你根据变化的顺序进行思考,但是变更集或特征分支或任何你称之为有凝聚力的相关变化的组合通常不是逻辑顺序的。 The order in which the code was written is incidental and not necessarily related to the order in which it should be read.
编写代码的顺序是偶然的,并不一定与它应该被读取的顺序相关。
I don't have a solution to that, but I have written a Perl script to help automate the process of rewriting history. 我没有解决方案,但我编写了一个Perl脚本来帮助自动化重写历史记录的过程。 It's similar to the Python script of @MikaEloranta which I hadn't seen when I wrote it.
它类似于@MikaEloranta的Python脚本,我在编写它时没有看到它。
commit --fixup
and rebase --autosquash
are great, but they don't do enough. commit --fixup
和rebase --autosquash
很棒,但是它们做得不够。 When I have a sequence of commits ABC
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!
当我有一系列提交
ABC
并且我在工作树中写了一些属于一个或多个现有提交的更改时,我必须手动查看历史记录,确定哪些更改属于哪些提交,进行提交并创建fixup!
commits. 提交。 But git already has access to enough information to be able to do all that for me.
但是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!
对于
git diff
的每个hunk,脚本使用git blame
来查找最后触及相关行的git commit --fixup
,并调用git commit --fixup
来编写适当的fixup!
commits, essentially doing the same thing I was doing manually before. 提交,基本上做我以前手动做的事情。
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. 如果脚本无法将hunk解析为单个,明确的提交,它会将其报告为失败的hunk,并且您将不得不回到该方法的手动方法。 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());
但是,它可能发生在一个bugfix分支中,设想一个例子行
foo(bar());
could be touched by commit A (rename foo
to fox
) and commit B (rename bar
to baz
). 可以通过提交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. 如果你发现这个脚本很有用,请随意改进并迭代它,也许有一天我们会在
git
得到这样的功能。 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. 我很想看到一个工具,可以理解当交互式rebase引入合并冲突时应该如何解决它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.