Основы VCS на примере Git

https://git-scm.com/ – тут крутые entry видео, документация и прочее.

VCS
Version Control System (VCS) – система контроля версий.
Из названия следует основной кейс применения таких систем – контроль версий систем. VCS сохраняет изменения, которые произошли от одной версии файла к другой. В качестве систем могут быть файлы с кодом программ, скриптов или конфигурационные файлы (например, файлы конфигурации DHCP, файлы зон DNS, настроек iptables или apache).
VCS позволяют не только сохранять историю кода, но и конфигов, template да и в целом любых текстовых файлов. Это нельзя забывать никогда - пригодится по жизни во многих местах.

Помимо возможности просмотра предыдущих версий файлов (не только текстовых) VCS позволяют:

  • track changes (what, when, who, why)
  • rollbacks
  • pull requests – очень крутая штука

Если более детально:

  • посмотреть когда файл был изменен
  • посмотреть кто внес изменения в какой файл
  • посмотреть что поменялось
  • посмотреть комменты к изменениям (commit)
  • можно вносить изменения в несколько файлов как одно изменение (commit)
  • позволяет легко откатить изменения (rollback)
  • позволяет одновременную работу нескольких кодеров над одним проектом
Исходя из плюсов можно определенно сказать, что VCS имеет смысл использовать даже если ты единственный программист.

Git
  • Для удобства работы в git с подсветкой статусов в командной строке можно использовать скрипт. Процесс установки прост – копируем репозиторий в домашнюю папку, в bashrc добавляем строку со ссылкой на скрипт из репозитория и перезапускаем bash для принятия изменений. Процесс расписан тут:
https://pyneng.github.io/docs/git-basics/

cd ~

git clone https://github.com/magicmonty/bash-git-prompt.git .bash-git-prompt --depth=1
GIT_PROMPT_ONLY_IN_REPO=1
source ~/.bash-git-prompt/gitprompt.sh
exec bash
Самый популярный VCS – Git. Так же есть mercurial, subversion, но они хуже по многим аспектам – бесплатность, коворкинг, скорость, открытость, децентрализация, офф-лайн режим, порог входа (особенно из-за крутого графического интерфейса, легкого для установки/настройки сервера на любую платформу, понятной истории), отслеживания содержимого (а не просто файлов), указание объектов для коммита.

Git является бесплатным open source VCS. Может быть установлен на Unix-based, Windows, OSX. Git был создан Linus Torvalds, создателем Linux, изначально как VCS для кода ядра Linux. На сайте https://git.kernel.org/ можно посмотреть как он используется при программировании ядра. А тут можно посмотреть коммиты самого Torvalds.
У Git, в отличии от многих других VCS, децентрализованная архитектура, т.е. он очень гибок:
  • нет необходимости работы клиента с каким то единым сервером
  • копия кода репозитория (клон), над которой производится работа, всегда находится локально на машине
  • можно установить Git на одном ПК, даже без интернет подключения этого ПК
  • ничто не мешает установить Git на сервере, а остальными цепляться к этому серверу как клиентами для получения актуальных данных репозитория, отправки туда изменений
  • Взаимодействие между Git клиентами и серверами может быть на основе HTTP, SSH или специального протокола Git.


Файлы в Git могут находиться в стадиях:

  • неотслеживаемые (untracked) – не добавленные в репозиторий (git add) или удаленные файлы, по умолчанию все новые файлы в этом состоянии
  • немодифицированные (unmodified) – файлы после commit
  • модификация (modified) – это новых файл или сделаны изменения в старом, не поставлен на коммит и не закоммичен (не применен commit)
    подготовленные (staged) – файлы, поставленные на commit (добавленные в следующий commit)
  • подтвержденные (commited) – срез (snapshot) файла сохранен в базе

Каждый Git проект имеет три секции:

  • Рабочее дерево (working tree) – тут проводяться модификации над файлами, тут обычно находяться последние версии файлов (modified files)
  • Область подготовки (staging area/index) – тут сохраняются подготовленные файлы (staged files)

Директория Git (Git directory) – по сути база твоего проекта. Тут сохраняются все snapshot проекта. Git в результате commit сохраняет в Git директории не просто разницу между предыдущим состоянием и текущим (diff), а полностью файлы. (commited files)

