rerere
— сокращение от «reuse recorded resolution» (повторное использование сохраненных разрешений конфликтов). С помощью этой команды Git запоминает, каким образом был разрешен конфликт, чтобы при возникновении подобного, разрешить его автоматически.
Рассмотрим сценарии, в которых может пригодиться эта функциональность.
Сценарии
- Допустим, у вас есть ветка темы, которую вы объединяете с главной для поддержания актуального состояния. При этом необходимо избежать засорения истории множеством промежуточных коммитов. С помощью
rerere
можно периодически выполнять слияние, разрешать конфликты, а затем прерывать слияние. При завершении ветки темы можно выполнить финальное слияние, аrerere
разрешит все проблемы автоматически. - Допустим, вы объединяете множество изменений ветки темы с тестовой веткой. При возникновении ошибки в тестовой ветке можно отменить слияние, исправить ошибку и выполнить повторное слияние без повторного разрешения конфликтов.
Как это работает
Для запуска rerere
введите следующую команду:
$ git config --global rerere.enabled true
Допустим, у нас есть файл с именем script.sh со следующим содержимым:
#!/bin/bash
# My first script
echo "Hello World!"
В главной ветке изменяем слово «Hello» на «Hola», а в ветке темы — «World» на «Git»:
При объединении двух ветвей возникает конфликт слияния:
$ git merge topic
Auto-merging script.sh
CONFLICT (content): Merge conflict in script.sh
Recorded preimage for 'script.sh'
Automatic merge failed; fix conflicts and then commit the result.
Это обычный конфликт слияния, но с дополнительным оператором «Recorded preimage for ‘script.sh'» (сохранение пред-образа для ‘script.sh’). При запуске git rerere status
вместо git status
можно выяснить, записан ли pre-merge-статус или пред-образ:
$ git rerere status
script.sh
Ниже показана информация, полученная с помощью git rerere diff
и git diff
:
Rerere сохраняет запись решения в папке .git/rr-cache. Заглянув в репозиторий, вы обнаружите файл preimage:
$ tree .git/rr-cache
.git/rr-cache
└── 54db390f1318184c7fb941c7c688546bdec9590a
└── preimage
Запись исправления путем разрешения конфликта
Попробуем разрешить этот конфликт, объединив «Hola» в главной ветке и «Git» в ветке темы. В результате получаем «Hola Git». Запустите rerere diff
еще раз, чтобы проверить сохраненную информацию:
$ git rerere diff
--- a/script.sh
+++ b/script.sh
@@ -1,8 +1,4 @@
#!/bin/bash
# My first script
-<<<<<<<
-echo "Hello Git!"
-=======
-echo "Hola World!"
->>>>>>>
+echo "Hola Git!"
Чтобы rerere запомнил разрешение конфликта, подготовьте файл и зафиксируйте изменение:
$ git add script.sh
$ git commit
Recorded resolution for 'script.sh'.
[master d0e6d1b] Merge branch 'topic'
rerere
сохранил разрешение для ‘script.sh’ или пост-образ. Представим изменение с помощью диаграммы:
Это исправление теперь находится в postimage:
tree .git/rr-cache
.git/rr-cache
└── 54db390f1318184c7fb941c7c688546bdec9590a
├── postimage
└── preimage
Таким образом, мы выполнили запись разрешения с помощью git rerere
.
Для каждого файла, содержащего конфликт, git создает новую директорию с хешем, в которой будут находиться «пред-образ» и «пост-образ».
- Когда
rerere
видит конфликт, он создает «пред-образ», содержимое которого такое же, как при запуске командыgit diff
. - Соответственно, «пост-образ» — это то, как выглядит файл после разрешения конфликта.
Как работает rerere
:
Когда rerere
видит конфликт для script.sh, он просматривает соответствующую директорию rr-cache, находит «пред-образ», который соответствует конфликту, и изменяет файл в рабочей директории для соответствия «пост-образу».
Воспроизведение конфликта для автоматического разрешения
Проверим, решится ли конфликт автоматически. Отменяем слияние, а затем перемещаем ветку темы поверх главной с помощью команды git reset -—hard HEAD^
:
$ git reset --hard HEAD^
HEAD is now at ca1bf2b Change Hello to Hola
Затем извлекаем ветку темы и перемещаем ее:
$ git checkout topic
Switched to branch 'topic'
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: Change World to Git
Using index info to reconstruct a base tree...
M script.sh
Falling back to patching base and 3-way merge...
Auto-merging script.sh
CONFLICT (content): Merge conflict in script.sh
Resolved 'script.sh' using previous resolution.
error: Failed to merge in the changes.
Patch failed at 0001 Change World to Git
Возникает тот же конфликт, однако появляется строка Resolved ‘script.sh’ using previous resolution (Разрешение ‘script.sh’ с помощью предыдущего разрешения). Заглянув в файл, мы обнаружим, что проблема уже решена:
$ cat script.sh
#!/bin/bash
# My first script
echo "Hola Git!"
git diff
также показывает, как проблема была решена автоматически:
$ git diff
diff --cc script.sh
index 46e4937,3108049..0000000
--- a/script.sh
+++ b/script.sh
@@@ -1,4 -1,4 +1,4 @@@
#!/bin/bash
# My first script
- echo "Hola World!"
-echo "Hello Git!"
++echo "Hola Git!"
Добавив следующий код, мы завершаем процесс перемещения:
$ git add script.sh
$ git rebase --continue
Applying: Change World to Git
Еще одна настройка
Несмотря на то, что rerere
автоматически разрешает конфликт, исправленные файлы остаются в состоянии unstaged. Это означает, что git add <file>
необходимо выполнить вручную.
Чтобы rerere
автоматически индексировал исправленные файлы, нужно воспользоваться следующей командой:
$ git config --global rerere.autoupdate true
Теперь rerere
автоматически исправляет и индексирует файлы.
git rebase master
First, rewinding head to replay your work on top of it...
Applying: Change text to Hello Git
Using index info to reconstruct a base tree...
M script.sh
Falling back to patching base and 3-way merge...
Auto-merging script.sh
CONFLICT (content): Merge conflict in script.sh
Staged 'script.sh' using previous resolution.
error: Failed to merge in the changes.
Patch failed at 0001 Change World to Git
Продолжите перемещение для завершения:
$ git rebase --continue
Applying: Change World to Git
Заключение
Если вы выполняете множество повторных слияний, хотите поддерживать актуальность ветки темы с главной веткой или часто выполняете перемещение, то воспользуйтесь rerere
. Просто включите его с помощью git config --global rerere.enabled true
и предоставьте Git всю оставшуюся работу.
Читайте также:
Перевод статьи Minh Pham: The Git Rerere Command — Automate Solutions to Fix Merge Conflicts