Advertisement
  1. Code
  2. Git

Переписывание истории с помощью Git Rebase

Scroll to top
Read Time: 7 min

Russian (Pусский) translation by Sergey Zhuk (you can also view the original English article)

Основной рабочий процесс Git выглядит следующим образом: вы делаете задачу в отдельной ветке, затем по завершению сливаете ее в основную ветку. Это делает git merge неотъемлемым инструментом для объединения веток. Однако это не единственный инструмент, который предлагает Git.

Combining branches by merging them togetherCombining branches by merging them togetherCombining branches by merging them together
Слияние веток объединением.

Альтернативой сценарию, представленному выше, может быть объединение веток с помощью команды git rebase. Вместо слияние веток в специальном коммите, сдвиг перемещает всю ветку с задачей на конец master ветки так, как показано ниже.

Combining branches with git rebaseCombining branches with git rebaseCombining branches with git rebase
Слияние веток с помощью git rebase

Это служит той же цели, что и git merge, связывая коммиты из различных веток. Но есть две причины, по которым мы могли бы выбрать сдвиг вместо слияния:

  • Сдвиг приводит к линейной истории проекта.
  • Это дает нам возможность очистить историю от локальных коммитов.

В этой статье мы будет рассматривать эти два общих случая использования git rebase. К сожалению преимущества git rebase приводят нас к компромиссу. При неправильном использовании, может быть одной из наиболее опасных операций, которые можно выполнять в Git-репозиторий. Таким образом нужно внимательно рассмотреть опасности при сдвиге веток.

Необходимые условия

Эта статья предполагает, что вы знакомы с основами Git. Вы должны уметь сохранять и фиксировать изменения, разработку задач в отдельных ветках, соединять ветки, а так же сливать и заливать ветки из удаленных репозиториев.

1. Сдвиг для получения линейной истории

Первый вариант использования, который мы будем изучать, включает в себя расходящаяся история проекта. Рассмотрим репозиторий в котором основная ветка ушла вперед, пока вы работаете над задачей в другой ветке.

Developing a feature on a feature branchDeveloping a feature on a feature branchDeveloping a feature on a feature branch

Чтобы сдвинуть ветку feature на ветку master, нужно выполнить следующие команды:

1
git checkout feature
2
git rebase master

Это перенесет ветку feature из ее текущего положения на конец ветки master.

Transplanting the feature branch to the tip of the master branchTransplanting the feature branch to the tip of the master branchTransplanting the feature branch to the tip of the master branch

Есть два возможных варианта, когда вам это может понадобится. Во-первых, ваша задача затрагивает новые коммиты из master, и теперь они ей будут доступны. Во-вторых, если задача закрыта, то теперь ее можно быстро слить поверх ветки master. В обоих случаях сдвиг приведет к линейной истории, тогда как git merge приведет к необязательным коммитам слияния.

Например, рассмотрим, что произойдет, если вы будете использовать слияние вместо сдвига.

1
git checkout feature
2
git merge master

Это создаст дополнительный коммит слияния в ветке feature. Более того это будет происходить каждый раз, когда вы хотели бы включить коммиты, находящиеся выше вашей ветки. В конце концов история вашего проекта будет завалена бессмысленными коммитами слияния.

Integrating upstream commits with a mergeIntegrating upstream commits with a mergeIntegrating upstream commits with a merge
Слияние коммитов, находящихся выше.

Это же можно увидеть при слиянии и в другом направлении. Без использования сдвига, слияние ветки feature в ветку master так же требует коммита слияния. Хотя это и кажется значимым коммитом (он представляет отдельную законченную задачу), в итоге история будет полна ответвлений:

Integrating a completed feature with a mergeIntegrating a completed feature with a mergeIntegrating a completed feature with a merge
Слияние законченной задачи с помощью merge

Если вы сделаете сдвиг перед слиянием, Git сможет наложить ветку master на конец feature. Увидеть линейную историю прогресса проекта можно с помощью команды git log - коммиты из ветки feature аккуратно сгруппированы поверх коммитов из master. Это не обязательно тот случай, когда ветви сливаются вместе с коммитом слияния.

Rebasing before mergingRebasing before mergingRebasing before merging
Сдвиг перед слиянием

Разрешение конфликтов

При выполнении команды git rebase, Git берет каждый коммит из ветки и переносит их по одному на новое место. Если любой из этих коммитов изменяет те же строчки, что и верхние по ветке коммиты, это приведет к конфликтам.

Команда git merge позволяет вам разрешить все конфликты в конце слияния, что является одной из основных целей создания коммита слияния. Однако все это происходит немного по-другому при использовании сдвига. Конфликты разрешаются после каждого коммита. Таким образом, если git rebase находит конфликт, то процедура сдвига приостанавливается и выводится сообщение с предупреждением:

1
Auto-merging readme.txt
2
CONFLICT (content): Merge conflict in readme.txt
3
Failed to merge in the changes.
4
....
5
When you have resolved this problem, run "git rebase --continue".
6
If you prefer to skip this patch, run "git rebase --skip" instead.
7
To check out the original branch and stop rebasing, run "git rebase --abort".

Визуально вот как выглядит история проекта, когда git rebase обнаруживает конфликт:

Конфликты могут быть просмотрены с помощью git status. Вывод выглядит очень похожим на конфликт при слиянии:

1
Unmerged paths:
2
  (use "git reset HEAD <file>..." to unstage)
3
  (use "git add <file>..." to mark resolution)
4
5
    both modified:   readme.txt
