cucumberian / git-tutorial

Git tutorial

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Git

Git cheat Sheet

https://training.github.com/downloads/ru/github-git-cheat-sheet/

Первоначальная настройка

Настройка информации о пользователе для всех локальных репозиториев

git config --global user.name "[имя]"

Устанавливает имя, которое будет отображаться в поле автора у выполняемых вами коммитов

git config --global user.email "[адрес электронной почты]"

Устанавливает адрес электронной почты, который будет отображаться в информации о выполняемых вами коммитах

Создание репозитория

Создание нового репозитория или получение его по существующему URL-адресу

git init [название проекта]

Создаёт новый локальный репозиторий с заданным именем

$ git clone [url-адрес]

Скачивает репозиторий вместе со всей его историей изменений

Операции с файлами

Перемещение и удаление версий файлов репозитория

git rm [файл]

Удаляет конкретный файл из рабочей директории и индексирует его удаление

git rm --cached [файл]

Убирает конкретный файл из контроля версий, но физически оставляет его на своём месте

git mv [оригинальный файл] [новое имя]

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

Игнорирование некоторых файлов

Исключение временных и вторичных файлов и директорий. Для исключения файлов из системы гит (чтобы она не проверяла в них на предмет изменений) надо добавить следующие строчки в файл .gitignore (в корневом каталоге).

_.log
build/
temp-_

Git будет игнорировать файлы и директории, перечисленные в файле .gitignore с помощью wildcard синтаксиса

git ls-files --others --ignored --exclude-standard

Список всех игнорируемых файлов в текущем проекте

Сохранение фрагментов git stash

Сохранение и восстановление незавершённых изменений

git stash

Временно сохраняет все незафиксированные изменения отслеживаемых файлов

git stash pop

Восстанавливает состояние ранее сохранённых версий файлов

git stash list

Выводит список всех временных сохранений

git stash drop

Сбрасывает последние временно сохранённыe изменения

Внесение изменений

Просмотр изменений и создание коммитов (фиксация изменений)

git status

Перечисляет все новые или изменённые файлы, которые нуждаются в фиксации

git diff

Показывает различия по внесённым изменениям в ещё не проиндексированных файлах

git add [файл]

Индексирует указанный файл для последующего коммита

git diff --staged

Показывает различия между проиндексированной и последней зафиксированной версиями файлов

git reset [файл]

Отменяет индексацию указанного файла, при этом сохраняет его содержимое

git commit -m "[сообщение с описанием]"

Фиксирует проиндексированные изменения и сохраняет их в историю версий

Коллективная работа

Именованные серии коммитов и соединение результатов работы

git branch

Список именованных веток коммитов с указанием выбранной ветки

git branch [имя ветки]

Создаёт новую ветку bash git switch -c [имя ветки]