Commit представляет собой по сути snapshot файлов, которые были поставлены на commit. В комментариях к commit нужно указывать нужную информацию о причинах commit, например номера тикетов или ссылок на документы, rfc, переписки и прочее. Если коммит длинный – то есть не нужно указывать непосредственно в cli, а просто ввести commit. Тогда откроется текстовый редактор, в нем первой строкой краткое описание, а далее подробный текст изменения. Есть даже сайт посвященный стилю commit https://commit.style/.

When you commit a snapshot to your repository, Git is doing which of the following?
Recording the entire contents of every file that you've changed and is part of the commit

Commit идентифицируется ID в виде HASH, расчитанного по алгоритму SHA1. Hash сделан чтобы можно было сравнивать что данные которые ты внес соответствуют данным, которые сохранены т.к. любое изменение данных приводит к изменению hash этих данных. Git ругнеться если hash от данных не будет соответсвовать hash commit. Изменение может быть безобидным – сетевая проблема, проблема с диском, но и номеренным внедрением. При использовании в командах можно указывать только первые символы hash, а не весь hash, git найдет нужный commit по вхождению в hash (обычно достаточно всего первых 4 символов).

HEAD – индикатор текущего commit для текущего branch в репозитории (по сути за ним скрывается ID текущего commit), что-то типо закладки для отслеживания местоположения (используется в командах git reset, diff, branch, status).

Github

Github – бесплатный web-based сервер репозиториев на основе git. Предоставляет доп. плюшки в виде wiki, bug tracking, task management. Бесплатен только если проект (репозиторий) публичен, если нужен приватный репозиторий – заплати денег, хотя и не много. GitHub является крупнейшим веб-сервисом для хостинга IT-проектов и их совместной разработки. Основан на системе контроля версий Git и разработан на Ruby on Rails и Erlang. Проекты можно выгружать через git clone или даже в  zip. Есть аналоги – BitBucket, Gitlab.






Команды
Базовая работа и команды

Узнать версию

git --version

Указание username и почты (чтобы размечать тебя в commit в публичных репозиториях)

git config --global user.name "Petr Redkin"
git config --global user.email "petr@redkin.net"
git config --global core.editor vim

Смотрим конфиг

git config -l

Инициализация нового проекта (создает автоматически директорию, эмулируя mkdir “myproject”, папку не обязательно указывать – тогда это будет .git в текущей)

git init |myproject|

Переходим в директорию с проектом (тут будут файлы самого проекта и файлы, которые хранят изменения в файлах проекта)

cd myproject

Добавляем элемент или всю папку в hold на commit (переводим модифицированный файл в commit, добавляем новый файл в tracked и переводим его в commit – проставляем на файлы staged статус)

git add |-p| index.html
git add *
git add .

Убираем элемент или всю папку из staging area

git reset HEAD|commit_hash_id sw

Можно удалять и перемешать файлы в репозитории – говорим Git не отслеживать файл, удаляем/перемещаем его из Git directory. Файл уходит в staged, поэтому нужно сделать commit для завершения удаления/перемещения.

git rm sw
git mv sw new_dir/
GITIGNORE

Через файл .gitignore мы говорим Git не отслеживать определенные файлы. Полезно если коммит делается через *. Есть рекомендации что включать в этот файл.

vi .gitignore
*.exe # ignore compiled files
*.rar # ignore archive files

Полезно часто просто игнорировать все файлы кроме python-скриптов (или исполняемых файлов другого языка, который нужен).

https://stackoverflow.com/questions/987142/make-gitignore-ignore-everything-except-a-few-files

# Ignore everything
*
# But not these files...
!.gitignore
!*.py
COMMIT

Делаем commit – создаем snapshot с данными.
-m – добавляем коммент прямо из консоли. По style так писать нежелательно, но в целом так делают даже в кратком review на сайте Git 🙂 Как по мне если изменение минорное – очень полезная вещь.
-a – автоматически добавляет все файлы, которые отслеживаются (находяться в репозитории Git) на commit (нет необходимости делать git add, если только это не новый файл)

git commit |-a| |-m "Imporing all the code"|

