воскресенье, 12 мая 2024 г.

Git cложное слияние

http://uneex.org/LecturesCMC/PythonDevelopment2023/04_MergetoolCommandline

https://youtu.be/uVh3BEL1iyU

Вообще merge commit опасная штука, их плодить не надо т.к. могут быть конфликты, когда редактируется одно и то же место, которое правится вручную при merge commit

Сложное слияние

При merge и rebase могут возникать конфликты: в двух историях изменён один и тот же контекст: 

  • Создадим заведомо конфликтующий коммиты на двух ветках 
    $ git init
    Initialized empty Git repository in /home/george/example/.git/
    $ git add .
    $ git commit -a -m "Initial commit"
    [master (root-commit) 8ab1be9] Initial commit
     1 file changed, 63 insertions(+)
     create mode 100644 keyword.py
    $ git branch second
    $ git branch
    * master
      second
    $ grep -Ev "except|False" /usr/lib64/python3.10/keyword.py > keyword.py
    $ git diff
    diff --git a/keyword.py b/keyword.py
    index cc2b46b..9f30ffb 100644
    --- a/keyword.py
    +++ b/keyword.py
    @@ -16,7 +16,6 @@ Alternatively, you can run 'make regen-keyword'.
     __all__ = ["iskeyword", "issoftkeyword", "kwlist", "softkwlist"]
    
     kwlist = [
    -    'False',
         'None',
         'True',
         'and',
    @@ -31,7 +30,6 @@ kwlist = [
         'del',
         'elif',
         'else',
    -    'except',
         'finally',
         'for',
         'from',
    $ git commit -a -m "False+except"
    [master f1fbdeb] False+except
     1 file changed, 2 deletions(-)
    $ git checkout second
    Switched to branch 'second'
    $ grep -Ev "finally|yield" /usr/lib64/python3.10/keyword.py > keyword.py
    $ git diff
    diff --git a/keyword.py b/keyword.py
    index cc2b46b..251bd3a 100644
    --- a/keyword.py
    +++ b/keyword.py
    @@ -32,7 +32,6 @@ kwlist = [
         'elif',
         'else',
         'except',
    -    'finally',
         'for',
         'from',
         'global',
    @@ -50,7 +49,6 @@ kwlist = [
         'try',
         'while',
         'with',
    -    'yield'
     ]
    
     softkwlist = [
    $ git commit -a -m "finally+yield"
    [second 0804e39] finally+yield
     1 file changed, 2 deletions(-)
    $ git log --graph --pretty=oneline --abbrev-commit --all
    * 0804e39 (HEAD -> second) finally+yield
    | * f1fbdeb (master) False+except
    |/
    * 8ab1be9 Initial commit
    
    
  • Итак, у нас есть три состояния файла keyword.py

    1. 8ab1be9 (общий предок) 

    2. f1fbdeb (на ветке master) — без False и except

    3. 0804e39 (на ветке second) — без finally и yield

  • Контекст изменений для except и finally пересекается 

    • ⇒ при слиянии будут конфликты 
  • Попробуем объединить: 
       1 $ git branch
       2   master
       3 * second
       4 $ git merge master
       5 Auto-merging keyword.py
       6 CONFLICT (content): Merge conflict in keyword.py
       7 Automatic merge failed; fix conflicts and then commit the result.
       8 $ grep -EC3 "<<<<|====|>>>>" keyword.py
       9     'del',
      10     'elif',
      11     'else',
      12 <<<<<<< HEAD
      13     'except',
      14 =======
      15     'finally',
      16 >>>>>>> master
      17     'for',
      18     'from',
      19     'global',
      20 
    
  • Часть изменений применены (про False и про yield), потому что контексты не пересекались, часть (про except и finally) — нет. 

  • Файл содержит вставки вида: 
    <<<<<<< HEAD
    =======
    >>>>>>> master
  • Это т. н. 3-way diff по схеме «общий предок + конфликтующие изменения» 
    • Все неконфликтующие изменения из обеих веток применены 

    • HEAD — это содержимое текущей ветка, master — с чем мержим 

      • {1} было бы неплохо ещё знать, что раньше-то было, но тут не показывается 

    • Все "<<<<<<<""=======" и ">>>>>>>" надо убрать (и ненужные изменения тоже) 

    • Получится merge commit с изменением, неравным тому, что делалось на ветках 
  • Если вас удовлетворяют изменения, проделанные на ветке master, можно просто git checkout master keyword.py<!> но тогда пропадут все изменения, включая уже применённые

  • Когда всё готово, делаем git commit -a

   1 $ vim keyword.py
   2 
   3 $ git commit -a
   4 [second 6568682] Merge branch 'master' into second
   5 $ git log --graph --pretty=oneline --abbrev-commit --all
   6 *   6568682 (HEAD -> second) Merge branch 'master' into second
   7 |\
   8 | * f1fbdeb (master) False+except
   9 * | 0804e39 finally+yield
  10 |/
  11 * 8ab1be9 Initial commit
  12 
  • Если в историях больше одного коммита, merge надо продолжить с помощью git merge --continue

  • Если вы окончательно запутались (особенно в многокоммитных мержах), всё можно откатить назад с помощью git merge --abort



    Mergetool

    Инструмент, в котором есть {1} , имеет общее название «merge tool». 

    • Список: git mergetool --tool-help

    • Запускается вместо ручного исправления конфликтов 
    • *vimdiff показывает четыре окна: 

      • «Эта» ветка (LOCAL) 

        Общий предок (BASE) 

        «Та» ветка (REMOTE)

        Файл с конфликтами (его и надо исправлять)

      • Могут остаться backup-файлы, их надо удалить git clean -f

    • Другие утилиты позволяют «накликивать» изменения) 

    Пример на том же репозитории: 

    • просто удалим merge-коммит (git reset --hard HEAD~

    • вызовем git merge

    • вызовем git merge-tool --tool=gvimdiff (или meld)

      • :diffget RE  " get from REMOTE
        :diffget BA  " get from BASE
        :diffget LO  " get from LOCAL

    Для постоянного вызова правильного mergetool: 

    • $ git config --global merge.tool ваш_mergetool


Комментариев нет:

Отправить комментарий