Что именно делает "git ls-files" и как мы можем удалить из него файл?

Показывает ли он файлы из локального хранилища, промежуточного хранилища, удаленного хранилища или откуда-то еще?

Я постоянно вижу файл, который присутствует в "git ls-files". Этот файл был удален из удаленного хранилища. После чего я попытался сделать git pull. Однако этот файл все еще отображается в этом списке команд. Он не должен присутствовать здесь, потому что он также отсутствует в удаленном хранилище.

6 ответов

Решение

Резюме

Вам нужно обдумать идею, что Git хранит как минимум три, а иногда до пяти активных копий каждого файла: одну в текущем коммите, одну (или две или три!) В индексе, а одну - единственную один, который вы можете видеть и работать - в вашем рабочем дереве. git ls-files Команда просматривает эти копии, а затем сообщает вам о некоторых из них, в зависимости от флагов, которые вы предоставляете git ls-files,

Без этой идеи о трех-пяти копиях каждого файла многие вещи в Git никогда не будут иметь никакого смысла. (Ну, некоторые вещи все еще сложно даже с этим, но это совсем другая проблема.)

Долго

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

Делает [ git ls-files ] показать файлы из локального хранилища,

Вроде, но:

промежуточное хранилище,

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

удаленный репозиторий

Определенно нет: не должно быть никаких удаленных репозиториев - то есть других Gits со своими собственными репозиториями - вообще, и, если есть, только git fetch а также git push пусть ваш Git вызывает их Git и обменивается с ними данными. (Что ж, git ls-remote делает первый немного git fetch, а также git pull работает git fetch Таким образом, эти два также обмениваются данными с удаленным. Но git ls-files нет.)

или откуда-то еще?

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

  • хранилище

    Коллекция ссылок вместе с объектной базой данных, содержащая все объекты, которые доступны из ссылок, возможно, сопровождаемые метаданными из одного или нескольких фарфоров. Репозиторий может совместно использовать базу данных объектов с другими репозиториями через альтернативный механизм. (все ссылки их)

    Это, конечно, полно еще большей терминологии. Чтобы попытаться немного де-мистифицировать его, они говорят, что собственно репозиторий не включает в себя индекс и рабочее дерево: он в основном состоит из коммитов (и их содержимого). Конечно, для этого необходимо определить "индекс" и "рабочее дерево", поэтому давайте перейдем к следующему:

  • индекс

    Коллекция файлов со статистической информацией, содержимое которых хранится в виде объектов. Индекс - это сохраненная версия вашего рабочего дерева. По правде говоря, он также может содержать вторую и даже третью версию рабочего дерева, которые используются при объединении.

  • рабочее дерево (я обычно называю это рабочим деревом):

    Дерево актуальных извлеченных файлов. Рабочее дерево обычно содержит содержимое дерева фиксации HEAD, а также любые локальные изменения, которые вы внесли, но еще не зафиксировали.

Коммиты заморожены навсегда

Когда ты бежишь git commit Git делает снимок всех ваших файлов - ну, в общем, всех ваших отслеживаемых файлов - и сохраняет их вместе с некоторыми метаданными, такими как ваше имя и адрес электронной почты, в коммите. Этот коммит в основном постоянный - вы можете избавиться от коммитов, как правило, с небольшими затруднениями, но просто подумайте о них как о постоянных для удобства и полностью, полностью, только для чтения. Это специально предназначено только для чтения, так как это позволяет другим коммитам совместно использовать идентичные копии файлов, так что если вы фиксируете один и тот же файл один раз, десять раз или даже миллион раз, на самом деле только одна копия этого файла в репозиторий. Только когда вы изменяете файл на новую версию, Git должен зафиксировать новую отдельную копию.

Коммиты нумеруются, но не с помощью простой удобной системы последовательной нумерации. То есть мы могли бы нарисовать их как ряд простых пронумерованных или обозначенных буквами вещей:

... <-C4 <-C5 <-C6 ...

