Сложное слияние и метки. Командная строка
Долг за прошлую лекцию:
Зависимости при разработке на Python
Зависимости на python-пакеты vs зависимости на системное окружение (то, чему нельзя сделать pip install: утилиты OS и т. п.)
- По этапу применения:
Эксплуатационные (runtime) — то, чему делается import (без них модуль не заработает)
- Сборочные (development) — то, без чего нельзя сформировать дистрибутив (например, средства форматирования документации)
- Инструментальные (environment) — то, без чего нельзя вести разработку (редактор, git, средства профилирования и отладки и т. п.)
pip freeze, requirements.txt и pip install -r
- что делать с версиями?
- отдельный req для сборочных зависимостей?
Решение от pipenv (warning: a bit of alien technology)
Работа с pipenv
pipenv install — эксплуатационные зависимости
pipenv install -d — сборочные зависимости
pip install — временные зависимости
pipenv erase + pipenv sync
Какие-то инструменты для выявления зависимостей?
тысячи их (буквально)
Например, pip-check-reqs (возможно, не лучший)
Замечание про зачаточное состояние и непростую задачу учёта зависимостей в Python
Метки (теги)
Указатели на коммиты, лежат в .git/refs/tags/
- Выступают в роли commit-ish (как commit ID, ветки и ссылки относительно HEAD)
Можно запушить с ключом --tags (но по умолчанию локальны)
Аннотированный тег сопровождается специальным объектом-тегом в .git/objects/**/
git tag [commitish] -a тег -m Аннотация
- Можно подписывать электронной подписью
Две роли тегов: информационная и управляющая (особенно подписанных)
Сложное слияние
При merge и rebase могут возникать конфликты: в двух историях изменён один и тот же контекст:
- Создадим заведомо конфликтующий коммиты на двух ветках
1 $ git init 2 Initialized empty Git repository in /home/george/example/.git/ 3 $ git add . 4 $ git commit -a -m "Initial commit" 5 [master (root-commit) 8ab1be9] Initial commit 6 1 file changed, 63 insertions(+) 7 create mode 100644 keyword.py 8 $ git branch second 9 $ git branch 10 * master 11 second 12 $ grep -Ev "except|False" /usr/lib64/python3.10/keyword.py > keyword.py 13 $ git diff 14 diff --git a/keyword.py b/keyword.py 15 index cc2b46b..9f30ffb 100644 16 --- a/keyword.py 17 +++ b/keyword.py 18 @@ -16,7 +16,6 @@ Alternatively, you can run 'make regen-keyword'. 19 __all__ = ["iskeyword", "issoftkeyword", "kwlist", "softkwlist"] 20 21 kwlist = [ 22 - 'False', 23 'None', 24 'True', 25 'and', 26 @@ -31,7 +30,6 @@ kwlist = [ 27 'del', 28 'elif', 29 'else', 30 - 'except', 31 'finally', 32 'for', 33 'from', 34 $ git commit -a -m "False+except" 35 [master f1fbdeb] False+except 36 1 file changed, 2 deletions(-) 37 $ git checkout second 38 Switched to branch 'second' 39 $ grep -Ev "finally|yield" /usr/lib64/python3.10/keyword.py > keyword.py 40 $ git diff 41 diff --git a/keyword.py b/keyword.py 42 index cc2b46b..251bd3a 100644 43 --- a/keyword.py 44 +++ b/keyword.py 45 @@ -32,7 +32,6 @@ kwlist = [ 46 'elif', 47 'else', 48 'except', 49 - 'finally', 50 'for', 51 'from', 52 'global', 53 @@ -50,7 +49,6 @@ kwlist = [ 54 'try', 55 'while', 56 'with', 57 - 'yield' 58 ] 59 60 softkwlist = [ 61 $ git commit -a -m "finally+yield" 62 [second 0804e39] finally+yield 63 1 file changed, 2 deletions(-) 64 $ git log --graph --pretty=oneline --abbrev-commit --all 65 * 0804e39 (HEAD -> second) finally+yield 66 | * f1fbdeb (master) False+except 67 |/ 68 * 8ab1be9 Initial commit 69
Итак, у нас есть три состояния файла 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)
Файл с конфликтами (его и надо исправлять)
Могут остаться backup-файлы, их надо удалить git clean -f
- Другие утилиты позволяют «накликивать» изменения)
Пример на том же репозитории:
просто удалим merge-коммит (git reset --hard HEAD~)
вызовем git merge
вызовем git merge-tool --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
- Просто проимпортить, и уже)
Есть urwid, модный prompt-toolkit и т. п., но это уже оверкилл
shlex — это split / join / quote для командной строки, похожей на shell
- см. примеры
cmd — собственная командная строка!
- Организует REPL
class commandline(cmd.Cmd): — и поехали!
Можно обмазать readline или любым другим враппером
do_COMMAND(строка)
COMMAND — команда
префикс docstring — это встроенный help()
строку можно распарсить shlex-ом или более сложным парсером
complete_COMMAND(префикс, строка_целиком, начало_префикса, конец_префикса)
префикс — что достраиваем (введённый текст от конца команды до курсора)
строка_целиком — всё, что успели ввести (вместе с командой и текстом после курсора)
начало_префикса, конец_префикса — индекс префикса в строке_целиком (мало ли, может надо проверить команду или что после курсора)
TODO пример
- Организует REPL
Д/З
Почитать про shlex и cmd; изучить API Python Cowsay
8 Наиболее сложная часть — что именно получают на вход методы complete_COMMAND(), разберитесь с этим
Создать в репозитории с Д/З подкаталог 04_MergetoolCommandline (по последнему фрагменту URL данной лекции) и поместить туда решение следующей задачи:
Переработать Домашнее задание из лекции 2 (возможно, лучше переписать заново), организовав командную строку с помощью cmd. Разбор командной строки делать с помощью shlex
Команды и их параметры проще всего сделать совпадающими с соответствующими функциями. Должны поддерживаться как минимум команды list_cows, make_bubble, cowsay и cowthink.
Параметры не надо реализовывать все. Например, для cowsay … достаточно четырёх:
message, cow, eyes, tongue
Везде, где это предусмотрено соответствующими функциями, должны подерживаться параметры по умолчанию (например, для cowsay достаточно только одного параметра — message)
К каждой команде должна быть достаточная справка (help COMMAND)
- Везде, где это возможно, должна поддерживаться подстановка вариантов (completion)
В частности, для eyes и tongue необходимо сделать несколько заранее заданных вариантов, и предлагать их
(в силу специфики библиотеки readline подстановка не-буквенных последовательностей может не работать, не боритесь с этим)
все остальные усложнения — только если захочется)
Разработку вести согласно дисциплине оформления коммитов в подкаталоге 04_MergetoolCommandline отчётного репозитория по Д/З
Предполагается, что модуль python-cowsay устанавливается в окружение с помощью pipenv, в каталоге должен присутствовать соответствующий Pipfile