Основы 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 changeset)
  • позволяет легко откатить изменения (rollback)
  • позволяет одновременную работу нескольких кодеров над одним проектом
Исходя из плюсов можно определенно сказать, что VCS имеет смысл использовать даже если ты единственный программист.

общее описания алгоритма изменения кода в существующем проекте
  1. Клонируем реплзиторий (если его еще нет)
  2. Создаем ветку (branch) на основе которой собираемся вносить изменения (чаще всего dev, но может быть и main) (pull)
  3. Вносим изменения в необходимые файлы/добавляем новые/удаляем старые, коммитимся
  4. push,им код и делаем pull request в original ветку, проходим code review maintainer
  5. При прохождении pull request планируется merge кода в целевую ветку repo
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.
Помимо git есть Mercurial, Subversion, Team Foundation Server  (TFS) но они хуже по многим аспектам – бесплатность, коворкинг, скорость, открытость, децентрализация, офф-лайн режим, порог входа (особенно из-за крутого графического интерфейса, легкого для установки/настройки сервера на любую платформу, понятной истории), отслеживания содержимого (а не просто файлов), указание объектов для коммита.

Git является бесплатным open source VCS. Может быть установлен на Unix-based, Windows, OSX. Git был создан Linus Torvalds, создателем Linux, изначально как VCS для кода ядра Linux. На сайте https://git.kernel.org/ можно посмотреть как он используется при программировании ядра. А тут можно посмотреть коммиты самого Torvalds.
У Git, в отличии от многих других VCS, децентрализованная архитектура, т.е. он очень гибок:
  • нет необходимости работы клиента с каким то единым сервером
  • копия кода репозитория (клон), над которой производится работа, всегда находится локально на машине
  • можно установить Git на одном ПК, даже без интернет подключения этого ПК
  • ничто не мешает установить Git на сервере, а остальными цепляться к этому серверу как клиентами для получения актуальных данных репозитория, отправки туда изменений (обычно так и происходит)
  • в git config можно указать даже pgp/gpg ключ для подписи коммитов
  • взаимодействие между 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, переписки и прочее. Если коммит длинный – то комментарий к commit не нужно указывать непосредственно в 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. Так же можно указать tag коммита и использовать его в последующем по аналогии с hash. Hash сделан чтобы можно было сравнивать что данные которые ты внес соответствуют данным, которые сохранены т.к. любое изменение данных приводит к изменению hash этих данных. Git ругнеться если hash от данных не будет соответсвовать hash commit. Изменение может быть безобидным – сетевая проблема, проблема с диском, но и намеренным внедрением (mitm). При использовании в командах можно указывать только первые символы hash, а не весь hash, git найдет нужный commit по вхождению в hash (обычно достаточно всего первых 4 символов).

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

gitflow

По сути это методология работы с git ветками.

  • Методика работы с Git в которой используются функциональные ветки и несколько основных
  • Именованные commit – tag, branch. Т.е. названный как то commit. Отличием tag от branch является то, что в случае branch происходит сдвиг commit таким образов, чтобы branch был впереди.

 

Шаг 1

Main / Master – основная ветка, которая выкладывается на prod, обычно тегируется (напр. V1.8.23)

Develop – рабочая ветка, куда попадают все принятые изменения

Нельзя делать commit в эти ветки напрямую!

 

Шаг 2

Ветки feature / bug создаются для реализации задачи, для commit в develop используются pull reguest

 

Шаг 3

создается ветка release для поддержки релиза, она содержит в себе фичи develop ветки, которые должны попасть в релиз и не тормозит дальнейшее развитие ветки develop.

  • Именно на ней проводятся тесты релиза, если находятся баги – они исправляются.
  • Именно она мерджится с мастером/мейн веткой, если были найдены баги – они переливаются в develop

Шаг 4

исправление критичных проблем/hotfix для текущего релиза – они ответвляются от мастера и вливаются в него же, но так же и в develop, во избежание повторения бага в следующем релизе

Cherry Pick

Это переносы отдельных коммитов между ветками.

 

Github

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

GIT LFS

Github начал использовать LFS (Git Large File Storage) для хранения в репозиториях больших файлов – по сути в репозитории теперь хранится ссылка/pointer на файл, а не сам файл.

An open source Git extension for versioning large files
Git Large File Storage (LFS) replaces large files such as audio samples, videos, datasets, and graphics with text pointers inside Git, while storing the file contents on a remote server like GitHub.com or GitHub Enterprise.

Загрузка из LFS хранилища всех LFS файлов для текущего репозитория. В данном случае не сработала из-за жадности github 😀

git clone <repo-url>
sudo apt-get install git-lfs
# Download Git LFS objects at the given refs from the specified remote. 
git lfs fetch
fetch: Fetching reference refs/heads/main
batch response: This repository is over its data quota. Account responsible for LFS bandwidth should purchase more data packs to restore access.
error: failed to fetch some objects from 'https://github.com/julienatry/VMware-ESXi-6.7.0-RTL8111.git/info/lfs'

 






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

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

git --version

Указание username и почты (чтобы размечать тебя в commit в публичных репозиториях). В git config можно указать даже pgp/gpg ключ для подписи коммитов.

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 (ругнеться если есть неза’commit’енные в 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)

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


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

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

#LATEST-VER git clone https://github.com/cisco-system-traffic-generator/trex-core.git
#SPECIFIC-VER git clone https://github.com/cisco-system-traffic-generator/trex-core.git -b v2.89

Вместо создания клона можно создать связь с удаленным репозиторием. Можно использовать 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