Git rebase: распространенные случаи при работе с git
Каждый разработчик работает с git, но к сожалению не все используют rebase в своей работе. Мало кто знает в каких рабочих кейсах он может пригодится, поэтому я написал данную статью с разборами рабочих ситуаций.
Предупреждение: Все примеры представленные здесь про изменение над одним файлом. Это удобно чтобы показать процесс работы, но увеличивает вероятность что при rebase возникнут конфликты. Поэтому разбор конфликтов в статье не будет.
Не удивляйтесь что после rebase придется пушить код в репозиторий через параметр “-f”. Это нужно чтобы принудительно переписать изменения в репозитории
git push origin head -f
Но есть более безопасный способ запушить код — использовать ключ force-with-lease
git push origin head --force-with-lease
Кейсы
- Поправить коммит в середине (fixup + rebase)
- Удалить коммит в середине (rebase + drop)
- Объединить коммиты и поменять текст итогового коммита (rebase/soft reset)
- Поправить последний коммит (soft reset)
- Поправить текст последнего коммита (amend)
- Поменять коммиты местами (rebase)
- Отребейзить ветку если родительская ветка была смерджена
1) Задача: поправить коммит в середине
Решение: сделать fixup
У нас есть 4 коммита в которых мы правили один файл
$ git log --oneline master..HEADaa871c7 (HEAD -> example_1, base_branch) add third paragraph to readme
3774350 add second paragraph to readme
2143008 add first paragraph to readme
1da47ab add title to readme
$ git diff master..HEADdiff — git a/README.md b/README.md
index e69de29..315b9d6 100644
— — a/README.md
+++ b/README.md
@@ -0,0 +1,7 @@
+## Hello
+
+First paragraph
+
+Second paragraph
+
+Third paragraph
Нам нужно поправить коммит который добавляет первый параграф (2143008). Открываем файл README.md и правим текст первого параграфа.
$ git diffdiff — git a/README.md b/README.md
index 315b9d6..14dc0b7 100644
— — a/README.md
+++ b/README.md
@@ -1,7 +1,3 @@
## Hello-First paragraph
-
-Second paragraph
-
-Third paragraph
+First paragraph :)
Так как мы в примере меняем один файл, то нужно сделать так каким он должен выглядеть после коммита (2143008) т.е там должен быть только заголовок и первый параграф.
Делаем “fixup коммит” задача которого поправить другой коммит в истории. Синтаксис операции следующий “git commit — fixup <commit_id>”
$ git add README.md
$ git commit — fixup 2143008[example_1 4d0edd6] fixup! add first paragraph to readme
1 file changed, 1 insertion(+), 5 deletions(-)
Готово, у нас есть новый коммит, но надо еще приложить усилия чтобы он применился к коммиту (2143008).
$ git log --oneline master..HEAD4d0edd6 (HEAD -> example_1) fixup! add first paragraph to readme
aa871c7 (base_branch) add third paragraph to readme
3774350 add second paragraph to readme
2143008 add first paragraph to readme
1da47ab add title to readme
Для этого нужно сделать rebase к коммиту на 1 ниже того который мы хотим поправить (1da47ab) (возможно здесь придется в явном виде решить конфликты, если git не понял как связать hotfix).
$ EDITOR=vim git rebase -i --autosquash 1da47ab# во время выполнения вы увидите что коммит fixup встал на свое “правильное” место, а не в конце истории1 pick 2143008 add first paragraph to readme
2 fixup 4d0edd6 fixup! add first paragraph to readme
3 pick 3774350 add second paragraph to readme
4 pick aa871c7 add third paragraph to readmeSuccessfully rebased and updated refs/heads/example_1.
Посмотрим теперь на историю.
$ git log --oneline master..HEAD8b8e8b2 (HEAD -> example_1) add third paragraph to readme
9dc3b2f add second paragraph to readme
557541f add first paragraph to readme
1da47ab add title to readme
И на изменения.
$ git diff master..HEADdiff — git a/README.md b/README.md
index e69de29..ff9fc5e 100644
— — a/README.md
+++ b/README.md
@@ -0,0 +1,7 @@
+## Hello
+
+First paragraph :)
+
+Second paragraph
+
+Third paragraph
Ура, у нас получилось!
2) Задача: удалить коммит в середине
Решение: сделать drop
У нас есть те же самые 4 коммита где мы правили один файл
$ git log --oneline master..HEADaa871c7 (HEAD -> example_2, base_branch) add third paragraph to readme
3774350 add second paragraph to readme
2143008 add first paragraph to readme
1da47ab add title to readme
$ git diff master..HEADdiff — git a/README.md b/README.md
index e69de29..315b9d6 100644
— — a/README.md
+++ b/README.md
@@ -0,0 +1,7 @@
+## Hello
+
+First paragraph
+
+Second paragraph
+
+Third paragraph
Мы поняли что первый параграф нам не нужен. Удалять изменения новым коммитом не хочется т.к. мы может просто удалить старый. Для этого делаем rebase к интересующему коммиту (1da47ab).
$ EDITOR=vim git rebase -i 1da47ab
И прописываем drop к тому коммиту, который мы хотим удалить.
1 drop 2143008 add first paragraph to readme
2 pick 3774350 add second paragraph to readme
3 pick aa871c7 add third paragraph to readme
Посмотрим что у нас получилось.
$ git log --oneline master..HEAD2b87851 (HEAD -> example_2) add third paragraph to readme
db792c4 add second paragraph to readme
1da47ab add title to readme
$ git diff master..HEADdiff — git a/README.md b/README.md
index e69de29..3354aa2 100644
— — a/README.md
+++ b/README.md
@@ -0,0 +1,5 @@
+## Hello
+
+Second paragraph
+
+Third paragraph
3) Задача: объединить коммиты и поменять текст итогового коммита
Решение: сделать rebase
У нас есть один README.md файл и несколько «wip» коммитов.
$ git log --oneline master..HEAD33e58db (HEAD -> example_3) wip
585c26b wip
173ab1f wip
c1f8895 wip
85855c0 wip
$ git diff master..HEADdiff — git a/README.md b/README.md
index e69de29..6222dc3 100644
— — a/README.md
+++ b/README.md
@@ -0,0 +1,7 @@
+## h2 header
+
+Some text. Bla-bla-bla
+
+### h3 header
+
+Some text again. LOL
Наша задача объединить это коммиты в один с осмысленным текстом. Для этого надо сделать rebase на 5 коммитов назад.
$ git log --oneline master..HEAD | wc -l
5$ EDITOR=vim git rebase -i HEAD~5
И поменять pick на squash
1 pick 85855c0 wip
2 squash c1f8895 wip
3 squash 173ab1f wip
4 squash 585c26b wip
5 squash 33e58db wip
После этого вручную поменять сообщение первого коммита.
1 # This is a combination of 5 commits.
2 # This is the 1st commit message:
3
4 Create README.md file
5
6 # This is the commit message #2:
7
8 wip
9
10 # This is the commit message #3:
11
12 wip
13
14 # This is the commit message #4:
15
16 wip
17
18 # This is the commit message #5:
19
20 wip
Вроде получилось
[detached HEAD 30310eb] Create README.md file
Date: Sun Dec 6 22:00:41 2020 +0300
1 file changed, 7 insertions(+)
Successfully rebased and updated refs/heads/example_3.
Посмотрим что у нас есть в итоге.
$ git log --oneline master..HEAD30310eb (HEAD -> example_3) Create README.md file
$ git diff master..HEADdiff — git a/README.md b/README.md
index e69de29..6222dc3 100644
— — a/README.md
+++ b/README.md
@@ -0,0 +1,7 @@
+## h2 header
+
+Some text. Bla-bla-bla
+
+### h3 header
+
+Some text again. LOL
Ура, данные объединились в один коммит.
Решение 2: сделать soft reset
Делаем soft reset на 5 коммитов назад
$ git log --oneline master..HEAD | wc -l
5$ git reset --soft HEAD~5
И сохранить изменения в один новый коммит.
$ git ci -m “Add README.md file”[example_3.2 22513de] Add README.md file
1 file changed, 7 insertions(+)
Посмотрим что получилось.
$ git log --oneline master..HEAD22513de (HEAD -> example_3.2) Add README.md file
$ git diff master..HEADdiff — git a/README.md b/README.md
index e69de29..6222dc3 100644
— — a/README.md
+++ b/README.md
@@ -0,0 +1,7 @@
+## h2 header
+
+Some text. Bla-bla-bla
+
+### h3 header
+
+Some text again. LOL
Все получилось!
4) Задача: поправить последний коммит
Решение: сделать soft reset
Итак, у нас есть два коммита. По каким-то причинам нам надо поправить последний коммит.
$ git log --oneline master..HEAD4efcc59 (HEAD -> example_3.2) Add more text to README.md
22513de Add README.md file
Можно сделать fixup, но он требует несколько действий. Гораздо быстрее сделать по-другому - убираем последний коммит, но оставляем все текущие изменения
$ git reset --soft HEAD~1
В итоге у нас в истории остался только предыдущий коммит.
$ git log --oneline master..HEAD22513de (HEAD -> example_3.2) Add README.md file
Но все последние изменения остались на файловой системе
$ git stOn branch example_3.2
Changes to be committed:
(use “git reset HEAD <file>…” to unstage)modified: README.md
Теперь мы можем поменять данные в файле которые нам надо и создать новый коммит.
$ git ci -a -m “Add more text to README.md”[example_3.2 87eaff0] Add more text to README.md
1 file changed, 2 insertions(+)
Вуаля, все получилось
$ git log --oneline master..HEAD87eaff0 (HEAD -> example_3.2) Add more text to README.md
22513de Add README.md file
5) Задача: поправить текст последнего коммита
Решение: сделать amend
Иногда в тексте коммита можно допустить ошибку и его надо переписать. Самый простой способ это сделать amend.
$ git log --oneline master..HEADfb77ed2 (HEAD -> example_3.2) improve documentattion
87eaff0 Add more text to README.md
22513de Add README.md file
В последнем коммите опечатка в слове “documentation”. Поправим его
$ git commit --amend -m “improve documentation”[example_3.2 ed02c8a] improve documentation
Date: Sun Dec 6 22:57:22 2020 +0300
1 file changed, 2 insertions(+)
git log --oneline master..HEADed02c8a (HEAD -> example_3.2) improve documentation
87eaff0 Add more text to README.md
22513de Add README.md file
6) Задача: поменять коммиты местами
Решение: сделать rebase
Итак, у нас опять есть три коммита
$ git log --oneline master..HEAD98bfce0 (HEAD -> example_6) add footer to Readme.md
9fd509c add header to Readme.md
589ad4d add text to Readme.md
В которых мы меняли один файл.
$ git diff master..HEADdiff --git a/README.md b/README.md
index e69de29..2c6d4f9 100644
— — a/README.md
+++ b/README.md
@@ -0,0 +1,7 @@
+# Header
+
+Text. Bla-bla-bla.
+Some text.
+And text, again.
+
+# Footer
Как поменять местами коммиты чтобы сперва в истории было добавление футера (98bfce0), а потом уже хедера (9fd509c)? Для этого делаем интерактивный rebase на 3 коммита назад
$ EDITOR=vim git rebase -i HEAD~31 pick 589ad4d add text to Readme.md
2 pick 9fd509c add header to Readme.md
3 pick 98bfce0 add footer to Readme.md
И меняем расположение коммитов
1 pick 589ad4d add text to Readme.md
2 pick 98bfce0 add footer to Readme.md
3 pick 9fd509c add header to Readme.md
Посмотрим что получилось.
$ git diff master..HEADdiff — git a/README.md b/README.md
index e69de29..2c6d4f9 100644
— — a/README.md
+++ b/README.md
@@ -0,0 +1,7 @@
+# Header
+
+Text. Bla-bla-bla.
+Some text.
+And text, again.
+
+# Footer
$ git log --oneline master..HEADa4e641f (HEAD -> example_6) add header to Readme.md
cf87d0a add footer to Readme.md
589ad4d add text to Readme.md
Давайте попробуем откатиться назад чтобы проверить что уберется хедер, а футер останется.
$ git reset --hard HEAD~1HEAD is now at cf87d0a add footer to Readme.md
$ cat README.md
Text. Bla-bla-bla.
Some text.
And text, again.# Footer
И взглянем на историю коммитов.
$ git log --oneline master..HEADcf87d0a (HEAD -> example_6) add footer to Readme.md
589ad4d add text to Readme.md
7) Отребейзить ветку если родительская ветка была смерджена
Бывает проблема когда делаешь ветку от ветки. Например родительская ветку уже смерджена в мастер, но коммиты были засквошены. Нужно отребейзить подветку от мастера, но не хочется решать много конфликтов.
Решение: Сделать ребейз только нужных коммитов.
От мастера была создана ветка с 2-мя комитами
O - A - B
От этой ветки мы сделали ветку с добавили еще 3 коммита
O - A - B - C - D - E
Первая ветка была смерджена в мастер и вместо 2 коммитов `A — B`, в мастере появился один коммит `F`. Для того чтобы отребейзить нашу ветку от мастера надо отребейзить только уникальные коммиты этой ветки. То есть 3 коммита `C — D — E`. (источник)
git rebase --onto master HEAD~3
Вот и все! Надеюсь что данная статья была полезная 😁