Основы GIT

Git — распределённая система контроля версий. Локальный репозиторий содержит историю и позволяет делать commits, branches и merges без постоянного соединения с сервером.

Три состояния

working tree  --git add-->  index  --git commit-->  repository
     ^                         |
     +------ git restore ------+
  • working tree — файлы, которые редактируются;
  • index / staging area — точный набор изменений следующего commit;
  • repository — сохранённый граф объектов и ссылок.

git status отвечает, где находится каждое изменение. git diff показывает working tree против index, git diff --staged — index против текущего commit.

Commit

Commit хранит ссылку на snapshot дерева, metadata и родительские commits. Его hash зависит от содержимого объекта. Git умеет переиспользовать одинаковые blobs, поэтому snapshot не означает полную физическую копию каталога на каждый шаг.

git add src/main/java/Example.java
git diff --staged
git commit -m "fix: handle empty response"

Хороший commit содержит одно связное изменение и объясняет намерение. Секрет, попавший в commit, остаётся в истории после обычного удаления файла.

Ветки и HEAD

Branch — изменяемая ссылка на commit. HEAD обычно указывает на текущую branch.

A---B---C  main
     \
      D---E  feature
git switch -c feature
git switch main
git branch -vv

Detached HEAD означает, что HEAD указывает прямо на commit. Новые commits не потеряны сразу, но для продолжения работы на них нужно создать branch.

Merge

Merge объединяет истории.

  • fast-forward — текущая branch просто перемещается вперёд;
  • three-way merge — создаётся commit с двумя родителями;
  • конфликт возникает, когда Git не может однозначно совместить изменения.
git switch main
git merge feature

После конфликта редактируют файлы, проверяют результат, выполняют git add и завершают merge commit. Маркеры конфликта нельзя удалять механически без понимания обеих версий.

Rebase

Rebase повторяет commits поверх нового основания:

до:       A---B---C main
               \
                D---E feature
 
после:    A---B---C---D'---E' feature
git switch feature
git rebase main

Hashes меняются, потому что меняются parents. Не следует переписывать общую опубликованную историю без договорённости: другим участникам придётся согласовывать свои ветки с новыми commits.

Remote

origin/main — локальная remote-tracking ссылка на последнее известное состояние remote branch.

git fetch origin
git log --oneline --graph --decorate --all
git push -u origin feature

fetch загружает объекты и обновляет remote-tracking refs, но не меняет working tree. pull обычно объединяет fetch с merge или rebase согласно настройке.

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

  • git restore file — убрать незастейдженные изменения файла;
  • git restore --staged file — убрать файл из index, сохранив working tree;
  • git revert <commit> — создать обратный commit, безопасно для общей истории;
  • git reset — переместить ref и, в зависимости от режима, index/working tree;
  • git reflog — найти прежние положения локальных refs.

Перед reset --hard, clean и force push нужно точно понимать область потери. Для опубликованной ветки предпочтительнее revert. Если force push необходим после согласованного rebase, --force-with-lease проверяет, что remote не ушёл вперёд незаметно.

Полезный цикл

git status
git diff
git add -p
git diff --staged
git commit
git fetch
git log --oneline --graph --decorate --all

git add -p позволяет собрать осмысленный commit из отдельных hunks одного файла.

На собеседовании

Чем merge отличается от rebase? Merge сохраняет обе линии истории и добавляет merge commit; rebase создаёт новые commits на другом основании.

Что хранит branch? Имя и ссылку на один commit; история достигается по parent-ссылкам.

Связанные заметки: Что такое Git, Файл gitignore.