[TOC] ---------------------------------------------------------------------------------------------------- # Система контроля версий Git ## Назначение систем контроля версий Предположим, у нас есть файл `file_1.txt` в котором присутствует слово "собака". Назовем это первой версией файла. Предположим, что мы заменили слово "собака" на слово "кошка" и таким образом получили вторую версию файла. После сохранения первая версия окажется недоступной. Если мы в рамках некоторого проекта (расположенного в определенном каталоге) поменяем содержимое нескольких файлов, добавим или удалим какие-то файлы, то вся совокупность этих изменений окажется недоступной для нас, мы будем видеть последнюю версию проекта. Может сложиться такая ситуация: в какой-то момент времени проект нас устраивает, однако мы добавляем в проект новые полезные качества, и проект в целом нас устраивает. Однако оказывается, что какие-то предыдущие функции проекта стали работать не так как должны. В данном случае нам было бы полезно просмотреть изменения проекта с момента добавления новых качеств, однако, если мы просто изменяли файлы и каталоги, мы будем видеть только последнюю версию и не будем видеть *истории* изменений. **Системы контроля версий** (VCS --- Version Control System) позволяют фиксировать состояния проекта в течении времени и просматривать получившуюся **историю** с возможностью полной или частичной отмены изменений. Основные команды по Git смотри [здесь](https://git-scm.com/book/ru/v2/%D0%9F%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5-C%3A-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D1%8B-Git-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D0%B5-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D1%8B) ## Работа с локальным репозиторием Основные команды Git. Создание нового *репозитория*: ````shell >git init Initialized empty Git repository in E:/dfly/dfly_i/study/python/.git/ ```` Репозиторий создан, но история пока не определена. В корневом каталоге проекта появился каталог `.git`. Посмотрим состояние репозитория: ````shell >git statu On branch master No commits yet Untracked files: (use "git add ..." to include in what will be committed) . . . code/ doc/ doc_src/ generate_doc.bat generate_doc_force.bat md2html_args.json nothing added to commit but untracked files present (use "git add" to track) ```` В репозитории имеются неотслеживаемые (untracked) файлы, историю которых Git не ведет. Добавим эти файлы в *индекс*, т.е. "попросим" Git отслеживать историю этих файлов. Однако сначала уберем из рассмотрения файлы, которые нам не надо отслеживать. Создадим в корневом каталоге проекта файл с именем `.gitignore` с содержимым: ```` __pycache__ ```` Содержимое каталога `__pycache__` генерируется интерпретатором Python. Это содержимое необходимо для выполнения учебных примеров, однако оно будет каждый раз перегенирироваться по необходимости. теперь добавим файл в индекс: ````shell >git add . ```` Посмотрим состояние репозитория снова: ````shell >git status On branch master No commits yet Changes to be committed: (use "git rm --cached ..." to unstage) . . . new file: code/hanoi.py new file: code/hello.py new file: code/my_lib.py . . . ```` Теперь мы видим список файлов подготовленных для фиксации состояния. Данное действие практически всегда называется "коммитом" (*англ.* commit). Итак, выполним коммит, но сначала пропишем необходимые для коммита идентификаторы пользователя (фиктивные): ````shell >git config --global user.email a@b.c >git config --global user.name a ```` Теперь сам коммит: ````shell >git commit -m init [master (root-commit) 72babed] init 130 files changed, 13098 insertions(+) . . . create mode 100644 code/hanoi.py create mode 100644 code/hello.py create mode 100644 code/my_lib.py . . . ```` Состояние проекта зафиксировано. При последующем изменении Git покажет нам, что изменилось с момента последнего коммита: ````shell >git status On branch master Changes not staged for commit: (use "git add ..." to update what will be committed) (use "git restore ..." to discard changes in working directory) modified: doc_src/related_technologies.txt no changes added to commit (use "git add" and/or "git commit -a") ```` Мы видим, что с момента последнего коммита изменился только один файл `doc_src/related_technologies.txt`. ## Структура репозитория Git Упрощенно репозиторий можно представить состоящим из двух частей: - **рабочего каталога** --- место, где находятся пользовательские файлы; - **индекса** --- специфической области, где Git хранит информацию об истории изменений, а также о самом репозитории. Данная область представлена скрытым каталогом с именем `.git`. ## Ветки *Коммит* является записью историй изменения и содержит: - уникальный идентификатор (хеш, hash); - дату, время и информацию о пользователе; - комментарий; - собственно изменения файлов *рабочего каталога*; - ссылку на один или несколько родительских коммитов. Ссылки на родительские коммиты формируют цепочку коммитов. Последний коммит в цепочке формирует **ветку (branch)**, которая является всего лишь меткой (или указателем) на коммит. При создании нового коммита ветка автоматически перемещается на этот коммит. Чтобы посмотреть в какой ветке ты находишься, надо выполнить команду `git branch`: ````shell >git branch favicon file_structure * main ```` Здесь видно, что в репозитории есть три ветки. Звездочкой помечена текущая ветка --- `main`. Текущую ветку можно посмотреть командой: ````shell >git branch --show-current main ```` ## Работа с удаленным репозиторием Системы контроля версий предназначены для командной работы над проектами, т.е. для ситуаций, когда несколько разработчиков совместно работают над одной кодовой базой. Git является **распределенной** системой контроля версий, т.е. в ней концептуально отсутствует центральный репозиторий. Все разработчики получают полную копию репозитория, модифицируют ее и в последствии синхронизируют с другими копиями. Некоторая копия может быть выбрана в качестве центральной что существенно повышает удобство использования. Примером центрального репозитория может быть репозиторий, открытый на хостинге GitHub. Получение кодовой базы из удаленного репозитория называется **клонированием**: ````shell $ git clone https://github.com/alovemycat/python_study.git Cloning into 'python_study'... ... Resolving deltas: 100% (390/390), done. ```` Здесь мы используем эмулятор командной строки Linux git-bash, который идет в комплекте с Git для Windows. В результате репозиторий с хостинга GitHub копируется в каталог `python_study` на локальном компьютере: ````shell $ ls python_study/ test/ ```` Про команду Linux `ls` см. [здесь](#ls_linux_command). Внесем изменения (подробности были рассмотрены выше): ````shell $ cd python_study $ git status On branch main Your branch is up to date with 'origin/main'. nothing to commit, working tree clean $ echo Hello! > some_file.txt $ ls LICENSE code/ doc_src/ generate_doc_force.bat pictures.drawio some_file.txt README.md doc/ generate_doc.bat md2html_args.json pictures.svg $ cat some_file.txt Hello! $ git status On branch main Your branch is up to date with 'origin/main'. Untracked files: (use "git add ..." to include in what will be committed) some_file.txt nothing added to commit but untracked files present (use "git add" to track) $ git add . $ git commit -m "test commit" [main 25c6093] test commit 1 file changed, 1 insertion(+) create mode 100644 some_file.txt $ git push Enumerating objects: 4, done. ... ```` Команда `push` отправляет в удаленный репозиторий те изменения локального репозитория, которые еще не были отправлены. Добавленный файл `some_file.txt` отправлен в центральный репозиторий. Чтобы получить эти изменения другому разработчику, у которого репозиторий был склонирован ранее, выполняется команда `pull`: ````shell >git pull remote: Enumerating objects: 4, done. ... Fast-forward some_file.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 some_file.txt ````