Основные команды Git: add, commit, push и что между ними

Git Автор: Среда и версия: git 2.55.0, Linux; весь вывод снят на чистом репозитории 31 августа 2026
содержание

Список команд Git выучить нельзя — их сотни, и половина в работе не встречается. Зато можно понять устройство, после которого команды перестают быть заклинаниями: у Git есть три места, где лежит твой код, и почти каждая команда просто переносит изменения из одного в другое.

Git / 01

Команды перемещают изменения между четырьмя местами

Команды перемещают изменения между четырьмя местами01 Рабочий каталог → индекс git add Редактор меняет файлы, add выбирает содержимое будущего коммита. 02 Индекс → локальная история git commit Коммит сохраняет снимок выбранных изменений. 03 Локальная → удалённая история git push Коммиты становятся доступны команде на сервере.01Рабочий каталог →индексgit addРедактор меняет файлы, add выбирает содержимое будущего коммита.02Индекс → локальнаяисторияgit commitКоммит сохраняет снимок выбранных изменений.03Локальная → удалённаяисторияgit pushКоммиты становятся доступны команде на сервере.
add выбирает содержимое будущего коммита, commit сохраняет снимок локально, push передаёт коммиты на сервер.

Первый репозиторий: четыре команды

git init -b main
Initialized empty Git repository in /home/anya/project/.git/

Появилась папка .git — в ней и живут индекс с историей. Пока в ней пусто, и git status это подтверждает:

git status
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	main.py

nothing added to commit but untracked files present (use "git add" to track)

Untracked значит «Git видит файл, но не следит за ним». Чтобы начал следить — git add; а чтобы перестал показывать вовсе — .gitignore:

git add main.py
git status
On branch main

No commits yet

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   main.py

Файл переехал в индекс — это и есть «Changes to be committed». Никуда, кроме индекса, он пока не попал: истории всё ещё нет.

git commit -m "первый коммит"
[main (root-commit) 2a57751] первый коммит
 1 file changed, 1 insertion(+)
 create mode 100644 main.py

2a57751 — короткий идентификатор коммита, по нему на него потом ссылаются. После коммита рабочий каталог, индекс и история совпадают, и git status становится скучным:

On branch main
nothing to commit, working tree clean

Доказательство, что мест действительно три

Пока всё совпадает, поверить в три дерева трудно. Разведём их: изменим файл, добавим в индекс, а потом изменим ещё раз — не добавляя.

echo "print('пока')" >> main.py
git add main.py
echo "print('ещё')" >> main.py
git status -s
MM main.py
?? scratch.log

Две буквы M подряд — это не опечатка. Левая означает «изменён относительно последнего коммита и добавлен в индекс», правая — «изменён относительно индекса и не добавлен». Один и тот же файл одновременно в двух состояниях. Полный status показывает его дважды:

Changes to be committed:
	modified:   main.py

Changes not staged for commit:
	modified:   main.py

Untracked files:
	scratch.log
Git / 02

Две команды diff сравнивают разные пары

Две команды diff сравнивают разные пары01 / слева 02 / сравнение 03 / справа HEAD: «привет» git diff --staged индекс: «пока» индекс: «пока» git diff каталог: «ещё»01 / слева02 / сравнение03 / справаHEAD: «привет»git diff --stagedиндекс: «пока»индекс: «пока»git diffкаталог: «ещё»
Две команды diff смотрят в разные щели между тремя версиями. Поэтому «я же видел изменения в git diff, а закоммитилось другое» — не мистика, а забытый git add.

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

git diff
diff --git a/main.py b/main.py
index fdcddf7..f3c6fb6 100644
--- a/main.py
+++ b/main.py
@@ -1,2 +1,3 @@
 print('привет')
 print('пока')
+print('ещё')
git diff --staged
diff --git a/main.py b/main.py
index 06e4cf4..fdcddf7 100644
--- a/main.py
+++ b/main.py
@@ -1 +1,2 @@
 print('привет')
+print('пока')

Без аргументов diff сравнивает рабочий каталог с индексом, с флагом --staged — индекс с последним коммитом. Отсюда правило: перед коммитом смотри git diff --staged, потому что именно это и будет записано.

Команды, которые нужны каждый день