Перезаписть последний commit новым (удаление старого commit, все что в текущий момент в staging area будет закоммичено). Можно использовать не только когда сменились данные, но и когда нужно только поправить комментарий. Время commit будет равно времени изначального commit, а не времени amend, все остальные данные будут перезаписаны (коммент, id). Крайне нежелательно использовать для публичных commit, вместо этого нужно использовать revert.

git commit --amend
Восстановить или откатить

Чтобы восстановить конкретный файл из последнего commit, новый файл еще не в staging area (после изменения или удаления ДО применения add/rm на новую версию файла)

git checkout -- deleted_file.rb

Чтобы восстановить конкретный файл из последнего commit, новый файл уже в staging area (после изменения или удаления И применения add/rm на новую версию файла)

git reset HEAD|commit_hash_id deleted_file.rb
git checkout -- deleted_file.rb

Откат текущего состояния на определенный commit делается через checkout.

git checkout `commit-id`

Откат изменений, внесенных определенным commit делается через revert. Revert делает новый коммит, который отменяет все изменения, сделанные в указанном коммите (HEAD – в последнем коммите или commit_hash_id, можно указать не полностью) – т.е. если строка была добавлена, то в результате revert эта строка будет удалена. Таким образом можно посмотреть что было не так в “плохом” commit после отката.

git revert HEAD|commit_hash_id
Сравнение и Логи

Статус файлов, в каком состоянии они находятся

git status

Логи

git log

Опции:
-p – показать что поменялось в файлах между текущим и предыдущим commit,
-2 – показать только два последних commit,

git log -2

–decorate – добавление информации о branch, в какой branch сейчас смотрит HEAD или проще говоря для какого branch в логах показаны commit
–stat – просмотр какие файлы поменялись
origin/master – можно указать удаленный repo и branch, для которого будут показаны логи

git log |--graph --decorate --abbrev-commit --all --pretty=oneline|
Сравниваем текущие данные (в staging area) с данными определенного commit с помощью git diff.
git diff dc3009ec74733
Сравниваем данные разных commit между собой.
git diff e15f6fe92b0c dc3009ec7473
BRANCHING AND MERGING

Branch – указатель на определенный commit. Каждый branch указывает на отдельную линию разработки в проекте. Master – branch по умолчанию, создаваемый при инициализации Git. Принято его считать и использовать как основную, production или known-good, ветку проекта. При изменениях проекта, например добавления новой фичи делается еще один branch, на основе которого происходит работа, без необходимости изменения production ветки. Branch’и позволяют очень легко экспериментировать с новыми идеями в проекте и используются повседневно. Основной профит branch состоит в том, что они не хранят и не копируют данные, они просто указывают на определенный commit.

Показать все branch в репозитории, звездой помечается в каком ты сейчас находишься

git branch

Создаем branch на основе текущего branch (не обязательно master)

git branch branch_name

Для переключения между branch’ами нужно использовать checkout, в результате команды 1) working tree изменяеться с предыдущего branch на последний новый, т.е. меняются файлы в соответствии с branch 2) HEAD указывает на последний commit в новом branch, а не в старом 3) меняется commit history т.к. она ведеться для branch

git checkout branch_name

Можно создать branch и сразу переключиться в него через checkout

git checkout -b branch_name

Удаление branch (ругнеться если есть неза’merge’нные в master изменения в удаляемом branch)

git branch -d branch_name

Соединяем данные и history log branch с текущим branch. Git использует два алгоритма для merge:

  • fast-forward – когда branch, из которого взяты обновления, имеет все commit текущего branch (история commit между branch не отличается, один просто имеет свежие данные). В таком случае git просто переносит указатель HEAD на основе merging branch (накатывает все commit из нового branch в текущий) и по сути нет никакого merging.
  • three-way-merge – когда branch, из которого взяты обновления, не имеет все commit текущего branch. т.е. в текущем branch были изменения после того как сделана копия данных (если говорить о master branch). В таком случае Git найдет самый последний commit, который совпадает между branch и будет делать merge различающихся commit’ов branch’ей после этого шага. В случае конфликтов (изменение одного региона одного файла), это называется merge conflict, Git об этом скажет. В таком случае можно использовать git status для просмотра конфликтных файлов и далее открыть сами конфликтные файлы, чтобы увидеть какие данные являются конфликтными. После разрешения конфликта (изменения необходимых строк в файле) нужно использовать git add на этот файл и сделать commit – все, конфликт разрешен.