где каждый последующий коммит указывает на своего непосредственного предшественника. Но их настоящие имена - большие уродливые хэш-идентификаторы. Каждый из них гарантированно уникален, поэтому он должен быть таким большим, безобразным и случайным. Каждый идентификатор хеша на самом деле представляет собой криптографическую контрольную сумму, рассчитанную по содержимому коммита, так что каждый Git во всем юниверсе согласится, что этот коммит и только этот коммит получает эту контрольную сумму. Это еще одна причина, по которой вы - и даже Git - не можете ее изменить: если вы берете коммит из базы данных репозитория, возитесь с ним, изменяете хотя бы один бит и затем помещаете его обратно в базу данных, то вы получаете новый коммит с новым и другим хеш-идентификатором.

Так что коммиты навсегда заморожены. Файлы внутри них также заморожены и сжаты в специальном формате Git-only. Мне нравится называть эти файлы "высушенными". Это означает, что, эй, они отлично подходят для архивирования, но они совершенно бесполезны для выполнения любой новой работы... и это означает, что Git должен предоставить какой-то способ взятия этих файлов, высушенных замораживанием, и регидратации их в полезная форма.

Рабочее дерево предоставляет копии полезной формы

На самом деле все не так просто: у рабочего дерева есть полезные формы, регидратированные копии ваших файлов. Поскольку они представляют собой обычные повседневные файлы на вашем компьютере, вы можете просматривать их, использовать их, изменять их по своему усмотрению и работать с ними. Технически они вообще не находятся в хранилище - они просто рядом с ним. В типичной конфигурации, само хранилище находится в .git каталог / папка верхнего уровня вашего рабочего дерева.

Очевидно, что если вы извлекли коммит для создания рабочего дерева, теперь должно быть две копии каждого файла: зафиксированный с фиксацией плюс обычный рабочий. Git может остановиться здесь. Mercurial останавливается на этом: если вы используете Mercurial вместо Git, вам не нужно беспокоиться о третьей копии, потому что третьей копии нет. Но Git продолжает хранить еще больше копий файлов.

Индекс / промежуточная область находится между коммитом и рабочим деревом

Что Git делает здесь, так это вставляет третью копию каждого файла между зафиксированной копией и копией рабочего дерева. Эта третья копия находится в формате зафиксированного файла, т. Е. Предварительно обезвожена, но, будучи не в коммите, она фактически не полностью заморожена: ее можно заменить в любой момент. Это то что git add делает: git add берет обычную копию файла из рабочего дерева, сжимает ее в лиофилизированный формат и заменяет копию в индексе. Или, если файл вообще не был в индексе, он помещает копию в индекс.

Вот почему вы должны git add файлы все время. В Mercurial вы только hg add файл один раз. После этого вы просто запускаете hg commit и Mercurial просматривает все файлы, о которых он знает, и фиксирует их в новом коммите. Это может занять много времени в большом хранилище. Git, напротив, уже имеет все файлы, о которых он должен знать, и уже обезвожен, в индексе, так что git commit может просто упаковать эти обезвоженные файлы в новый замороженный коммит. Стоимость этой скорости git add, но если вы начнете играть умные трюки с индексными копиями - например, используя git add -p - вы получаете больше преимуществ, чем просто ускорение.

Как упоминалось в глоссарии Git при описании индекса, индекс играет расширенную роль во время конфликтующего слияния. Когда вы делаете операцию слияния - будь то из git merge или из git revert или же git cherry-pick или любая другая команда Git, которая использует механизм слияния - и она не работает гладко, Git завершает помещение всех трех входов для каждого файла в индекс, так что вместо одной копии file.ext Вы получаете три. Но пока вы не находитесь в середине слияния, в индексе есть только одна копия.

Обычно индексная копия соответствует HEAD замороженная копия или совпадает с копией рабочего дерева, или обе. Например, после свежего git checkout Все три копии совпадают. Затем вы модифицируете file.ext в рабочем дереве: теперь фиксация и индекс совпадают, но они не совпадают с копией рабочего дерева. Затем вы git add file.ext, а теперь индекс и рабочее дерево совпадают, но они отличаются от замороженной копии. Затем вы git commit сделать новый коммит, который становится текущим коммитом, и все три копии снова совпадают.

