Skip to main content

02. Коммиты

Для выполнения этого задания вам нужен репозиторий, созданный в предыдущем задании.

Клонирование

Если у вас его нет на компьютере или лень искать, можно просто заново загрузить его с github.

Для этого откройте свой репозиторий на сайте github.com и скопируйте путь к нему:

sign in

После этого нужно перейти в папку в которой будет лежать папка вашего репозитория. Если вы клонируете репозиторий, не нужно создавать папку, в которой он будет лежать. Онв создаётся автоматически.

У меня эта папка - D:\Repositories.

Внутри папки для репозиториев запустим консоль git и скачаем тестовый репозиторий (вам нужно указать свой адрес вместо https://github.com/aoklyunin/test-repository.git)

git clone https://github.com/aoklyunin/test-repository.git

Получим:

Cloning into 'test-repository'...
remote: Enumerating objects: 17, done.
remote: Counting objects: 100% (17/17), done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 17 (delta 1), reused 16 (delta 0), pack-reused 0
Receiving objects: 100% (17/17), done.
Resolving deltas: 100% (1/1), done.

Так git показывает, сколько и чего загружено.

Обратите внимание

Команда git clone всегда создаёт новую папку с таким же названием, как у удалённого репозитория. Чтобы работать с ним, нужно перейти в созданную клонированием папку и запустить гит-консоль в ней.

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

cmd

Содержимое папки будет выглядеть примерно так:

cmd

Получение изменений

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

Чтобы получить информацию о всех изменениях на удалённом сервере, используется команда git fetch <server-name>

git fetch pb master

Чтобы загрузить новые коммиты с сервера используют команду git pull <server-name>

Например:

git pull pb master

Обычно основной сервер называют origin, поэтому если вы работаете только с одним, команды будут выглядеть так:

git fetch origin master
git pull origin master

Состояния

Напомню: каждому файлу внутри выбранной нами папки git присваивает своё состояние, их может быть всего три:

  1. Зафиксированное - никаких новых изменений в файле нет, при создании коммита git просто пробросит ссылку на последнее изменение этого файла. Таким изменением может быть и просто сохранение файла после добавления его в список индексации;
  2. Изменённое - файл изменён или просто создан, но пока что не добавлен в список индексации следующего коммита;
  3. Индексированное - изменения в файле будут учтены при создании нового коммита.

Узнаем теперь состояние нашего репозитория

git status

Если вы ничего не меняли, то получите примерно такой ответ:

On branch master

nothing to commit, working tree clean

Это означает, что всё окей, нет файлов, за которыми git не следит, хотя мог бы. При этом нет никаких изменений в отслеживаемых.

В первой строчке говорится о том, в какой ветке мы находимся. О ветках гит будет рассказано ниже. Пока что у нас она одна и называется master. Git говорит, что мы в ней. Вторая строчка говорит о том, что нам нечего добавлять в коммит.

Если запустить статус для пустого репозитория, то ответ будет таким:

On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)

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

Чтобы получить короткий статус, используйте ключ -s:

git status -s

Т.к. информацию выводить не о чем (у нас нет изменений), то гит вернёт просто пустую строку.

Напишем в файле test нашего репозитория слово тест.

cmd

Вызовем снова

git status

Получим:

On branch master

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: test1.txt

no changes added to commit (use "git add" and/or "git commit -a")

Так гит отображает список изменённых файлов и предлагает либо добавить их в список индексации при помощи команды git add <file>..., либо откатить изменения при помощи команды git restore <file>...

Если запросить сжатое состояние репозитория

git status -s

то выдача будет такой:

 M test1.txt

Восстановим файл до предыдущего состояния:

git restore test1.txt

Если всё ок, то файл снова станет пустым

cmd

Теперь напишите в файле test1.txt строку:

Это тестовый файл

cmd

Чтобы сохранить файл, нужно либо нажать Ctrl+S, либо закрыть блокнот. Система предложит вам сохранить изменения, нажмите Сохранить.

cmd

Обратите внимание