Переключается на выбранную ветку и обновляет рабочую директорию до её состояния
```bash
git merge [имя ветки]

Вносит изменения указанной ветки в текущую ветку

git branch -d [имя ветки]

Удаляет выбранную ветку

Просмотр истории

Просмотр и изучение истории изменений файлов проекта

git log

История коммитов для текущей ветки

git log --follow [файл]

История изменений конкретного файла, включая его переименование

git diff [первая ветка]...[вторая ветка]

Показывает разницу между содержанием коммитов двух веток

git show [коммит]

Выводит информацию и показывает изменения в выбранном коммите

Откат коммитов

Удаление ошибок и корректировка созданной истории

$ git reset [коммит]

Отменяет все коммиты после заданного, оставляя все изменения в рабочей директории

$ git reset --hard [коммит]

Сбрасывает всю историю вместе с состоянием рабочей директории до указанного коммита.

Синхронизация с удалённым репозиторием

Регистрация удалённого репозитория и обмен изменениями

git fetch [удалённый репозиторий]

Скачивает всю историю из удалённого репозитория

git merge [удалённый репозиторий]/[ветка]

Вносит изменения из ветки удалённого репозитория в текущую ветку локального репозитория

git push [удалённый репозиторий] [ветка]

Загружает все изменения локальной ветки в удалённый репозиторий

git pull

Загружает историю из удалённого репозитория и объединяет её с локальной. pull = fetch + merge

Требования к именам коммитов

https://github.com/rolling-scopes-school/docs/blob/master/docs/git-convention.md

  • Названия коммитов должны быть согласно гайдлайну
  • Тип коммита должен быть только в нижнием регистре (feat, fix, refactor, docs и т.д.)
  • Должен использоваться present tense ("add feature" not "added feature")
  • Должен использоваться imperative mood ("move cursor to..." not "moves cursor to...")

Примеры имен коммитов

  • init: - используется для начала проекта/таска. Примеры:
init: start youtube-task
init: start mentor-dashboard task
  • feat: - это реализованная новая функциональность из технического задания (добавил поддержку зумирования, добавил footer, добавил карточку продукта). Примеры:
feat: add basic page layout
feat: implement search box
feat: implement request to youtube API
feat: implement swipe for horizontal list
feat: add additional navigation button
feat: add banner
feat: add social links
feat: add physical security section
feat: add real social icons
  • fix: - исправил ошибку в ранее реализованной функциональности. Примеры:
fix: implement correct loading data from youtube
fix: change layout for video items to fix bugs
fix: relayout header for firefox
fix: adjust social links for mobile
  • refactor: - новой функциональности не добавлял / поведения не менял. Файлы в другие места положил, удалил, добавил. Изменил форматирование кода (white-space, formatting, missing semi-colons, etc). Улучшил алгоритм, без изменения функциональности. Примеры:
refactor: change structure of the project
refactor: rename vars for better readability
refactor: apply eslint
refactor: apply prettier
  • docs: - используется при работе с документацией/readme проекта. Примеры:
docs: update readme with additional information
docs: update description of run() method

FAQ

Можно ли ревертнуть отправленный (запушенный) в репозиторий коммит без снижения оценки?

Да, можно.

Git Теория

Структура папок

Структура папок

  • ./hooks/ - события на которые можно настроить события (например прогон линтеров при ошибках).
  • ./info/ - может содержать файл exclude, который является гиперлокальный амналогом .gitignore
  • ./objects/ - сохраняются все почти все объекты, которые нужны для сохранения соcтояния кода. Коммиты, блобы и т.д.
  • ./refs/ -
    • heads/ - ветки
    • tags/ - теги
    • remotes/ - ветки слежения, которые показывают в каком состоянии был удаленный репозиторий, когда за ним следили крайний раз
  • config - настройки
  • description
  • HEAD - указатель HEAD, который указаывает на место, с которого будет продолжена история

Сущности

Blob

Blob = big binary object - большой бинарный объект. В блобы гит превращает файлы ск оторыми мы ряботает.

Tree

Tree - дерево объектов. Содержит ссылки либо на деревья объектов, либо на блобы. Отвечает за состояние файлов.

Данная команда показывает тип обхекта по его хэшу

git cat-file -t <hash>

Данная команда вывоидит содержимое объекта по его хэшу

git cat-file -p <hash>

Таким образом можно просмотреть тип и содержание каждого коммита. В дерево объектов будут храниться или деревья или блобы, блобы хранить в сжатом ввиде файл.

Commit

Это снимок состояния файлов с неокторой метаинформацией. Каждый коммит, кроме первого указывает на родителя Обычно состоит из 7 элементов:

  • ссылка на родителя
  • ссылка на дерево объектов, которые входят в состояние коммита
  • собственный хэш (на основе дерева объектов)
  • автор и комиттер
  • метка времени
  • сообщение

ветка

Логически ветка - последовательность коммитов. Но коммит ничего не знает в какой он ветке. Ветка реализуется как ссылка на последний коммит. Ветки хранятся в папке ./refs/heads каждая в виде отдельного файла. В этом файле записпн коммит на который ссылкается каждая ветка (последний).

ссылки

Это удобные переменные, которые указывают на определенные коммиты. Гит использует хэши. Основной указатель HEAD указывает на то место, с которого будет продолжена история коммитов. HEAD в большинстве случаев и в нормальном состоянии указывает не на конкретный коммит, а на ветку, а веткка в свою очередь, есть указатель на последний коммит. HEAD показывает на то место, в котором мы сейчас находимся. При смене указателя HEAD на другой коммит (например смене ветки) указатель HEAD переместится на тот коммит и рабочий код каталоге с гитом ихменится. HEAD хранится в файле ./git/HEAD и в нем указана ссылка ref: на файл с хэшом каммита.

Тэги tags

Тэги - это статичные указатели на определенный коммит. Их можно использовать для личных нужд, чтобы помечать определенные версии программы, ключевые моменты разработки. Они нужны для удобства, чтобы не запоминать хэш отдельного коммита. Тэги могут быть:

  • легковесными - есть только название
  • аннотированными - можно писать дополнительную информацию как у коммита

репозиторий

Место хранения кода с историей всех изменений. Может быть:

  • локальный
  • удаленный - может быть как gitub, gitlb, друго сервис, репозиторий коллеги или соседней папкой.

Состояния

Файлы в гите могут находиться в двух глобальных состояниях: они могут быть отслеживаемые и могут быть неотслеживаемыми. Гит знает про существование неостжеиваемых файлов в рабочей директории, но изменения рабочий файлов никак не затрагивают внутреннего сотсояния гита. Отслеживаемые файлы могут получиться несколькими путями:

  • были неотлежвиваемыми и добавлены в индекс пользователем - стенут отслеживаемыми при коммите
  • были отслеживаемыми раньше и модифицированы

Отслеживаемые файлы огут находиться в трех состояниях:

  • неизмнененные
  • измененные
  • staged - подготовленные к внесению ихменений в след коммите
  • stash - заначка (при помещении заначку)

Index

Index в гите - это черновик будущего коммита. По умолчанию гит не сохраняет все изменения, которые делает пользователь.

Гит работает с тремя областями или деревьями:

  1. Рабочая директория - песочница с текущими файлами
  2. Индекс - черновик коммита
  3. HEAD - указатель на историю коммитов

git checkout -b feature_1 - создать ветку с именем feature_1 и установить на нее указатель HEAD git checkout -b feature_2 main - создать ветку от указателя main. Можно также передать хэш коммита, вместо указателя.

git remote add origin <remote_rep_address> - добавить удаленный репозиторий под именем origin git --set-upstream origin main - установить как ветку загрузки для удаленного репозитория main

git branch --all - просмотреть все ветки, включая и из удаленных репозиториев git push -u origin --all - как git push --set-upstream origin --all - установить все ветки как синхронихируемые с удаленным репозиторием и выгрузить их. В файл config добавятся аналогичные записи для этих настроек - связи с удаленными репозиториями и списки связей для локальных и ветвей и удаленного репозитория.

По умолчаниюю при клонировании репозитория, клонируетс одна ветка, а остальные остаются удаленном репозитории. Для просмотер списка веток можно восползовться командой git branch --all. Для создания локальной версии ветки с удаленного репозитория надо выполнить команду git checkout <имя удаленной ветки>.

git pull = git fetch + git merge* - глобальными настройками используется. Можно изменять флагами на rebase. git fetch - забирвет все изменения

Рекомендуется отдельноисползовать команду git fetch чтобы заняться решением конфликтов когда будет время. Сама структура веток предполагает объединение в будущем. Гит предлагает две стратегии объединения.

  1. Merge - слияние.
    • выбирает последний коммит в каждой сливаемой ветке и создает на их основе merge-commit, который будет содаржать объединенное состояние. Возможно надо будет решать конфликты. После рзрашения создасться коммит и на него передвинется указатель ветвки в которой мы нахоились. Почти всегда при merge создается один дополнительный коммит. Особый случай merge - fast forward. - если во влиаемой ветке на было изменений с момента форка их нее и все коммиты из ветки просто переносятся в другую ветку. Таким образом нового коммита не возникает. Сожно отменить и гит будет создавать фиктивный коммит Сохраняет историю коммитов. Идеально подходит для публичных репо, где важна соанность истории.
  2. Rebase ребеиз из двх веток.
    • количество коммитов отличается при rebase и при merge. В случае merge он такое же, что и было до marge. История коммитов исзменяется. Нету лшних коммитов слияния.

Конфилкты

  • чаще синзронизируйтесь с репозиторием с которым работаете - перенос конфликтов на более ранний уровень
  • не трогайте чужой код - лучше обсудите с коллегами, а потом решайте кто в какие ветки внесет в изменения
  • лучше делайте селкие коммиты - проще перемещаться между ниими и в случае rebase они будут требовать меньше внимания
  • разделяйте задачи так, чтобы они не качались обзего кода

git merge <имя ветки> - слить текущую ветку с целевой и передвинуть указатель на новый коммит слияния. git merge --abort - прервать процесс слияния

Конфликты при merge и при rebase чаще всего отличаются. Rebase - итеративная операция. Каждый коммит, который мы переносим при rebase, может создать потенциальные конфликты. В процессе ребейза может создаться несколько конфликтов на каждой итерации. Надо их разрешить (в файлах), добавить файл с разрешенным конфликтом в индекс комнадой git add и перейтина следующую итерацию комнадой git rebase --continue.

Stash

Бывают ситуации, когда нельзя коммитить, но надо переключиться на следующую заачу. Тогда на помощь приходит Stash. Stash - "заначка".

git stash

При выполнении этой команды

  • все изменения помещяются в состояние stash,
  • индекс приводится к сотсоянию последнего коммита (как при git reset)- очищается
  • все изменения из рабочей директории передйут в специальный коммит, на который указывает указатель stash,
  • рабочая дректория откатится к состоянию при последнем коммите

После выполнения внеплановой задачи можно при помози команды

git stash apply

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

Случайно закомиченные файлы

Лучшая драка - это та, которой удалось избежать.

.gitignore

Файлы, которые есть в игноре не отслеживаются гитом.

  • .gitigore - глобальный
  • .gitignore - в репозитории
  • ./.git/info/excude - на уровне текущего локального репозитория Если быть более точным, то кроме отслеживаемых и неотслеживаемых файлов есть еще и игнорируемый файлы. Игнорируемые файлы могут обаховаться если новый файл был создан и он попал под маску gitignore. Но уже закомичееные файлы не могут стать игнорируемыми! Поэтому любые добавления закомиченных файлов в гитигнор не распространаются на них. Надо удалить файл и
  1. Добавляем файлы в .gitignore
  2. Удаляем файл их кэша гита git rm --cache file
  3. Коммитим изменения без файла git add .gitingore, git commit -m "new commit" Таким образом файл уже не будет отслеживаемым и будет игнорироваться через gitignore.

Случайно закомичченые пароли и токены или другая секретаня (персональная) информация

  • поменять пароли токены
  • добавить в .gitignore
  • очистить историю (не поможет, но будет не так стыдно (не факт))
Если в последнем коммите

Если это было в последем коммите репозитория, внести изменения в рабочую директорию и выполнить командуgit commit --amend. После изменения коммита при помощи команды git commit --amend гит создаст нвый коммит, который будет видоизменным предыдущим коммитом, но уже без секретный файлов. И уже новый измененный коммит будет ссылатьс на ноду, куда ссылался неимененный коммит. Но надо помнить, что неправльный коммит не удалится из дерева коммитов гита, просто на него не будет ссылок. Поэтому надо задуматься над тем какие использовать инструмаенты для очистки дерева гита.

Если надо внести изменения раньше

Интерактивный ребейз

git rebase -i <хэш коммита с которого перебазируемся>

Затем можно вбрать какие комиты оставить, какие удалить, поменять мествми. Например удалить коммит с паролем. Тогда перепишется история и коммит с паролем пропадет из истории.

Но пароль все еще можно достать из git! Он сохранен в коммите, в который можно получить доступ если знать его хэш. Ну или просто найти его в объектах. Хэш можно узнать через git reflog.

Опасные операции

Есть несколько операция которые риводят к безвозвратным изменениям. Все остальные команды не удалют никакие объекты. И скорее всего эти именения можно вернуть. Если же изменения не были заккомичены, то их потерять легко

  • git pul -f - изменения применяются к нашей рабочей директории
  • git reset (git reset --hard) - откат изменений к какому-то конкретному коммиту
  • git checkout -f git switch -f- переключение на другую ветку в результате чего состояние рабочей директории приводится в стостояние той ветки.
  • git clean -f - очищает рабочую директорию от всех неотслеживаемых файлов

Чтобы ничего не потерялось

Выполняйте команду git status. Закомиттьте или засташьте.

Бывает что мы закоммитили, а потом передвинули указатель ветки и потеряли связь с прежним состоянием после очередного коммита. Коммит все ещё останется в гите, но мы не увидим его через команду git log.

Как найти потерянные коммиты

  • git reflog - логи гита
  • git fsch --full

Логи гита отдельно собираются для каждой ветки в папке ./git/logs/refs. Если удалить папку логов ./git/logs, то и команда git reflog ничего не выдаст. В таком случае можно использовать git fsck --full - она проверить весь репозиторий с учтетом того, чтобы на все объекты были ссылки и выдаст список коммитов, на которые нет ссылок.

Отмена изменений

Отменить коммит

Чтобы отменить коммит можно воспользоватьс командой

git revert <хэш коммита>

после которой гит создаст патч к текущему коммиту, отменяющий изменения того коммита, который был указан. Например мы можем отменить какую-то отдельную фичу из отдельного коммита, в котором была обнаружена ощибка.

Отмена состояния одного файла

  • откад до состояния в предыдущем коммите git checkout -- <file> git restore <file>
  • откад состояния файла до конкретного коммита git restore --source=<hash> <file>

Откат изменений всего репозитория

git reset

  • git reset --soft - отмена изменений только с помощью указатедя HEAD. Т.е. только передвинется указатель на указанный коммит. А в индексе и в рабочей директории файлы будут все ещё соотвествовать коммиту с которого мы откатывались.
  • git reset git reset --mixed - удаляет данные из индекса до того состояния и передвигвает указатель HEAD
  • git reset --hard - изменяетс все три области (рабочую директорию, индекс и HEAD)

GIT команды

git init

git Инициализирует git в текущей директории и создает директорию .git с настройками и индексами git'a.

git init

git add --all

Добавляем все измененные файлы в git.

Игнорирует содержимое файла .gitignore

git add --all

Добавляем файл к индексу.

git add <имя_файла>

.gitignore - специальный файл, содержимое которого будет игнорироваться системой git при проверки изменений. Например

./venv/
.env

Первая строка говорит системе git игнорировать директорию venv в корне проекта, а вторая строка - игнорировать скрытый файл .env.

Сказать дотнету добавить все файлы в гитигнор, которе не нужны(нужно для .NET, кто знает, те поймут, остальным не нужно=):

dotnet add gitignore

git commit

Фиксация изменений.

Открывается редактор по-умолчанию для написания комментария коммита:

git commit -a

Внесение изменений с указанием коментария и без открытия редактораq:

git commit -m "init: start project git-tutorial"

git diff

Показывает разницу между файлами

git diff [с чем сравнить]

Сравнить можно, например с каким-ть коммитом, унав его id из git log.

+ стоят у того что добавляется

- стоят у того что убирается в текущей версии по сравнению с камитом


git log

Позволяет вывести список всех комитов.

git log

Cписок камитов и отношения между вевями в виде графа:

git log --graph

Просмотреть все коммиты во всех ветках:

git log --oneline --all

git reflog

Появилась в последних версиях git Показывает всю историю и ветки, что происходила и когда ветка была главной. Сочетая git reflog и git diff ID можно просмотреть разницу между камитами.


Работа с ветками

git branch

Показывает список веток


git branch имя_ветки

Создает новую ветку. Также можно сипользовать:

git checkout -b имя_ветки

Чтобы сразу создать новую ветвь и перейт в неё.


git branch -d имя_ветви

Удаляем ветку. Удаление происходит если ветка уже слита.


git branch -D имя_ветви

Насильное удаление ветви без проверки того, что она уже слита.


Перемещение по веткам

HEAD- это символическое имя текущего выбранного коммита.

По-умаолчанию HEAD указывает на последний коммит из локального дерева.

Обычно HEAD указывает на имя ветки, например master или bugFix. При коммите мы меняем статус ветки и это вижно через изменение HEAD.

Detaching HEAD

Отделение Detaching HEAD означает присвоение HEAD не ветке, а конкретному коммиту.

git checkout

Позволяет переключиться на нужный коммит указав его hash.

git checkout commit_hash

Или можно переключиться на ветвь разработки:

git checkout branch_name

При переходе на коммит, текущий указатель *HEAD будет отделен от листа ветки. Чтобы его вернуть можно ввести команду перехода на ветку:

git checkout master

Можно использовать не только абсолютные ссылки в виде коммитов или названий веток, а переходить к их родительским элементам, с помощью относительных ссылок: ^ или ~<num>.

Чтобы перейти в родительский элемент от ветки master (на один коммит назад по дереву):

git checkout master^

Чтобы перейти на 2 элемента назад:

git checkout master^^

Или то же самое можно записать в виде относительной ссылки:

git checkout master~2

Вместо названий веток можно использовать идентификаторы коммитов.

При переходе по относительным ссылкам можно использовать указатель HEAD, например, чтобы перейти на 2 уровеня назад:

git checkout HEAD^^

или

git checkout HEAD~2

Слияние веток

git merge branch_name
git merge branch_name

Вливание в текущую ветку указанной ветви branch_name.


git rebase branch_name_<куда_переносим>

При ребейзе git по сути копирует набор коммитов и переносит их в указанное место.

Преимущество rebase в том, что с его помощью можно делать чистые линейные последовательности коммитов. Например, если существеут ветка master и bugFix, и мы хотим сдвинуть наши изменения из ветки bugFix прямо на вершину ветки master, то находясь в ветку bugFix можем набрать следующую команду:

git rebase master

Таким образом мы скопируем и перенесем коммиты из ветки bugFix в ветку master, как будто изменения делались последовательно, хотя на самом деле параллельно. В ветке master сощдастся новый коммит.

Если делаем rebase master при этом находясь в master^ или в другом потомке, то указатель просто сдвинется на ветку master.


Работа с удаленным репозиторием

git remote add origin url

Чтобы связать локальный репозиторий с удаленным репозиторием используется команда git remote add, например:

git remote add origin git@github.com:username/project.git

origin - это название, которое мы даем этому удаленному репозиторию.

Затем переименовваем текущую ветку master в main, т.к. на гитхабе при создании репо главная ветка это main:

git branch -M main

Затем отправляем командой git push данные из ветки main в удаленный репозиторий origin:

git push -u origin main

Далее можноотправлять изменения просто командой

git push

git pull

Данная команда загружает актуальную версию с удаленного репозитория в локальный.

git pull

pull - это составная команда, она не только загрузит состояние с гитхаба, но и произведет merge с текущим состоянием локального репозитория.


pull request

Чтобы отправить запрос на изменение в чужой репозиторий нужно:

  1. Создать форк fork чужого проекта на своем аккаунте.
  2. Внести изменения
    1. Склонировать форк к себе в локальную среду командой
    git clone https://github.com/my_nickname/forked_project
    1. Создать ветку с изменениями, напрмиер:
    git branch fixes
    ``` или
    ```bash
    git checkout -b fixes
    1. Внести все изменения в эту ветку:
    git add file
    git commit -m "fix: fix description"
    1. Запушить изменения в свой аккаунт:
    git push
  3. Подтвердить создание pull request'a на github с изменениями.

Если вы не делаете форк, и пытаетесь загруить в оригинальный репозиторий, то система это не позволит. Надо сделать копию клон и потом отправить клон-копию себе. Создастся запрос на изменение в главную ветку в оригинал.


quiz

`git clone`` - клонирование, создание локальной копии удаленного репозитория.

fork - копия чужого реозитория

git pull - стянуть все изменения из удаленного репозитория. Cоздает вопрос слияния веток, иногда проще клонировать весь блок в отдельную папку.

git pull - выкачивает данные из репозитория и делает слияние с локальным репозиторием Если не нужно слияние то луче использовать команду git clone.

git push - отправляет изменения в удаленный репозиторий

git push ssh_name или git push https://...

git должен быть авторизован на внесение изменений в удаленный репозиторий, чтобы выполнить команду git push

git add - добавляет файлу версионность в локальном репозитории

git checkout - позволяет перемещаться между ветками и сохранениями

git diff - разница между текущей и зафиксированной версией файла

git commit - фиксация изменений в репозитории

git branch - вывод всех веток на экран

git branch new_branch_name - создание новой ветки

git merge branch_name - слияние веток: в текущую branch_name

git merge lists - в текущую ветку добавит информацию из lists

git reset --hard HEAD^ - сбросить мастер на 1 коммит назад (символ ^)

git push origin master --force - запушить изменения в мастер, но т.к. мы переписываем история - поэтому --force