19

I notice when I rebase and have a merge conflict, it has the staged changes for the current commit, so it is easy to find what was done in the commit and change it, but then you can also just do 'git rebase --continue' and it will apply the change with the same commit message as before.

Is there a way to force this to happen for specific commits, or all commits, so I can easily rebase and edit the previous changes I made to work with the changes I have rebased on? It seems like this would be much easier than just setting edit on all the commits and then doing 'git reset --soft HEAD~', so that the changes are visible and I can check they make sense for the new HEAD, editing, and then having to do 'git commit -m "message" '.

My use case is that the repository I am working with has had its file structure heavily refactored and git is unable to make the appropriate changes, but says the merge is successful.

Thanks!

GDYendell
  • 377
  • 2
  • 11
  • [You can replay the commit message, too](http://stackoverflow.com/questions/16858069/git-how-to-reuse-retain-commit-messages-after-git-reset), which sort of helps make the `git reset HEAD^` less painful, but I agree - it's much more convenient where `git rebase` stops when it hits a merge conflict, than where it stops on edit in an interactive rebase. I'm putting a bounty on this. – Gordon May 09 '17 at 01:09
  • Let me guess. Someone moved all the files AND changed their contents in the same commit? Rule #1, don't do that – Joe Phillips May 09 '17 at 01:26
  • 3
    Yeah I hate that too. But my particular use case is that I maintain a bunch of open source projects and I often want to go back and fix a bunch of commits, e.g. to remove artifacts. Granted, there is probably a way to do that in particular, but in general I'd like to step through an interactive rebase, stopping just *before* each commit instead of *after* it, while keeping the message cached like a merge conflict does. I find myself relieved when I start getting merge conflicts, just for the nicer interface. – Gordon May 09 '17 at 15:46
  • @Gordon Thanks, that's pretty cool. That gets me most of the way to what I want to do. As you say, it would be nicer to have the conflict interface. Thanks for bumping this. – GDYendell May 10 '17 at 08:21
  • @JoePhillips Actually I think that was done correctly. The only issue is that the code has references for the old C++ namespaces in function calls etc and Git hasn't handled that. – GDYendell May 10 '17 at 08:27

4 Answers4

14

I think what you are looking for is interactive rebasing and rewriting git history. It will let you squash commits as well as change the commit messages.

git rebase -i branch # <branch> should be the parent branch i.e. develop/master

Rewriting git history

# for instance
>>> git rebase -i branch
pick 8705209 first
edit 7979797 second # Use edit wherever you want to view and edit commit
squash 9793757 third # Use squash to squash commits into the previous one.
hspandher
  • 15,934
  • 2
  • 32
  • 45
  • 10
    changing each commit to 'edit' rather than 'pick' is the key here. Rebase will stop at each commit and let you make changes. – Ben Neill May 16 '17 at 00:01
5

force this to happen for specific commits

Start an interactive rebase with git rebase -i HEAD~~~.

This opens an editor with a list of commits:

pick 1234567 fix stuff
pick 789abcd new features
pick 0102030 break everything

Change the pick to edit on a commit (or commits) that you want to edit. Save, and exit the editor.

Then, git will bring you back to these commits, one by one, and you can edit them. I think what you're missing is that, at this point, you can view diffs that are staged, with git diff --cached.

After you've edited each one, use git rebase --continue to continue.

guest
  • 6,450
  • 30
  • 44
4

Just do git commit --amend before git rebase --continue.

Alternatively - use git-cherry-pick --no-commit commit1..commitN.

kan
  • 28,279
  • 7
  • 71
  • 101
  • 1
    That works, but doesn't enable me to see the staged changes for the commit while I'm editing it. In a merge conflict, it seems to do a `git reset --soft` for you and saves the commit message, allowing you to view and add to the commit, and then you can just `git rebase --continue` and it will apply the same commit message again. – GDYendell Apr 27 '17 at 14:01
  • 1
    @GDYendell: I think you misunderstand something (but I am not sure what, exactly) about what `git rebase` actually does. Fundamentally, rebase is *copying* commits, as if by doing repeated `git cherry-pick` commands. It builds up the new copies in a *new* branch, and then switches the branch name so that it remembers only the new copies, instead of the originals. You can see this in action when you use `git rebase -i` since each commit to be copied has the word "pick" in front of it. – torek Apr 27 '17 at 14:34
  • Yes I understand that. I am using rebase to edit previous commits. I have rebased on another branch and git reports it has dones so cleanly, but it hasn't. I want to then rebase the branch on itself so that I can edit the commits I made such that it would have been the correct thing to do had I actually been working off of the branch I rebased on. The only way I know to do this is quite messy (`git reset --soft HEAD~`, edit, `git commit -m`...), however when there is a merge conflict it is easy; you just edit and do `git rebase --continue`. I am looking for a way to force that behaviour. – GDYendell Apr 27 '17 at 14:40
  • @GDYendell if you want to see changes, you could just do `git show`, `git diff HEAD~`, `git diff --cached` and so on depending on which kind of changes you want to see. Not sure what exactly you are trying to achieve by the `reset`. – kan Apr 27 '17 at 15:22
  • @kan The reset makes the changes show up in CLion, which makes it easy to find and edit the relevant lines. – GDYendell Apr 27 '17 at 15:36
1

This is not really an answer, but my alternate solution for the original goal is now just doing the rebase and then editing the commits using lazygit.

GDYendell
  • 377
  • 2
  • 11