Управление состоянием файлов

Управление состоянием файлов#

В этом уроке:

  • узнаем о состояниях файлов, в которых они могут находиться: отслеживаемый и неотслеживаемый, игнорируемый, зафиксированный, измененный, индексированный;

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

  • познакомимся с командами:

    • git status выводит состояние файлов на экран;

    • git add добавляет измененные и неотслеживаемые файлы в индекс;

    • git restore убирает файл из индекса или отбрасывает изменения, возвращая файл в начальное состояние;

    • git rm выводит файл из-под контроля;

    • git mv переименовывает или перемещает файл;

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

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

  • как убрать файл из индекса;

  • как быстро откатить изменения в файлах;

  • как убрать из вывода git status “мусорные” файлы.

Перед тем как ответить на эти вопросы, узнаем, в каких состояниях могут быть файлы для Git.

Git интересуют файлы в рабочем каталоге, но не за его пределами. Интерес выражается в том, что в последующем пользователю будет предложено добавить изменения в них в историю хранилища. Все файлы в рабочем каталоге делятся на отслеживаемые (tracked) и неотслеживаемые (untracked). К отслеживаемым файлам относятся те, которые до этого присутствовали в ревизии, из которой извлечена рабочая копия. Недавно созданные файлы, еще не попавшие в историю или индекс (который мы рассмотрим ниже), будут относится к категории неотслеживаемых.

../_images/file-states.png

В процессе работы над проектом в рабочем каталоге появляются новые файлы, например, сгенерированные системой сборки, компилятором, тестовой системой, архиватором. Сюда входят объектные, исполняемые, временные и архивные файлы. Эти файлы производны от исходных кодов, сценариев сборки и представляют краткосрочный интерес. Их еще называют артефактами сборки. Даже если их удалить из файловой системы, они могут быть повторно получены пересборкой программы. Считается хорошей практикой держать артефакты сборки в отдельном каталоге, как например в CMake. Но система сборки Make создает артефакты рядом с файлами исходным кодом, что засоряет рабочий каталог.

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

  • новые файлы, добавляемые в скором времени в следующую ревизию;

  • игнорируемые, путь в хранилище которым закрыт навсегда.

Чтобы файлы из первой категории не потерялись среди многочисленных файлов второй, в Git добавлены списки игнорируемых файлов и каталогов. Они перечисляются в файле .gitignore в формате регулярных выражений. Сам файл располагается в корне рабочего каталога. Содержимое этого файла зависит от используемого языка программирования, библиотек и инструментов. Разработчики GitHub в открытом проекте gitignore собрали примеры этих файлов с привязкой к используемым программным технологиям.

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

  • зафиксированные;

  • измененные;

  • проиндексированные.

../_images/file-states-flow.png

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

Файл, помещенный в индекс, уже не является измененным. Но если после индексации файл изменить еще раз, то он одновременно будет измененным и проиндексированным. Изменение в файле оценивается относительно индексированного файла, а если такого файла нет, то относительно зафиксированного. Получается, что один и тот же файл может быть измененным и индексированным. Это позволяет фиксировать только часть изменений в файле. Команда git add --patch <имя_файла> в интерактивном режиме добавляет только те измененные фрагменты, которые вы выберете.

Состояние файлов в рабочем каталоге показывает команда git status. В примере ниже она показывает, что файл LampServer.cpp изменен и не отслеживаются файлы из каталога build/.

skt@home:~/MyProjects/RemoteLamps$ git status
На ветке main
Ваша ветка обновлена в соответствии с «origin/main».

Изменения, которые не в индексе для коммита:
  (используйте «git add <файл>…», чтобы добавить файл в индекс)
  (используйте «git restore <файл>…», чтобы отменить изменения в рабочем каталоге)
	изменено:      LampServer.cpp

Неотслеживаемые файлы:
  (используйте «git add <файл>…», чтобы добавить в то, что будет включено в коммит)
	build/

нет изменений добавленных для коммита
(используйте «git add» и/или «git commit -a»)

Кроме состояний файлов, команда также подсказывает как изменить эти состояния. На первых порах эти подсказки помогают, но в будущем многословность только мешают. Компактный вывод делает опция -s (--short) (“short” переводится с английского как “короткий”).

skt@home:~/MyProjects/RemoteLamps$ git status -s
M  LampServer.cpp
?? build/

Вернемся к рисунку с графом состояний файлов в Git и приведем команды, которые переводят файл из одного состояния в другое. Для изменения текстового файла достаточно воспользоваться редактором. Откатить изменения и вернуть содержимое файла в начальное состояние позволяет команда git restore. Команда git add помещает измененные и неотслеживаемые файлы в индекс. Команда git restore с опцией --staged исключает файл из индекса.

../_images/file-states-flow2.png

На практике возникает ситуация, когда нужно удалить зафиксированный файл. Например, если файл с исходным кодом больше не используется или по ошибке был зафиксирован ненужный файл. Системная утилита rm удаляет файл из файловой системы. После такого удаления файл отметится как измененный с пометкой “удалено” в выводе команды git status. Команда git add проиндексирует измененный (удаленный) файл, чтобы в последующем зафиксировать его в истории. Последовательность эффектов от rm и git add повторяет команда git rm, которая удалит файл и сразу поместит его в индекс. Git защищает данные от потери и ограничивает случайное удаление измененных файлов. Удаляемый файл должен быть фиксированным. Если это не так, то придется отменить изменения или передать команде удаления опцию --force или --cached.

Команды git restore, git add, git rm выше мы использовали только с одним аргументом-файлом. На практике приходится оперировать несколькими файлами. Вызывать команду для каждого файла по отдельности утомительно и не эффективно. Есть два способа, чтобы обойти эту ситуацию:

  1. передать несколько аргументов;

  2. использовать регулярные выражения в аргументах.

Так, команда git add *.c добавляет в индекс все файлы в текущем каталоге с расширением .c, а команда git rm **/*.log удалит во всех вложенных каталогах зафиксированные файлы с расширением .log.

Следующая операция, кроме удаления – это перемещение файлов, которая также реализует переименование. Перемещение файлов и каталогов выполняет системная утилита mv. Состояние перемещенного файла команда git status показывается как изменение файла с пометкой “удалено” и появление неотслеживаемого файла. После индексации обоих файлов, Git обнаружит и покажет перемещение.

bob@pc:~/projects/repo$ mv main.c main.cpp
bob@pc:~/projects/repo$ git status -s
 D main.c
?? main.cpp
bob@pc:~/projects/repo$ git add main.c
main.cpp
bob@pc:~/projects/repo$ git status -s
R  main.c -> main.cpp

Команда git mv объединяет в себе функции утилиты mv и команды git add. Она перемещает файлы и сразу добавляет информацию об этом в индекс. Тот же самый эффект выше можно было бы достигнуть командой git mv main.c main.cpp.