6
7
no changes added to commit (use "git add" and/or "git commit -a")

Для разрешения конфликта следует открыть файл с конфликтами (в нашем примере это readme.txt), найти затрагиваемые строки, и вручную внести необходимые изменения. Затем сообщить Git, чтобы конфликт разрешен, зафиксировав изменения в файле:

1
git add readme.txt

Обратите внимание, что это тот же самый способ, каким разрешаются конфликты при использовании git merge. Но необходимо помнить, что мы все еще находимся в середине процесса сдвига, и не следует забывать об оставшихся коммитах. Последним шагом будет - сообщить Git, что можно продолжить сдвиг, используя опцию --continue.

1
git rebase --continue

Оставшиеся коммиты по одному будут перемещены, если снова возникнут конфликты, то следует повторить процесс их разрешения.

Если вы не хотите разрешать конфликт, можно воспользоваться флагами --skip или --abort. Последний особенно полезен, если вы не знаете что происходит, и просто хотите безопасно вернуться назад.

1
# Ignore the commit that caused the conflict

2
git rebase --skip
3
4
# Abort the entire rebase and go back to the drawing board

5
git rebase --abort

2. Сдвиг для очищения от локальных коммитов

Пока мы только использовали git rebase для перемещения веток, но это гораздо более мощный инструмент. Указав флаг -i, можно начать интерактивную сессию сдвига. Интерактивное перемещение позволяет вам точно определить каким образом каждым коммит будет перемещен. Это дает возможность очистить историю задачи, перед тем как делиться ей с остальными разработчиками.

Например, допустим вы закончили работу в ветке feature и готовы к сливанию ее в master. Чтобы начать сессию интерактивного сдвига, необходимо выполнить следующую команду:

1
git checkout feature
2
git rebase -i master

Будет открыт редактор со всеми коммитами из ветки feature, которые нужно сдвинуть:

1
pick 5c43c2b [Description for oldest commit]
2
pick b8f3240 [Description for 2nd oldest commit]
3
pick c069f4a [Description for most recent commit]

Этот список показывает, как ветка feature будет выглядеть после перемещения. Каждая строчка представленная отдельный коммитом и командой pick перед каждым хэшем коммита, определяет что будет происходить с коммитом во время сдвига. Обратите внимание, что коммиты перечислены от самого старого к новому. Изменяя этот список, вы получаете полный контроль над историей проекта.

Если нужно изменить порядок коммитов, то просто меняем строки местами. Если нужно изменить сообщение коммита, используем команду reword. Если хотим соединить два коммита в один - меняем команду pick на squash. Это перенесет все изменения из данного коммита в тот, что находится выше. Например, если мы соединим второй коммит в списке с верхним, то ветка feature после сохранениябудет выглядеть следующим образом:

Squashing the 2nd commit with an interactive rebaseSquashing the 2nd commit with an interactive rebaseSquashing the 2nd commit with an interactive rebase
Сжимаем второй коммит с помощью интерактивного сдвига.

Команда edit является особенно мощной. При достижении определенного коммита Git останавливает процедуру сдвига, точно так же как при возникновении конфликта. Что дает возможность изменить содержимое коммита с помощью команды git commit --amend или добавить новые коммиты с помощью стандартных команд git add/git commit. Любые новые коммиты станут частью новой ветки.

Интерактивный сдвиг может иметь глубокое воздействие на ваш процесс разработки. Можно сфокусироваться на написании кода, не заботясь о том, чтобы верно распределить свои изменения по коммитам. Если вы в конечном итоге решили соединить четыре разных коммита в один большой, то это не проблема - переписываем историю с помощью git rebase -i и соединяем их в один.

3. Опасности сдвига

Теперь когда у вас есть представление о git rebase, можно поговорить о том, когда его не следует применять. Внутри Git сдвиг в действительности же не перемещает коммиты на новую ветку. Вместо этого он создает новые, которые содержат нужные изменения. Имея это в виду, можно представить процесс сдвига следующим образом:

После сдвига коммиты в ветке feature будут иметь другие хэши. Это означает, что мы не просто изменили положение ветки, а действительно переписали историю проекта. Это очень важный побочный эффект git rebase.

Когда вы один работаете над проектом, переписывание истории не является очень существенным. Однако, как только вы начинаете работать в среде совместной работы, он может стать очень опасным. Если вы перепишите коммиты, которые используются другими разработчиками (например в ветке master), то в следующий раз, когда они будут сливать себе ваши изменения, их собственные коммиты исчезнут. Что приведет к неприятных результатам, после которых тяжело будет все восстановить.

Имя это в виду, вам никогда не следует делать сдвиг коммитов, которые были залиты в публичный репозиторий, до тех пор пока вы не будете уверены, что никто их сейчас в своей работе не использует.

Заключение

В этой статье было представлено два наиболее частых способа использования команды git rebase. Мы много обсуждали перемещение веток, но запомните главное, что сдвиг коммитов помогает контролировать историю вашего проекта. Возможность в будущем переписать коммиты, позволяет больше сосредоточиться на задачах разработки вместо разбиения работы на раздельные этапы.

Обратите внимание, что сдвиг является обязательным дополнением к вашему инструментарию Git. Вы все еще можете сделать все, что вам нужно с простой старой командой git commit. Она гораздо безопаснее, поскольку позволяет избежать возможности переписывания общей истории. Однако если вы понимаете риски, с git rebase можно получить гораздо чистую интеграцию веток, по сравнению со слиянием коммитов.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.