Если вы не сохраните изменения в файле, то и в список индексации добавлять нечего.

Теперь нам нужно добавить его в список индексации, можно снова вызвать команду git add test1.txt, но проще использовать git add *. В этом случае в список индексации добавляются все изменённые файлы, и вам не нужно помнить их названия.

Введём команду:

git add *
Не забывайте про пробел

Между add и * обязательно должен быть пробел.

Снова запросим состояние репозитория:

git status

В ответ получим:

On branch master

Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: test1.txt

В нём перечисленны изменения, добавленные в индексацию. У нас изменился один файл test1.txt.

Создадим теперь второй коммит:

git commit -m "test1.txt filled"

В ответ получим:

$ git commit -m "test1.txt filled"
[master 85d79a4] test1.txt filled
1 file changed, 1 insertion(+)

В выдаче git перечислит все изменения, образующие новый коммит.

Снова запросим состояние репозитория:

git status

Получим:

On branch master
nothing to commit, working tree clean

Значит, несохранённых изменений нет, можно двигаться дальше.

Дополним содержимое файла test1.txt:

Это тестовый файл

Или нет

Также создадим пустой файл test2.txt в папке нашего репозитория и выполним команду:

git status

Теперь выдача будет выглядеть так:

On branch master
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: test1.txt

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

no changes added to commit (use "git add" and/or "git commit -a")

Гит отдельно выводит список изменённых файлов и отдельно список добавленных.

Вызовем теперь укороченный статус:

git status -s

Получим:

 M test1.txt
?? test2.txt

Добавим все изменения

git add *

И снова запросим состояние:

git status

Получим:

On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: test1.txt
new file: test2.txt

Сжатая информация о состоянии будет такой:

M  test1.txt
A test2.txt

Создадим новый коммит:

git commit -m "add test2.txt"

Получим:

[master 0bf151b] add test2.txt
2 files changed, 3 insertions(+), 1 deletion(-)
create mode 100644 test2.txt

Отображение изменений

Теперь напишите в файле test2.txt следующее:


Это второй тестовый файл

Но это неточно

Добавьте изменения в список индексации:

git add *

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

git commit -m "test2.txt filled"

Теперь поменяйте текст в test2.txt


Это второй тестовый файл

Однозначно тестовый

и запустите команду

git diff

В ответ вы получите примерно такой вывод:

diff --git a/test2.txt b/test2.txt
index a8f4692..87b925d 100644
--- a/test2.txt
+++ b/test2.txt
@@ -1,3 +1,3 @@
Это второй тестовый файл

-Но это неточно
\ No newline at end of file
+Однозначно тестовый
\ No newline at end of file

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

Первая строка показывает, какие именно версии мы сравниваем. В логике такой разности a - это версия файла в последнем коммите, b - актуальная версия.

Следующая строчка - это хэши рассматриваемых версий. Потом идёт две строчки до знаков @. Это - блок отображения цепочки изменений. Версии файлов могут пробрасываться из предыдущих коммитов. Тогда строк может быть больше.

Между @@ указано, сколько строк удалено и сколько добавлено. В нашем случае мы удалили одну строку и одну прибавили. Это там и выведено. После запятой указывается номер строки файла, с которой начинаются изменения.

Потом выводятся сами изменения. Строка \ No newline at end of file говорит о том, что в конце файла нет символа перехода на новую строку. Её может и не быть, если вы добавили пустую строку в конец.

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

git diff --staged

Пока что в ответ мы получим пустую строку, потому что у нас всего одно изменение, и оно не проиндексировано. Добавим изменения в список индексации и снова вызовем разность:

git add test2.txt
git diff --staged

Теперь получим такую же выдачу, как ранее у неиндексированных изменений:

diff --git a/test2.txt b/test2.txt
index a8f4692..87b925d 100644
--- a/test2.txt
+++ b/test2.txt
@@ -1,3 +1,3 @@
Это второй тестовый файл

-Но это неточно
\ No newline at end of file
+Однозначно тестовый
\ No newline at end of file