git merge branch_name

Сбросить процедуру merge, если конфликты слишком сложные, чтобы их быстро разрешить. Данные в working tree будут восстановлены на основе текущего branch, не включая конфликты. При использовании –hard все незакомиченные изменения в текущем branch будут сброшены!

git reset --hard




КОЛЛАБОРАЦИЯ

Основные команды:

  • git push – отправить на сервер изменения в локальном репозитории после commit. Git push делается после локального коммита. Настроенные remote (настраиваются автоматически при использовании git clone) позволяют это делать одной командой.
  • git merge – соединение твоей копии репозитория с основной на сервере.

Админ репозитория

git checkout master
git commit -a -m "my new logo"
git push

Обычный пользователь, ответственный за какую то ветку (branch)

git checkout -b dana
git commit -a -m "my new code"
git push origin dana

Админ после этого собирает к себе на комп (git pull) и объединяет изменения последних коммитов в репозитории (git merge), с вопросами какие изменения сохранить в случае наличия конфликтов и прочим

git pull
git merge dana



УДАЛЕННЫЕ РЕПОЗИТОРИИ (github)

  • На github удобно diff смотреть – есть режим просмотра unified (последовательно origin – версия до изменения и change – версия после изменения) и split (на одной стороне экрана origin, на второй change – как по мне, удобнее классической) .
Крайне удобно комментировать код – можно выбрать конкретные строки, отписать коммент и уведомить автора что что-то не так или наоборот так.


git clone
– копируем удаленный репозиторий для локального использования (используется вместо git init, остальные команды все аналогичные, до тех пор пока не понадобится обновить удаленный репозиторий – там нужно добавить в обычной последовательности действий push). Репозиторий будет скопирован и будет иметь имя по умолчанию origin. Git clone возможен по ssh или https. В первом случае можно аутентифицироваться по ключу (и паролю), во втором по логину и паролю.

git clone https://github.com/1111/some_repo.git
git clone https://github.com/pmcgleenon/wireplay.git

Вместо создания клона можно создать связь с удаленным репозиторием. Можно использовать HTTP (обычно только RO доступ к repo), HTTPS, SSH, Git. Связь автоматически создается при использовании git clone.

git remote add origin https://github.com/paulboone/ticgit

Просмотреть все связи с удаленными репозиториями (-v показывает URL и какой будет использовать для fetch from repo и push to repo, обычно один и тот же)

git remote |-v|

Переименовка

git remote rename origin new-origin

Удаление

git remote rm origin

git pull – получаем изменения из repo. Обновление данных локального репозитория на основе удаленного. Git автоматически это не делает. Например, когда на работу пришел и нужно получить изменения, запушенные в репозиторий из дома. Команда которая делает одновременно fetch из remote repo (url shortcut) и merge на основе удаленного repo. По умолчанию branch определяется на основе текущего branch, но можно поменять. Перед git pull можно посмотреть что изменилось в repo с помощью команды git diff HEAD..origin

git pull origin |master|

git push – Обновляем удаленный репозиторий изменениями (всеми snapshot после всех commit), которые мы сделали. Нужно указать и repo (url shortcut) и branch. Git сервер скорей всего спросит username/password перед апдейтом. При использовании -all будут переданы все branch текущего репозитория. В случае конфликтов Git скажет что нужно сначала получить актуальный репозиторий через git pull, сделать merge локально (автоматически вызывается при использовании pull), а потом загружать.

git push origin master
get push --all origin
git checkout <branch_name> – для перехода в другую ветку git. Ветка по умолчанию (основная) называется обычно master. Git checkout master – переход в master.

git fetch – получаем данные из удаленного репозитория в локальный как remote branch. Для получения конкретной ветки можно указать эту ветку. git branch -r смотрим такие ветки в нашем репозитории. Такие branch можно только просмотривать, не изменять.

git fetch origin |master|
git branch -r

git merge – обновляем данные нашего репозитория на основе данных удаленного.

git merge origin/master

Fork для изменения без прав + pull request для запроса на редактирование основной ветки

Leave a Reply