Почему мой Git Merge прерывается с ошибкой: ваши локальные изменения в следующих файлах будут перезаписаны слиянием? - PullRequest
0 голосов
/ 11 октября 2019

У меня очень простой репо для одного модуля. У меня есть «развивающаяся» ветка, в которую я хочу объединить «функциональную» ветку. Моя ветвь разработки чиста, и ничего не нужно делать. Я зафиксировал в своей ветке возможностей, проверенную ветку разработки. Когда я попробовал git merge --no-ff featurebranch, Git прервался с ошибкой «Ваши локальные изменения в следующих файлах будут перезаписаны слиянием». Я не уверен, что теперь делать ... единственный файл, который я изменил в featurebranch, был файлом с именем styles.css ... Я хочу, чтобы мои изменения были объединены в версию styles.css

для ветки разработки

Ответы [ 3 ]

1 голос
/ 12 октября 2019

То, что вы получаете эту ошибку, означает, что что-то не"чистое".

Вы сказали:

МойРазработка ветки чиста, ничего не нужно совершать.

Но в Git это не имеет смысла, потому что ветви никогда не бывают чистыми или грязными, как бы мы ни определяли слово"ветка". (См. Что именно мы подразумеваем под «ветвью»? ). Что является чистым или не очень чистым в Git, так это index и / или ваше рабочее дерево.

Прежде чем мы определим эти два термина - вам понадобятся довольно хорошие определения для продолжения слияния - мы должны сначала отметить тот факт, что Git действительно все о коммитах . Ветви, или, точнее говоря, ветви имён , такие как develop и feature, в основном просто сопливы для нас, простых людей, которые не могут иметь дело с фактическими именами коммитов, которые являются большими уродливыми идентификаторами хешей.

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

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

С другой стороны, индекс может быть «чистым» или «грязным», как и ваше дерево работ. Термины «чистый» и «грязный» не являются четко определенными и не очень формальными, хотя gitglossary фактически определяет их (хотя только в терминах рабочего дерева). Прежде чем перейти к следующему фрагменту, мы должны точно определить, что такое индекс и рабочее дерево .

Рабочее дерево - это место, где вы выполняете свою работу

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

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

Ваше рабочее дерево "чистое", если оно соответствует ... ну, что-то. Гитлоссарий не говорит точно, что, хотя он использует фразу "не совершено". В противном случае он «грязный».

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

Индекс более сложный, но мы можем назвать его предложенным next commit

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

Индекс, также называемый промежуточной областью или (редко сейчас) кеш , содержит эту третью копиюкаждого файла, который будет в следующем коммите, который вы делаете. Эти файлы уже в специальном формате Git-only, готовые для фиксации. Фактически, наличие файла в области index / staging - это то, что делает файл отслеженным . Файл без отслеживания находится в рабочем дереве, но не в индексе. отслеживаемый файл находится в обоих. 2

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

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

Во время слияния индекс играет расширенную роль, но мы не будемвдаваться в подробности здесь. Мы немного поговорим об этом в следующем разделе ниже.

В gitglossary не говорится о «чистом» или «грязном» индексе, но мы можем определить его аналогично. Если индексная / промежуточная область точно совпадает с текущим коммитом , он должен быть «чистым»: в нем нет файлов, которые, если мы заменим их на какой-то другой коммит, мыможет потерять их. Если нет - если в индексе есть некоторые файлы, которые не являются в текущем коммите или чье содержимое изменилось, - индекс должен быть «грязным». Мы можем очистить его двумя способами:

  • записать его в новый коммит (например, git commit или git stash - отметим, что git stash делает коммиты!), Или
  • полностью удаляет все новые файлы и заменяет любые существующие файлы копиями из текущего коммита (например, git reset HEAD).

Очевидно, что последний рискует потерять файлы. Например, если мы сделаем:

echo precious > new-file    # put some precious data in a new untracked file
git add new-file            # copy the new file into the index (make it tracked)
rm new-file                 # remove the work-tree copy (for no good reason)

, тогда наши ценные данные будут находиться только в одной копии new-file, которая существует в области индекса / подготовки. Если бы мы не удалили копию рабочего дерева, удаление индексной копии было бы безопасным - или, по крайней мере, безопасным - потому что у нас все еще была бы копия рабочего дерева. Но если «драгоценный» контент на самом деле просто мусор, это нормально.


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

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

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


Теперь, когда все это прояснено, мы можем приступить к объединению

git merge --no-ff featurebranch