Теперь обычная команда git diff вернёт нам пустую строку.

Создадим новый коммит:

git commit -m "test2.txt changed"

Получим:

[master e43dc46] test2.txt changed
1 file changed, 1 insertion(+), 1 deletion(-)

Удаление файлов

Удалим теперь файл test1.txt и выполним снова команду git status, получим:

On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: test1.txt

no changes added to commit (use "git add" and/or "git commit -a")

Git говорит нам о том, что у нас есть непроиндексированные изменения. Раньше мы использовали команду git add <file>. Для того, чтобы удалить файл из списка индексации, когда он уже удалён, можно использовать ещё команду git rm <file>.

Удалим наш файл из списка индексации:

git rm test1.txt

Получим:

rm 'test1.txt'

Чтобы удалить папку, нужно добавить ключ -r(для выполнения задания это не нужно)

git rm -r folder_name

где folder_name - название папки

Снова вызовем статус git status , получим:

On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: test1.txt

Теперь наши изменения будут учтены, создаём новый коммит:

git commit -m "test1 removed"

Получим:

[master cea6176] test1 removed
1 file changed, 3 deletions(-)
delete mode 100644 test1.txt

Если вы хотите удалить файл из индекса, но при этом оставить его на диске, то нужно использовать ключ --cached.

git rm --cached test2.txt

Получим:

rm 'test2.txt'

Вызовем статус, получим:


On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)

Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: test2.txt

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

При этом сам файл не удалился.

cmd

Добавим его снова в список индексации и вызовем статус

git add test2.txt
git status

Получим:

On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)

nothing to commit, working tree clean

Гит говорит, что изменений нет. Так и должно быть: мы сначала извлекли файл из интдекса, а потом добавили.

Удаление с ключём --cached очень полезно, когда вы забыли добавить в список игнорирования(.gitignore, о нём будет рассказано в следующих главах) большой файл или папку и она уже есть в списке индексации. Если мы её просто удалим, то её предыдущие версии всё равно останутся внутри git.

Команда git rm также, как и git add на самом деле принимает регулярное выражение, поэтому вы можете одной командой удалить сразу набор файлов, удовлетворяющих регулярному выражению.

Помимо удаления иногда мы перемещаем файлы внутри рабочей директории, переименовываем их. Для того, чтобы гит учёл такие изменения, нужно использовать команду git mv <source> <target>

Поменяем называние нашего файла с test2.txt на test1.txt:

git mv test2.txt test1.txt

В нашем случае перемещение происходит внутри папки, поэтому оно в своей сути переименование. В логике git и вообще unix переименование всегда является частным случаем перемещения.

cmd

Обратите внимание

Эта команда перемещает и физический файл, и его индекс. Т.е. выполненная команда эквивалентна тому, если бы мы вручную переместили(переименовали) файл, а потом выполнили эти две:

git rm test2.txt
git add test1.txt

Если вы вручную удалили файл, то не забудьте добавить все изменения в индекс:

git add *

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

git commit -m "test2.txt moved to test1.txt"

История коммитов*

Теоретический блок

Вам нужно просто прочитать этот и следующий блоки и приступать к выполнению задания. Команды выполнять не требуется.

Чтобы посмотреть историю коммитов, выполним команду:

git log

Получим:

commit b110a9a89cdcf58f8493bec0f865029d0805b826 (HEAD -> master)
Author: alex <aok@buran.rest>
Date: Wed Sep 7 18:27:37 2022 +0300

test2.txt moved to test1.txt

commit cea61760b9ee1597baddbb5ee1d32d19a5bca029
Author: alex <aok@buran.rest>
Date: Wed Sep 7 18:21:13 2022 +0300

test1 removed

commit da3a16675b300de24c2e2d13ce75b05ca445d0d4
Author: alex <aok@buran.rest>
Date: Wed Sep 7 18:20:11 2022 +0300

test2.txt changed

commit f7dfda1acda795011800a1a392447396e643440c
Author: alex <aok@buran.rest>
Date: Wed Sep 7 18:14:33 2022 +0300

