Я часто сталкиваюсь со следующей проблемой конфликта слияний и решаю ее, но я хотел бы спросить, правильное ли мое решение. Лично я не вижу никаких проблем, учитывая, что функциональные тесты проходят, но было бы хорошо иметь представление об этом.
Итак, допустим, у нас есть две ветви:
develop
feature
Я меняю код в feature
филиал, а затем создать пиар, чтобы слить в develop
, Тем не мение, Github
или Bitbucket
Дифференцирующий инструмент предполагает, что у нас есть конфликт слияния; довольно распространенная ситуация в кодировании.
Затем я предпринимаю следующие шаги:
git checkout develop
а также git pull origin develop
git checkout feature
а потом git pull origin feature
git merge develop
, чтобы обновить мою текущую ветку. Эти шаги помогают мне определить файлы, в которых происходят конфликты.`У тебя есть незавершенные пути.
(исправить конфликты и запустить «git commit»)
Изменения должны быть совершены:
modified: app/BlablaFile.php
modified: app/Blabla2File.php
renamed: app/OldName.php -> app/NewName.php
а потом:
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: app/UnmergedFile.php
это в основном список файлов, которые я отредактировал для разрешения конфликта слияния.
Вопрос должен я только git add app/UnmergedFile.php
а потом git commit -m "Updated app/UnmergedFile.php"
? Или я должен git add
также файлы, которые включены в Changes to be committed:
раздел?
Я обычно делаю первый, но было бы хорошо иметь другую мысль.
TL; DR: вам не нужно повторно добавлять файлы, которые находятся в «изменениях, которые будут зафиксированы».
У Git есть вещь, называемая по-разному: индекс или плацдарм, или иногда даже кэш (как в git diff --cached
). Это не очень хорошо объяснено, отчасти потому, что это сложно. К счастью, использование индекса довольно просто, за исключением (увы) во время слияния. 🙂
Что в индексе лучше всего описано как следующий коммит вы сделаете. Это также помогает помнить, что Git всегда имеет текущий коммит, названный HEAD
и (кроме как в --bare
хранилища), работа дерево или же рабочее дерево, где ваши файлы готовы для просмотра, компиляции, редактирования и всего, что вы делаете со своими файлами.
Первоначально ваше рабочее дерево, индекс и HEAD
зафиксировать все совпадения (когда вы впервые клонируете репозиторий). Вы не можете касаться файлов непосредственно в индексе, поэтому вы вносите какие-либо изменения или даже создаете новые файлы в рабочем дереве, где вы выполняете всю свою работу. Затем, чтобы поместить измененный файл обратно в индекс, вы запускаете git add
по пути, так что Git копирует измененный файл в индекс. Чтобы поместить новый файл в индекс, вы также запускаете git add
на пути.
Если бы вы были по какой-то причине, чтобы все взволнованы и бежать git commit
прямо сейчас Git прочитает ваш текущий индекс и упакует его как новый коммит. Новый коммит будет иметь в качестве своего снимка рабочего дерева каждый файл, который сейчас находится в индексе, в состоянии, в котором он сейчас находится, в индексе.
Если это состояние Матчи HEAD
совершить, git commit
просто говорит: «Ха? Почему ты пытаешься сделать новый коммит сейчас?» По-видимому, к этому моменту вы что-то изменили в индексе. git status
покажет вам, что в индексе это отличный от HEAD
commit: это «изменения, которые нужно совершить».
Бег git merge
иногда раскрывает секрет об индексе. Каждая запись в индекс на самом деле имеет четыре игровые автоматы, которые пронумерованы. Обычно вы видите только нулевой слот, который зарезервирован для «обычных» файлов, готовых к фиксации.
Слияние предназначено для скомбинировать два (или иногда больше, но давайте придерживаться двух) наборов изменений. Например, возможно, вы и Боб начали с одних и тех же файлов. Затем вы внесли некоторые изменения в некоторые файлы, и git add
и сделал новые снимки источника. Тем временем Боб внес некоторые изменения в некоторые файлы — может быть, те же файлы, даже — и также git add
и настало время, и теперь пришло время объединить ваши изменения и изменения Боба.
Чтобы выполнить слияние, чтобы объединить некоторые файлы, Git сначала находит объединить базу, то есть момент, когда вы с Бобом были синхронизированы. Затем Git запускает два git diff
s: один из этой базы слияния в ваш последний коммит и один из той же базы слияния в бобПоследний коммит. Это показывает «что ты изменил» и «что изменил Боб».
Git делает все возможное, чтобы скомбинировать эти изменения (включая, как в этом случае, выяснение и последующее переименование файла, и выяснение, что делать, если только один вы переименовали какой-то файл — но большинство конфликтов слияния не включают переименования файлов, если вы не пишете много кода Java …). Иногда, однако, Git не может сделать это самостоятельно. В этом конкретном случае Git возвращает вам работу по слиянию, и в этот момент вы видите все игровые автоматы в указателе.
Слот индекса 1 — это место, где Git хранит база версия файла. Это общая версия, с которой вы оба начали.
Индекс слот 2 является HEAD
или же --ours
версия файла. (Это также иногда называют «локальной» версией.)
Индексный слот 3 является другим или --theirs
(или иногда «удаленная») версия файла.
Это почти все, что нужно сделать: три слота содержат три версии, и в итоге вы получите «неразделенные пути». Файл в вашем рабочем дереве содержит первоначальные усилия Git по слиянию, а также маркеры конфликтов и некоторые сочетания --ours
а также --theirs
версия. Если вы установите merge.conflictstyle
в diff3
, Git также будет включать в себя части из база версия файла, хранящаяся в индексном слоте 1. Теперь вы должны решить слияние.
После того, как вы выяснили правильное разрешение и обновили версию файла рабочего дерева, вы должны git add
путь. Это сохраняет версию рабочего дерева в нулевом слоте индекса, стирая три записи в слотах 1-3. Поскольку нулевой слот содержит нормальную версию для фиксации, файл теперь готов к фиксации.
Для файлов, где не было никаких изменений, или же только один из вас что-то изменил, или же где Git считает, что правильно совмещал ваши изменения и изменения Боба, те файлы уже обновлены как в рабочем дереве, так и в (нулевой слот) индекса. Если результат отличается от текущего HEAD
совершить, git status
показывает файл как «изменения, которые должны быть зафиксированы».
Я сказал выше, что это почти все, что нужно сделать Последние несколько вещей, которые следует помнить о слотах индекса:
Некоторые слоты могут быть пустыми. Предположим, что вы оба а также Боб добавил файл. В этом случае вы получите «добавить / добавить конфликт». Какая версия файла является общей базовой версией? Там нет, конечно: вы оба добавили новый файл. Таким образом, в этом случае базовый слот — слот 1 — пуст.
То же самое происходит, если вы удаляете файл, а Боб меняет его. В этом случае --ours
Слот, слот 2, пуст, а слоты 1 и 3 содержат базовую версию и версию Боба. Вам решать, сохранить ли версию Боба, удалить файл или использовать третий вариант, но опять-таки заполняются только два из трех слотов.
Если один или оба из вас переименовывает файл, версии файлов в трех слотах могут исходить из файлов, которые имели разные имена в предыдущих коммитах. Например, ваш вывод показывает:
renamed: app/OldName.php -> app/NewName.php
Здесь не было никакого конфликта, но если бы был, по крайней мере, один из слотов для app/NewName.php
будет заполнен из версии какого-то другого коммита app/OldName.php
,
Это в основном имеет значение, когда вы смотрите на старую версию. Если в отдельном клоне или рабочем дереве вы извлекаете один из коммитов, где файл еще не переименован, или если вы используете git show <commit>:app/OldName.php
чтобы просмотреть файл без проверки — вы должны использовать старый Имя везде, где оно имеет старое имя. Но если вы используете git checkout --ours
или же git checkout --theirs
чтобы извлечь только одну из этих двух версий в рабочее дерево, вы должны использовать новый имя, потому что индекс теперь хранит файл под новым именем.
если ты делать использование git checkout --ours
или же git checkout --theirs
чтобы получить вашу версию файла Боба или, соответственно, Боба, это не разрешает файл, но делает попытку Git слить их в рабочее дерево. Если вы хотите восстановить попытку Git объединить их, используйте git checkout -m
, Обратите внимание, что все они перезаписывают версию файла рабочего дерева, оставляя только три неразрешенных слота индекса.
Странно, однако, если вы git checkout <commit-id> -- <path>
получить старую версию файла из какого-то конкретного коммита, который перезаписывает индекс: он копирует версию файла коммита в нулевой слот индекса, забивая записи слота 1-3. Файл теперь выглядит решенным! Опять же, вы можете восстановить неразрешенное состояние с помощью git checkout -m
, (Конечно, это переписывает версию дерева работ.)
Точно так же, если вы по ошибке решите файл с git add
когда это не довольно сделано еще, вы можете git checkout -m
чтобы разрешить это — но, конечно, это переписывает версию рабочего дерева. Если вы в основном закончили с разрешением, вы могли бы также закончить разрешение и повторноgit add
результат. Это заменяет запись индекса нулевого слота версией, только что скопированной из рабочего дерева: файл остается разрешенным, но версия, готовая для следующей фиксации, изменяется на последнюю git add
версия
Я обычно делаю последнее и не сталкивался с этим.
Я думаю, это зависит от того, чего вы собираетесь достичь.
Если вы хотите явно указать, с какими файлами вы точно решили конфликты, вы можете сделать первый. В противном случае вы могли бы сделать другой.
Файлы, названные в списке «изменения должны быть зафиксированы», уже находятся в промежуточной области (также называемой индексом).
Git add — это то, что вы используете для помещения своей работы в индекс, что говорит git, что вы хотите, чтобы они были включены в коммит.
Таким образом, добавление мерзавцев из уже подготовленных файлов будет излишним.
Дополнительное добавление файлов в список «для передачи» необходимо только в том случае, если файлы перечислены в обоих списках (что иногда происходит, если вы создаете файлы, а затем модифицируете их еще больше).