Search code examples
gitcommitrebasehistoryrevert

GIT : edit staged files in old commit but keep changes


I looked all over and couldn't find a solution to what I'm trying to do. Maybe I just missed it.

Say I have:

Commit A: 
- file 1
- file 2
- file 3
Commit B:
- file 4
Commit C:
- file 5
- file 6
HEAD: has files 1-6. 

Is there a way to interactively go back to Commit A to uncommit file 2 and file 3, then replay the remaining commits? So that I'm left with

Commit A: 
- file 1
Commit B:
- file 4
Commit C:
- file 5
- file 6
HEAD: has files 1-6, with file2 and file3 as unstaged.

Incomplete solutions I found:

- git reset --soft <commit A>
- make changes
- git commit

but this leaves me without the remaining commits (B, C).

- git rebase -i HEAD~n
- edit <commit>
- make changes
- git commit --amend
- git rebase --continue

but changes here are on top of the committed files at that commit, and I don't want to undo the file changes, just uncommit/unstage them.

- git revert -n <commit> 

but this undoes the changes.

- git rebase drop <commit> 

but this drops the entire commit.

I'm looking for the playback/--continue of rebase, but the editability of a soft reset. Is there a way to do that?

Thanks


Solution

  • I would recommend:

    git rm -- file_2 file_3
    git commit
    

    This creates a new commit that removes the two files. History is not altered.

    To recover the two files in your working tree:

    git checkout HEAD^ -- file_2 file_3
    git reset HEAD -- file_2 file_3
    

    git checkout recovers the files and git reset removes them from the staging areas so that you won't commit them.

    Alternatively, if you have not pushed these commits to a shared repository yet, and wish to change history, use git rebase -i

    git rebase -i HEAD~n #n should be one more than the number of commits to change
    #mark commit A to edit 
    git reset -- file_2 file_3 # remove file_2 and file_3 from staging so they won't be committed
    git commit --amend
    git rebase --continue
    

    I believe this will leave file_1 and file_2 in your working tree. If not, they can be recovered from your pre-rebase commit using git checkout as above.