test2.txt filled
:
Не спешите ничего нажимать

Ответ Git на команду git log - это не просто вывод в консоль. Это - интерактивная консольная программа. В ней уже не ввести обычные команды. Такие программы перехватывают управление консолью и сами обрабатывают нажатия на клавиатуру. Попробуйте нажать пару раз кнопку "вниз" на клавиатуре. Выведется новый текст, если теперь нажать "вверх", то мы вернёмся к изначальной выдаче. Обычно интерактивные команды завершаются при помощи сочетания клавиш Ctrl+C, но в git чтобы выйти, нужно нажать клавишу q.

Снова выведем лог:

git log

При помощи этой команды гит выводит список коммитов в текущей ветке. Т.к. коммитов бывает очень много, то по умолчанию Git выводит несколько последних, а остальные можно посмотреть, переместившись к ним при помощи клавиши "вниз" на клавиатуре.

Посмотрим на описание последнего коммита

commit b110a9a89cdcf58f8493bec0f865029d0805b826 (HEAD -> master)
Author: alex <aok@buran.rest>
Date: Wed Sep 7 18:27:37 2022 +0300

test2.txt moved to test1.txt

В первой строчке указан хэш коммита, также у него в скобках указано, что он является актуальной версией в ветке master. Во второй указаны автор коммита и его почта, третья - это дата и время создания коммита, потом его описание. У остальных коммитов после хэша ничего нет. Напомню хэш Git высчитвает по алгоритму SHA-1, отображается он при помощи 40 шестнадцатиричных цифр.

Если добавить ключ -p, то гит выведет список изменений в коммите, если добавить после ключ -<num-of-pathces>, то будет выведено заданное количество изменений. Эти изменения называются патчами. Например, чтобы вывести информацию только о двух коммитах, нужно ввести команду: git log -p -2.

Посмотрим патчи:

git log -p

Получим:

commit b110a9a89cdcf58f8493bec0f865029d0805b826 (HEAD -> master)
Author: alex <aok@buran.rest>
Date: Wed Sep 7 18:27:37 2022 +0300

test2.txt moved to test1.txt

diff --git a/test2.txt b/test1.txt
similarity index 100%
rename from test2.txt
rename to test1.txt

commit cea61760b9ee1597baddbb5ee1d32d19a5bca029
Author: alex <aok@buran.rest>
Date: Wed Sep 7 18:21:13 2022 +0300

test1 removed

diff --git a/test1.txt b/test1.txt
deleted file mode 100644
index aecca03..0000000
--- a/test1.txt
+++ /dev/null
@@ -1,3 +0,0 @@
:

Т.е. в вывод добавилась информация о пачтчах в каждом из коммитов.

Если мы хотим получит краткую информацию по каждому коммиту, нужно использовать ключ --stat

Вводим команду:

git log --stat

Получим:

commit b110a9a89cdcf58f8493bec0f865029d0805b826 (HEAD -> master)
Author: alex <aok@buran.rest>
Date: Wed Sep 7 18:27:37 2022 +0300

test2.txt moved to test1.txt

test2.txt => test1.txt | 0
1 file changed, 0 insertions(+), 0 deletions(-)

commit cea61760b9ee1597baddbb5ee1d32d19a5bca029
Author: alex <aok@buran.rest>
Date: Wed Sep 7 18:21:13 2022 +0300

test1 removed

test1.txt | 3 ---
1 file changed, 3 deletions(-)

commit da3a16675b300de24c2e2d13ce75b05ca445d0d4
Author: alex <aok@buran.rest>
Date: Wed Sep 7 18:20:11 2022 +0300

test2.txt changed
:

Чтобы отфильтровать выдачу по нужным файлам, следует в конце команды добавить двойной дефис -- и через пробел путь к файлу:

git log --stat -- test1.txt

Получим:

commit b110a9a89cdcf58f8493bec0f865029d0805b826 (HEAD -> master)
Author: alex <aok@buran.rest>
Date: Wed Sep 7 18:27:37 2022 +0300

