Is it possible to return to the commit state and do it differently, or how to exclude files from the commit in order to include these files in another commit?

    3 answers 3

    Yes, you can fix the old commit as follows:

    • create a branch on a fixable one, embark on it ( checkout -b )
    • correct the code, fill in the commit (but!) with the ammend last commit option (there is such a switch in the UI, and there is a parameter in the command line)
    • all commits from the old branch going after the corrected move to the new branch ( rebase - move at once, or cherry-pick - one by one). I prefer one at a time for better conflict resolution. cherry-pick can also make cherry-pick from UI - with the right button on the commit - and there will be such an option.

    If you want to exclude files from it - at the stage of editing the code - remove them from the commit - "hide" with the command git stash save , ammend last commit to reload the current, cherry-pick to the desired commit in which you want to insert files and the command git stash pop pull them out of the cache and zakomite as it should.

    • Wait. I'll try to make git stash did not see it in this book. I tried it with git rm --cashed НеобходимыйФайл did not work. - Bald
    • After executing this command, the files excluded from the commit will not hang as non-found changes, but will be on the "side hidden branch". - Mira

    The most functional way, which works without exceptions like "if this is the last commit", and is suitable for any commit is the git rebase .

    This command allows you to remap commits in any way: merging commits, rearranging them, transfer files from one commit to another.

    A programming culture is not only a culture of writing the code itself, but due to the widespread use of version control systems, it is also a culture of doing development in VCS. In teams, it is often important not only that the code be good, but also order in your commits and branches.

    Important! Remember that in most commands it is customary to rebase only for local changes, so that you do not have to perform git push -f later.

    So, how to exclude files from a commit.

    First of all, make sure that there are no monitored files with changes - otherwise rebase will simply not start.

    You can put these changes in your pocket through git stash .

    If this requirement is fulfilled, you can run rebase interactively, for example:

    git rebase -i HEAD ~ 4

    After that, you need the commit from which you want to select the pick files to edit (you can simply e ) and exit the editor with saving the changes.

    Having reached the commit that is marked with the letter e git will stop and wait for your actions to change the commit.

    The easiest thing to do is to give the git reset HEAD~ command and collect all the files (both new and modified) into the commit, except for those that you want to use in another commit.

    After you add changes through git add we commit it as usual with git commit -m "message" .

    Then we add the remaining files and make another commit (if the files go to two commits, then we will repeat this operation several times)

    At some point in the folder there will be no uncommitted changes - you can take and make new changes and new commits. I often use this to add several commits after a selected one.

    When you decide that you have done enough, you can give the git rebase --continue . At this point, the git will start adding all the other commits that came after the edited one, and if there are no conflicts, it will go on to the next and next, until it reaches your last commit. If there are conflicts, they will need to be resolved and continue the git rebase --continue operation git rebase --continue .

    I recommend doing similar operations on some test repositories in order to practice and it was not scary to do something wrong.

    For example, here’s an example of selecting files from a commit.


    So, let's begin.

    Suppose we have such commits:

     pick 0517714 add file1.txt pick 13ae975 add file2.txt and file3.txt pick b967f0f add file4.txt and file5.txt pick 412ab2d add file6.txt 

    Made as follows:

     $ git clone ak@localhost:aaa.git bbb Cloning into 'bbb'... warning: You appear to have cloned an empty repository. $ cd bbb $ touch .gitignore $ git add .gitignore $ git commit -m "empty .gitignore" [master (root-commit) f3f251b] empty .gitignore 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .gitignore $ touch file1.txt $ git add file1.txt $ git commit -m "add file1.txt" [master 0517714] add file1.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 file1.txt $ touch file2.txt $ git add file2.txt $ touch file2.txt $ touch file3.txt $ git add file3.txt $ git commit -m "add file2.txt and file3.txt" [master 13ae975] add file2.txt and file3.txt 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 file2.txt create mode 100644 file3.txt $ touch file4.txt $ git add file4.txt $ touch file5.txt $ git add file5.txt $ git commit -m "add file4.txt and file5.txt" [master b967f0f] add file4.txt and file5.txt 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 file4.txt create mode 100644 file5.txt $ touch file6.txt $ git add file6.txt $ git commit -m "add file6.txt" [master 412ab2d] add file6.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 file6.txt $ 

    And, let's say, we want to transfer file 5 to the previous commit (where files are 2 and 3).

    Do git rebase -i HEAD~4 :

     $ git rebase -i HEAD~4 pick 0517714 add file1.txt pick 13ae975 add file2.txt and file3.txt pick b967f0f add file4.txt and file5.txt pick 412ab2d add file6.txt # Rebase f3f251b..412ab2d onto f3f251b # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out ~ "~/bbb/.git/rebase-merge/git-rebase-todo" 22L, 742C 

    In b967f0f, we replace pick with edit and save the changes, guit re-apply commits 0517714 and 13ae975 (you may not even notice it, the changes on the screen flicker quickly) and stop at b967f0f:

     $ git rebase -i HEAD~4 Stopped at b967f0f... add file4.txt and file5.txt You can amend the commit now, with git commit --amend Once you are satisfied with your changes, run git rebase --continue $ 

    Here we take a simple step:

     $ git reset HEAD~ $ git status # HEAD detached at 13ae975 # You are currently editing a commit while rebasing branch 'master' on 'f3f251b'. # (use "git commit --amend" to amend the current commit) # (use "git rebase --continue" once you are satisfied with your changes) # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # file4.txt # file5.txt nothing added to commit but untracked files present (use "git add" to track) $ 

    (git status just for clarity)

    Create a new commit from file 4:

     $ clear && history -c $ git add file4.txt $ git commit -m "add file4.txt" [detached HEAD 4c72f5d] add file4.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 file4.txt 

    File 5 is left, we will create a new commit from it:

     $ git status # HEAD detached from 13ae975 # You are currently editing a commit while rebasing branch 'master' on 'f3f251b'. # (use "git commit --amend" to amend the current commit) # (use "git rebase --continue" once you are satisfied with your changes) # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # file5.txt nothing added to commit but untracked files present (use "git add" to track) $ git add file5.txt $ git commit -m "add file5.txt" [detached HEAD f0cd810] add file5.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 file5.txt $ 

    After that, the working directory is clean and you can tell git that you can continue the rebase operation and roll the following commits:

     $ git rebase --continue Successfully rebased and updated refs/heads/master. $ 

    You can't see here, while you hit enter and before the message 'Successfully rebased' appears, the git will consistently apply the remaining commits - in our case, this is only the commit 412ab2d.

    Here is what we got a new story:

     pick 0517714 add file1.txt pick 13ae975 add file2.txt and file3.txt pick 4c72f5d add file4.txt pick f0cd810 add file5.txt pick 6952a0b add file6.txt 

    Operation rearrange commits 4 and 5 and merge commits 13ae975 followed f0cd810 trivial - you need to give the command again rebase, to interchange commits 4c72f5d and f0cd810 and squash make a commit to commit f0cd810 13ae975 - here I wrote in more detail in a separate post: Can I in git add another file to the last local commit?

    With rebase, you can transfer not only new files, but changes and deletions.

    It is also important to remember that if you did something wrong in the middle of a rebase, then remember that you have the option to cancel the rebase: you must give the git rebase --abort .

    • one
      Great answer! I will add a little bit: в большинстве команд принято делать rebase только для локальных изменений - push -f is often allowed in its own branch. Moreover, the repository manager can support rebase and / or squash before merge, which is essentially equivalent to rebase -i; push -f rebase -i; push -f . Example here: docs.gitlab.com/ee/workflow/… , docs.gitlab.com/ee/workflow/rebase_before_merge.html - Nick Volynkin

    If this is the last commit and it is local, then no problem.

    Suppose that the last commit we have is B , preceding it commit A :

    1. Reset our commit with git reset B
    2. Make git status and see the following picture:

    The last commit will be commit A , all changes will remain, but they will not stagged be able to be not stagged .

    1. We create a new index to create a new first commit using the git add список_файлов_через_пробел (you can git add список_файлов_через_пробел each file one by one).

    2. We do git commit -m "C" , this will be our first commit.

    3. We do git commit -am "D" , this will be our second commit.

    The -a adds all modified files to the index.

    Alternative options for step 5:

     git add . git commit -m "D" 

    Or

     git add название_файлов_через_пробел git commit -m "D" 

    Or

     git add название_файла_1 ... git add название_файла_N git commit -m "D"