44

I have two commits on the same branch, one right after another. I added changes to file A to the first commit, and then I made some changes to other files and then made another commit. Now I want the changes to file A to be on the second commit rather than the first. What's the most elegant way?

Rose Perrone
  • 61,572
  • 58
  • 208
  • 243
  • 1
    you can check git rebase [here](https://git-scm.com/docs/git-rebase#_splitting_commits) – rumman0786 Apr 21 '17 at 16:15
  • This question is a special case of "Squash my last X commits together using Git" https://stackoverflow.com/questions/5189560/squash-my-last-x-commits-together-using-git – Andrew Palmer May 24 '17 at 20:41

5 Answers5

24

I wrote a script to meet this purpose. You can checkout out it here.

Using the script it would be as simple as:

mv-changes HEAD~ HEAD fileA
Mikhail Golubtsov
  • 6,285
  • 3
  • 29
  • 36
24

I know this is an old question but I had the exact same request and I just figured out the best way to resolve this issue: you need to use interactive rebase and choose "edit" for the two commits you want to move a change from and to.

For instance, let say you have 3 commits:

$ git log -3 --oneline
97e9d62 Commit C, editing file2.txt
34b066b Commit B, creating file2.txt + editing file1.txt
73ff1bb Commit A, creating file1.txt

Now, if you want the Commit B to only contain the file2.txt creation but not the file1.txt edition which should be in Commit C, git rebase -i will display this:

pick 73ff1bb Commit A, creating file1.txt
pick 34b066b Commit B, creating file2.txt + editing file1.txt
pick 97e9d62 Commit C, editing file2.txt

# ...

If I replace "pick" with "edit" or "e" for Commit B and Commit C and close my editor, git will stop on the second commit and let me amend my changes:

pick 73ff1bb Commit A, creating file1.txt
edit 34b066b Commit B, creating file2.txt + editing file1.txt
edit 97e9d62 Commit C, editing file2.txt
Stopped at 34b066b... Commit B, creating file2.txt + editing file1.txt
You can amend the commit now, with

    git commit --amend

Once you are satisfied with your changes, run

    git rebase --continue

$ vi file1.txt    # edit of file1.txt to remove the updated line
$ git commit --amend file1.txt
$ git rebase --continue

# -- git continues rebasing and stops on the Commit C to edit it

$ vi file1.txt   # edit of file1.txt to put the removed line
$ git commit --amend file1.txt
$ git rebase --continue

And that's it.

Take care to save somewhere the line(s) you're removing from first commit to put them in the second one. It can be in clipboard, text file, etc. I guess it should be possible to rely on git stash too but if it's a simple change it's easier to keep it in the clipboard.

Loic
  • 451
  • 3
  • 6
  • 6
    If the change is bigger than just a line or two it might be useful to use (while in the edit mode of the first commit) `git reset HEAD~1 -p` to move the needed changes to staging. This could be then followed by `git commit --amend --no-edit` which would reverse the change from commit B. It would leave you with unstaged changes which can be now stashed and applied while editing the commit C. – sloneorzeszki Apr 01 '21 at 07:22
  • Excelent answer! just what I was looking for. In most cases you don't need to rebase too many commits before HEAD, use `git rebase -i HEAD~n` `n` being the number of previous commits to take – Gjaa May 12 '22 at 23:44
7

If they're small commits, and commits should always be small in git, the simplest way is to git reset HEAD^^ and then just do them again. Note that any solution to this involves rewriting history, and if you've already pushed these commits somewhere, you shouldn't do this unless you know what you're doing.

Ryan Stewart
  • 126,015
  • 21
  • 180
  • 199
1

Another solution to this problem

  • Find the commit just before the two commits (likely this is the master branch)
  • Run git rebase -i <commit before commits to be reordered> (git rebase -i master in most cases)
  • In the text editor, swap the order of the commits (for vim, use the sequence ddp while on the line that should move down)
  • Save, quit, and let git rebase do the rest
Berik
  • 7,816
  • 2
  • 32
  • 40
0

In the, probably unlikely, event you have just created those commits, undo the second commit with git reset HEAD^ and then add the changes to the first commit with git commit --amend.

In the more likely event you have moved on and the commits were at some point in the past, rebasing is your best option. Without knowing your exact situation, citing a reference that explains it well is probably best.

In both the first and second event, the Pro Git book's section on rewriting history explains the options well -- both when they should be used and when caution should be used.

6.4 Git Tools - Rewriting History

David Culp
  • 5,354
  • 3
  • 24
  • 32