Обратите внимание, что вы можете изменить копию рабочего дерева:

vim file.ext

затем скопируйте обновленный в индекс:

git add file.ext

затем отредактируйте его снова:

vim file.ext

и таким образом вы можете сделать все три копии разными. Если ты это сделаешь, git status скажет, что у вас есть изменения, подготовленные для фиксации, потому что копия индекса отличается от текущей фиксации, и скажет, что у вас есть изменения, не подготовленные для фиксации, потому что копия рабочего дерева отличается от копии индекса.

Рабочее дерево может содержать файлы, которых вообще нет в индексе

Индекс изначально является копией текущего коммита. Затем Git также копирует эти файлы в рабочее дерево, чтобы вы могли их использовать. Но вы можете создавать файлы в рабочем дереве и не запускать git add на них. Этих файлов нет в индексе сейчас, и если вы запустите git commit они также не будут включены в новый коммит, потому что Git создает новый коммит из индекса.

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

git rm --cached file.ext

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

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

Теперь мы можем ясно видеть, что git ls-files делает

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

git ls-files --stage

перечисляет файлы, которые находятся в области индекса / промежуточной области, а также номера их промежуточных слотов. (Это ничего не говорит о копиях в HEAD коммит и дерево работ.) Или:

git ls-files --others

перечисляет (имена) файлы, которые находятся в рабочем дереве, но отсутствуют в индексе. (Это ничего не говорит о копиях в HEAD совершить.) Или:

git ls-files --modified

перечисляет (имена) файлы, которые находятся в индексе и отличаются от их копий в HEAD совершить (или не в HEAD совершать вообще). Без вариантов:

git ls-files

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

git ls-files работает правильно в вашем случае. Как твой git status показывает, что файл X удален из рабочего каталога, это означает, что файл все еще существует в индексе. Вот почему git ls-files показывает X, потому что команда показывает содержимое индекса.

Теперь вам нужно удалить этот файл из индекса, просто запустите:

git rm --cached <pathToXFile>

Просто хотел поделиться:

Ссылаясь на принятый ответ /questions/48343145/chto-imenno-delaet-git-ls-files-i-kak-myi-mozhem-udalit-iz-nego-fajl/48343159#48343159 и обсуждение с torek:

Если вопрос был в том, как мне узнать, какие файлы / объекты должны быть там, если я проверил специальную фиксацию, другой ответ может быть примерно таким:

      git ls-tree -r -l HEAD

Торек также упомянул «(HEAD может быть символической ссылкой на несуществующее имя ветки)», но я пока этого не понимаю.

так более общий:

      git ls-tree -r -l commit-hash

Это также работает в репозиториях, клонированных с ключом -n (без проверки)

Просто интересно, где документирована магия вывода

извлечь из репо, клонированного с помощью: git clone -n https://github.com/nvie/gitflow.git

      100755 blob fd16d5168d671b8f9a8a8a6a140d3f7b5dacdccd    git-flow
100644 blob 55198ad82cbfe7249951aa75f1373a476997d33a    git-flow-feature
100644 blob ba485f6fe4b7d9c35bc01d2a6bd4ae201bccc9bd    git-flow-hotfix
100644 blob 5b4e7e807423279d5983c28b16307e40dfdb51d7    git-flow-init
100644 blob cb95bd486deb7089939362705d78b2197893f578    git-flow-release
100644 blob cdbfc717c0f1eb9e653a4d10d7c4df261ed40eab    git-flow-support
100644 blob 8c314996c0ac31f1396c48af5c6511124002dab7    git-flow-version
100644 blob 33274053347f4eec2f27dd8bceca967b89ae02d5    gitflow-common
120000 blob 7b736c183c7f6400b20ea613183d74a55ead78b5    gitflow-shFlags
160000 commit 2fb06af13de884e9680f14a00c82e52a67c867f1  shFlags

