简体   繁体   English

git:squash / fixup之前的提交

[英]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 --fixupgit 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 --fixuprebase --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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM