Могу ли я переопределить целевые файлы запроса на извлечение? - PullRequest
0 голосов
/ 01 июля 2019

У меня есть 2 хранилища (A) и (B). (B) является вилкой (A) и получила переименование каталога файлов. Многие из файлов в (B) просто имеют другое имя родительской папки. Сейчас я пытаюсь выполнить запрос на извлечение (A) в (B), но репозиторий Azure сообщает, что целевые файлы были удалены.

Есть ли способ вручную переопределить запрос на извлечение, чтобы можно было отображать файлы с (A) на (B), если файлы (A) отображаются на файлы (B)? Опять же, это одни и те же файлы, просто с другой родительской папкой. Я хотел бы избежать изменения структуры папок (A), если смогу помочь.

1 Ответ

1 голос
/ 01 июля 2019

Краткий ответ - «нет», но сам вопрос немного проблематичен. Если вы зададите правильный вопрос, ответом может стать "да".

Во-первых, «запрос на извлечение» - это не Git, а надстройка, предлагаемая различными веб-службами, такими как GitHub или Bitbucket или (в вашем случае) Azure. Что у Git действительно есть, так это способность извлекать коммитов - получать нетронутые коммиты из какого-либо другого репозитория Git - и объединять .

Когда вы получаете чьи-то коммиты, вы получаете буквально их коммиты. Каждый коммит во вселенной имеет свой собственный уникальный хэш-идентификатор. Идентификатор хэша представляет собой криптографическую контрольную сумму всего, что вошло в коммит: все файлы в моментальном снимке, имя и адрес электронной почты человека, который сделал коммит, его сообщение журнала и - что крайне важно для Git - вся история 1015 *, которая привела к этому моменту времени. То есть, чтобы поместить этот коммит в ваш репозиторий, вы также должны взять все коммиты - с их снимками и их авторами, сообщениями журнала и т. Д. - которые привели к * 1020. * этот коммит.

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

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

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

Давайте теперь посмотрим на вторую, более интересную часть:

Есть ли способ переопределить запрос на извлечение вручную, чтобы можно было отображать файлы с (A) на (B), если файлы (A) отображаются на файлы (B)? Опять же, это одни и те же файлы, просто с другой родительской папкой. Я хотел бы избежать изменения структуры папок (A), если смогу помочь.

Теперь, когда мы знаем, что в Git нет такой вещи, как запрос на извлечение, мы можем превратить это в правильный вопрос:

Теперь, когда у меня есть их коммиты в моем репозитории, могу ли я объединить их коммиты с моими коммитами, используя параметры, которые ослабляют правило соответствия Git для файлов?

Ответ на этот вопрос да . Вам, вероятно, нужно делать это с помощью командной строки Git, а не с причудливым веб-интерфейсом - например, веб-интерфейс GitHub с щелчками не имеет такой возможности.

Когда исполняется GitПри слиянии (как в git merge otherbranch) в этом слиянии есть три входа.Одним из трех входных данных является ваш текущий коммит - кончик ветки, в которой вы находитесь, или коммит HEAD: это два имени для одного и того же коммита, истинное имя которого - его большой уродливый идентификатор хеша.Одним из входных данных является другой указанный вами коммит - otherbranch в этом случае, но вы также можете просто указать необработанный хэш-идентификатор;Git просто превращает name otherbranch в необработанный хеш-идентификатор для цели слияния.

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

...--o--o--*--o--o--L   <-- yourbranch (HEAD)
            \
             A--B--R   <-- theirbranch (or theirrepo/branch or whatever)

Здесь L обозначает ваш текущий ( L eft или L ocal или --ours) коммит, иR обозначает их ( R ight или othe R или полученный из- R emote-Git или --theirs) коммит.A и B заменяют хэш-идентификаторы коммитов, которые вы должны были получить от них, чтобы получить коммит R, а * - это хэш-идентификатор родителя их фиксации Aчто у вас уже было. 1

Способ git merge работает, для этих истинных случаев слияния - ваш, безусловно, будет одним из таких - что Git запускает two git diff с, чтобы выяснить что вы изменили и что они изменили .По сути, первый diff:

git diff --find-renames <hash-of-*> <hash-of-L>

Обратите внимание на аргумент --find-renames.Второй diff эквивалентен:

git diff --find-renames <hash-of-*> <hash-of-R>

Если вы не переименовали папку, между * и L, и они сделали переименуйте папку, между * и L, Git попытается - во время слияния - сопоставить файлы в * и R, даже если они имеют разные имена. Эта попытка зависит от сходства содержимого файлов.

Между тем, если вы переименовали папку, между * и L, и они не не переименовали эту папку, Git делает то же самое.Он пытается сопоставить базовые имена в * с вашими именами в L.Эта попытка зависит от сходства содержимого файлов.

Если вы оба переименовали папки, это тоже нормально.Git пытается найти исходный файл в коммите *, основываясь на его содержимом сходстве с содержимым каждого из новых имен файлов "возможно, то же самое, но не может быть"в двух советах по ветвям.

Спарив все переименованные файлы в * и L и обнаружив, что файл path/to/file.ext в * теперь path/different/file.ext в L, Gitзнает, что изменения , которые вы внесли в file.ext, - это изменения, полученные путем сравнения file.ext оригинала *1159* с новым именем L для того же файла.Он также знает, что вы переименовали файл.Аналогично, выполнив сопряжение всех переименований от * до R, Git знает, что изменения , которые они внесли в file.ext, - это те, которые получены путем сравнения * оригинального file.extНовое имя R для того же файла.

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

Все это может пойти не так, как надо:

  • Если вы оба переименовалиfile.ext, Git не знает какое новое имя оставить .Вы получите конфликт rename/rename, который вам придется решить вручную.Это отдельно от любых других конфликтов слияния, которые вы также должны разрешать самостоятельно.Когда вы закончите разрешение, git mv файл при необходимости, чтобы дать ему имя, которое вы хотите сохранить в конце концов, и git add объединенные изменения под именем, которое вы хотите сохранить.

  • Если тот, кто изменил имя файла , также слишком сильно изменил содержимое, Git не сможет соединить старые и новые файлы. Сколько это слишком много? Ну, у Git есть концепция порога подобия . Когда Git выполняет часть --find-renames операции git diff old-commit new-commit, Git для каждого файла, который, как представляется, был удален из старой фиксации, сравнивает содержимое удаленного файла с содержимым каждого файла кажется, что он был создан с нуля в новом коммите. Если старый file.ext на 30% похож на новый different.ext и на 70% похож на новый other.ext, то выигрыш, подобный на 70%, выигрывает. Но если ни один файл не достигает "50% соответствия", по умолчанию принимается решение, что файл был удален в конце концов.

    Если вы запускаете git diff --find-renames самостоятельно, вы можете добавить Порог переименования * Коэффициент 1204 *, который по умолчанию равен 50%, но настраивается. Отрегулируйте его вверх или вниз по мере необходимости, чтобы заставить Git создать пару файлов right Git сообщит вам в своей разнице, что это был за индекс сходства.

    Вы можете запустить этот тип git diff вручную до того, как запустите git merge, и найдете правильный индекс подобия, который заставляет Git сопоставлять файлы. Затем вы можете запустить git merge -X find-renames=<em>number</em>, чтобы указать git merge использовать это число для обеих своих операций git diff --find-rename.

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

Итак, рецепт как бы здесь заключается в том, чтобы выполнить слияние вручную. Сначала используйте git fetch, чтобы получить коммиты, которые вы предлагаете объединить. Затем используйте git merge-base --all, чтобы найти общий коммит на основе слияния, который найдет git merge. Запустите git diff --find-renames, используя эту общую базу слияния в качестве отправной точки фиксации, а ваш и / или их идентификатор хита коммита ветвления или имя ветвления в качестве фиксации конечной точки. Добавьте --name-status к этому git diff, чтобы получить только сводную информацию о том, какие файлы были объединены в пару и были найдены измененными, а какие считаются удаленными. Изменяйте порог обнаружения переименования (--find-renames=<em>number</em> или -M<em>number</em>, если вы хотите использовать короткое написание), пока не получите наилучшие возможные результаты. Затем используйте git merge с опцией -X rename-threshold=<em>number</em>, чтобы git merge передавал это же число двум разным базовым различиям.


1 Возможно, у вас уже были A и B в любом случае. Что делает коммит * важным, так это то, что это лучший общий коммит: из всех коммитов, которые находятся на и вашей ветви и их ветви, это лучший из тех. Технически это коммит самого низкого общего предка (LCA) двух выбранных коммитов в направленном ациклическом графе (DAG) коммитов, которые составляют ваш репозиторий. Вы можете найти хеш-код этого коммита, используя:

git merge-base --all HEAD otherbranch

например. Иногда вообще нет общего коммита, а иногда - редко - в группе DAG существует более одного LCA из двух коммитов, но обычно это создает только один хэш-идентификатор, и это база слияния.

...