Что git merge делает, так это находит, затем объединяет три версии каждого файла:

  • Одна копия получается из того, что у вас есть в текущем коммите (HEAD). Этот коммит имеет моментальный снимок, то есть целую кучу файлов.
  • Одна копия получена из аргумента, который вы дали git merge: featurebranch. Это имя ветви идентифицирует один конкретный коммит, а именно tip commit этой ветки. Этот коммит также имеет снимок.
  • Третья, и в некоторых отношениях наиболее важная, версия каждого файла происходит из базы слияния . База слияния является коммитом, и Git находит этот коммит самостоятельно. Git делает это, используя информацию parent в каждом коммите.

Мы пропустим детали того, как Git находит базу слияния, но вы можете запустить:

git merge-base --all HEAD featurebranch

чтобы увидеть, какие коммиты являются базой (ами) слияния HEAD и featurebranch.

Когда я пытался git merge --no-ff featurebranch Git прервался с ошибкой "Вашлокальные изменения в следующих файлах будут перезаписаны слиянием ".

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

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

  • Слот 1 получает файл из базы слияния.
  • Слот 2 получает копию файла с HEAD.
  • Слот 3 получает копию файла с featurebranch.

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

  • Никто не изменил файл: все три соответствуют друг другу, все будет в порядке.
  • Вы изменили файл, а они этого не сделали:два матча, ваш - правильный, переместите слот 2 в слот 0 (и поместите его в рабочее дерево) и очистите другие слоты, и мы закончили.
  • Ониизменили файл, а вы этого не сделали: два совпадения, их - правильный, переместите слот 3 в слот 0 (и поместите его в дерево обработки), очистите другие слоты, и мы 'готово.

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

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

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

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

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

Git будет нуждаться в каракули по всему индексу. Поэтому ему нужен чистый индекс.

Ваш git merge отказывается запускаться, потому что индекс и / или рабочее дерево загрязнены. Точное сообщение об ошибке подразумевает, что проблема заключается в рабочем дереве.

Чтобы исправить это, убедитесь, что рабочее дерево действительно чистое. Есть несколько особых случаев, которые раздражают:

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

    Если они имеют этот файл в их коммит, тем не менее, ваше слияние будет необходимо скопируйте этот файл из их коммита в ваше рабочее дерево. Это перезапишет файл.

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

    Вы можете использовать git stash, чтобы временно сохранить его, но это просто делает некоторые коммиты, которые не включенылюбая ветвь, оставляя вас с проблемой, что вам придется объединить эти коммиты обратно, чтобы получить материал из них, что снова вызовет проблему. Я вообще не рекомендую использовать git stash.

  • Или, возможно, вы использовали git update-index с флагами --assume-unchanged или --skip-worktree. Это позволяет вам иметь файл в вашем рабочем дереве, который также есть в вашем индексе, а затем позволяет вам изменять копию work-tree , не заставляя Git обновлять копию index также. Новые коммиты, которые вы делаете, будут использовать индексную копию, которая, без сомнения, возникла в результате некоторых предыдущих коммитов.

    слияние процесс не может оставить этот файл в покое! Он должен работать с совершенными копиями. Решение этой проблемы аналогично: уберите файл с дороги. Повторно извлеките индексную / коммитную копию и отключите флаг (ы) с помощью git update-index --no-assume-unchanged и / или git update-index --no-skip-worktree. Теперь, когда все вернулось к нормальному лечению и файл очищен, git merge может работать.

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

Это все довольно болезненно, и я не рекомендую интенсивно использовать --assume-unchanged и / или --skip-worktree. (Я сделал это сам по разным причинам, но я не рекомендую делать это. :-))

0 голосов
/ 11 октября 2019

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

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

Тогда я бы посоветовал вам использовать rebase вместо слияния. Лично я бы никогда не использовал слияние, это зло. Перебазирование означает, что коммиты из функционального блока применяются к вашей ветке ДО ваших коммитов и ТОГДА ваши коммиты применяются сверху. После того, как вы спрятались, для ребазирования сделайте git rebase -p feature-branch. -p означает сохранение слияний, чтобы слияния из вашей функциональной ветви не терялись.

Наконец, снимите изменения с git stash pop, а затем разрешите конфликты, если таковые имеются.

ОБНОВЛЕНИЕ: Фактически, -p устарела, поэтому просто используйте git rebase -r feature-branch, он делает то же самое.

0 голосов
/ 11 октября 2019

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...