Новые файлы при разрешении конфликта слияния в Git - PullRequest
0 голосов
/ 01 апреля 2020

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

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

1 Ответ

1 голос
/ 01 апреля 2020

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

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

Подробнее

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

  • Git не совсем о файлы , и даже не совсем о ветках . Git это все о коммитах . Это означает, что вам необходимо точно знать, что такое коммит и что он делает.

  • Каждый коммит содержит две вещи: его основные данные в виде снимка всех ваших файлов; и некоторые метаданные , или данные о данных, которые описывают этот коммит. Метаданные включают в себя то, кто сделал это, когда и - важно для людей, хотя и не имеет отношения к Git себе - почему вы (или кто бы то ни было) сделали коммит. Они также включают в себя commit ha sh ID . Эти идентификаторы ha sh очень важны для Git, хотя вы сами можете не заботиться о них.

  • Каждый коммит получает уникальный га sh ID. «Истинное имя» каждого коммита - это его ha sh ID. Эти идентификаторы ha sh показывают, как Git находит коммиты. Если вы хотите получить файлы из из Git, вы будете использовать идентификатор ha sh, даже если он скрыт с помощью имени: имя ветви, например master или develop, или имя тега типа v2.1 или что-то еще.

    Эти идентификаторы ha sh большие и некрасивые, и людям невозможно иметь с ними дело. Они нуждаются в , чтобы быть большими и безобразными из-за этого ограничения, что каждый коммит имеет уникальный идентификатор ha sh. Очевидный подход - просто нумеровать их последовательно (фиксация # 47 наступит после фиксации # 46 и т. Д. c) может сработать, за исключением того факта, что Git является распределенным . Нет центрального Git Commit Number Assigner, чтобы каждый мог go получить следующий номер.

    Так как они большие и некрасивые, мы, как правило, вообще не смотрим на них. Мы используем имена, о которых мы подробнее расскажем через мгновение.

  • Каждый коммит, ну почти каждый коммит, имеет родительский коммит. Родитель - это коммит, который пришел как раз перед этим. Вот о чем эти дополнительные метаданные: коммиты хранят идентификатор ha sh своих родителей. Коммиты слияния являются особенными ровно одним способом: они хранят более одного родителя, т. Е. Прямо перед ним имеется более одного коммита.

    (Самый первый коммит, сделанный кем-то, имеет нет родителя, потому что нет более раннего коммита. Этот коммит называется root commit . У каждого непустого репозитория есть по крайней мере один, и необычно иметь больше чем один, хотя возможно сделать новые root коммитов или приобрести их другими способами.)

  • A имя ветви , как и master, просто запоминает необработанный ха sh Идентификатор последнего коммита в ветви.

  • Следовательно, ветви растут , на добавляя новые коммиты . Добавление одного нового коммита состоит из создания нового снимка - новой копии каждого файла - с parent , установленным на current commit . Git затем заставляет имя ветви помнить новый коммит га sh ID.

Поэтому мы можем рисовать коммиты в виде цепочки, с самым новым коммитом справа (или сверху git log --graph), например:

... <-F <-G <-H   <--master

Здесь каждая заглавная буква обозначает какой-то большой уродливый коммит, ха sh. последний коммит ha sh равен H, а H содержит идентификатор ha sh его родителя G. Git может использовать идентификатор ha sh в H, чтобы найти G. Commit G содержит идентификатор ha sh его родителя F, поэтому Git может найти F. F содержит ha sh ID своего родителя и т. Д.

Но как нам найти H? Вот где приходит имя ветви : имя ветви просто содержит идентификатор ha sh * last commit.

Следовательно, для добавления коммита master, Git:

  • записывает коммит, включая родительский га sh ID H
  • , который вычисляет новый уникальный идентификатор ha sh который мы назовем I
  • , который Git затем вставляется в имя ветви master, давая

    ... <-F <-G <-H <-I   <--master
    

Коммиты являются снимками, но вы просматриваете изменения

Когда у нас есть линейная цепочка коммитов, подобная этой:

...--F--G--H   <-- master

и мы просим Git show us commit H, мы видим изменения , а не снимок. Но это потому, что это полезно , поэтому Git фактически извлекает и фиксирует G и H во временные области (действительно в памяти), а затем сравнивает им.

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

Конечно, вы можете иметь разные файлы в двух коммитах. Возможно, у G и H есть README.md - и, возможно, они даже одинаковы в обоих коммитах - но, возможно, H имеет file.py, которого вообще нет в G. В этом случае G -vs- H показывает новый файл .

Обратите внимание, что в G также могут быть файлы, которых нет в H; в этом случае сравнение, как это делает Git, говорит о том, что файл удален . Это все еще там в коммите G, как полный снимок. Его просто нет в H.

Несколько ветвей

Если у вас более одного имени ветви, то вы можете нарисовать так:

          I--J   <-- master
         /
...--G--H
         \
          K--L   <-- other

Два имени, master и other, выбирают два коммита по ha sh ID. Коммит J - это вершина master - последнего там - а коммит L - это вершина other.

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

          I--J   <-- master (HEAD)
         /
...--G--H
         \
          K--L   <-- other

Обратите внимание, что коммиты, включая H, включены обе ветви . (Git здесь необычно; большинство систем контроля версий не работают так.) Коммиты I и J работают только на master, и - по крайней мере, прямо сейчас - коммиты K-L только на other.

Объединение - это объединение работы

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

Поскольку Git хранит только снимки а как Git найдет изменения? Мы уже видели, как Git может сравнивать коммит с его родителем. Но предположим, что у вас есть:

          I--J   <-- master (HEAD)
         /
...--G--H
         \
          K--L   <-- other

Как мы можем сравнить сделанные вами изменения в master с изменениями, которые они внесли в other? Ответ Git на это: найти лучший общий коммит, который находится на обеих ветвях. Здесь это, очевидно, коммит H. Поэтому Git теперь сравнивает все файлы в H со всеми файлами в J:

git diff --find-renames <hash-of-H> <hash-of-J>   # what we changed

Затем Git сравнивает все файлы в H со всеми файлами в L:

git diff --find-renames <hash-of-H> <hash-of-L>   # what they changed

Git может затем объединить изменения . Что бы мы ни делали, Git может сделать то же самое с файлами в H, но и сделать то же, что они сделали с файлами в H.

Конфликты слияния

Иногда, хотя, пытаясь объединить эти изменения, Git сталкивается с проблемой. Например, что если мы изменили строку 42 в README.md, а они также изменили строку 42 в README.md, но мы сделали различных изменений? В этом случае Git делает все возможное объединение и затем останавливается с конфликтом слияния .

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

Впрочем, есть хорошие шансы, что вы будете работать git status во время и / или после вашего процесса разрешения. Это git status говорит о разных вещах в зависимости от того, где вы находитесь в процессе разрешения. Здесь я собираюсь предположить, что вы закончили с разрешением - что git status говорит все конфликты разрешены или ничего не говорит о неотправленных файлах . (Точный результат зависит от вашего Git года изготовления вина; старые Gits, выпущенные до серии 2.x, здесь не так хороши, а все, что старше 1.8.4, действительно не годится.)

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

          I--J   <-- master (HEAD)
         /
...--G--H
         \
          K--L   <-- other

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

Теперь предположим, что при разнице от H до L, Git обнаружили, что они добавлено несколько новых файлов. Эти файлы не в H, а в L. Эти файлы, вероятно, не находятся в I и J.

Итак, Git взял эти новые файлы из L, и теперь они находятся в вашем предложенном следующем коммите . Если вы сделаете коммит слияния M сейчас, эти файлы будут существовать. Сравнивая J с этим предложением, эти файлы новые файлы .

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

Вы все еще находитесь на этапе "разрешения", независимо от того, считаете ли вы, что закончили. Вы сказали Git, что все конфликтующие файлы, на которые Git жаловались, завершены. Их разрешенные версии готовы к добавлению go в новый коммит, а git status сравнивает файлы в J с файлами в предлагаемом новом коммите и говорит, что они разные (если они есть). Но вы можете, находясь в этом состоянии, продолжать делать больше изменений - тех, которые произошли не из коммитов J или L.

Редко хорошая идея сделать больше меняется. Люди называют изменения, сделанные в этой точке, злым слиянием 1348 * 1349. См. Зло сливается в git? для получения дополнительной информации об этом. Вы можете сделать это, и если вы чувствуете, что для этого есть веская причина, возможно, вам следует все же это сделать. Помните, что у вас есть шанс объяснить, что вы сделали это, и почему вы это сделали, когда делаете новый коммит слияния. Но вы, вероятно, хотите сохранить новые файлы здесь, как новые файлы.

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

git merge --continue    # or git commit, if your Git does not have --continue

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

          I--J
         /    \
...--G--H      M   <-- master (HEAD)
         \    /
          K--L   <-- other

Новый коммит M теперь имеет двух родителей, вместе со снимком, как и любой коммит, а также с автором и датой и временем. печать и запись сообщения и так далее. Первый родитель - J, а второй - L, потому что вы сказали git merge other и other фиксация имен L.

Просмотр коммитов слияния отличается

Когда Вы go, чтобы просмотреть этот коммит позже, Git не покажет , что по умолчанию изменилось. Это потому, что Git не знает, с кем из родителей сравнивать. Должны ли Git извлечь J и M и сравнить эти два? Или он должен извлечь L и M и сравнить их? Команда git log -p является ленивой и просто не выполняет ни одной из них.

Существуют другие команды Git и другие способы просмотра изменений, позволяющие выбрать, какого из родителей использовать. Самое простое - добавить -m к git log -p. Это говорит: Когда вы нажимаете коммит слияния, запускайте один diff для каждого родителя. То есть git log теперь будет сначала сравнивать J -vs- M и показывать это; затем сравните L -vs- M и покажите это. Но вы должны спросить об этом.

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

...