Управление историей git
СкопированоПредставим, что у вас есть git-репозиторий с несколькими ветками. Вы работаете в одной из них и хотите перенести свои изменения в другую ветку. Такое приходится делать очень часто, если хотите актуализировать свои изменения.
Рассмотрим несколько распространённых сценариев.
Вы начали разрабатывать фичу и создали ветку my из основной main. За время разработки в ветку main приехали изменения коллег. Как переместить ваши изменения на актуальный main?

В вашей команде есть фронтенд- и бэкенд-разработчики. Вы работаете в одном репозитории, создали ветку frontend из основной ветки main, а ваш коллега — ветку backend тоже из main. В ветке frontend два коммита: Кнопочка 1 и Тултипчик 2, в backend их тоже два — API 1 и API 2. Теперь вы хотите потестировать ваш фронтенд с новым бэкендом. Как это сделать?

В вашей ветке есть несколько коммитов, которые нужны коллеге: Кнопочка 1 и Тултипчик 2. Кроме них в ветке есть коммит, который сломает ветку коллеги — Собака-всё-ломака. Как потестировать только не ломающие изменения?

Все эти задачи решаются с помощью команды git rebase .
Немного теории
СкопированоGit хранит коммиты (commits). Коммит — всего лишь ссылка на некоторое состояние репозитория. Состояние — это немножко метаданных и ссылка на предыдущий коммит. Сам по себе коммит — это 40 символов SHA-1 хеша этого состояния.
Ветка (branch) — это ссылка на определённый коммит. У вас может быть только одна активная ветка. На неё (т. е. на определённый коммит) указывает специальный указатель HEAD.
Многие команды гит принимают в качестве параметра ревизию. Так как всё в гите это захешированные объекты, то в качестве ревизии можете передавать:
- длинный хеш, например вот так
29aeb39eac6b8d9e0f4cdc1459376599d1aba43c; - короткий хеш, например вот так
29aeb39e; - тег, если он имеется, например вот так
v1;. 0 . 0 - имя ссылки; в основном используется для веток, например
main,origin,/ dev refs./ heads / foo
Можете добавлять магические символы (@{n}) к этим параметрам, чтобы ещё эффективнее выбирать нужные коммиты. Подробности можно посмотреть в документации про gitrevisions. Особенно полезен символ ^. Он позволяет выбирать предыдущие коммиты.
Как готовить
СкопированоПодробности можно найти, например, в официальной документации.
Команда git rebase с флагом - принимает несколько аргументов:
-— коммит, на который нужно перенести изменения. Новые коммиты добавятся сразу после него;- onto from— коммит, с которого нужно перенести изменения. Коммит, который укажете, не перенесётся, а следующие за ним перенесутся;to— коммит, до которого нужно перенести изменения. Коммит, который укажете, перенесётся.
Последний аргумент можно не использовать. Тогда перенесутся все коммиты, начиная от следующего за from коммита до конца ветки.
Например:
git rebase --onto main feature1
git rebase --onto main feature1
Помните, что коммит — это всего лишь хеш? В качестве каждого аргумента можете указывать хеш, например вот так:
git rebase --onto 29aeb39e 5fd88295
git rebase --onto 29aeb39e 5fd88295
Хеши, вообще-то, длиннее 8 символов, но git понимает короткую версию. Можете также указать полный хеш, например вот так:
git rebase --onto 29aeb39eac6b8d9e0f4cdc1459376599d1aba43c 5fd882953924b47a10794619c3063e7a50257af6
git rebase --onto 29aeb39eac6b8d9e0f4cdc1459376599d1aba43c 5fd882953924b47a10794619c3063e7a50257af6
Вы можете указать название ветки вместо хеша, например вот так:
git rebase --onto main feature
git rebase --onto main feature
Если хотите перенести несколько коммитов из основной ветки, можете использовать магию HEAD нотации.
git rebase --onto main HEAD^3 HEAD^
git rebase --onto main HEAD^3 HEAD^
Эта команда перенесёт 2 коммита из текущей ветки в ветку main. Самый последний не перенесёт.
Рассмотрим один из примеров выше.
Было так:

А нужно, чтобы коммиты Кнопочка 1 и Тултипчик 2 оказались в ветке backend.

Для этого выполните команду:
git rebase --onto backend main frontend
git rebase --onto backend main frontend
git rebase— переносим;- - onto - backend — на последний коммит ветки backend, т. е. на API 2;
- main — все коммиты в моей ветке frontend, начиная с того, которым заканчивается ветка main (Статья 3);
- frontend — заканчивая последним коммитом ветки frontend (Тултипчик 2).
Бонус
СкопированоЕсли хотите посмотреть какой красивой стала ваша история, используйте эту команду:
git log --oneline --decorate --graph --all
git log --oneline --decorate --graph --all
На собеседовании
Скопировано отвечает
СкопированоОбе команды нужны для одной цели, но имеют существенные отличия в том, как работают.
git merge объединяет изменения из одной ветки в другую, создавая новый коммит слияния merge commit, который сохраняет полную историю проекта. Это позволяет проследить все коммиты и увидеть, когда и как ветки были объединены, хотя такая история может быть сложнее для чтения. Однако, если целевая ветка не имеет новых коммитов, Git использует fast, просто перемещая указатель вперёд без создания дополнительного коммита, что сохраняет линейную и более простую для чтения историю.
Обратите внимание, что, при использовании merge, мы должны находиться в ветке, в которую делаем перемещение.
git checkout maingit merge feature
git checkout main
git merge feature
git rebase перемещает или переписывает базу текущей ветки на указанную базу другой ветки, изменяя хэши коммитов и делая историю линейной, что упрощает чтение последовательности внедрения фич. При этом не создаётся новый коммит слияния merge commit, но это может затруднить отслеживание точной хронологии. Важно, что git rebase позволяет переносить только часть коммитов и не требует находиться в целевой ветке — можно использовать флаг -, чтобы выполнить ребейз на другую ветку или коммит, гибко управляя изменениями. Будьте осторожны, так как команда может иметь негативный эффект.
Обратите внимание, что, при использовании rebase, мы должны находиться в ветке, из которой делаем перемещение.
git checkout featuregit rebase main
git checkout feature
git rebase main
Используйте merge, когда хотите сохранить всю историю разработки, включая все ветвления и слияния. Это полезно для командной работы, где важно видеть весь контекст изменений.
Используйте rebase, когда хотите поддерживать чистую и линейную историю. Это особенно полезно для интеграции изменений в основную ветку перед созданием pull request, чтобы история коммитов была более понятной.