28. Перебазування

Цілі

  • Використовувати команду rebase замість команди merge.

Ми повернули гілку style до стану перед першим злиттям. В гілці main є дві коміти, яких зараз немає у гілці style: новий файл README і конфліктна зміна у файлі index.html. Цього разу ми перенесемо ці зміни до гілки style за допомогою команди rebase, а не merge.

01 Перебазуйте гілку style на main.

Виконайте

git switch style
git rebase main
git status

Результат

$ git switch style
Already on 'style'
$ git rebase main
Rebasing (1/3)
Rebasing (2/3)
Auto-merging hello.html
CONFLICT (content): Merge conflict in hello.html
error: could not apply 903eb1d... Included stylesheet into hello.html
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 903eb1d... Included stylesheet into hello.html
$ git status
interactive rebase in progress; onto 85c14e9
Last commands done (2 commands done):
   pick 555372e Added css stylesheet
   pick 903eb1d Included stylesheet into hello.html
Next command to do (1 remaining command):
   pick 0ee0113 Renamed hello.html; moved style.css
  (use "git rebase --edit-todo" to view and edit)
You are currently rebasing branch 'style' on '85c14e9'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Unmerged paths:
  (use "git restore --staged <file>..." to unstage)
  (use "git add <file>..." to mark resolution)
	both modified:   hello.html

no changes added to commit (use "git add" and/or "git commit -a")

Знову виник конфлікт! Зверніть увагу, що конфлікт стався в hello.html, а не в index.html, як минулого разу. Це тому, що rebase був у процесі застосування змін style поверх гілки main. У той момент в гілці main ще не було перейменовано файл hello.html, тому він все ще має стару назву.

При злитті виник би "зворотній" конфлікт. Під час злиття зміни гілки main були б застосовані поверх гілки style. У гілці style файл перейменовано, тому конфлікт виник би у файлі index.html.

Файл: hello.html

<!-- Author: Alexander Shvets (alex@githowto.com) -->
<html>
  <head>
<<<<<<< HEAD
    <title>Hello World Page</title>
=======
    <link type="text/css" rel="stylesheet" media="all" href="style.css" />
>>>>>>> 903eb1d (Included stylesheet into hello.html)
  </head>
  <body>
    <h1>Hello, World!</h1>
    <p>Let's learn Git together.</p>
  </body>
</html>

02 Розв'яжіть конфлікт

Сам конфлікт можна усунути так само, як ми це робили раніше. По-перше, ми відредагуємо файл hello.html, щоб він відповідав нашим очікуванням.

Файл: hello.html

<!-- Author: Alexander Shvets (alex@githowto.com) -->
<html>
  <head>
    <title>Hello World Page</title>
    <link type="text/css" rel="stylesheet" media="all" href="style.css" />
  </head>
  <body>
    <h1>Hello, World!</h1>
    <p>Let's learn Git together.</p>
  </body>
</html>

Але після цього нам не потрібно комітити зміни. Ми можемо просто додати файл до індексу і продовжити процес перебазування. Ось за що я люблю rebase! Ця команда дозволяє мені усувати конфлікти, не створюючи купу потворних конфліктів злиття.

Для простоти ми можемо додати всі зміни, використовуючи ., що означає шлях до поточної директорії. Git інтерпретує це як «додати всі зміни з поточної директорії та її піддиректорій».

Виконайте

git add .
git rebase --continue

Тут, найімовірніше, Git знову відкриє редактор, щоб дозволити нам змінити текст коміту. Ми можемо залишити текст без змін. Після збереження змін Git завершить процес rebase, і ми зможемо виконати наступні команди:

Виконайте

git status
git log --all --graph

Результат

$ git add .
$ git rebase --continue
[detached HEAD 23149b5] Included stylesheet into hello.html
 1 file changed, 1 insertion(+)
Rebasing (3/3)

Successfully rebased and updated refs/heads/style.
$ git status
On branch style
nothing to commit, working tree clean
$ git log --all --graph
* 39a1e0f 2023-11-28 | Renamed hello.html; moved style.css (HEAD -> style) [Alexander Shvets]
* 23149b5 2023-11-28 | Included stylesheet into hello.html [Alexander Shvets]
* b9e6de1 2023-11-28 | Added css stylesheet [Alexander Shvets]
* 85c14e9 2023-11-28 | Added meta title (main) [Alexander Shvets]
* ee16740 2023-11-28 | Added README [Alexander Shvets]
* 9288a33 2023-11-28 | Added copyright statement with email [Alexander Shvets]
* b7614c1 2023-11-28 | Added HTML header (tag: v1) [Alexander Shvets]
* 46afaff 2023-11-28 | Added standard HTML page tags (tag: v1-beta) [Alexander Shvets]
* 78433de 2023-11-28 | Added h1 tag [Alexander Shvets]
* 5836970 2023-11-28 | Initial commit [Alexander Shvets]

03 Злиття VS перебазування

Кінцевий результат перебазування дуже схожий на результат злиття. Гілка style зараз містить всі свої зміни, а також всі зміни гілки main. Однак, дерево комітів значно відрізняється. Дерево комітів гілки style було переписано таким чином, що гілка main є частиною історії комітів. Це робить ланцюг комітів лінійним і набагато більш читабельним.

04 Коли використовувати команду rebase, а коли команду merge?

Використовуйте команду rebase:

  • Коли ви підтягуєте зміни з віддаленого репозиторія і хочете злити їх до вашої локальної гілки.
  • Якщо ви хочете, щоб історія комітів була лінійною і легкою для читання.

Не використовуйте команду rebase:

  • Якщо поточна гілка є загальнодоступною. Перезапис таких гілок заважатиме роботі інших членів команди.
  • Коли важлива точна історія гілки комітів (оскільки команда rebase переписує історію комітів).

Враховуючи наведені вище рекомендації, я вважаю за краще використовувати rebase для короткострокових, локальних гілок, а команду merge — для гілок у публічному репозиторії.