test2.txt moved to test1.txt

test1.txt | 3 +++
1 file changed, 3 insertions(+)

commit cea61760b9ee1597baddbb5ee1d32d19a5bca029
Author: alex <aok@buran.rest>
Date: Wed Sep 7 18:21:13 2022 +0300

test1 removed

test1.txt | 3 ---
1 file changed, 3 deletions(-)

commit 0bf151bc6c5fbd646b818f69569555cbf4684307
Author: alex <aok@buran.rest>
Date: Wed Sep 7 17:54:13 2022 +0300

add test2.txt

test1.txt | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

commit 85d79a40621cef6bc903347dab67583339bf7fdd
Author: alex <aok@buran.rest>
Date: Wed Sep 7 17:34:18 2022 +0300

test1.txt filled

test1.txt | 1 +
1 file changed, 1 insertion(+)

commit 7856af838da90be3db0674724050bbefc16b4116
Author: alex <aok@buran.rest>
Date: Wed Sep 7 16:51:26 2022 +0300

init

test1.txt | 0
1 file changed, 0 insertions(+), 0 deletions(-)

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

Также есть возможность выводить информацию по каждому коммиту вообще в одну строку:

git log --pretty=oneline

Получим:

b110a9a89cdcf58f8493bec0f865029d0805b826 (HEAD -> master) test2.txt moved to test1.txt
cea61760b9ee1597baddbb5ee1d32d19a5bca029 test1 removed
da3a16675b300de24c2e2d13ce75b05ca445d0d4 test2.txt changed
f7dfda1acda795011800a1a392447396e643440c test2.txt filled
0bf151bc6c5fbd646b818f69569555cbf4684307 add test2.txt
85d79a40621cef6bc903347dab67583339bf7fdd test1.txt filled
7856af838da90be3db0674724050bbefc16b4116 init

Теперь рассмотрим параметр -S <changed-line>, он выводит только те коммиты, в которых эта строка присутствует в изменениях. Например, если мы хотим найти коммиты, в которых изменился текст второй тестовый

git log -S "второй тестовый"

Получим:

Author: alex <aok@buran.rest>
Date: Wed Sep 7 18:14:33 2022 +0300

test2.txt filled

Операции отмены*

Теоретический блок

Вам нужно просто прочитать этот блок и приступать к выполнению задания. Команды выполнять не требуется.

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

git commit --amend

В этом случае все проиндексированные патчи просто добавятся в последний сделанный коммит.

Не все операции отмены обратимы

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

Очистим содержимое файла test1.txt и сохраним:

checkout

git status

Получим:

On branch master
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: test1.txt

no changes added to commit (use "git add" and/or "git commit -a")

Теперь отменим изменения в файле test1.txt при помощи команды git checkout:

git checkout test1.txt

Получим:

Updated 1 path from the index

Содержимое файла вернулось:

checkout

Задание

Если вы выполнили все блоки теории в этой главе, то вам останется только опубликовать ваш локальный репозиторий на сервере github.

Вам необходимо создать свой репозиторий, в котором добавлены следующие коммиты (для каждого сначала указывается название, потом требования):

  1. test1.txt filled - заполнить файл test1.txt текстом Это тестовый файл

  2. add test2.txt - создать файл test2.txt и заполнить файл test1.txt текстом

    Это тестовый файл

    Или нет
  3. test2.txt filled - заполнить файл test2.txt текстом

    Это второй тестовый файл

    Но это неточно
  4. test2.txt changed - поменять текст файла test2.txt на:

    Это второй тестовый файл

    Однозначно тестовый
  5. test1 removed - удалить файл test1.txt

  6. test2.txt moved to test1.txt - переименовать файл test2.txt в test1.txt

Когда задание будет выполнено, не забудьте сделать push локальных изменений на сервер командой

git push origin master

Ссылку на github-репозиторий необходимо отправить в поле ввода задания на сайте mdl.

Ссылка на контест.