Управление состоянием файлов#
В этом уроке:
узнаем о состояниях файлов, в которых они могут находиться: отслеживаемый и неотслеживаемый, игнорируемый, зафиксированный, измененный, индексированный;
разберемся, почему файл может быть одновременно индексированным и измененным;
познакомимся с командами:
git statusвыводит состояние файлов на экран;git addдобавляет измененные и неотслеживаемые файлы в индекс;git restoreубирает файл из индекса или отбрасывает изменения, возвращая файл в начальное состояние;git rmвыводит файл из-под контроля;git mvпереименовывает или перемещает файл;
научимся оперировать несколькими файлами, используя шаблоны в аргументах команд.
При работе с файлами рабочей копии возникает множество вопросов:
как убрать файл из индекса;
как быстро откатить изменения в файлах;
как убрать из вывода
git status“мусорные” файлы.
Перед тем как ответить на эти вопросы, узнаем, в каких состояниях могут быть файлы для Git.
Git интересуют файлы в рабочем каталоге, но не за его пределами. Интерес выражается в том, что в последующем пользователю будет предложено добавить изменения в них в историю хранилища. Все файлы в рабочем каталоге делятся на отслеживаемые (tracked) и неотслеживаемые (untracked). К отслеживаемым файлам относятся те, которые до этого присутствовали в ревизии, из которой извлечена рабочая копия. Недавно созданные файлы, еще не попавшие в историю или индекс (который мы рассмотрим ниже), будут относится к категории неотслеживаемых.
В процессе работы над проектом в рабочем каталоге появляются новые файлы, например, сгенерированные системой сборки, компилятором, тестовой системой, архиватором.
Сюда входят объектные, исполняемые, временные и архивные файлы.
Эти файлы производны от исходных кодов, сценариев сборки и представляют краткосрочный интерес.
Их еще называют артефактами сборки.
Даже если их удалить из файловой системы, они могут быть повторно получены пересборкой программы.
Считается хорошей практикой держать артефакты сборки в отдельном каталоге, как например в CMake.
Но система сборки Make создает артефакты рядом с файлами исходным кодом, что засоряет рабочий каталог.
Таким образом, все неотслеживаемые файлы можно разделить на две категории:
новые файлы, добавляемые в скором времени в следующую ревизию;
игнорируемые, путь в хранилище которым закрыт навсегда.
Чтобы файлы из первой категории не потерялись среди многочисленных файлов второй, в Git добавлены списки игнорируемых файлов и каталогов.
Они перечисляются в файле .gitignore в формате регулярных выражений.
Сам файл располагается в корне рабочего каталога.
Содержимое этого файла зависит от используемого языка программирования, библиотек и инструментов.
Разработчики GitHub в открытом проекте gitignore собрали примеры этих файлов с привязкой к используемым программным технологиям.
Отслеживаемые файлы составляют проект программы и основу для следующей ревизии. Они в свою очередь делятся на три части:
зафиксированные;
измененные;
проиндексированные.
После клонирования и последующего извлечения рабочей копии все файлы в рабочем каталоге относятся к зафиксированным. Если отредактировать зафиксированный файл, то он перейдет в измененное состояние. Если откатить изменения у измененного файла, то он перейдет обратно в зафиксированное состояние.
Файл, помещенный в индекс, уже не является измененным.
Но если после индексации файл изменить еще раз, то он одновременно будет измененным и проиндексированным.
Изменение в файле оценивается относительно индексированного файла, а если такого файла нет, то относительно зафиксированного.
Получается, что один и тот же файл может быть измененным и индексированным.
Это позволяет фиксировать только часть изменений в файле.
Команда 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 исключает файл из индекса.
На практике возникает ситуация, когда нужно удалить зафиксированный файл.
Например, если файл с исходным кодом больше не используется или по ошибке был зафиксирован ненужный файл.
Системная утилита rm удаляет файл из файловой системы.
После такого удаления файл отметится как измененный с пометкой “удалено” в выводе команды git status.
Команда git add проиндексирует измененный (удаленный) файл, чтобы в последующем зафиксировать его в истории.
Последовательность эффектов от rm и git add повторяет команда git rm, которая удалит файл и сразу поместит его в индекс.
Git защищает данные от потери и ограничивает случайное удаление измененных файлов.
Удаляемый файл должен быть фиксированным.
Если это не так, то придется отменить изменения или передать команде удаления опцию --force или --cached.
Команды git restore, git add, git rm выше мы использовали только с одним аргументом-файлом.
На практике приходится оперировать несколькими файлами.
Вызывать команду для каждого файла по отдельности утомительно и не эффективно.
Есть два способа, чтобы обойти эту ситуацию:
передать несколько аргументов;
использовать регулярные выражения в аргументах.
Так, команда 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.