Комментарии 20
Кстати, из мастера в фичу можно мёржить и в несколько этапов.
2 — да, стандартный костыль. Увы, git, в отличие от mercurial, не хранит информацию о переименованиях, пытаясь искать их «на ходу» (криво и косо, но если ему помочь — справляется).
Про 1 — это именно один из способов мержа.
Один накодил. А как мержить, сказал — не, "у меня лапки", давайте сами.
Второй попробовал — "нахрапом" не срослось, и не нашёл ничего лучше, как начать откатывать изменения мастера (типа, раз тут не соображаю, как мержить, значит и в оригинале им не быть).
И это всё как раз вокруг той самой задачи — "смержить из мастера в feature".
Т.е. задача доросла до размеров, когда "тупо в лоб" решаться перестала, и назрел букет творческих подходов. (я в итоге небольшими шагами всё сделал, и это заняло пол-дня неспешной работы, при этом 90% этого времени гонялись тесты после каждого шага, а я сидел в левых чатиках/на хабре).
И основная мысль (надо было иначе в рецепте её сформулировать) — в том, что commit --amend переписывает ЛЮБОЙ коммит, независимо от количества родителей. А не только "обычный".
А почему не делать просто мерж отдельно по одному комитету поверх? В чем глобальное преимущество переписывать предыдущие коммиты?
Да, я напутал — почему-то решил, что мержим в мастер (видимо, потому, что мне в голову бы не пришло в feature-ветке биться за идеальную историю: важно лишь, что она вливается в мастер одним коммитом, и я решил, что речь о нём; "букет творческих подходов" у меня, скорее всего, прошёл бы серией коммитов, amend бы использовал только для совсем ерунды).
amend на merge — ну да, какая разница… Просто полдня работы — по мне слишком много для него.
Если просто отстала — то да, можно и ребейзом. Финальный момент — подмена мерж-коммита — останется; а ребейз — это лишь один из возможных вариантов получить конечный коммит для этой подмены (ветка опубликована; пушить ребезнутую ветку у нас, хм, не принято).
Ещё иногда бывает проще вообще не мержить, а вручную отредактировать. Например, если в пачке коммитов есть одно глобальное переименование какой-нибудь функции, которая вызывается постоянно из разных мест. Тут проще в ветке сделать ровно то же переименование (всё равно IDE поможет; найдёт все контексты и сделает это правильно), чем мержить результат и потом разрешать логические конфликты (типа, всё слилось ок, но вот в новом коде в ветке по-прежнему используется прежнее имя, а гит об этом в принципе знать не может). А полученное при этом правильное состояние можно вот так же вкоммитать как результат "мержа" с переименующим коммитом (чтобы на будущее была новая точка мержа).
В megcurial есть transplant и graft; утащить из старой ветки нужные комитеты порой проще, чем решать конфликты после merge.
Проблема ребейз в том, что конфликты будут неотличимы от намеренных изменений. А так как мерж/ребейз иногда делают в самом конце без повторного тестирования, то отдельный мерж очевиднее.
По первому пункту: git merge --no-commit, правите что хотите, git commit. Только лишние сложности создали.
Ну и еще мне идея «только одного мерджа» не нравится тем, что это сложно отдавать на ревью.
Надо либо как выше советовали, мастер туда вмерджить, либо все же создать вспомогательную ветку, в которой всё решено — а ее уже выкатывать на ревью.
Это да, но когда есть чатик, всегда можно и лично спросить.
"Я мержу твой коммит, получилось 12 промежуточных слияний, и они все элементарны. Ты собрался туда же присаживать свою локальную ветку (поверх). Тебе оставить эту всю историю, или можно засквашить в единственный мерж-коммит"?
И в ответ — "да не, у меня чисто локальные изменения, давай в единственный". Ок.
Это конструктив, так захотелось. Задача решена. Но...
Однако надо соображать. Пройдёт несколько лет. (хорошо, если мы будем в то время в том же коде шариться тем же коллективом. Это упростит хоть некоторые моменты. Но останется один, который станет серьёзной попоболью:
Вот невнятный код (ну, чисто гипотетически — вдруг вот тут сортируется "пузырьком" гигабайтный массив байтов). Откуда он взялся? Зачем? Кто написал вот эти 10 строчек и в каком коммите? (ну т.е. либо там решена серьёзная проблема, почему стало именно так, или я сейчас всё поменяю, чтоб стало шустрее, и вдруг это разбудит древнего монстра… Как последняя "громыхнувшая" бага в ssl). И вот роешь, и тут гилт говорит, ага, "merged branch 'master' into feature". И всё! Тут как раз и начнётся всё самое интересное…
Так что тут даже не совсем "ревью", а сам же через несколько лет вляпаешься, и будешь ревьювить....
«merged branch 'master' into feature». И всё!
В таком случае как раз можно посмотреть историю самого master. Если там пузырёк есть — то возможны монстры и т.д., а если появились только в «merged ...» то наверняка ошибка мержа и можно спокойно править.
Обычно если squash то описание будет нормальное (не «merge»), и вот тогда сложно понять это был мерж такой или разработчик так и хотел.
Или я неправильно понял что Вы имеете в виду?
1. Отделение части репозитория с историей. Вы находитесь на последней версии начального репозитория. Задача — отделить одну папку. Я видел варианты на несколько папок, но проще и понятнее либо сначала сложить всё в одну, либо повторить ниженаписанное несколько раз.
Внимание: все перемещения делать командой
git mv
, иначе гит может потерять историю.Выполняем:
git filter-branch --prune-empty --subdirectory-filter "{directory}" [branch]
{directory} — та папка, которую надо отделить. В итоге получаем папку вместе с полной историей коммитов только в неё, то есть в каждом коммите отображаются файлы только из этой папки. Естественно, часть коммитов получатся пустыми, их убирает --prune-empty.
Теперь меняем origin:
git remote set-url origin {another_repository_url}
git checkout move_from_Repo_1
Если второй репозиторий чистый, можно сразу в master. Ну и push:
git push -u move_from_Repo_1
2. После этого всего слить 2 репозитория достаточно просто. Допустим, вы проделали то что выше 2 раза и получили бранчи move_from_Repo_1 и move_from_Repo_2, и в каждом перенесли файлы с помощью
git mv
туда, где они должны оказаться после слияния. Теперь осталось смержить:git checkout master
git merge origin/move_from_Repo_1 --allow-unrelated-histories
git merge origin/move_from_Repo_2 --allow-unrelated-histories
git push
Весь фокус в "--allow-unrelated-histories". В итоге получаем один репозиторий с полной историей всех изменений.
Пара иногда востребованных хитростей при работе с git