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

У меня и еще нескольких человек есть доступ к репозиторию, который включает один файл, автоматически сгенерированный IDE. Этот файл специфичен для ПК, поэтому его не должно быть в системе контроля версий, но в настоящее время он есть. Я хочу удалить его и добавить в .gitignore, но не хочу, чтобы его удаляли другие соавторы, когда они извлекают мои изменения. Есть много вопросов об удалении файла, но сохранении моей локальной копии; но они не покрывают других пользователей, поэтому, когда они потянут, они все равно потеряют свою копию, несмотря на то, что я оставил свою:

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

Как мне git rm файл без удалить его с диска?

Есть также вопросы и решения, чтобы не потерять локальные файлы при вытягивании, чтобы они могли сохранить файлы, но для этого требуются явные действия от тех, кто вытягивает, и я не хочу идти и рассказывать всем, как именно вытащить только этот время. Я нашел два повторяющихся вопроса. Были ответы, что это невозможно, но они оба были 5 лет назад - изменилось ли что-нибудь за это время?

Git удаляет отслеживаемые файлы, но сохраняет локальные И удаленные

Git игнорирует файл, не удаляя его

Это важно, потому что файл создается автоматически при первом импорте всего проекта и содержит информацию о локальных версиях компилятора / библиотеки. Поэтому для его удаления потребуется повторный импорт. Если это имеет значение, то это .idea/scala_compiler.xml и .idea/scala_settings.xml (на самом деле весь каталог .idea следует игнорировать). По сути, я хочу, чтобы Git установил, что файл больше не отслеживается, но никому не удалял его.


person Y_Less    schedule 09.08.2020    source источник
comment
Почему бы не поместить его в .gitignore? При вытягивании никому не нужно будет удалять.   -  person Tomer Shetah    schedule 09.08.2020
comment
Я лично не согласен с игнорированием файла и продолжением отслеживания версии, которая была актуальной на момент, когда вы решили прекратить ее отслеживание. Зачем сохранять на все время случайную версию файла, которая создается автоматически? Если он сгенерирован автоматически, его следует полностью удалить из git и добавить в .gitignore. Что касается принуждения людей к повторному импорту ... если они разработчики, им нужно знать, как это работает. Это хорошее неудобство. Отправьте быстрое электронное письмо, чтобы предупредить их и сделать правильный выбор.   -  person JoelFan    schedule 09.08.2020
comment
›Его следует полностью удалить из git и добавить в .gitignore. Да, это то, что я хотел, но, возможно, это было не очень понятно. Я не хочу отслеживать файл, я хочу, чтобы он всегда игнорировался с самого начала. Я просто не хотел, чтобы все потеряли свои локально неотслеживаемые, а теперь и неверсированные копии.   -  person Y_Less    schedule 10.08.2020
comment
см. этот окончательный вопрос / ответ - это возможно !: stackoverflow.com/questions/57418769/   -  person goofology    schedule 11.08.2020


Ответы (1)


Вы не можете.

Хм, позвольте мне попробовать еще раз: вы не можете, но они могут. Что ж, вы можете, но только для вас, а они могут, но только для них. Вы или они должны запустить git rm --cached в нужное время. Конечно, это решение, которое вы не хотите использовать.

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