КомандаЧто делаетОткуда куда
git initзаводит репозиторий в текущей папкесоздаёт .git
git clone <адрес>скачивает чужой репозиторий с историейудалённый → диск
git statusпоказывает, что где лежитничего не меняет
git add <файл>помечает изменения для следующего коммитарабочий каталог → индекс
git commit -m "…"записывает содержимое индекса в историюиндекс → репозиторий
git diffизменения, которые не попадут в коммитрабочий каталог ↔ индекс
git diff --stagedизменения, которые попадутиндекс ↔ последний коммит
git logистория коммитовчитает репозиторий
git pushотправляет свои коммитырепозиторий → удалённый
git pullзабирает чужие и вливает к себеудалённый → репозиторий
git switch <ветка>переключает веткуменяет рабочий каталог
git restore <файл>откатывает файл к состоянию индексаиндекс → рабочий каталог

История

git log --oneline
67b7a6c третья строка
42217e9 добавил прощание
2a57751 первый коммит

Без --oneline каждый коммит занимает шесть строк с автором и датой — на длинной истории это неудобно. Формат настраивается:

git log --oneline --format='%h %ad %s' --date=short
67b7a6c 2026-08-31 третья строка
42217e9 2026-08-31 добавил прощание
2a57751 2026-08-31 первый коммит

Ветки

Ветка в Git — это не копия проекта, а движущийся указатель на коммит. Поэтому создание ветки мгновенно независимо от размера репозитория:

git switch -c feature/greeting
Switched to a new branch 'feature/greeting'
git branch
* feature/greeting
  main

Звёздочка отмечает текущую. Вернуться — git switch main. Старый способ, git checkout, делает и это, и ещё десяток разных вещей, из-за чего его и разделили на switch (ветки) и restore (файлы). В новых проектах пиши switch.

Чем ветка является на самом деле, чем -d отличается от -D и почему origin/main — не ветка, разобрано отдельно в статье про ветки.

Удалённый репозиторий

git remote add origin https://github.com/anya/project.git
git push -u origin main
To https://github.com/anya/project.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.

origin — просто имя по умолчанию для «того самого» удалённого репозитория, не ключевое слово. Флаг -u связывает локальную ветку с удалённой один раз, после чего хватает голого git push.

Обратная команда:

git pull
Already up to date.

git pull сначала выполняет git fetch, затем интегрирует полученную историю. Режим зависит от флагов и настроек: слияние, rebase или только fast-forward. Конфликты возникают при интеграции, а не при скачивании; команда отмены зависит от того, идёт merge или rebase.

Что путают чаще всего

  1. add — не «сохранить»Он лишь помечает, что войдёт в следующий коммит. Изменив файл после add, ты закоммитишь старую версию — ровно то, что показано выше двумя буквами MM.
  2. commit — не «отправить»Коммит остаётся на твоей машине. Пока не сделан push, ни коллеги, ни GitHub о нём не знают, и переустановка системы унесёт его с собой.
  3. pull — это fetch плюс интеграцияЕсли не хочется сразу сливать, делай git fetch и смотри, что прилетело. Особенно полезно, когда в ветке есть незакоммиченные правки.
  4. «Ветка» — это указатель, а не папкаПереключение ветки меняет содержимое рабочего каталога на месте. Никаких project-v2 рядом заводить не нужно, в этом и смысл.
  5. Незакоммиченное не защищено ничемИстория хранит только коммиты. Правки в рабочем каталоге не переживут неудачную команду вроде git reset --hard — коммить часто, это дёшево.

Частые вопросы

Чем git add отличается от git commit

add переносит изменения из рабочего каталога в индекс, commit записывает содержимое индекса в историю. Разделение нужно, чтобы собрать коммит из части правок: поправил три файла, а закоммитил один — остальные останутся ждать.

Зачем вообще нужен индекс

Чтобы коммит был осмысленной единицей, а не слепком папки на случайный момент. Индекс позволяет разложить работу по коммитам задним числом и посмотреть git diff --staged перед записью.

Как отменить git add

git restore --staged <файл> — уберёт из индекса, сам файл не тронет. Эту подсказку Git печатает прямо в выводе git status, её достаточно прочитать.

Что такое origin и main

origin — обычное имя удалённого репозитория после клонирования. Имя начальной ветки задаётся через git init -b или настройку init.defaultBranch; в примере выше мы явно выбрали main. Без настройки Git 2.55 использует master. Оба — просто имена, их можно менять.

Почему git checkout заменили на switch и restore

checkout перегружен: он переключал ветки, восстанавливал файлы и создавал ветки одной командой, и опечатка в аргументе приводила к неожиданному результату. В Git 2.23 работу разделили: switch — про ветки, restore — про файлы.

Где потренироваться

Git осваивается только руками: заведи пустую папку (mkdir и cd, если терминал пока непривычен), сделай в ней git init и повтори последовательность из этой статьи, подглядывая в git status после каждого шага. Проверить себя на вопросах, которые задают на собеседовании, можно в тесте по Git.

Источники