Моя интерпретация:

Хэши кажутся «контрольными суммами больших двоичных объектов» (без хэшей фиксации). Одна и та же контрольная сумма может отображаться более одного раза, если в фиксации было несколько файлов. Последние три полубайта, например, 100644 выглядят как свойства доступа к файлу linux (rw-r -r--). Первые три полубайта не равны 100, если объект не является обычным файлом. В реальной жизни gitflow-shFlags - это символическая ссылка, а shflags - это каталог подмодуля.

РЕДАКТИРОВАТЬ: только что наткнулся на https://github.com/git/git/blob/master/Documentation/technical/index-format.txt (GOOGLE: git --index-info, STACKOVERFLOW: Что ТОЧНО содержит индекс git? )

      32-bit mode, split into (high to low bits)

  4-bit object type
  valid values in binary are 1000 (regular file), 1010 (symbolic link)
  and 1110 (gitlink)

  3-bit unused

  9-bit unix permission. Only 0755 and 0644 are valid for regular files.
  Symbolic links and gitlinks have value 0 in this field.

Итак, если вы интерпретируете полубайты как восьмеричные значения

100644: 1'000' 000'110'100'100 -> тип объекта - обычный файл

120000: 1'010 ' 000'000'000'000 -> тип объекта - символьная ссылка

160000: 1'110' 000'000'000'000 -> тип объекта - gitlink

OMG: Почему так сложно извлечь такую ​​информацию напрямую из справочных страниц git?

Следующие вопросы: что такое gitlink? Это связано только с подмодулями git?

В Git 2.35 (1 квартал 2022 г.) "" изучает параметр "" для облегчения отладки.

Используется с разреженным индексом после git sparse checkoutкоманда .

См. коммит 408c51f , коммит c2a2940 , коммит 3a9a6ac , , коммит 5a4e054 (22 декабря 2021 г.) Деррика Столи ( derrickstolee).
(Объединено Junio ​​C Hamano -- gitster-- в коммите 3c0e417, 10 января 2022 г.)

коммит 7808709ls-files: добавить опцию --sparse

Подписал: Деррик Столи

Существующие абоненты ' ( man ) ' ожидают имена файлов, а не каталогов. В этом случае лучше всего расширить разреженный индекс, чтобы показать все содержащиеся в нем файлы.

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

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

git ls-filesтеперь включает в свою справочную страницу :

--sparse

Если индекс разреженный, покажите разреженные каталоги без расширения до содержащихся файлов.
Разреженные каталоги будут показаны с косой чертой в конце, например " x/"для разреженного каталога" x".

Я постоянно вижу файл, который присутствует в "git ls-files". Этот файл был удален из удаленного хранилища. После чего я попытался сделать git pull.

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

Если вы не хотите, чтобы это было в вашем индексе, удалите его. Обычный git rm --cached или если вы также хотите, чтобы это ушло из вашего рабочего дерева просто git rm,

Довольно часто, пока вы работаете, вы обнаружите какую-то глупую небольшую ошибку, которая нуждается в исправлении, но на самом деле не является частью вашей текущей задачи. Git делает обработку таких вещей очень простой: проверьте ветку с исправлениями ошибок из вашей базы обслуживания, зафиксируйте только это исправление, вернитесь к тому, что вы делали, и объедините это исправление.

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

Вы найдете другие случаи, когда Git обрабатывает работу в полете, избегая бесполезного оттока, важно то, как Git обрабатывает работу в полете: он остается в индексе, пока вы не решите, что с ним делать. До тех пор, пока вы не скажете Git поместить что-то еще, Git несет то, что вы добавили молча.

git ls-files объединяет список файлов в индексе кэша каталогов с фактическим списком рабочих каталогов и показывает различные комбинации этих двух.

для информации можно найти здесь

Редактировать:

Фактическая директория - это ваша текущая ветка на вашем локальном компьютере (только отслеживаемые файлы), и вы можете удалить файл из наличных, например: git rm --cached fileName

Другие вопросы по тегам