Pull to refresh

Comments 20

Git неисчерпаем. Утащил себе в копилку рецептов
1 — так делать не надо. Вместо всех этих танцев с бубном — мержите из master в feature (конечно, это надо было делать регулярно, но раз уж забивали на это — надо навёрстывать), спокойно разрешаете конфликты, тестируете, а потом уже ветку, «догнавшую» master без труда вливаете в него (тривиальный merge).

Кстати, из мастера в фичу можно мёржить и в несколько этапов.

2 — да, стандартный костыль. Увы, git, в отличие от mercurial, не хранит информацию о переименованиях, пытаясь искать их «на ходу» (криво и косо, но если ему помочь — справляется).
UFO just landed and posted this here

Про 1 — это именно один из способов мержа.
Один накодил. А как мержить, сказал — не, "у меня лапки", давайте сами.
Второй попробовал — "нахрапом" не срослось, и не нашёл ничего лучше, как начать откатывать изменения мастера (типа, раз тут не соображаю, как мержить, значит и в оригинале им не быть).
И это всё как раз вокруг той самой задачи — "смержить из мастера в feature".
Т.е. задача доросла до размеров, когда "тупо в лоб" решаться перестала, и назрел букет творческих подходов. (я в итоге небольшими шагами всё сделал, и это заняло пол-дня неспешной работы, при этом 90% этого времени гонялись тесты после каждого шага, а я сидел в левых чатиках/на хабре).
И основная мысль (надо было иначе в рецепте её сформулировать) — в том, что commit --amend переписывает ЛЮБОЙ коммит, независимо от количества родителей. А не только "обычный".

А почему не делать просто мерж отдельно по одному комитету поверх? В чем глобальное преимущество переписывать предыдущие коммиты?

Да, я напутал — почему-то решил, что мержим в мастер (видимо, потому, что мне в голову бы не пришло в feature-ветке биться за идеальную историю: важно лишь, что она вливается в мастер одним коммитом, и я решил, что речь о нём; "букет творческих подходов" у меня, скорее всего, прошёл бы серией коммитов, amend бы использовал только для совсем ерунды).
amend на merge — ну да, какая разница… Просто полдня работы — по мне слишком много для него.

1 — я бы лучше сделал ребейз, раз уж ветка отстала от мастера

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


Ещё иногда бывает проще вообще не мержить, а вручную отредактировать. Например, если в пачке коммитов есть одно глобальное переименование какой-нибудь функции, которая вызывается постоянно из разных мест. Тут проще в ветке сделать ровно то же переименование (всё равно IDE поможет; найдёт все контексты и сделает это правильно), чем мержить результат и потом разрешать логические конфликты (типа, всё слилось ок, но вот в новом коде в ветке по-прежнему используется прежнее имя, а гит об этом в принципе знать не может). А полученное при этом правильное состояние можно вот так же вкоммитать как результат "мержа" с переименующим коммитом (чтобы на будущее была новая точка мержа).

В megcurial есть transplant и graft; утащить из старой ветки нужные комитеты порой проще, чем решать конфликты после merge.

git cherri-pick, если нужна не вся ветка. Но я так понял, речь о другом — что в ветке переменная переименована в 5 местах, а в мастере она за это время расползлась ещё на 10, git это автоматом не отработает (емнип такое darcs умеет, но, во первых, он экзотичен, а во вторых — переименовывать надо его командой).

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

По первому пункту: 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". В итоге получаем один репозиторий с полной историей всех изменений.

Я смотрю, кнопка "редактировать" ещё активно.
Если Вы не против, я могу добавить это в пост (с Вашим авторством, разумеется)?

Да, конечно не против. Добавляйте.
Sign up to leave a comment.

Articles