Вы не можете.
Хм, позвольте мне попробовать еще раз: вы не можете, но они могут. Что ж, вы можете, но только для вас, а они могут, но только для них. Вы или они должны запустить 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 три вещи. Первые два обычно являются двумя важными. Последний - ловушка.
Если имя или шаблон неотслеживаемого файла появляется в .gitignore
, команда git status
не будет жаловаться на неотслеживаемый файл.
Если имя или шаблон неотслеживаемого файла появляется в .gitignore
, git add
не будет добавлять файл в индекс Git (вы можете заставить git add
переопределить это, если хотите). Это означает, что файл не будет отслеживаться в течение обычных ежедневных git add
сек.
Если имя или шаблон неотслеживаемого файла указаны в .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