#git
Возможно ли вернуться в состояние до коммита и сделать иначе, или как исключить файлы из коммита, чтобы включить эти файлы в другой коммит?
Ответы
Ответ 1
Да, исправить старый коммит можно следующим образом: создать на исправляемом коммите ветку, встать на нее (checkout -b) поправить код, залить коммит (но!) с опцией ammend last commit (есть такой переключатель в UI, и в командной строке параметр) все коммиты со старой ветки идущие после исправленного переместить на новую ветку (rebase - переместить разом, либо cherry-pick - по одному). Я предпочитаю по одному для лучшего разрешения конфликтов. cherry-pick можно так же делать из UI - правой кнопкой на коммите - и там будет такая опция. Если вы хотите исключить из него файлы - на этапе правки кода - уберите их из коммита - "спрячьте" командой git stash save, ammend last commit чтоб перезалить текущий, cherry-pick до нужного коммита, в котором вы хотите файлы вставить и командой git stash pop вытащите их из тайника и закомитьте как надо.Ответ 2
Самый функциональный способ, который работает без исключений вида "если это последний коммит", а подходит для любого коммита -- это команда git rebase. Эта команда позволяет как угодно перекраивать коммиты: объединяя коммиты, переставляя их местами, переносить файлы из одного коммита в другой. Культура программирования -- это не только культура написания самого кода, но в связи с широким распространением систем версионного контроля -- ещё и культура ведения разработки в VCS. В командах часто важно не только чтобы код был хорош, но и был порядок в ваших коммитах и ветках. Важно! Помните о том, что в большинстве команд принято делать rebase только для локальных изменений, чтобы не пришлось потом выполнять git push -f. Итак, как исключить файлы из коммита. Прежде всего убедитесь, что нет отслеживаемых файлов с изменениями -- иначе rebase просто не запустится. Можете положить эти изменения в карман через git stash. Если это требование выполнено -- можете запускать rebase в интерактивном режиме, например: git rebase -i HEAD~4 После чего нужно у коммита, из которого вы хотите выделить файлы pick на edit (можно просто e) и выйти из редактора с сохранением изменений. Дойдя до коммита, который отмечен буквой e гит остановится и будет ожидать ваших действий по изменению коммита. Самое просто будет -- это дать команду git reset HEAD~ и собрать в коммит все файлы (как новые, так и изменённые), за исключением тех, которые вы хотите использовать в другом коммите. После того, как вы выполните добавление изменений через git add мы коммитим это как обычно через git commit -m "message". Затем оставшиеся файлы добавляем и оформляем ещё один коммит (если файлы пойдут в два коммита -- значит повторим эту операцию несколько раз) В какой-то момент в папке не останется никаких незакоммиченных изменений -- можно взять и внести новые изменения и новые коммиты. Я часто пользуюсь этим, чтобы добавить сразу несколько коммитов после какого-то выбранного. Когда вы решите, что сделано достаточно -- можно дать команду git rebase --continue. В этот момент гит начнёт добавлять все остальные коммиты, которые шли после редактируемого и если не будет конфликтов -- то будет переходить к следующему и следующему, пока не дойдёт до вашего последнего коммита. Если же конфликты будут -- их нужно будет резолвить и заново продолжать операцию git rebase --continue. Я рекомендую подобные операции делать на каких-то тестовых репозиториях, чтобы попрактиковаться и не страшно было что-то сделать не так. Для примера приведу некоторый пример выделения файлов из коммита. Итак, начнём. Допустим у нас есть такие коммиты: 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 Сделанные следующим образом: $ 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 $ И, допустим, мы хотим перенести файл 5 в предыдущий коммит (где файлы 2 и 3). Делаем 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 У коммита b967f0f заменяем pick на edit и сохраняем изменения, гит заново последовательно применяет коммиты 0517714 и 13ae975 (вы можете даже не заметить это, так быстро мелькают изменения на экране) и останаваливается на коммите 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 $ Тут мы делаем простой шаг: $ 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..." 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 просто для наглядности) Составим новый коммит из файла 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 Остался файл 5, создадим из него новый коммит: $ 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 ..." 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 $ После чего рабочий каталог чист и можно сказать гит, что можно продолжить операцию rebase и накатывать следующие коммиты: $ git rebase --continue Successfully rebased and updated refs/heads/master. $ Здесь вам не видно, между тем как вы нажали enter и перед тем, как появится надпись 'Successfully rebased' гит последовательно применит остающиеся коммиты -- в нашем случае это только коммит 412ab2d. Вот какая у нас получилась новая история: 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 Операция перестановки местами коммитов 4 и 5 и слияния коммитов 13ae975 с последующим f0cd810 тривиальна -- нужно снова дать команду rebase, переставить местами коммиты 4c72f5d и f0cd810 и сделать squash коммита f0cd810 в коммит 13ae975 -- вот тут я писал подробнее в отдельном посте: Можно ли в GIT добавить еще один файл в последний локальный комит? При помощи rebase переносить можно не только новые файлы, но изменения и удаления. Также важно помнить, что если вы что-то неправильно сделали в середине rebase'а -- то помните, что у вас есть возможность отменить rebase: нужно дать команду git rebase --abort. Ответ 3
Если это последний коммит и он находится локально, то без проблем. Допустим, последний коммит у нас B, предшествующий ему коммит A: Сбросим наш коммит командой git reset B. Делаем git status и видим следующую картину: Последним коммитом будет коммит A, все изменения останутся, но они будут в состоянии not stagged. Формируем новый индекс для создания нового первого коммита с помощью команды git add список_файлов_через_пробел (можно каждый файл по одному добавлять). Делаем git commit -m "C", это будет наш первый коммит. Делаем git commit -am "D", это будет наш второй коммит. Параметр -a - добавляет все изменённые файлы в индекс. Альтернативные варианты шага 5: git add . git commit -m "D" Или git add название_файлов_через_пробел git commit -m "D" Или git add название_файла_1 ... git add название_файла_N git commit -m "D"
Комментариев нет:
Отправить комментарий