Я не могу раздавить мои коммиты - не могу отсоединить ГОЛОВУ

Я исправил свой.gitignore, но мне потребовалось несколько коммитов - теперь мне нужно их раздавить, но перебазировка закончилась ошибкой, и перебазировка была прервана

Я попытался перебазировать с изменением выбора на сквош, используя sourcetree и -f

`git rebase -i e9147 -f
error: The following untracked working tree files would be overwritten by checkout:
        .vscode/settings.json
        DatingApp.API/.vscode/launch.json
        DatingApp.API/.vscode/tasks.json
Please move or remove them before you switch branches.
Aborting
could not detach HEAD`

Как я могу сделать это правильно?

1 ответ

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

Эта ситуация - по крайней мере, непосредственная проблема, даже до того, как нужно применить магию перебазирования, - эквивалентна случаю, когда у вас есть неотслеживаемые файлы, но вы хотите git checkout какая-то другая ветка, в которой отслеживаются те же файлы. Подробнее об этом см. В разделе " Невозможно переключить ветви в git": ошибка: Следующие неотслеживаемые файлы рабочего дерева будут перезаписаны извлечением, а git сообщает: "Следующие неотслеживаемые файлы рабочего дерева будут перезаписаны извлечением" при переключении ветвей. Однако многие из этих решений заключаются в том, чтобы преднамеренно отслеживать эти файлы и фиксировать их сейчас, чтобы их можно было безопасно удалить. Кажется вероятным, что .vscode/settings.json По крайней мере, действительно не следует отслеживать (и, следовательно, не совершать).

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

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

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

Please move or remove them before you switch branches

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

Долго: что такое файл без отслеживания и почему это происходит?

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

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

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

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

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

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

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

Индекс, или промежуточная область, на самом деле имеет много ролей, но его роль номер один в том, что он содержит предложенный вами следующий коммит. То есть, в нем есть все ваши файлы в высушенном виде. Затем вы редактируете некоторый файл рабочего дерева, чтобы изменить его. Это изменение не будет в следующем коммите - пока нет! git add файл. какая git add делает, это взять копию рабочего дерева, ту, которую вы только что отредактировали, и повторно сжать ее до лиофилизированного формата. Новый, обновленный, высушенный замораживанием файл попадает в вашу область индекса / промежуточного хранения, перезаписывая старый. Теперь новый и обновленный файл будет в следующем коммите: вы изменили ваш предложенный коммит, чтобы использовать новую копию (уже высушенную) вместо старой.

(Это одна из причин git commit это так быстро, по сравнению с другими старыми системами контроля версий. Все git commit нужно сделать, это упаковать предварительно высушенные файлы. Другие VCSs во время фиксации не имеют индекса с готовой фиксацией. Они должны собрать каждый файл из вашего рабочего дерева, превратить их в любой формат, используемый внутри, и зафиксировать это; и этот процесс медленный, занимает несколько секунд или даже минут. Git даже не нужно смотреть на ваше рабочее дерево: индекс всегда готов к работе.)

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

Обратите внимание, что при запуске git status Git выполняет два отдельных сравнения:

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

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

Когда вы делаете новый коммит, Git упаковывает все в индекс - все лиофилизированные файлы - и добавляет метаданные, и у вас есть новый коммит, который сохраняет эти файлы навсегда (или до тех пор, пока новый коммит все равно существует). Но - вот идут неотслеживаемые файлы! - что произойдет, если в вашем рабочем дереве есть файл, такой как .vscode/settings.json, а это не в индексе?

Что ж, если файл отсутствует в индексе, он не будет в следующем коммите. Так что если вы git commit прямо сейчас, новый коммит не будет иметь .vscode/settings.json в этом. Файл будет продолжать находиться в вашем рабочем дереве, а не в вашем индексе.

Вот что такое неотслеживаемый файл. Файл без отслеживания - это файл, который находится в рабочем дереве и отсутствует в индексе.

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

Мы также можем в любое время удалить файлы из индекса, используя git rm, если ты git rm .vscode/settings.json (и файл находится в индексе), Git удалит файл и из индекса, и из рабочего дерева. если ты git rm --cached .vscode/settings.json, Git удалит файл из индекса, но оставит версию рабочего дерева в покое.

Все идет нормально: git add помещает файлы, git rm --cached вынимает их, и никто не портит копию рабочего дерева (если вы помните --cached). Вот проблема, однако: git checkout также помещает файлы и извлекает файлы из индекса... и рабочего дерева.

предполагать .vscode/settings.json прямо сейчас Это означает, что оно находится в рабочем дереве, а не в индексе. Предположим, совершить a123456 на какой-то конкретной ветке, есть лиофилизированная копия .vscode/settings.json, Это может иметь то же содержимое, что и в вашем рабочем дереве, или это может быть другая версия, но дело в том, что это в коммите. если ты git checkout что совершить, Git поместит эту версию .vscode/settings.json в свой индекс и положить эту версию .vscode/settings.json в ваше рабочее дерево. И теперь файл отслеживается!

Вы хотите, чтобы файл не был отслежен? Вы все еще хотите, чтобы он не был отслежен? Ну, это отслеживается сейчас, потому что вы проверили a123456, Ваш единственный вариант - не проверять a123456 после всего.

Единственный способ справиться с этим - зафиксировать текущую версию .vscode/settings.json - теперь он заморожен навсегда, и вы можете получить его обратно, пока ваш новый коммит продолжает существовать - или убрать его с пути, чтобы, когда Git извлекает коммит a123456 и переписывает это, ну, это не мешает и не перезаписывается.

Единственное, что может быть болезненным, - это "переписать историю". Если файл .vscode/settings.json Первоначально никогда не нужно было фиксировать, вы можете найти каждый коммит в репозитории, в котором есть файл, извлечь этот коммит, удалить файл и сделать новый и улучшенный коммит, который похож на оригинал, но не имеет файл. Затем, улучшив все эти коммиты - что в Git также требует улучшения всех коммитов их детей, так что это довольно большая проблема - вы должны прекратить использовать старые коммиты в пользу новых и улучшенных.

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

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

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

Первый требует заботы навсегда; второй требует, чтобы каждый клон хранилища был исправлен.

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