Что изменилось: git diff
git diff в Git — команда, которая показывает, что именно изменилось в файлах с последнего коммита, строка за строкой. Вы научитесь читать её вывод, отличать git diff от git diff --staged и собирать коммит из нужных файлов с помощью индекса.
Один коммит у вас уже есть, но проект будет меняться, и перед коммитом хочется убедиться, что вы изменили именно то, что собирались. Для этого и нужен git diff.
Простыми словами
Это как режим рецензирования в текстовом редакторе: Git кладёт две версии файла рядом и подчёркивает разницу.
Практика с примером
Добавим в index.html подключение стилей: одну строку <link rel="stylesheet" href="style.css"> сразу после строки с <title>. Сохраните файл и спросите у Git:
$ git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")
modified: index.html — файл отслеживается и изменился. Раздел Changes not staged for commit означает «изменения, не подготовленные к коммиту»: они ещё не в индексе. Подсказка про git restore — снова отмена, до неё доберёмся в следующем модуле. Теперь посмотрим, что именно изменилось:
$ git diff
diff --git a/index.html b/index.html
index 10ae62b..5c2151d 100644
--- a/index.html
+++ b/index.html
@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<title>Алекс Иванов - Визтка</title>
+ <link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Алекс Иванов</h1>
Разбор по строкам
diff --git a/index.html b/index.html— сравниваются две версии файлаindex.html:a/— старая (из последнего коммита),b/— новая (в рабочей папке).index 10ae62b..5c2151d 100644— служебная строка: короткие номера старой и новой версии содержимого.--- a/index.htmlи+++ b/index.html— условные обозначения: минусы относятся к старой версии, плюсы к новой.@@ -3,6 +3,7 @@— заголовок блока изменений.-3,6: в старой версии блок начинается с 3-й строки и занимает 6 строк.+3,7: в новой — с 3-й строки и занимает 7 (на одну больше).- Строки, начинающиеся с пробела, — контекст: они не менялись, Git показывает их, чтобы вы понимали, где находитесь.
+ <link rel="stylesheet" href="style.css">— плюс в начале означает «строка добавлена». Удалённая строка начиналась бы с минуса, а изменённая показывается парой: старая со знаком-и новая со знаком+.
Пейджер: как выйти, если вывод «завис»
Если вывод длиннее экрана, Git открывает его в постраничном просмотрщике, внизу которого стоит двоеточие или слово (END). Это не зависание: пробел — следующая страница, стрелки — прокрутка, q — выход и возврат в терминал.
Частая путаница: git diff молчит про новые файлы
git diff сравнивает только отслеживаемые файлы. Создали новый файл, а git diff ничего не показывает? Всё в порядке: файл неотслеживаемый, и сравнивать его пока не с чем. Он виден в git status.
Подробно — в документации: git diff.
git diff --staged: что попадёт в коммит
Когда вы выполняете git add, правка переезжает в индекс, и git diff перестаёт её показывать: сравнивать рабочую папку не с чем, она совпадает с индексом. Но убедиться перед коммитом, что именно туда попало, тоже нужно. Для этого есть флаг --staged (staged — «подготовленный», так называют файлы в индексе).
| Команда | Что с чем сравнивает | Отвечает на вопрос |
|---|---|---|
git diff | рабочая папка и индекс | Что я изменил, но ещё не подготовил к коммиту? |
git diff --staged | индекс и последний коммит | Что войдёт в коммит, если я прямо сейчас его сделаю? |
Привычка, которая сэкономит нервы: перед каждым коммитом смотреть на git diff --staged. Читается он так же, как обычный git diff.
Индекс — черновик коммита
Теперь понятно, зачем нужен индекс. Допустим, за рабочий час вы сделали сразу три дела: подключили стили к странице, написали сами стили и завели README. Одним коммитом «Всякое» можно всё закрыть, но через месяц не вспомнишь, что где менялось. Лучше разложить по смыслу: стили — в один коммит, README — в другой. Индекс позволяет собрать коммит из выбранных файлов, а остальное оставить на потом.
Создайте файл style.css (пока только первый блок, остальное допишем чуть позже) и файл README.md. Теперь в проекте три разных состояния файлов сразу:
$ git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: index.html
Untracked files:
(use "git add <file>..." to include in what will be committed)
README.md
style.css
no changes added to commit (use "git add" and/or "git commit -a")
Для стилей нужны index.html и style.css. README пока подождёт:
$ git add index.html style.css
$ git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: index.html
new file: style.css
Untracked files:
(use "git add <file>..." to include in what will be committed)
README.md
В индексе два файла, README остался неотслеживаемым. Коммит заберёт только то, что в индексе — именно поэтому индекс называют черновиком коммита: пока вы не нажали commit, его можно пополнять.
Файл, изменённый после add
Есть тонкость, на которой спотыкаются почти все. Индекс запоминает файл таким, каким он был в момент git add. Если после этого вы продолжили править файл, новые правки в индекс сами не попадают.
Проверим. Допишите в конец style.css остальные правила (код ниже), а затем посмотрите на статус:
h1 {
margin-bottom: 4px;
}
.lead {
color: #666;
margin-top: 0;
}
h2 {
border-bottom: 2px solid #3b82f6;
padding-bottom: 4px;
}
$ git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: index.html
new file: style.css
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: style.css
Untracked files:
(use "git add <file>..." to include in what will be committed)
README.md
Файл style.css теперь значится в двух разделах сразу: как новый в индексе и как изменённый в рабочей папке. Противоречия нет: в индексе лежит старая версия (только блок body), в рабочей папке — новая, полная. Сравнить их поможет пара команд:
$ git diff
diff --git a/style.css b/style.css
index 64b1bf7..f6c2e01 100644
--- a/style.css
+++ b/style.css
@@ -7,3 +7,17 @@ body {
color: #222;
background: #fafafa;
}
+
+h1 {
+ margin-bottom: 4px;
+}
+
+.lead {
+ color: #666;
+ margin-top: 0;
+}
+
+h2 {
+ border-bottom: 2px solid #3b82f6;
+ padding-bottom: 4px;
+}
git diff показывает только то, что вы дописали после add. А git diff --staged показал бы то, что уже ждёт коммита (первая версия style.css и правку в index.html). Чтобы в коммит попала полная версия, нужен ещё один git add:
$ git add style.css
$ git diff --staged --stat
index.html | 1 +
style.css | 23 +++++++++++++++++++++++
2 files changed, 24 insertions(+)
Разбор по строкам
--stat— сокращённый вывод: вместо самих строк Git показывает, в каких файлах сколько строк изменилось.index.html | 1 +— в файле одна добавленная строка (+).style.css | 23 +++…— в новом файле 23 строки, и это уже полная версия:addположил в индекс то, что было в рабочей папке сейчас.2 files changed, 24 insertions(+)— итог: два файла, 24 добавленных строки (1 + 23).
Забыли повторный add: в коммит уйдёт старая версия
Если бы вы сейчас сразу сделали коммит, в историю попала бы первая версия style.css без заголовков, а дописанные правила остались бы «висеть» в рабочей папке. Поэтому после любой правки файла, который уже добавлен в индекс, снова делайте git add и сверяйтесь со status: нужный файл не должен оставаться в разделе Changes not staged for commit.
Практика у себя
Доведём начатое до двух коммитов в папке ~/projects/portfolio. Если вы уже повторяли примеры выше, часть шагов сделана, пропустите их.
Шаг 1. Подключите стили. В index.html после строки <title>…</title> вставьте строку <link rel="stylesheet" href="style.css">. Сохраните и выполните git diff. Вывод должен совпасть с разобранным выше: одна добавленная строка с плюсом, + <link …>.
Шаг 2. Создайте style.css с первым блоком:
body {
max-width: 640px;
margin: 40px auto;
padding: 0 16px;
font-family: Arial, sans-serif;
line-height: 1.6;
color: #222;
background: #fafafa;
}
Шаг 3. Создайте README.md. Это файл с описанием проекта в простом текстовом формате Markdown; решётка в начале строки делает её заголовком.
# portfolio
Личный сайт-визитка Алекса Иванова. Учебный проект курса по Git.
Шаг 4. Подготовьте к коммиту только файлы про стили и убедитесь, что README остался в стороне:
$ git add index.html style.css
$ git status
Ожидаемо: в Changes to be committed два файла (modified: index.html и new file: style.css), в Untracked files только README.md.
Шаг 5. Допишите в конец style.css остальные правила (блок «CSS · style.css (добавить в конец файла)» выше), сохраните, выполните git status и убедитесь, что style.css теперь значится в двух разделах. Выполните git diff: вы должны видеть только дописанные правила.
Шаг 6. Добавьте файл ещё раз и проверьте, что в индексе полная версия. В git status файл style.css должен остаться только в разделе Changes to be committed:
$ git add style.css
$ git diff --staged --stat
index.html | 1 +
style.css | 23 +++++++++++++++++++++++
2 files changed, 24 insertions(+)
Шаг 7. Первый коммит дня:
$ git commit -m "Добавить стили"
[main 8c23940] Добавить стили
2 files changed, 24 insertions(+)
create mode 100644 style.css
Теперь git status показывает только README.md среди Untracked files. Хеш в скобках у вас будет другой.
Шаг 8. Второй коммит:
$ git add README.md
$ git commit -m "Добавить README"
[main f58d529] Добавить README
1 file changed, 3 insertions(+)
create mode 100644 README.md
$ git status
On branch main
nothing to commit, working tree clean
Проверьте себя
- Рабочая папка чиста:
nothing to commit, working tree clean. - Вы сделали два отдельных коммита: «Добавить стили» (два файла) и «Добавить README» (один файл).
- Вы можете объяснить, чем отличаются
git diffиgit diff --staged. - Вы знаете, что после правки уже добавленного файла нужен повторный
git add.
Частые ошибки
Забыли add после правки: в коммит ушла старая версия. В git status файл остался в Changes not staged for commit, хотя коммит уже сделан. Исправление простое — выполнить git add и сделать ещё один коммит. Но лучше проверять статус до коммита.
git add . добавил лишнее. Точка добавляет всё в текущей папке, включая черновики, архивы и файлы редактора. Перед git add . смотрите на git status, а после — на git diff --staged или git status. Если что-то лишнее попало, Git подсказывает в выводе, как убрать файл из индекса (git restore --staged); подробно — в следующем модуле.
git diff пустой, хотя вы правили файл. Причины: правка уже в индексе (смотрите git diff --staged), файл новый и неотслеживаемый (смотрите git status) или вы не сохранили файл в редакторе.
Вывод «завис» с двоеточием внизу. Это пейджер. Нажмите q.
Что важно запомнить
git diffпоказывает изменения в рабочей папке, которые ещё не в индексе;git diff --staged— то, что уже в индексе и войдёт в коммит.- В выводе diff строка с
+добавлена, с-удалена, строки с пробелом — контекст. - Индекс — черновик коммита: в коммит попадают только добавленные файлы, а остальные можно закоммитить отдельно.
- Индекс хранит файл таким, каким он был в момент
git add. После новых правок нужен повторныйgit add. - Перед каждым коммитом смотрите
git statusиgit diff --staged.
Теперь в проекте три коммита. Как посмотреть список всех сохранённых версий, открыть любую из них и сравнить между собой — в следующем уроке.