Git: ключи и слияние. Python: зависимости
Авторизация на удалённом хранилище
- (повтор) любой репозиторий — хранилище
то же самое, если использовать ssh-доступ напрямую
Проблема публикации в не-bare репозиторий (receive.denyCurrentBranch)
- по https — было по умолчанию на GH, теперь отключили
чтение, разумеется, оставили — это основной способ взаимодействия
по протоколу git (есть такой) — в действительности по ssh
- У разных хостингов есть ещё какие-то свои «access tokens», но стандарта на них нет
Отступление про SSH
- Зачем нужен
- Авторизация по паролю
- Суть асимметричного шифрования и авторизация по ключу
- Свойства публичного и приватного ключей
- Агент
Подробнее про SSH в прошлогодней лекции
Процедура слияния в GIT
- Ветка (branch) возможность иметь несколько путей разработки
Не похожа на настоящую ветку: растёт оттуда, где в данный момент расходятся истории
- ⇒ при изменении истории внезапно начинает отрастать не там!
Ветки можно объединять (git merge)
- Пример:
- Сделать три коммита
- Отпочковать ветку от предпоследнего
- Переключиться на ней и сделать туда неконфликтующий коммит (например, создать файл)
Просмотри истории:
Всегда есть git log --all --graph --oneline
Часто есть gitk и git-gui (в составе git, в дистрибутивах их выпиливают в отдельные пакеты)
Можно пользоваться qgit, и вообще тысячи их
Ещё больше TUI, типа tig (для любителей)
- Встроенные в IDE…
Применение веток для параллельной разработки
- Совместная разработка
- Схема «devel - testing - production»
- Разработка, временно ухудшающая качество
В этой лекции мы рассматриваем только неконфликтующие операции.
Слияние (merge):
git merge commit-ish
- Слияние бывает с разными стратегиями
Например, git pull из обновлённого репозитория (при неизменённом локальном) — это fetch + merge, fast-forward
- Если прямого наследования нет, нужен специальный «merge commit»
- Пример
- Сделать три коммита: второй коммит задаёт большой файл, третий модифицирует его в начале
- Отпочковать ветку от предпоследнего
- Переключиться на ней и сделать туда неконфликтующий коммит на большом файле в конце
- Переключиться в основную ветку и помержить
Если «чистое» слияние невозможно (изменения в разных ветках затрагивают один и тот же контекст, и изменения неравны), конфликты следует устранить вручную, закоммитить и проложить слияние. Об этом в следующей лекции.
Процедура перебазирования в GIT
Ту же задачу — объединение двух вариантов истории — можно решить путём её переписывания (rebase). Как если бы ту же самую работу выполнили после совсем другого коммита.
- (повторение) переписывание в рамках одной ветки
- Rebase делает из двух веток одну (вторая как бы «переотрастает» на конце первой)
- Пример:
- Сделать три коммита
- Отпочковать ветку от предпоследнего
- Переключиться на ней и сделать туда неконфликтующий коммит (например, создать файл)
- Поребейзить её поверх основной ветки
Пример, когда в результате rebase ветка начинает ответвляться всё раньше по истории
- Сделать три коммита
- Отпочковать ветку от предпоследнего
- Переключиться на ней и сделать туда неконфликтующий коммит (например, создать файл)
- Переключиться на основную ветку слить два последних коммита (отдельный и общей со второй веткой)
См. замечание относительно конфликтов выше.
Сравнение с merge
- - Переписывать историю нехорошо, если она хоть кому-то нужна (что делать людям, которые ведут разработку на основе уже несуществующей истории?)
(общее свойство и для rebase, и для merge, aka «чудес на свете не бывает») При перенакладывании изменений контекст синтаксически не изменяется, но не совпадает семантика изменений.
Например, сделали ветку с исправлением некоторой функции, а в основной ветке тем временем запилили вторую похожую функию с той же ошибкой.
- Например, в одной ветке исправили ошибку строгим кастингом возвращаемого значения, а во второй — исправлением типа в объявлении переменной (оба изменения вместе смысла не имеют).
- + merge-коммиты усложняют историю, могут сделать её нечитаемой
+ изменения, сделанные с помощью merge, «опускаются на дно» истории (поскольку мержится вполне определённый коммет, его время не меняется)
Для сравнения, rebase всегда выглядит как «свежие» правки
Пример: неадекватный апстрим и набор фиксов в одной ветке
- + rebase:
фиксы поверх апстрима («самые свежие правки»)
- апстрим «вырывается вперёд»
- фиксы ребейзятся поверх апстрима, далее п. 1
- - merge:
фиксы поверх апстрима
- апстрим «вырывается вперёд»
- ветка разработки мержится с апстримом
- ⇒ фиксы по времени раньше обновлений апстрима, «тонут»
- логически «основная ветка» и «ветка с изменениями» поменялись ролями
- если есть конфликты, они распадаются на исходный коммит и правки в merge-коммите
Для совместной разработки, тем не менее, merge намного удобнее — сохраняется история.
Зависимости при разработке на Python
Зависимости на python-пакеты vs зависимости на системное окружение (то, чему нельзя сделать pip install: утилиты OS и т. п.)
- По этапу применения:
Эксплуатационные (runtime) — то, чему делается import (без них модуль не заработает)
- Сборочные (development) — то, без чего нельзя сформировать дистрибутив (например, средства форматирования документации)
- Инструментальные (environment) — то, без чего нельзя вести разработку (редактор, git, средства профилирования и отладки и т. п.)
pip freeze, requirements.txt и pip install -r
- что делать с версиями?
- отдельный requirements для сборочных зависимостей?
(повторение): pip → pipx → pipenv
Решение от pipenv
pipenv shell — создать окружение (чёрт-те где), если его нет + активировать
pipenv install — эксплуатационные зависимости
pipenv install -d — сборочные зависимости
pip install — временные зависимости
pipenv erase + pipenv sync — восстанавливает только зависимости, зафиксированные в Pipfile
Какие-то инструменты для выявления зависимостей?
тысячи их (буквально)
Например, pip-check-reqs (возможно, не лучший)
Py3DepHell (Py3DepHell) (возможно, лучший ☺)
Замечание про зачаточное состояние и непростую задачу учёта зависимостей в Python
Пример: установка pip_search / удаление одной зависимости
(если успеем) Другие практические примеры.
Д/З
Изучить правила игры Быки и коровы (похожая игра — Wordle
Написать модуль bullscows.py, предоставляющий две функции:
bullscows(guess: str, secret: str) -> (int, int) — возвращает количество «быков» и «коров» из guess в secret
«быки» — это одинаковые буквы, которые в словах стоят в одинаковых местах, «коровы» — сколько букв догадки использовано в загадке
Например, bullscows("ропот", "полип") -> (1, 2) («о» — бык, «о» и «п» — коровы)
это условие не совпадает с Википедией, по правилам которой было бы (1, 1)
gameplay(ask: callable, inform: callable, words: list[str]) -> int — функция-приложение, обеспечивающая геймплей:
Задумывает случайное слово из списка слов words: list[str]
Спрашивает у пользователя слово с помощью функции ask("Введите слово: ", words)
Выводит пользователю результат с помощью функции inform("Быки: {}, Коровы: {}", b, c)
- Если слово не отгадано, переходит к п. 1
Если слово отгадано, возвращает количество попыток — вызовов ask()
Свойства функции ask():
ask(prompt: str, valid: list[str] = None) -> str
Если необязательный параметр valid не пуст, допустим только ввод слова из valid, иначе спрашивает повторно
Функция inform():
inform(format_string: str, bulls: int, cows: int) -> None
Вызов python -m bullscows словарь длина должен запускать референс-реализацию игры:
словарь — это имя файла или URL (в этом случае необходимо его скачать, например, с помощью urllib)
- Варианты словарей:
длина — необязательный параметр, указывающий длину используемых слов (по умолчанию 5)
Реализация ask() — обычный input() (если нужно, в цикле с проверкой валидности)
Реализация inform() — обычный print()
- В конце выводится количество попыток
- Замечание: игра не совпадает с Wordle, потому что неизвестны позиции быков и коров, только количество
Создать отдельную ветку cowsay, в которой функцию inform() и подсказку в функции ask() делают случайные коровы из модуля python-cowsay (именно этого)
В следующем коммите этой ветки нарисовать свою «корову» (небольшую) и использовать её для вывода подсказки ask().
Создать в каталоге с решениями подкаталог 03_MergeRequirements (совпадающий с URL этой страницы) и положить каталог с решением туда
- Соблюдать дисциплину оформления коммитов (одно изменение — один коммит, описание в commit message и т. п.)