Перед началом работы необходимо знать несколько полезных фоновых вещей:
- Git хранит только файлы , а не папки.
- Каждый коммит Git хранит полный снимок из всех ваших файлов, а точнее, всех ваших совершенных файлов.Это то, что Git называет неотслеживаемыми файлами * площадка .(У него есть еще одно старое имя, которое теперь предполагается использовать для чего-то другого, но иногда некоторые вещи будут ссылаться на кеш . Все три имени относятся к одному и тому же.)
Git хранит эти файлы в коммитах .Git действительно все о коммитах.Каждый коммит пронумерован, но не в виде простого последовательного способа «коммит № 1, коммит № 2, ...».Вместо этого каждый коммит получает уникальный хеш-идентификатор , причем хеш-идентификаторы выглядят совершенно случайными и не связаны с предыдущими коммитами.Эти хэш-идентификаторы представляют собой большие уродливые строки букв и цифр, такие как 83232e38648b51abbcbdb56c94632b6906cc85a6
, из которых git log
выплевывает.
Поскольку каждый файл находится в каждый коммит, важно, чтобы Git сохранял их так, чтобы сразу не использовать весь ваш дисковод.Таким образом, сохраненные файлы сжимаются и, кроме того, shared для разных коммитовGit может сделать это, потому что для хранения файлов используется специальный формат Git-only, freeze -ried.Файлы в этой форме не могут быть изменены , но могут использоваться совместно.Это означает, что никакая существующая фиксация не может быть изменена . Каждый коммит в вашем хранилище архивируется, более или менее постоянно. 1 Думайте о коммитах как о постоянных (они в основном есть) и неизменных.Они являются историей, хранящейся в хранилище.
1 Возможно удалить коммиты, но это немного сложно, а Git обычно не делаетон тоже сразу - так что даже если вы думаете коммит ушел и не можете сразу его найти, он, вероятно, все еще там.
Выполняется работа
Теперь, это все хорошо и хорошо для архивирования, но эти только для чтения сублимированные файлы совершенно бесполезны для фактического выполнения любой работы .Для этого Git предоставляет то, что Git называет рабочим деревом .Это просто место, где вы выполняете свою работу.
В рабочем дереве Git извлекает лиофилизированные файлы из некоторых коммитов, повторно их гидрируя, чтобы они имели свою обычную повседневную форму.Теперь вы можете видеть и работать с этими файлами.Вы просто выбираете один коммит - обычно, последний коммит в некоторой ветви - и говорите: Получите мне этот коммит , и Git сделает это.Он находит фиксированный коммит и перечисляет все файлы в нем:
main-folder/sub1/file1
: Ага , говорит Git, это рабочее дерево не имеет main-folder
,давайте сделаем один.И у него нет sub1
в main-folder
, который я только что сделал, давайте сделаем это тоже.Теперь я могу создать новый файл main-folder/sub1/file1
. main-folder/sub1/file2
: Привет , говорит Git, там уже есть главная папка / sub1, я могу просто создатьтам новый файл file2
.
Этот процесс повторяется по мере необходимости: Git имеет файлов , как указано в коммите, которые он должен восстановить.Когда с этим покончено, если рабочее дерево было пустым при запуске, ну, теперь у него есть регидратированная версия каждого файла из этого коммита. папки не хранились, но их не нужно было хранить.
Если выТеперь вы переключаетесь с , чтобы зафиксировал другой коммит, Git удалит все файлы, созданные для этого коммита, и заменит их файлами для другого, другого коммита. Если он удаляет все файлы из main-folder/sub1
, он также удаляет каталог main-folder/sub1
. Если в main-folder
он все удаляет, он тоже удаляется. Затем идет извлечение всех файлов из коммита, который вы хотите сейчас, создание любых каталогов / папок, как требуется.
Фактически, Git чередует всю эту работу, создавая и удаляя и оптимизируя: если вы переключаетесь с коммита a123456...
на коммит b789abc...
, и 99% файлов в двух коммитах одинаковы , ну, в конце концов, нет необходимости копаться с ними в рабочем дереве, не так ли? И с этой конкретной формой git checkout
Git добавляет проверку безопасности перед переключением коммитов: Для каждого файла, который я должен удалить или заменить, является ли файл в рабочем дереве "чистым"? Если Файл «чистый», его можно удалить или заменить. Если он «грязный» - если вы изменили его с тех пор, как Git извлек его, и вы можете сохранить свои изменения, чтобы переключение могло сработать - Git предупредит вас об этом и по умолчанию откажется переключать коммиты.
Указатель / промежуточная зона
В этом процессе есть одна огромная морщинка. Прочитав вышесказанное, вы подумали бы: Хорошо, у нас есть коммиты с сублимированными файлами и рабочим деревом с обычными файлами. Но есть третий объект, который Git помещает между эти двое. Это индекс / область подготовки.
Как и сами коммиты, индекс в основном невидим. На самом деле это просто обычный файл, в большинстве случаев .git/index
- со временем это усложняется, но начинается как простой файл. В этом файле находится, по сути, копия извлеченного вами коммита - все лиофилизированные файлы, использующие только хеш-идентификатор (например, хеш-идентификаторы коммитов) для их идентификации. Однако, в отличие от фактически замороженных файлов в коммитах, копии в индексе можно изменить. 2
Это то, что делает git add
: он замораживает файл и вставляет эту версию в индекс. Если файл не был в индексе раньше, ну, теперь это так. Если это было в индексе ранее, это исключает предыдущую версию. В любом случае новый высушенный замораживанием файл готов к фиксации. Когда вы запускаете git commit
, Git просто упаковывает все готовые к использованию файлы из индекса в новый коммит. Именно поэтому почему git commit
так быстро: на самом деле осталось совсем немного работы.
Файлы в индексе вообще не хранятся в папках. Есть только один гигантский список: файл path/to/file1
содержит эти лиофилизированные файлы, файл path/to/file2
содержит эти другие лиофилизированные материалы и так далее. Но так или иначе, наличие файла в индекса - вместе с лиофилизированным содержимым, готовым к фиксации, - это то, что делает регидратированный файл в рабочем дереве отслеживаемым . отслеживаемый файл - это файл, который находится в индексе, поэтому неотслеживаемый файл - это просто любой файл, который находится в рабочем дереве, но отсутствует в индексе. Так как git commit
архивирует то, что находится в index , а не то, что находится в рабочем дереве, только 1181 * отслеженные файлы фиксируются.
2 Сложность в том, что помещение нового файла в индекс фактически останавливает его сушку и сохраняет его в репозитории , создавая новый идентификатор хэша, если новый содержимое действительно новое или разделяет какой-либо существующий хэш-идентификатор, если сублимированное содержимое совпадает с любым существующим файлом Теперь, когда предположительно новый файл уменьшен до хеш-идентификатора, он помещается в тот же слот в индексе, который занимал старый файл!
С этим из пути ответ теперь прост
МакКаждый новый коммит, который в этом коммите хранит только определенных файлов, просто настройте так, чтобы в вашем index были только эти файлы.Для этого удалите все полностью нежелательные файлы из вашего индекса, что также удалит копии рабочего дерева:
git rm ...
, так как индекс хранит их по их путь относительно вершины рабочего дерева , вам нужно сохранить все файлы, которые вы хотите сохранить где-нибудь.Самый простой способ сделать это - переименовать их в рабочем дереве и с индексом:
git mv main-folder/sub1 sub1
, который создаст (переименовав в этомcase) папку sub1
в вашем рабочем дереве, если необходимо, затем переименуйте все отслеживаемые файлы в индексе - помните, что git mv
должен работать как с индексом, так и с рабочим деревом -из их main-folder/sub1/file1
и т.д. путей, чтобы иметь sub1/file1
и т.д. пути.Команда git mv
, как и команда git rm
, затем перетаскивает файлы рабочего дерева вместе с ней.
(Удобно, а иногда и нет, когда git mv
переименовывает папку на месте, чтотакже переименовывает любые неотслеживаемые файлы внутри него. Так как остальная часть Git на самом деле не заинтересована в неотслеживаемых файлах, более поздняя git checkout
не будет перемещать их назад!)
Так как нижевсе, Git хранит файлы по содержимому - используя хэш-идентификаторы высушенных замораживанием файлов - все это переименование в основном бесплатное.Git нужно немного места для хранения обновленных имён - коммиты должны хранить полные имена вместе с хэш-идентификаторами, и имён здесь нельзя легко разделить, так как они 3 - но фактическое содержимое передается файлам с другими именами в других коммитах.
Обратите внимание, что когда вы переключаетесь между этими коммитами, с этими * 1237При вводе имен *, для любого коммита, который имеет имена типов main-folder/sub1/file1
, Git, возможно, придется сильно сжать ваше рабочее дерево, сначала удалив все имена sub1/file1
, а затем создав новые, пустые main-folder
и main-folder/sub1
каталоги для хранения (в конце концов, таких же!) файлов, которые были в sub1/file1
и так далее.Когда и если Git может быть достаточно умным, чтобы понять, что он может просто переименовать эти файлы в рабочем дереве, Git может сделать это, но простой тупой способ, с которого Git обычно начинает работать, это простоудалить и воссоздать их.Это будет отображаться в отметках времени файла на уровне ОС: если Git удаляет файл и создает его заново, он получает отметку «сейчас» в качестве временной отметки файла рабочего дерева на диске.
3 Внутри коммитов - но не в индексе - Git возвращается к древовидной схеме именования.Так что если sub1
на верхнем уровне этого нового коммита на 100% идентичен sub1
, который был в main-folder/sub1
некоторого другого коммита, Git фактически поделится базовым деревом объекта для sub1
поддерево корневого дерева нового коммита.Разумеется, корневое дерево будет другим, поскольку оно будет называть sub1
одним из своих поддеревьев, а не называть main-folder
одним из своих поддеревьев.Но все это просто детали реализации: ничего из этого не отображается в индексе и рабочем дереве.