Сложное слияние и метки. Командная строка
Метки (теги)
Указатели на коммиты, лежат в .git/refs/tags/
- Выступают в роли commit-ish (как commit ID, ветки и ссылки относительно HEAD)
Можно запушить с ключом --tags (но по умолчанию локальны)
Аннотированный тег сопровождается специальным объектом-тегом в .git/objects/**/
git tag [commitish] -a тег -m Аннотация
- Можно подписывать электронной подписью
Две роли тегов:
- закладка (особенно аннотированные теги): как commitish не зависит от ветки / commit ID
- управление внешними инструментами (особенно подписанные теги) — например, автоматическая сборка и т. п.
Сложное слияние
При merge и rebase могут возникать конфликты: в двух историях изменён один и тот же контекст:
- Создадим заведомо конфликтующие коммиты на двух ветках
1 $ git init 2 Initialized empty Git repository in /home/george/example/.git/ 3 $ cp /usr/lib64/python3.13/keyword.py keyword.py 4 $ git add . 5 $ git commit -a -m "Initial commit" 6 $ git branch second 7 $ grep -Ev "except|False" /usr/lib64/python3.13/keyword.py > keyword.py 8 $ git commit -a -m "False+except" 9 $ git checkout second 10 $ grep -Ev "finally|yield" /usr/lib64/python3.13/keyword.py > keyword.py 11 $ git commit -a -m "finally+yield"
Итак, у нас есть три состояния файла keyword.py:
8ab1be9 (общий предок)
f1fbdeb (на ветке master) — без False и except
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 — с чем мержим
было бы неплохо ещё знать, что раньше-то было, но тут не показывается
- Чтобы получить чистый файл, надо убрать
- нежелательные изменения,
"<<<<<<<", "=======" и ">>>>>>>"
- Получится 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
Инструмент, в котором есть
, имеет общее название «merge tool».
Список: git mergetool --tool-help
- Запускается вместо ручного исправления конфликтов
*vimdiff показывает четыре окна:
«Эта» ветка (LOCAL)
Общий предок (BASE)
«Та» ветка (REMOTE)
Файл с конфликтами (MERGED, его и надо исправлять)
Могут остаться backup-файлы, их надо удалить git clean -f
]c/[c для перехода по контекстам
zo для открытия свёрток (см. другие folding-команды)
diffget/diffput с параметрами LO, RE, BA
:cquit для выходя с ошибкой
для тех, у кого не четыре полушария
, есть gvimdiff3 (только MERGED), gvimdiff2 (LOCAL, MERGED, REMOTE, без общего предка) и gvimdiff1 (LOCAL и REMOTE — вот это непонятно зачем) - Утилиты из списка выше позволяют «накликивать» изменения
Пример на том же репозитории:
просто удалим merge-коммит (git reset --hard HEAD~)
вызовем git merge
вызовем git mergetool --tool=gvimdiff
:diffget RE " get from REMOTE :diffget BA " get from BASE :diffget LO " get from LOCAL
Для постоянного вызова правильного mergetool:
$ git config --global merge.tool ваш_mergetool
Организация командной строки
На Windows не работает, замена — pyreadline3
- Просто проимпортить, и уже имеются:
- Редактирование ввода
- История
- Хранение истории (см. примеры в конце документации)
Дополнение в readline делается с помощью rlcompleter
Есть urwid, модный prompt-toolkit и т. п., но это уже оверкилл
shlex — это split / join / quote для командной строки, похожей на shell
- см. примеры в документации
потестируем split()
cmd — собственная командная строка!
- Организует REPL
class commandline(cmd.Cmd): — и поехали!
Можно обмазать readline или любым другим враппером
do_COMMAND(строка)
COMMAND — команда
префикс docstring — это встроенный help()
строку можно распарсить shlex-ом или более сложным парсером
complete_COMMAND(префикс, строка_целиком, начало_префикса, конец_префикса)
префикс — что достраиваем (введённый текст от конца команды до курсора)
строка_целиком — всё, что успели ввести (вместе с командой и текстом после курсора)
начало_префикса, конец_префикса — индекс префикса в строке_целиком (мало ли, может надо проверить команду или что после курсора)
- Пример
1 import cmd 2 3 DIGITS = {'zero': 0, 'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 4 'six': 6, 'seven': 7, 'eight': 8, 'nine': 9, 'ten': 10} 5 TEENS = {'eleven': 11, 'twelve': 12, 'thirteen': 13, 'fourteen': 14, 'fifteen': 15, 6 'sixteen': 16, 'seventeen': 17, 'eighteen': 18, 'nineteen': 19} 7 DECS = {'twenty': 20, 'thirty': 30, 'forty': 40, 'fifty': 50, 'sixty': 60, 'seventy': 70, 8 'eighty': 80, 'ninety': 90} 9 ANY = DIGITS | TEENS | DECS 10 11 12 class numbername(cmd.Cmd): 13 prompt = "cmd>> " 14 15 def do_number(self, arg): 16 """Translate two-digit number from english""" 17 match arg.split(): 18 case [dec, digit]: 19 if dec in DECS and digit in DIGITS: 20 print(DECS[dec] + DIGITS[digit]) 21 case [single]: 22 if single in ANY: 23 print(ANY[single]) 24 25 def complete_number(self, text, line, begidx, endidx): 26 words = (line[:endidx] + ".").split() 27 DICT = [] 28 match len(words): 29 case 2: 30 DICT = ANY 31 case 3: 32 if words[1] in DECS: 33 DICT = DIGITS 34 return [c for c in DICT if c.startswith(text)] 35 36 37 if __name__ == '__main__': 38 numbername().cmdloop()
- Организует REPL
- TUI:
curses — встроенный в Python!
Как синтез TUI+cmdline: Python Prompt Toolkit
Д/З
Почитать про shlex и cmd; изучить API Python Cowsay
Наиболее сложная часть — что именно получают на вход методы complete_COMMAND(), разберитесь с этим (см. пример из лекции)
Создать в репозитории с Д/З подкаталог 04_MergetoolCommandline (по последнему фрагменту URL данной лекции) и поместить туда решение следующей задачи:
Переработать Домашнее задание из лекции 2 (возможно, лучше переписать заново), организовав командную строку с помощью cmd. Разбор командной строки реализовть с помощью shlex
Команды и их параметры проще всего сделать совпадающими с соответствующими функциями. Должны поддерживаться как минимум команды list_cows, make_bubble, cowsay и cowthink.
Пример синтаксиса cowsay (не обязательно реализовывать именно его; в квадратных скобках указаны необязательные параметры):
cowsay сообщение [название [параметр=значение …]] reply ответ [название [[параметр=значение …]]
сообщение и ответ — это реплики двух персонажей
название — это название коровы (должно поддерживаться достраивание)
параметр=значение — это eyes и tongue, а значение — строка
twocows> cowsay "Hi there" moose eyes="^^" reply "Ahoy!" sheep __________ < Ahoy! > < Hi there > ------- ---------- \ \ \ \ \_\_ _/_/ __ \ \__/ UooU\.'@@@@@@`. (^^)\_______ \__/(@@@@@@@@@@) (__)\ )\/\ (@@@@@@@@) ||----w | `YY~~~~YY' || || || ||
К каждой команде должна быть достаточная справка (help COMMAND)
все остальные усложнения — только если захочется)
Разработку вести согласно дисциплине оформления коммитов в подкаталоге 04_MergetoolCommandline отчётного репозитория по Д/З
Предполагается, что модуль python-cowsay устанавливается в окружение с помощью pipenv, в каталоге должен присутствовать соответствующий Pipfile

Чуть ли не половина перечисленного — проприетарные поделия