-
1. Введение
- 1.1 О системе контроля версий
- 1.2 Краткая история Git
- 1.3 Что такое Git?
- 1.4 Командная строка
- 1.5 Установка Git
- 1.6 Первоначальная настройка Git
- 1.7 Как получить помощь?
- 1.8 Заключение
-
2. Основы Git
-
3. Ветвление в Git
- 3.1 О ветвлении в двух словах
- 3.2 Основы ветвления и слияния
- 3.3 Управление ветками
- 3.4 Работа с ветками
- 3.5 Удалённые ветки
- 3.6 Перебазирование
- 3.7 Заключение
-
4. Git на сервере
- 4.1 Протоколы
- 4.2 Установка Git на сервер
- 4.3 Генерация открытого SSH ключа
- 4.4 Настраиваем сервер
- 4.5 Git-демон
- 4.6 Умный HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Git-хостинг
- 4.10 Заключение
-
5. Распределённый Git
-
6. GitHub
-
7. Инструменты Git
- 7.1 Выбор ревизии
- 7.2 Интерактивное индексирование
- 7.3 Припрятывание и очистка
- 7.4 Подпись
- 7.5 Поиск
- 7.6 Перезапись истории
- 7.7 Раскрытие тайн reset
- 7.8 Продвинутое слияние
- 7.9 Rerere
- 7.10 Обнаружение ошибок с помощью Git
- 7.11 Подмодули
- 7.12 Создание пакетов
- 7.13 Замена
- 7.14 Хранилище учётных данных
- 7.15 Заключение
-
8. Настройка Git
- 8.1 Конфигурация Git
- 8.2 Атрибуты Git
- 8.3 Хуки в Git
- 8.4 Пример принудительной политики Git
- 8.5 Заключение
-
9. Git и другие системы контроля версий
- 9.1 Git как клиент
- 9.2 Переход на Git
- 9.3 Заключение
-
10. Git изнутри
- 10.1 Сантехника и Фарфор
- 10.2 Объекты Git
- 10.3 Ссылки в Git
- 10.4 Pack-файлы
- 10.5 Спецификации ссылок
- 10.6 Протоколы передачи данных
- 10.7 Обслуживание репозитория и восстановление данных
- 10.8 Переменные окружения
- 10.9 Заключение
-
A1. Приложение A: Git в других окружениях
- A1.1 Графические интерфейсы
- A1.2 Git в Visual Studio
- A1.3 Git в Visual Studio Code
- A1.4 Git в Eclipse
- A1.5 Git в IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.6 Git в Sublime Text
- A1.7 Git в Bash
- A1.8 Git в Zsh
- A1.9 Git в PowerShell
- A1.10 Заключение
-
A2. Приложение B: Встраивание Git в ваши приложения
- A2.1 Git из командной строки
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Приложение C: Команды Git
- A3.1 Настройка и конфигурация
- A3.2 Клонирование и создание репозиториев
- A3.3 Основные команды
- A3.4 Ветвление и слияния
- A3.5 Совместная работа и обновление проектов
- A3.6 Осмотр и сравнение
- A3.7 Отладка
- A3.8 Внесение исправлений
- A3.9 Работа с помощью электронной почты
- A3.10 Внешние системы
- A3.11 Администрирование
- A3.12 Низкоуровневые команды
7.9 Инструменты Git - Rerere
Rerere
Функциональность git rerere
— частично скрытый компонент Git.
Её имя является сокращением для «reuse recorded resolution» («повторно использовать сохранённое решение»).
Как следует из названия, эта функциональность позволяет попросить Git запомнить то, как вы разрешили некоторую часть конфликта, так что в случае возникновения такого же конфликта, Git сможет его разрешить автоматически.
Существует несколько ситуаций, в которых данная функциональность может быть действительно удобна.
Один из примеров, упомянутый в документации, состоит в том, чтобы обеспечить в будущем простоту слияния некоторой долгоживущей ветки, не создавая при этом набор промежуточных коммитов слияния.
При использовании rerere
вы можете время от времени выполнять слияния, разрешать конфликты, а затем откатывать слияния.
Если делать это постоянно, то итоговое слияние должно пройти легко, так как rerere
сможет разрешить все конфликты автоматически.
Такая же тактика может быть использована, если вы хотите сохранить ветку легко перебазируемой, то есть вы не хотите сталкиваться с одними и теми же конфликтами каждый раз при перебазировании. Или, например, вы решили ветку, которую уже сливали и разрешали при этом некоторые конфликты, вместо слияния перебазировать — навряд ли вы захотите разрешать те же конфликты ещё раз.
Другая ситуация возникает, когда вы изредка сливаете несколько веток, относящихся к ещё разрабатываемым задачам, в одну тестовую ветку, как это часто делается в проекте самого Git. Если тесты завершатся неудачей, вы можете откатить все слияния и повторить их, исключив из них ветку, которая поломала тесты, при этом не разрешая конфликты снова.
Для того, чтобы включить функциональность rerere
, достаточно изменить настройки следующим образом:
$ git config --global rerere.enabled true
Также вы можете включить её, создав каталог .git/rr-cache
в нужном репозитории, но включение через настройки понятнее и может быть сделано глобально.
Давайте рассмотрим простой пример, подобный используемому ранее. Предположим, у нас есть файл вида:
#! /usr/bin/env ruby
def hello
puts 'hello world'
end
Как и ранее, в одной ветке мы изменили слово «hello» на «hola», а в другой — слово «world» на «mundo».
Когда мы будем сливать эти две ветки в одну, мы получим конфликт:
$ git merge i18n-world
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Recorded preimage for 'hello.rb'
Automatic merge failed; fix conflicts and then commit the result.
Вы должно быть заметили в выводе новую строку Recorded preimage for FILE
.
Во всём остальном вывод такой же, как при обычном конфликте слияния.
В этот момент rerere
может сообщить нам несколько вещей.
Обычно в такой ситуации вы можете выполнить git status
, чтобы увидеть в чем заключается конфликт:
$ git status
# On branch master
# Unmerged paths:
# (use "git reset HEAD <file>..." to unstage)
# (use "git add <file>..." to mark resolution)
#
# both modified: hello.rb
#
Однако, с помощью команды git rerere status
вы также можете узнать, для каких файлов git rerere
сохранил снимки состояния, в котором они были до начала слияния:
$ git rerere status
hello.rb
А команда git rerere diff
показывает текущее состояние разрешения конфликта — то, с чего вы начали разрешать конфликт, и то, как вы его разрешили (фактически, патч, который в дальнейшем можно использовать для разрешения такого же конфликта).
$ git rerere diff
--- a/hello.rb
+++ b/hello.rb
@@ -1,11 +1,11 @@
#! /usr/bin/env ruby
def hello
-<<<<<<<
- puts 'hello mundo'
-=======
+<<<<<<< HEAD
puts 'hola world'
->>>>>>>
+=======
+ puts 'hello mundo'
+>>>>>>> i18n-world
end
Также (и это уже не относится к rerere
), вы можете использовать команду ls-files -u
, чтобы увидеть конфликтующие файлы, их общую родительскую версию и обе сливаемых версии:
$ git ls-files -u
100644 39804c942a9c1f2c03dc7c5ebcd7f3e3a6b97519 1 hello.rb
100644 a440db6e8d1fd76ad438a49025a9ad9ce746f581 2 hello.rb
100644 54336ba847c3758ab604876419607e9443848474 3 hello.rb
Теперь вы можете разрешить конфликт, используя puts 'hola mundo'
, и снова выполнить команду rerere diff
, чтобы увидеть, что именно rerere
запомнит:
$ git rerere diff
--- a/hello.rb
+++ b/hello.rb
@@ -1,11 +1,7 @@
#! /usr/bin/env ruby
def hello
-<<<<<<<
- puts 'hello mundo'
-=======
- puts 'hola world'
->>>>>>>
+ puts 'hola mundo'
end
То есть, когда Git увидит в файле hello.rb
конфликт, в котором с одной строны стоит «hello mundo» и «hola world» с другой, он разрешит его как «hola mundo».
Теперь мы можем отметить конфликт как разрешённый и закоммитить его:
$ git add hello.rb
$ git commit
Recorded resolution for 'hello.rb'.
[master 68e16e5] Merge branch 'i18n'
Как вы видите, при этом было «сохранено разрешение конфликта для ФАЙЛА» («Recorded resolution for FILE»).
Теперь давайте отменим это слияние и перебазируем ветку i18n-world
поверх master
.
Как мы видели в Раскрытие тайн reset, мы можем переместить нашу ветку назад, используя команду reset
.
$ git reset --hard HEAD^
HEAD is now at ad63f15 i18n the hello
Наше слияние отменено.
Теперь давайте перебазируем ветку i18n-world
.
$ git checkout i18n-world
Switched to branch 'i18n-world'
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: i18n one word
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Resolved 'hello.rb' using previous resolution.
Failed to merge in the changes.
Patch failed at 0001 i18n one word
При этом мы получили ожидаемый конфликт слияния, но обратите внимание на строку Resolved FILE using previous resolution
.
Если мы посмотрим на содержимое файла, то увидим, что конфликт уже был разрешён, и в файле отсутствуют маркеры конфликта слияния.
#! /usr/bin/env ruby
def hello
puts 'hola mundo'
end
При этом команда git diff
покажет вам как именно этот конфликт был автоматически повторно разрешён:
$ git diff
diff --cc hello.rb
index a440db6,54336ba..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,7 @@@
#! /usr/bin/env ruby
def hello
- puts 'hola world'
- puts 'hello mundo'
++ puts 'hola mundo'
end
С помощью команды checkout
вы можете вернуть этот файл назад в конфликтующее состояние:
$ git checkout --conflict=merge hello.rb
$ cat hello.rb
#! /usr/bin/env ruby
def hello
<<<<<<< ours
puts 'hola world'
=======
puts 'hello mundo'
>>>>>>> theirs
end
Мы видели пример этого в Продвинутое слияние.
Теперь давайте повторно разрешим конфликт используя rerere
:
$ git rerere
Resolved 'hello.rb' using previous resolution.
$ cat hello.rb
#! /usr/bin/env ruby
def hello
puts 'hola mundo'
end
Мы автоматически повторно разрешили конфликт, используя сохранённый rerere
вариант разрешения.
Теперь вы можете добавить файл в индекс и продолжить перебазирование ветки.
$ git add hello.rb
$ git rebase --continue
Applying: i18n one word
Итак, если вы выполняете много повторных слияний или хотите сохранять тематическую ветку в состоянии, актуальном вашей основной ветке, без множества слияний в истории, или часто перебазируете ветки, то вы можете включить rerere
.
Это, в какой-то мере, упростит вам жизнь.