Как распилить одно большое изменение на несколько коммитов?
Создадим три файла:
george@inspiron:~/src/gitadd-p> git init george@inspiron:~/src/gitadd-p> cal > month george@inspiron:~/src/gitadd-p> cal -3 > 3months george@inspiron:~/src/gitadd-p> cal -y > year george@inspiron:~/src/gitadd-p> git add . george@inspiron:~/src/gitadd-p> git commit -a -m "Initial" [master (root-commit) 6b5e406] Initial 3 files changed, 50 insertions(+) create mode 100644 3months create mode 100644 month create mode 100644 year
Общий вид:
Отредактируем эти файлы:
month:
3months:
year:
Что покажет git diff:
george@inspiron:~/src/gitadd-p> git diff
В этом diff-е несколько ханков, каждый начинается (воображаемой) командой diff --git, которая сравнивает два (воображаемых) каталога a (предыдущее состояние) b (текущее). А файлы в этих каталогах настоящие.
Дальше "--- a/" отмечает, как файл назывался раньше, а "+++ b/" — как он называется после изменения (например, его ещё и переименовали при этом)
Затем "@@@" описывает, какие строки файла подверглись изменению (какие откуда удалить и какие куда вставить)
После чего показаны изменившиеся части файла в окружении неизменившихся(т. н. контексте). Контекст нужен для того, чтобы была возможность применить ханк, даже если соответствующие строки находятся в другом месте файла, например, если мы хотим накатить свой коммит на файл, в котором кто-то за это время сделал свои изменения. "--- " означает, что было в старом файле, а "+++ " — что получилось в новом.
Появившиеся в концах строк пробелы git diff закрашивает красным, потому что не любит их. И нам не советует.
Вот здесь приложен получившийся патч в текстовом формате.
Получится вот такая картина:
george@inspiron:~/src/gitadd-p> git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: 3months modified: month modified: year no changes added to commit (use "git add" and/or "git commit -a")
Три файла изменены, коммитить пока ничего не прошено.
Если сейчас закоммитить все эти изменения, получится один коммит, включающий в себя все три файла. Но предположим, что мы хотим разбить изменения на три коммита (выделение даты в одном файле, подчёркивания в двух и осень в другом).
С первым изменением (выделением даты) всё понятно: это единствнный ханк в файле month. Добавим его, а вот git commit вызовем с ключом --verbose:
george@inspiron:~/src/gitadd-p> git add month george@inspiron:~/src/gitadd-p> git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: month Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: 3months modified: year george@inspiron:~/src/gitadd-p> git commit --verbose
В этом состоянии git diff будет показывать только незарегистрированные изменения (в файлах 3months и year), а для просмотра застейдженных изменений надо вызывать git diff --cached (проверьте!).
Запустится текстовый редактор для написания commit message, в котором из-за --verbose для нашего удобства припишется ещё полученный diff:
# Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # On branch master # Changes to be committed: # modified: month # # Changes not staged for commit: # modified: 3months # modified: year # # ------------------------ >8 ------------------------ # Do not modify or remove the line above. # Everything below it will be ignored. diff --git a/month b/month index cc9c9a4..444b0c6 100644 --- a/month +++ b/month @@ -1,8 +1,9 @@ Март 2019 + Пн Вт Ср Чт Пт Сб Вс 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 -25 26 27 28 29 30 31 +25 26 27 28`29`30 31
Несмотря на множество текста, это пустой commit message! С символа "#" начинаются комментарии, а строка, содержащая «ножнички» (---- >8 ----), определяет, что все последующие строки — тоже комментарии.
Впишем commit message и закоммитим:
Mark current date # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # . . . все эти строки можно удалить, а можно не удалять, # . . . в сообщение они не попадут
Теперь мы здесь:
george@inspiron:~/src/gitadd-p> git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: 3months modified: year no changes added to commit (use "git add" and/or "git commit -a") george@inspiron:~/src/gitadd-p> git log commit eb16f08872c152358a56683bdd5d411ef343c4e1 Author: George V. Kouryachy (Fr. Br. George) <george@altlinux.ru> Date: Fri Mar 29 12:38:15 2019 +0300 Mark current date commit 6b5e40643c4e45e367b012bd867292bb74f22d78 Author: George V. Kouryachy (Fr. Br. George) <george@altlinux.ru> Date: Fri Mar 29 11:45:32 2019 +0300 Initial
По нашей задумке, следующий коммит должен содержать только подчёркивания — это изменения во втором и в третьем файле, причём не все. Выберем нужные ханки из всех проделанных изменений с помощью ключа --patch к команде git add:
george@inspiron:~/src/gitadd-p> git add --patch diff --git a/3months b/3months index dbe1a3b..e1d398f 100644 --- a/3months +++ b/3months @@ -1,4 +1,5 @@ Февраль 2019 Март 2019 Апрель 2019 +---------------------------------------------------------------- Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс 1 2 3 1 2 3 1 2 3 4 5 6 7 4 5 6 7 8 9 10 4 5 6 7 8 9 10 8 9 10 11 12 13 14 Stage this hunk [y,n,q,a,d,e,?]?
Git показывает на первый попавшийся ханк и спрашивает, что с ним делать? А что можно-то?
Stage this hunk [y,n,q,a,d,e,?]? ? y - stage this hunk n - do not stage this hunk q - quit; do not stage this hunk or any of the remaining ones a - stage this hunk and all later hunks in the file d - do not stage this hunk or any of the later hunks in the file e - manually edit the current hunk ? - print help @@ -1,4 +1,5 @@ Февраль 2019 Март 2019 Апрель 2019 +---------------------------------------------------------------- Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс 1 2 3 1 2 3 1 2 3 4 5 6 7 4 5 6 7 8 9 10 4 5 6 7 8 9 10 8 9 10 11 12 13 14 Stage this hunk [y,n,q,a,d,e,?]?
Нажимаем "y", смотрим два оставшихся ханка, на одном из них нажимаем "y" (это тоже подчёркивание), на другом — "n":
diff --git a/3months b/3months index dbe1a3b..e1d398f 100644 --- a/3months +++ b/3months @@ -1,4 +1,5 @@ Февраль 2019 Март 2019 Апрель 2019 +---------------------------------------------------------------- Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс 1 2 3 1 2 3 1 2 3 4 5 6 7 4 5 6 7 8 9 10 4 5 6 7 8 9 10 8 9 10 11 12 13 14 Stage this hunk [y,n,q,a,d,e,?]? y <stdin>:7: trailing whitespace. ---------------------------------------------------------------- warning: 1 line adds whitespace errors. diff --git a/year b/year index 1b02bfa..1c0b8be 100644 --- a/year +++ b/year @@ -1,4 +1,5 @@ 2019 +------------------------------------------------------------------ Январь Февраль Март Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? y @@ -16,14 +17,25 @@ 22 23 24 25 26 27 28 20 21 22 23 24 25 26 17 18 19 20 21 22 23 29 30 27 28 29 30 31 24 25 26 27 28 29 30 - Июль Август Сентябрь -Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс - 1 2 3 4 5 6 7 1 2 3 4 1 - 8 9 10 11 12 13 14 5 6 7 8 9 10 11 2 3 4 5 6 7 8 -15 16 17 18 19 20 21 12 13 14 15 16 17 18 9 10 11 12 13 14 15 -22 23 24 25 26 27 28 19 20 21 22 23 24 25 16 17 18 19 20 21 22 -29 30 31 26 27 28 29 30 31 23 24 25 26 27 28 29 - 30 + Июль Август +Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс + 1 2 3 4 5 6 7 1 2 3 4 + 8 9 10 11 12 13 14 5 6 7 8 9 10 11 +15 16 17 18 19 20 21 12 13 14 15 16 17 18 +22 23 24 25 26 27 28 19 20 21 22 23 24 25 +29 30 31 26 27 28 29 30 31 + +Осень! + + Сентябрь +Пн Вт Ср Чт Пт Сб Вс + 1 + 2 3 4 5 6 7 8 + 9 10 11 12 13 14 15 +16 17 18 19 20 21 22 +23 24 25 26 27 28 29 +30 + Октябрь Ноябрь Декабрь Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс 1 2 3 4 5 6 1 2 3 1 Stage this hunk [y,n,q,a,d,K,g,/,e,?]? n
Получаем такую ситуацию: для year есть застейжденные (поставленные в коммит) и незастейдженные изменения одновременно:
george@inspiron:~/src/gitadd-p> git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: 3months modified: year Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: year
Посмотрим уже поставленные в коммит изменения:
george@inspiron:~/src/gitadd-p> git diff --cached | cat diff --git a/3months b/3months index dbe1a3b..e1d398f 100644 --- a/3months +++ b/3months @@ -1,4 +1,5 @@ Февраль 2019 Март 2019 Апрель 2019 +---------------------------------------------------------------- Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс 1 2 3 1 2 3 1 2 3 4 5 6 7 4 5 6 7 8 9 10 4 5 6 7 8 9 10 8 9 10 11 12 13 14 diff --git a/year b/year index 1b02bfa..ffc8dc1 100644 --- a/year +++ b/year @@ -1,4 +1,5 @@ 2019 +------------------------------------------------------------------ Январь Февраль Март Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс Пн Вт Ср Чт Пт Сб Вс
Закоммитим их по-быстрому, а затем и всё, что осталось (ключ -m позволяет задать commit message из командной строки, а -a — автоматически (без git add) добавить в коммит изменённые файлы:
george@inspiron:~/src/gitadd-p> git commit -m "Underscores" [master ad8856b] Underscores 2 files changed, 2 insertions(+) george@inspiron:~/src/gitadd-p> git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: year no changes added to commit (use "git add" and/or "git commit -a") george@inspiron:~/src/gitadd-p> git commit -a -m 'Announcing Autumn!' [master a218c80] Announcing Autumn! 1 file changed, 19 insertions(+), 8 deletions(-) george@inspiron:~/src/gitadd-p> git status On branch master nothing to commit, working tree clean
Вот два способа просмотреть историю:
george@inspiron:~/src/gitadd-p> git log commit a218c80232a112bfe91a001deac87b8eb0b1a6a4 Author: George V. Kouryachy (Fr. Br. George) <george@altlinux.ru> Date: Fri Mar 29 13:04:09 2019 +0300 Announcing Autumn! commit ad8856b667beb386e131403559f55d42627c95bf Author: George V. Kouryachy (Fr. Br. George) <george@altlinux.ru> Date: Fri Mar 29 13:03:37 2019 +0300 Underscores commit eb16f08872c152358a56683bdd5d411ef343c4e1 Author: George V. Kouryachy (Fr. Br. George) <george@altlinux.ru> Date: Fri Mar 29 12:38:15 2019 +0300 Mark current date commit 6b5e40643c4e45e367b012bd867292bb74f22d78 Author: George V. Kouryachy (Fr. Br. George) <george@altlinux.ru> Date: Fri Mar 29 11:45:32 2019 +0300 Initial
Второй включает в себя сводку по изменениям:
george@inspiron:~/src/gitadd-p> git log --status commit a218c80232a112bfe91a001deac87b8eb0b1a6a4 Author: George V. Kouryachy (Fr. Br. George) <george@altlinux.ru> Date: Fri Mar 29 13:04:09 2019 +0300 Announcing Autumn! year | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) commit ad8856b667beb386e131403559f55d42627c95bf Author: George V. Kouryachy (Fr. Br. George) <george@altlinux.ru> Date: Fri Mar 29 13:03:37 2019 +0300 Underscores 3months | 1 + year | 1 + 2 files changed, 2 insertions(+) commit eb16f08872c152358a56683bdd5d411ef343c4e1 Author: George V. Kouryachy (Fr. Br. George) <george@altlinux.ru> Date: Fri Mar 29 12:38:15 2019 +0300 Mark current date month | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) commit 6b5e40643c4e45e367b012bd867292bb74f22d78 Author: George V. Kouryachy (Fr. Br. George) <george@altlinux.ru> Date: Fri Mar 29 11:45:32 2019 +0300 Initial 3months | 8 ++++++++ month | 8 ++++++++ year | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+)
Наконец, git log --patch позволит посмотреть также и diff-ы этих изменений.