Помните, что каждый коммит содержит полный и полный снимок всех файлов, о которых знает Git. (Мы немного уточним это через мгновение.) Если Git знает о .idea/*, Git поместит их в новых коммитах, и когда вы нажимаете эти коммиты - вы не можете нажимать файлы, только коммиты - эти коммиты вместе с этими файлами будут перемещаться. Когда вы получаете новые коммиты - опять же, вы получаете целые коммиты, а не файлы - эти коммиты приходят с этими файлами.

Тогда основная проблема становится такой:

  • Вы или они находитесь в коммите, в котором Git знает о .idea/*. У вашего текущего коммита есть файлы.
  • Вы или они получили новую фиксацию (и). Эти новые коммиты не содержат эти .idea/* файлы.
  • Если вы (или они) теперь попросите ваш (или их) Git переключить вас с текущего коммита, на коммит, в котором отсутствуют файлы. , ваш (или их) Git видит, что вы (или они) явно указываете своему (их) Git удалить файлы. Так и будет.

Решение этой проблемы:

  • Вы (они) должны указать своему (их) Git забыть эти файлы сейчас, чтобы копии этих файлов в рабочем дереве не отслеживались:

     git rm -r --cached .idea      # note the --cached
    
  • Теперь вы (они) говорите своему Git: переключитесь на новую фиксацию. Неотслеживаемые файлы вообще не находятся в представлении Git и не входят в новую фиксацию, поэтому Git не удаляет копии этих файлов из рабочего дерева.

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

Лонг: что здесь происходит

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

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

Поскольку вы буквально не можете использовать эти файлы для выполнения какой-либо реальной работы, Git должен извлечь фиксацию. Это то, что делает git checkout (или, начиная с Git 2.23, git switch): он извлекает сублимированные файлы из некоторого коммита в форму, которую вы действительно можете использовать (и изменять). Фиксация, которую вы выбрали для извлечения, а затем работы и / или работы, является вашей текущей фиксацией.

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

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

Вместо этого Git хранит третью копию - ну, на самом деле не копию, потому что он использует лиофилизированный, дедуплицированный формат - в том, что Git называет своим < em> index, или staging area, или (редко в наши дни) cache. Эта кешированная, сублимированная, предварительно дедуплицированная копия файла в формате сублимационной сушки готова к использованию в следующей фиксации, которую вы сделаете.

Повторим это жирным шрифтом, потому что это ключ здесь: Индекс Git содержит файлы, которые войдут в следующую фиксацию в формате сублимационной сушки, готовые к работе. Операция git checkout или git switch заполняет индекс Git и ваше рабочее дерево из фиксации, которая теперь является текущей фиксацией. Все три копии теперь совпадают, за исключением того, что копию рабочего дерева можно использовать вместо сублимационной сушки.

Если вы измените копию рабочего дерева, вы должны запустить git add на нем. Команда git add сообщает Git: Сделайте так, чтобы ваша индексная копия соответствовала моей копии рабочего дерева. Git теперь будет читать копию рабочего дерева, сжимать и дедуплицировать ее в лиофилизированный формат < / em>, готов перейти к следующей фиксации. Таким образом, файлы в индексе больше не соответствуют файлам в текущей фиксации. Другими словами, ключевое различие между index и commit состоит в том, что вы можете изменять содержимое индекса, заменяя файлы оптом, как это.

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

Команда git rm

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

Теперь, когда файл или файлы не в индексе Git, они не будут в следующей фиксации. Итак, как только вы удалите файлы, вы можете сделать новую фиксацию, у которой нет файлов:

git rm -r --cached .idea && git commit

например.

Коммиты переключения

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

Если Git знает о .idea/*, это то, что заставляет .idea/* файлы удаляться. Если они не в новой фиксации, они не возвращаются из новой фиксации.

.gitignore имеет ловушку для неосторожных

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

Отметим здесь, что неотслеживаемый файл - это файл, который находится в вашем рабочем дереве прямо сейчас, но не в индексе Git прямо сейчас < / em>. Это означает, что если .idea/* отслеживались - например, вышли из текущей фиксации, - но вы только что запустили git rm --cached .idea/* или git rm -r --cached .idea, эти копии рабочего дерева теперь не отслеживаются. Не имеет значения, находятся ли они в текущей commit:, важно то, находятся ли они в index Git прямо сейчас.

.gitignore сообщает Git три вещи. Первые два обычно являются двумя важными. Последний - ловушка.

  1. Если имя или шаблон неотслеживаемого файла появляется в .gitignore, команда git status не будет жаловаться на неотслеживаемый файл.

  2. Если имя или шаблон неотслеживаемого файла появляется в .gitignore, git add не будет добавлять файл в индекс Git (вы можете заставить git add переопределить это, если хотите). Это означает, что файл не будет отслеживаться в течение обычных ежедневных git addсек.

  3. Если имя или шаблон неотслеживаемого файла указаны в .gitignore, Git иногда может затереть файл.

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

Возможно, вам знакома эта проблема: вы начинаете работать с каким-то файлом, то есть с копией в вашем рабочем дереве, а затем понимаете: Ой, я хотел проделать эту работу в другой ветви. Вы запускаете git checkout branch или git switch branch, и Git говорит несколько загадочно: Я не могу этого сделать. Git сообщает вам, что у вас есть несохраненные изменения, которые могут быть затерты.

(Иногда Git все равно позволяет переключать ветки. Все это опять же связано с индексом Git. Подробности см. В Оформить заказ в другой ветке когда в текущей ветке есть незафиксированные изменения)

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

Единственное полное решение болезненно

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

Для этого используйте git filter-branch или git filter-repo (относительно новый и еще не распространяемый с самим Git), или BFG, или любую подобную систему редактирования истории коммитов Git. По необходимости все они работают так: они копируют старые коммиты - те, у которых есть файлы, - в новые коммиты с разными идентификаторами хэша, в которых эти файлы никогда не появляются. Это изменение затем распространяется со временем на все последующие коммиты. Вот почему новый репозиторий несовместим со старым.

Если вы когда-нибудь позволите старому репозиторию встретиться с новым, и есть какая-либо связанная история, которая не изменилась, 1 два Git объединят старую и новую истории и вы по существу удвоите размер своего репозитория, добавив обратно все коммиты, от которых, как вы думали, избавились.


1 Это будут исторические коммиты, предшествующие существованию нежелательных файлов. Например, если вы воспользуетесь уловкой GitHub, начав с файла README.md и LICENSE, эта фиксация не потребует перезаписи, останется неизменной и установит общую историю фиксации между старым и новым репозиториями.

Кроме того, если вы используете старый Git, созданный до флага --allow-unrelated-histories, или передаете --allow-unrelated-histories в git merge, он также может объединить старую историю с новой.

person torek    schedule 09.08.2020
comment
Спасибо за очень подробное объяснение, даже если оно было очень подробным, почему вы не можете сделать то, на что я надеялся. Это также подчеркнуло проблему, заключающуюся в том, что даже если это будет сделано жестким (ручным) способом, это все равно вызовет проблемы при переключении на другие коммиты. Я надеялся, что, возможно, для этого был добавлен какой-то нечистый метод, даже если он не совсем соответствовал логической модели файла / фиксации. Я действительно думаю, что это все еще может быть каким-то особенным образом, даже если это еще не так. Но спасибо. - person Y_Less; 10.08.2020
comment
К сожалению, даже если Git обзаведется каким-то специальным методом для решения этой проблемы, он не сможет исправить какие-либо старые версии Git. Поэтому я не думаю, что люди из проекта Git считают это приоритетом. Я не уверен, что произойдет, если вы напишете свой собственный метод и отправите его (см. Документацию по отправке исправлений в исходном зеркале Git на github.com/git/git). - person torek; 10.08.2020
comment
думал, что вмешаюсь - файлы могут быть удалены из истории git (забыты) И проигнорированы (но не удалены!) для локальных И удаленных пользователей! Конечно, любые НОВЫЕ тяги не будут иметь файла, и историю git необходимо переписать, что потенциально является ОГРОМНОЙ сделкой, но это МОЖНО сделать: stackoverflow.com/questions/57418769/ - person goofology; 11.08.2020
comment
@goofology: это все правильно, но учтите, что другие пользователи, у которых все еще есть оригинальные клоны, по-прежнему будут иметь все исходные коммиты. Таким образом, эти удаленные пользователи должны избегать git pull, чтобы не слить исходные коммиты обратно в перезаписанную историю: им следует просто выбросить свои существующие клоны и создать новые. (В этом случае подойдет любой метод, очищающий старые коммиты.) - person torek; 11.08.2020
comment
Мне нужно это исследовать. Я считаю, что команды «fetch --all» и «git reset FETCH_HEAD» позволят избежать слияния исходных коммитов обратно? Я не пробовал git pull. Или, может быть, я это сделал, и вышеназванный метод оказался неудачным. - person goofology; 11.08.2020