Comments 148
Благодарю за интересную сказку на вечер.
У нас в команде простое правило — ребейзи у себя локально сколько влезет, но если коммит уже попадал в общий репозиторий — то его нельзя ребейзить (и push --force делает только тимлид в экстремальных ситуациях).
Поэтому обычно и большая часть истории линейная, и для удобства ревью можно коммиты клеить, но когда идёт слияние веток разных разработчиков — то это мерж. Поэтому таких историй нет и вообще видно реально сделанные коммиты, а не некое совместное творчество коммитера и ребейзера, и восстановить реально существовавшую точку в истории всегда можно.
Любой инструмент нужно применять к месту и со здравым смыслом, а фанатики ребейза — они как любые фанатики. (Ц) Кэп.
Поэтому обычно и большая часть истории линейная, и для удобства ревью можно коммиты клеить, но когда идёт слияние веток разных разработчиков — то это мерж. Поэтому таких историй нет и вообще видно реально сделанные коммиты, а не некое совместное творчество коммитера и ребейзера, и восстановить реально существовавшую точку в истории всегда можно.
Любой инструмент нужно применять к месту и со здравым смыслом, а фанатики ребейза — они как любые фанатики. (Ц) Кэп.
Согласен. Автор рассмотрел только два крайних случая — либо глобальный ребейз в проекте компании, либо глобальный мердж. Вариант хорошей организации проекта не рассмотрен. Например в центральные репозитории force push запрещен, а локально до пуша хоть ребейзи, хоть интерактивно ребейзи, хоть амендь коммиты…
Force push тут вообще ни при чем (то, что он неприемлем, по-моему очевидно). Пример был создан как раз при локальном rebase, это стандартный workflow.
Ок. Тогда согласен. Хотя на моей памяти таких случаев не было. Чаще проблемы от неудачного разруливания конфликтов.
При локальном ребейзе обычно автор ребейзит свои патчи. С реальным кодом, такие засады бывают редко, обычно все же возникают конфликты, которые пользователь должен разрулить руками. Частично эта проблема ловится на ревью, частично ее ловят тесты.
PS: на моей памяти подобная штука происходила при накладывании OpenVZ патча, когда там sysctl-ли неуправляемо переезжали в другие подсистемы.
PS: на моей памяти подобная штука происходила при накладывании OpenVZ патча, когда там sysctl-ли неуправляемо переезжали в другие подсистемы.
Объясните мне хабралогику, пожалуйста.
Когда я написал то же самое в комментариях к другой статье
habrahabr.ru/post/179045/#comment_6213807
Вся ветка сплошь заминусована.
Когда я написал то же самое в комментариях к другой статье
habrahabr.ru/post/179045/#comment_6213807
Вся ветка сплошь заминусована.
Логики нет никакой. В комментарии выше идёт посыл «ребейзи у себя локально сколько влезет, и таких историй не будет», хотя в статье как раз и показан пример, когда локальный rebase теряет важный контекст, и может ввести в заблуждение.
К слову, та ваша ветка как раз и побудила меня составить этот пример для наглядности.
К слову, та ваша ветка как раз и побудила меня составить этот пример для наглядности.
Как уже указали, ваш комментарий не имеет ничего общего со статьёй. ;)
В статье как раз показан пример вполне себе локального ребейза.
+35 к этому комменту наглядно показывает, как внимательно хабраюзеры читают статьи и комментарии к ним ;)
В статье как раз показан пример вполне себе локального ребейза.
+35 к этому комменту наглядно показывает, как внимательно хабраюзеры читают статьи и комментарии к ним ;)
Совершенно верно! Я как-то на один из подобных постов отвечал, что народ путает терминологию, а именно: опубликованные vs. публичные ветки или репозитории. Вот в первых как раз
git rebase ...
противопоказан.а можно же после rebase делать merge --no-ff и все будет красивенько и удобненько
А почему Вася после ребейза не проверил свой код? Имхо, уволен справедливо.
А при беглом просмотре Вася бы и не заметил ошибки.
Т.е. данный кусочек кода не был покрыт тестами, хотя проект очень важен для компании?
Мы же не в идеальном мире живем, в реальности в проекте не 8 строчек, в ветке не 1 коммит, а покрытие кода тестами не 100-процентное. И при слиянии (как через merge, так и через rebase) ошибку можно проглядеть, тем более, если слияние без конфликтов (как в примере).
Только при merge в истории сохранится информация, чем руководствовался автор при написании кода коммита, и в какой момент возникла ошибка (при слиянии, а не при написании кода). А при rebase — нет. А не зная причин ошибки, исправить её может быть труднее.
Только при merge в истории сохранится информация, чем руководствовался автор при написании кода коммита, и в какой момент возникла ошибка (при слиянии, а не при написании кода). А при rebase — нет. А не зная причин ошибки, исправить её может быть труднее.
Согласен с теми, кто написал, что былина интересная, как раз то чтение, которые приятно читать на Хабре под конец рабочего для, +.
Выводы из конкретного примера однако неоднозначные. Почему объектом критики выбран rebase?
Почему бы не «наехать» на:
— Алгоритм текствого diff'а/patch'а конкретного в git
— Алгоритм текствого diff'а/patch'а вообще
Алгоритм текстового diff'а — эвристика, а уж применение его как patch — эвристика в квадрате. Стоит удивляться и восхищаться, что при этом метод rebase был реализован *для специальных целей* и на практике работает весьма неплохо.
Учить «пионеров», не нюхавших CVSа, не юзать rebase направо и налево конечно стоит, но использовать для этого недостатки алгоритма diff — «не очень красиво» (хотя еще раз, былина получилась интересная). Для пущего эффекта, статью можно было бы озаглавить «Как я похакал diff» или вообще по-ализаровски: «В git найдена критическая уязвимость» ;-).
Выводы из конкретного примера однако неоднозначные. Почему объектом критики выбран rebase?
Почему бы не «наехать» на:
— Алгоритм текствого diff'а/patch'а конкретного в git
— Алгоритм текствого diff'а/patch'а вообще
Алгоритм текстового diff'а — эвристика, а уж применение его как patch — эвристика в квадрате. Стоит удивляться и восхищаться, что при этом метод rebase был реализован *для специальных целей* и на практике работает весьма неплохо.
Учить «пионеров», не нюхавших CVSа, не юзать rebase направо и налево конечно стоит, но использовать для этого недостатки алгоритма diff — «не очень красиво» (хотя еще раз, былина получилась интересная). Для пущего эффекта, статью можно было бы озаглавить «Как я похакал diff» или вообще по-ализаровски: «В git найдена критическая уязвимость» ;-).
Да ладно, суть примера не в том, как diff работает, а в том, что rebase контекст меняет.
Ну т.е. другой алгоритм diff'а безусловно мог бы показать конфликт в конкретно этом примере, но лишь потому, что я пытался ужать его в несколько строк. В реальном проекте изменения Пети и Васи могут быть вообще в разных файлах, так что алгоритм diff'а может быть каким угодно, но смена контекста всё равно может привести к похожим последствиям.
Кстати, немного отвлекаясь от темы, в git-diff можно использовать разные алгоритмы:
Кроме того, есть опции
Ну т.е. другой алгоритм diff'а безусловно мог бы показать конфликт в конкретно этом примере, но лишь потому, что я пытался ужать его в несколько строк. В реальном проекте изменения Пети и Васи могут быть вообще в разных файлах, так что алгоритм diff'а может быть каким угодно, но смена контекста всё равно может привести к похожим последствиям.
Кстати, немного отвлекаясь от темы, в git-diff можно использовать разные алгоритмы:
--diff-algorithm={patience|minimal|histogram|myers}
Choose a diff algorithm. The variants are as follows:
default, myers
The basic greedy diff algorithm. Currently, this is the default.
minimal
Spend extra time to make sure the smallest possible diff is produced.
patience
Use "patience diff" algorithm when generating patches.
histogram
This algorithm extends the patience algorithm to "support low-occurrence common elements".
Кроме того, есть опции
--word-diff
и прочие.Давайте уволим ещё старшего QA (за непокрытие) и менеджера проекта (за накосячившего QA) =)
Как сказали выше — «спасибо за сказку». Сказка не касалась таких (безусловно важных) тем, как контроль качества и управления разработкой бизснес-критикал проектов. Так что я думаю тема достаточно неплохо раскрыта. Простим же автору недостаточное раскрытие прочих важных, но не обозначеных в статье тем?
Как сказали выше — «спасибо за сказку». Сказка не касалась таких (безусловно важных) тем, как контроль качества и управления разработкой бизснес-критикал проектов. Так что я думаю тема достаточно неплохо раскрыта. Простим же автору недостаточное раскрытие прочих важных, но не обозначеных в статье тем?
Программисты бывает двух типов — которые ещё не делают юнит-тесты и которые уже делают юнит-тесты.
Проект 1М+ строк кода, потом пришли к покрытию тестами. За сколько, как Вы думаете, покруют на 100%?
P.S. Да, кстати, важно не забывать что Code Coverage = 100% не означает 100% покрытия тестовых случаев.
P.S. Да, кстати, важно не забывать что Code Coverage = 100% не означает 100% покрытия тестовых случаев.
Конечно, все ситуации покрыть малореально. А ведь ещё есть сроки и прочее, что ещё сильнее ограничивает время на написание тестов.
Я как раз на это намекаю, что тесты часто дописываются когда работа в полном разгаре, а не в самом начале.
У меня лично много проектов, которые я начинаю, а потом они идут в мусорку по ненадобности. Было бы грустно тратить время на юнит-тесты для таких, я бы тогда вообще ничего не успевал бы.
А вот когда проект пошёл удачно, то тогда уже начинаются тесты, фреймворки для тестирования, тестовые фиды, prod/qa/dev и прочие специи по вкусу и по задачам.
При текущей конкуренции на рынке это достаточно адекватный вариант разработки новых идей.
Я как раз на это намекаю, что тесты часто дописываются когда работа в полном разгаре, а не в самом начале.
У меня лично много проектов, которые я начинаю, а потом они идут в мусорку по ненадобности. Было бы грустно тратить время на юнит-тесты для таких, я бы тогда вообще ничего не успевал бы.
А вот когда проект пошёл удачно, то тогда уже начинаются тесты, фреймворки для тестирования, тестовые фиды, prod/qa/dev и прочие специи по вкусу и по задачам.
При текущей конкуренции на рынке это достаточно адекватный вариант разработки новых идей.
Я всегда начинаю проекты с тестов, но пишу минималку для проверки тех сценариев, что сразу в голову приходят. Это быстро. Остальные тесты пишет QA.
Это очень классно когда есть QA, много заинтересованных людей в компании и достаточно времени. А когда у тебя стартап из тебя одного или маленькая команда в два с половиной человека, то в одном лице может быть и разработчик и QA и менеджер. В этом случае приоритеты, к сожалению, идут не в пользу юнит-тестов.
Тесты не панацея.
Ради интереса сделал:
создал файл, в master написал 2+2=4 (в столбик)
потом создал ветку исправил 2*3=5 (в столбик)
в master исправил 2*2=4 (в столбик)
и всё прекрасно слилось воедино. git ругнулся на то что есть конфликт, но kdiff3 его прекрасно разрулил без участия человека.
Rebase и Merge дали один и тот же результат. Кстати, вброшу ка я:
rebase и merge в 95% дают один и тот же результат, если конечно во время ребэйза чего-нибудь кардинально не менять.
Вообще и merge, и rebase опасны тем, что это объединение двух историй, а значит вероятны конфликты, неправильно принятые решения и т.д. Тут уже всё зависит от человека, а не от VCS.
создал файл, в master написал 2+2=4 (в столбик)
потом создал ветку исправил 2*3=5 (в столбик)
в master исправил 2*2=4 (в столбик)
и всё прекрасно слилось воедино. git ругнулся на то что есть конфликт, но kdiff3 его прекрасно разрулил без участия человека.
Rebase и Merge дали один и тот же результат. Кстати, вброшу ка я:
rebase и merge в 95% дают один и тот же результат, если конечно во время ребэйза чего-нибудь кардинально не менять.
Вообще и merge, и rebase опасны тем, что это объединение двух историй, а значит вероятны конфликты, неправильно принятые решения и т.д. Тут уже всё зависит от человека, а не от VCS.
Я специально не писал «2+2=4 (в столбик)», чтобы и merge и rebase прошли без конфликтов. См. репозиторий.
И да, rebase и merge дают идентичный результат, только с разной историей.
И да, rebase и merge дают идентичный результат, только с разной историей.
Если посмотреть на изменения Васи при варианте с ребейзом, видно, что он поменял 2 на 3, и 4 на 5. А произведение не трогал, и дальше по истории можно увидеть, что сумму на произведение поменял Петя. Какие вопросы к Василию?
Петя изменил 2+2=4 на 2*2=4. Корректно? Пожалуй, да.
А Вася якобы изменил 2*2=4 на 2*3=5. Корректно?
А Вася якобы изменил 2*2=4 на 2*3=5. Корректно?
А Антон, который смержил 2*3=5? Корректно? Почему еще не уволен?
Вот видите, в варианте с merge четко видно причину ошибки: слияние.
А в варианте с rebase создается ложное впечатление, что был написан некорректный код.
А в варианте с rebase создается ложное впечатление, что был написан некорректный код.
Согласен. Рациональное зерно в этом есть.
Профит только в том, что Васе не придется оправдываться. Ведь баг будет как в первом, так и во втором случае.
Но когда все будут мержить branch в origin/branch, хотел бы я посмотреть как Антон будет морщиться при поиске бага.
Профит только в том, что Васе не придется оправдываться. Ведь баг будет как в первом, так и во втором случае.
Но когда все будут мержить branch в origin/branch, хотел бы я посмотреть как Антон будет морщиться при поиске бага.
Он не только морщится, он ещё и «периодически матерится, глядя на паутину слитых веток». :)
В обоих подходах есть свои плюсы и минусы. Важно о них знать, особенно о минусах того, который используешь.
В обоих подходах есть свои плюсы и минусы. Важно о них знать, особенно о минусах того, который используешь.
Подобная ситуация справедлива для любого патча применяемого не для той ревизии из которой он получен. Например, мы сделали патч для ревизии 10, а применяем для ревизии 20. Допустим патч успешно применился, но код может оказаться неработоспособным.
А как это у вас автоматически мержится при ребэйзе?
Только что проверил, будет конфликт, что собственно ожидаемо.
Applying: 2+3=5
Falling back to patching base and 3-way merge…
Failed to merge in the changes.
Patch failed at 0001 2+3=5
И тут уж явно никак не закомитишь не правильно, собственно на чем вся статья и основана.
Что бы быть уверенным что мы говорим об одном и том же вот короткий скрипт, который воссоздает ваш вариант и воспроизводит конфликт при ребэйзе:
Только что проверил, будет конфликт, что собственно ожидаемо.
Applying: 2+3=5
Falling back to patching base and 3-way merge…
Failed to merge in the changes.
Patch failed at 0001 2+3=5
И тут уж явно никак не закомитишь не правильно, собственно на чем вся статья и основана.
Что бы быть уверенным что мы говорим об одном и том же вот короткий скрипт, который воссоздает ваш вариант и воспроизводит конфликт при ребэйзе:
git init
touch test.txt
echo -e "2\n+\n2\n=\n4" > test.txt
git add test.txt
git commit -m "2+2=4"
git checkout -b my
echo -e "2\n+\n3\n=\n5" > test.txt
git add test.txt
git commit -m "2+3=5"
git checkout master
echo -e "2\n*\n2\n=\n4" > test.txt
git add test.txt
git commit -m "2*2=4"
git checkout my
git rebase master
Я запутался. Должно быть в столбик или в строку? Потому что все скрины у вас в столбик. И если в столбик, то будет конфликт.
Вообще как то странно ребэйз используется, но да ладно. Если использовать как написал выше, то будет конфликт.
В вашем же случае, вы делаете перенос патча на другую ветку и как мне кажется просто описание комита не верно, от того кажется что и все не верно, и ребеэз работает не верно.
У вас есть комит с описанием «2+3=5», но это же неверное описание, вы должны описать что делает патч, а не состояние кода в момент комита, таким образом описание должно быть «заменили 2 на 3 и 4 на 5». Вот теперь все встает сразу на свои места.
Иначе в таком случае ЛЮБАЯ git операция подходит под статью. Возьмем git cherrypick — снова не верно, rebase — неверно, merge — неверно. И все от неправильного описания патча и его трактовки.
В вашем же случае, вы делаете перенос патча на другую ветку и как мне кажется просто описание комита не верно, от того кажется что и все не верно, и ребеэз работает не верно.
У вас есть комит с описанием «2+3=5», но это же неверное описание, вы должны описать что делает патч, а не состояние кода в момент комита, таким образом описание должно быть «заменили 2 на 3 и 4 на 5». Вот теперь все встает сразу на свои места.
Иначе в таком случае ЛЮБАЯ git операция подходит под статью. Возьмем git cherrypick — снова не верно, rebase — неверно, merge — неверно. И все от неправильного описания патча и его трактовки.
Ниже уже ответили, но я пожалуй добавлю для полной ясности.
1) Rebase используется так же, как у вас в скрипте, возможно вас смутила запись
2) Любой rebase с мастером + мердж в мастер — это и есть перенос патчей в мастер.
3) Rebase работает верно, только при rebase происходит потеря контекста. Изначально коммит это патч+контекст, при rebase же остаётся только патч, а контекст меняется.
4) Какие должны быть сообщения коммитов — вопрос дискуссионный, статья не про это. Отмечу только, что если написать, как предполагаете вы, то в репозитории не останется даже намека на то, какие причины побудили Васю написать этот код.
5) Не любая операция искажает историю. Rebase и cherrypick — да, контекст теряют. Merge — сохраняет историю неизменной.
В остатке: если хотим иметь в репозитории набор патчей — используем rebase, если важна правдивая история — используем merge.
1) Rebase используется так же, как у вас в скрипте, возможно вас смутила запись
git rebase cae31030
, — так это потому, что в репозитории я rebase уже делал, и мастера уже нет; если повторять с нуля, то будет git rebase master
.2) Любой rebase с мастером + мердж в мастер — это и есть перенос патчей в мастер.
3) Rebase работает верно, только при rebase происходит потеря контекста. Изначально коммит это патч+контекст, при rebase же остаётся только патч, а контекст меняется.
4) Какие должны быть сообщения коммитов — вопрос дискуссионный, статья не про это. Отмечу только, что если написать, как предполагаете вы, то в репозитории не останется даже намека на то, какие причины побудили Васю написать этот код.
5) Не любая операция искажает историю. Rebase и cherrypick — да, контекст теряют. Merge — сохраняет историю неизменной.
В остатке: если хотим иметь в репозитории набор патчей — используем rebase, если важна правдивая история — используем merge.
Назревает необходимость в системе контроля версий для систем контроля версий :-)
Исходники git-а лежат в git-е. Думаю это верно для большинства систем контроля версий.
проще: пользуемся mercurial — у них идеологически нет команды rebase
Да и для освоения она легче чем git
Да и для освоения она легче чем git
ну это всегда так было — проще всего тот дистрибутив linux которым пользуется ближайший к вам админ, и даже если это gentoo — он с админом будет проще любого debian :)
Я к примеру въехал в mercurial довольно быстро, а вот в гит до сих пор путаюсь в командах.
Я к примеру въехал в mercurial довольно быстро, а вот в гит до сих пор путаюсь в командах.
git — сложнее:
— не логичные команды
— нет именованных веток
— нужно указывать ветку при pull или push или дополнительно настраивать, что бы происходило автоматом
— не логичные команды
— нет именованных веток
— нужно указывать ветку при pull или push или дополнительно настраивать, что бы происходило автоматом
В Hg логики тоже не нашёл, этим он не легче.
Например:
git branch -a
git tag
git branch name
git tag -a name
hg branch name
hg tag name
hg branches
hg tags
Чушь, всегда переключался по имени ветки.
Прочитайте сначала, что такое именованная ветка. И их действительно нет в git'е.
pqr7.wordpress.com/2010/10/10/a-guide-to-branching-in-mercurial/
push да, а при pull какая разница? Легко решается алиасами.
Не легко. У меня никто в команде этого делать не умеет. И мало того, на всяких сходках программистов, никто мне не сказал, как же это сделать. Все ссылались на чтение мана. В Hg это делается с помощью одной команды «hg push».
Если вы говорите, что нет почти никому не нужной штуки с несоответсвующим именем, то так и говорите, потому что в русском языке «именованная ветка» — ветка, которая имеет имя, и в гите ветки имена-таки имеют. (P.S. Ссылка нерабочая)
Вроде как у всех кому давал — открывается. И эта никому не нужная штука, одна из основных фич из-за которых большие проекты переходят/остаются на Hg.
Эта одна из самых нужных фич, для тимлидов, которая позволяет легко отслеживать все изменения проекта в истории.
В Git, например, не сохраняется история, к какой ветке принадлежал commit после merge.
Спросите у коллег, которые раньше работали в Мегаплане, на сколько проще работать с Hg, они вам расскажут.
Я перешел с SVN на Hg за 2-3 дня.
Потом осваивал не меньше полу-месяца Git(что бы полноценно управлять репозиторем и настраивать deploy)
Опыт тесной работы как с Git, так и с Hg более года.
Потом осваивал не меньше полу-месяца Git(что бы полноценно управлять репозиторем и настраивать deploy)
Опыт тесной работы как с Git, так и с Hg более года.
Переходил по манам + советом помогал 1 человек, который внедрял в своё время Git в одной крупной it-конторе
Сколько уже этой фраз лет отбиваются поклонники Git от простой истины:
«В Git не хранится история commit'a, а поэтому это сложный в работе интсрумент»
Результатом этого мы можем взглянуть на множество примеров известных продуктов. Там просто не сливают ветки, что бы можно было нормально работать. Они одни и те же изменения копируют из ветки в ветку и делают commit.
Ну к примеру PostgreSQL: github.com/postgres/postgres/network
Или каждый commit подписывают в комментарии, к какой «named branch» он относится, к примеру ядро линукса.
Я прежде чем составить свое мнение о работе изучил около сотни репозиториев известных больших продуктов. Результат один: история, в какой ветке был создан commit — необходима. В Hg она есть и ничего «эмулировать» там не надо.
«В Git не хранится история commit'a, а поэтому это сложный в работе интсрумент»
Результатом этого мы можем взглянуть на множество примеров известных продуктов. Там просто не сливают ветки, что бы можно было нормально работать. Они одни и те же изменения копируют из ветки в ветку и делают commit.
Ну к примеру PostgreSQL: github.com/postgres/postgres/network
Или каждый commit подписывают в комментарии, к какой «named branch» он относится, к примеру ядро линукса.
Я прежде чем составить свое мнение о работе изучил около сотни репозиториев известных больших продуктов. Результат один: история, в какой ветке был создан commit — необходима. В Hg она есть и ничего «эмулировать» там не надо.
Гугл подсказывает, что можно даже проще, всего лишь настроить конфиг:
git config --global push.default current
Да, вы сами ответили положительно на моё утверждение. Без google с git очень тяжело работать.
И тот же .gin/config не так уж и по настраиваешь по памяти, не потратив времени на освежение памяти из доков по разделам remote и branch
c Hg не нужен Google.
Один раз ознакомившись, ты уже не разучишься, как на велосипеде — вот в чем прелесть.
У меня команда, которая больше года работала с GIT и которая в должной мере не могла его осилить при всех попытках, пересела на Hg за 2 дня и начала успешно работать. Спустя неделю, ребята уже каждый под себя TortouseHg заточил с custom visual diff tools.
Один раз ознакомившись, ты уже не разучишься, как на велосипеде — вот в чем прелесть.
У меня команда, которая больше года работала с GIT и которая в должной мере не могла его осилить при всех попытках, пересела на Hg за 2 дня и начала успешно работать. Спустя неделю, ребята уже каждый под себя TortouseHg заточил с custom visual diff tools.
И мартышку можно научить делать последовательность комманд.
Вот пример, который был недавно. Тестировщик.
По бумажке работал с Git. Выливал изменения в основную ветку, после тестирования их в своей ветке, таким образом, что бы тестовые изменения в эту ветку не попали.
В Hg он выбросил инструкцию спустя два дня. Хотя до этого, бумажка с последовательностью команд Git у него висела очень долго.
Показатель?
Если нет, тогда мы совсем по разному с вами глядим на этот мир.
Вот пример, который был недавно. Тестировщик.
По бумажке работал с Git. Выливал изменения в основную ветку, после тестирования их в своей ветке, таким образом, что бы тестовые изменения в эту ветку не попали.
В Hg он выбросил инструкцию спустя два дня. Хотя до этого, бумажка с последовательностью команд Git у него висела очень долго.
Показатель?
Если нет, тогда мы совсем по разному с вами глядим на этот мир.
да, кстати, если не согласны с каким пунктом — отпишитесь. А то как то не понятно, где я заблуждаюсь.
Есть у них rebase, использовать только не принято. Нет push -f и push --delete, но статья не о том.
да, мне уже заметили комментарием выше, что я погорячился с отсутствием команды rebase. Только это не отменяет того факта, что она практически не используется в mercurial — ну и по умолчанию она выключена как вы сами заметили.
А изучать матчасть этой команды нет никакого желания (и без нее я вполне обхожусь), но спасибо за предложение.
А изучать матчасть этой команды нет никакого желания (и без нее я вполне обхожусь), но спасибо за предложение.
если бы это было действительно так, с чего бы класть этот плагин в коробку?
я полагаю — для удобства людям которые ранее использовали git и считают подмену истории в системе контроля версий нечто само собой разумеющимся.
б) если бы это было действительно так, с чего бы класть этот плагин в коробку?
Rebase — это расширение и по умолчанию отключено, а merge находится в ядре.
Команды rebase нет наверное ни в какой другой системе контроля версий. Это подчеркивает, насколько развит git и насколько у него большая community, что в нем реализованы *специальные* возможности для *специальных* целей, которые другим системам и не снились. Конечно же, Капитан подсказывает, что всем подряд использовать специальные возможности ни к чему.
Напишите в какой, я после того, как освоил git, за другими пристально не слежу, но быть в курсе эволюции всей области хочется.
Собственно, я по-моему слышал о rebase-плагине для bazaar, но плагин это не совсем то… Когда мне нужен будет rebase для bazaar (что иногда случается), я не побегу искать плагин, а матернусь и с'merge'у. (А merge'ы у bzr особо плохие по сравнению с git'ом, они фактически squash'ат по-умолчанию, докопаться по внутренней истории можно, но нужно давать дополнительные ключи в bzr log -p и т.д.; итог: по практичности далеко до git'а).
Собственно, я по-моему слышал о rebase-плагине для bazaar, но плагин это не совсем то… Когда мне нужен будет rebase для bazaar (что иногда случается), я не побегу искать плагин, а матернусь и с'merge'у. (А merge'ы у bzr особо плохие по сравнению с git'ом, они фактически squash'ат по-умолчанию, докопаться по внутренней истории можно, но нужно давать дополнительные ключи в bzr log -p и т.д.; итог: по практичности далеко до git'а).
У mercurial это стандартное дополнение (т.е. присутствует сразу после установки, но отключено). Если напишете
hg rebase
, не включив его, то получитеhg: неизвестная команда 'rebase'
'rebase' предоставляется следующим расширением:
rebase команда для перемещения наборов ревизий к другому предку
наберите "hg help extensions" для справки по включению расширений
.Nice! Не знаете, много ли пользователей hg включают и используют его? С одной стороны, логика не включения — это advanced'нутая фишка, и ее неразумное использование приводит к примерам, как в этой статье. С другой стороны, это advanced'нутая фишка, и требуется много глаз и рук, чтобы сделать ее реально пригодной и удобной в использовании.
Я помню как rebase в git был весьма нервотрепным делом. Собственно, и сейчас там есть неочевидные моменты (например, если какой-то патч уже в upstream'е, то может выйти конфликт, после резолюции которого будут нулевые изменения в рабочей копии, которые соответственно нельзя за'add'ить в индекс, а значит нельзя сделать rebase --continue; нужно знать, что в это момент нужно дать rebase --skip).
Я помню как rebase в git был весьма нервотрепным делом. Собственно, и сейчас там есть неочевидные моменты (например, если какой-то патч уже в upstream'е, то может выйти конфликт, после резолюции которого будут нулевые изменения в рабочей копии, которые соответственно нельзя за'add'ить в индекс, а значит нельзя сделать rebase --continue; нужно знать, что в это момент нужно дать rebase --skip).
А почему «записывают на бумажке хэши» без пруфлинка? :-E
> Если «удобной и пригодной» == «один в один как в git»
Нет, это имеющей как можно более простой процесс применения и с возможно меньшим количество corner cases. Ну и en.wikipedia.org/wiki/Principle_of_least_astonishment. git в этом не идеален, конкретный пример я привел — когда должен был бы работать rebase --continue, «почему то» требуется rebase --skip (детали почему простым пользователям не интересны).
> Ну и да, rebase — детерминированная операция
Статья выше аргументирует, что нет.
Спасибо за ссылку на Phases, рад знать, что фичи hg развиваются. Уверен, что тот, кому нужно маркировать коммиты как public/draft/secret его сразу находит и использует. Увы, в mercurial достаточно своих некрасивостей и нурешений принципа least user surprise…
> Если «удобной и пригодной» == «один в один как в git»
Нет, это имеющей как можно более простой процесс применения и с возможно меньшим количество corner cases. Ну и en.wikipedia.org/wiki/Principle_of_least_astonishment. git в этом не идеален, конкретный пример я привел — когда должен был бы работать rebase --continue, «почему то» требуется rebase --skip (детали почему простым пользователям не интересны).
> Ну и да, rebase — детерминированная операция
Статья выше аргументирует, что нет.
Спасибо за ссылку на Phases, рад знать, что фичи hg развиваются. Уверен, что тот, кому нужно маркировать коммиты как public/draft/secret его сразу находит и использует. Увы, в mercurial достаточно своих некрасивостей и нурешений принципа least user surprise…
> какой пруфлинк?
Это была ирония на вашу иронию. Просто вы дали три ссылки подряд, какой hg хороший, что можно было подумать, что про «на бумажке» тоже прям из какого-то man git скопировано ;-).
> То, что git никак не помечает, выпинан ли коммит куда-нибудь или нет
Вы явно что-то путаете. Это SVN никак не помечал (и то в 2.0 или около исправили), был ли коммит смержен, например при мерже одной и той же ветки дважды вылезут конфликты. git разумеется ведет историю merge'ей и все работает, как надо.
Это не относится к rebase, потому что rebase работает не на уровне истории основной ветки, а на уровне последовательности отдельных патчей в нашей ветке. Наши коммиты попросту применяются один за другим к *совершенно новой истории upstream'а*, и либо применяется, либо patch «видит», что он уже применен (тогда из нашей ветки этот патч уходит), либо есть конфликт (и пользователь исправляет). Автор этой статьи вот нашел, как подстроить конфликт, который patch git'а не заметил. Кулхацкер. Итог, при rebase'е нечего «записывать», кроме самих патчей, что git делает неплохо, но поскольку patch — штука эвристическая, то ошибиться может.
> а разве --interactive не добавляет corner cases в rebase?
Дай бог многим системам такого пользовательского интерфейса, как в git rebase --interactive. Все в вашем любимом текстовом редакторе, помощь перед глазами, все весьма очевидно — хочешь переставить коммиты — переставь строки в редакторе. Я никогда не пользуюсь git merge --squash — зачем мне помнить об этом corner case'е, если git rebase --interactive позволяет добиться и его эффекта, и многих других.
Это была ирония на вашу иронию. Просто вы дали три ссылки подряд, какой hg хороший, что можно было подумать, что про «на бумажке» тоже прям из какого-то man git скопировано ;-).
> То, что git никак не помечает, выпинан ли коммит куда-нибудь или нет
Вы явно что-то путаете. Это SVN никак не помечал (и то в 2.0 или около исправили), был ли коммит смержен, например при мерже одной и той же ветки дважды вылезут конфликты. git разумеется ведет историю merge'ей и все работает, как надо.
Это не относится к rebase, потому что rebase работает не на уровне истории основной ветки, а на уровне последовательности отдельных патчей в нашей ветке. Наши коммиты попросту применяются один за другим к *совершенно новой истории upstream'а*, и либо применяется, либо patch «видит», что он уже применен (тогда из нашей ветки этот патч уходит), либо есть конфликт (и пользователь исправляет). Автор этой статьи вот нашел, как подстроить конфликт, который patch git'а не заметил. Кулхацкер. Итог, при rebase'е нечего «записывать», кроме самих патчей, что git делает неплохо, но поскольку patch — штука эвристическая, то ошибиться может.
> а разве --interactive не добавляет corner cases в rebase?
Дай бог многим системам такого пользовательского интерфейса, как в git rebase --interactive. Все в вашем любимом текстовом редакторе, помощь перед глазами, все весьма очевидно — хочешь переставить коммиты — переставь строки в редакторе. Я никогда не пользуюсь git merge --squash — зачем мне помнить об этом corner case'е, если git rebase --interactive позволяет добиться и его эффекта, и многих других.
Сейчас в mercurial пилят changeset obsolescense: хотя эта возможность и предполагается как замена MQ, я в первую очередь вижу в ней возможность полностью безопасного изменения истории (в репозитории остаются все изменения, просто при переписывании mercurial указывает, что данное изменение является obsolete, указывает, что данное изменение является заменой другого (или наоборот, указывает, какое изменение является заменой данного — не узнавал, в метаданных какого изменения всё это сохраняется), а также по‐умолчанию отдаёт только не‐obsolete изменения (дополнительно скрывая уже имеющиеся)).
Есть и дополнение, автоматическим образом разрешающее часть проблем вроде «изменение в вашем репозитории имеет obsolete предка» и д.р. Поищите «mercurial changeset obsolescense». То есть, теперь уже «mercurial changeset evolution».
Есть и дополнение, автоматическим образом разрешающее часть проблем вроде «изменение в вашем репозитории имеет obsolete предка» и д.р. Поищите «mercurial changeset obsolescense». То есть, теперь уже «mercurial changeset evolution».
Такого рода изменения, кстати, хорошо показывают, у кого лучше архитектура. Я лично не знаю ни одного примера добавления возможности в, так сказать, ядро git с самого момента начала его мною использования. В Mercurial же спокойно добавляют largefiles, phases и changeset evolution, да ещё и без проблем с совместимостью.
Не говоря уже о том, что рабочий прототип на Python с использованием довольно неплохого внутреннего API (позволяющего в т.ч. изменять поведение встроенных команд), написать гораздо легче, чем тот же прототип для git на C без специальной поддерки изменения поведения команд со стороны уже имеющегося кода.
Не говоря уже о том, что рабочий прототип на Python с использованием довольно неплохого внутреннего API (позволяющего в т.ч. изменять поведение встроенных команд), написать гораздо легче, чем тот же прототип для git на C без специальной поддерки изменения поведения команд со стороны уже имеющегося кода.
Кстати, в случае с mercurial с или без changeset evolution тот, кто делал rebase, мог бы восстановить контекст и объяснить действия Василия (с changeset evolution это мог бы дополнительно сделать любой, кто сделал pull до rebase; правда требуется либо настройка сервера, чтобы он не считал находящиеся на нём изменения публичными, либо принудительное выставление фаз администратором на сервере и rebase его же силами). Без changeset evolution rebase делает резервные копии удаляемых изменений в
.hg/strip-backup
, с — просто оставляет их в репозитории (никакого автоматического gc!). Git же оставляет эти резервные копии на милость своего gc.Ах, да, и еще, под «rebase в git» и «нет ни в какой другой системе», я понимаю так же и rebase --interactive, т.е. не просто перекинуть «свой» блок коммитов на (новый) верх «чужой ветки», а вообще легко и удобно переписывать историю.
еще можно делать git merge --squash, получается что все одним коммитом и в истории красота
Спасибо автору за хороший пример. Хочу еще раз отметить, что ошибка возникает и в том и другом случае, просто в случае с rebase сложнее понять историю и контекст ее внесения.
Нужно просто помнить, что любое решение в IT это всегда компромисс. Серебряной пули не существует и приходится постоянно делать осознанный выбор, чем-то жертвуя и что-то получая взамен.
Git очень гибок, предоставляет кучу возможностей и допускает разные сценарии работы с ним, но требует хорошего понимания что происходит и что может случиться.
У нас в проекте сейчас используется стратегия rebase (+squash), что позволяет держать историю коммитов красивой, компактной и почти линейной. Проект не очень большой, и ошибки такого рода у нас возникают очень редко (если вообще возникают), благодаря изолированности изменений (как правило разные разработчики редко правят одни и те же файлы) и тестам. Ну а если вдруг возникают — то у нас это не повод кого-то увольнять, а повод покрыть этот код тестом и исправить ошибку. Причина появления ошибки вторична.
Выбор, конечно, должен быть осознанным. Но ведь как чертовски хорошо, что Git нам дает право на этот выбор!
Ваш КО.
Нужно просто помнить, что любое решение в IT это всегда компромисс. Серебряной пули не существует и приходится постоянно делать осознанный выбор, чем-то жертвуя и что-то получая взамен.
Git очень гибок, предоставляет кучу возможностей и допускает разные сценарии работы с ним, но требует хорошего понимания что происходит и что может случиться.
У нас в проекте сейчас используется стратегия rebase (+squash), что позволяет держать историю коммитов красивой, компактной и почти линейной. Проект не очень большой, и ошибки такого рода у нас возникают очень редко (если вообще возникают), благодаря изолированности изменений (как правило разные разработчики редко правят одни и те же файлы) и тестам. Ну а если вдруг возникают — то у нас это не повод кого-то увольнять, а повод покрыть этот код тестом и исправить ошибку. Причина появления ошибки вторична.
Выбор, конечно, должен быть осознанным. Но ведь как чертовски хорошо, что Git нам дает право на этот выбор!
Ваш КО.
К сожалению(к счастью?) пример не корректный. Выше я описал почему.
Прочитал выше — не понимаю, что конкретно не корректно. Я специально проверил прежде чем писать комментарий. Все получилось как у автора, конфликта при rebase не было, а ошибка в итоге появилась. Пробовал именно такой сценарий, который используеся у нас (и не только, он довольно стандартен). Опишу подробнее по шагам, чтобы прояснить:
1. Есть master в котором есть файл с таким содержимым:
сумма
2
и
2
равна
4
2. Есть 2 разработчика, каждый создает свой бранч (feature1, feature2)
3. В feature1 вносится изменение: слово «сумма» меняется на «произведение». Коммитит.
4. feature1 мержится в мастер (не важно через pull request или нет)
5. В feature2, другой разработчик меняет «2» и «4» на «3» и «5» соответсвенно. (У него по-прежнему «сумма», а не «произведение», потому код верный). Коммитит.
6. В feature2 делается
7. feature2 мержится в мастер. В результате ошибка оказывается в мастере.
Еще раз хочу отметить, что при merge ошибка точно также возникнет. Но как справедливо заметил автор, по истории git-а в случае merge будет легче разобраться как и почему так получилось. Вроде об этом и статья.
1. Есть master в котором есть файл с таким содержимым:
сумма
2
и
2
равна
4
2. Есть 2 разработчика, каждый создает свой бранч (feature1, feature2)
3. В feature1 вносится изменение: слово «сумма» меняется на «произведение». Коммитит.
4. feature1 мержится в мастер (не важно через pull request или нет)
5. В feature2, другой разработчик меняет «2» и «4» на «3» и «5» соответсвенно. (У него по-прежнему «сумма», а не «произведение», потому код верный). Коммитит.
6. В feature2 делается
git rebase master
. Тут rebase проходит автоматически, конфликта НЕ возникает, в коде появляется ошибка, как описал автор.7. feature2 мержится в мастер. В результате ошибка оказывается в мастере.
Еще раз хочу отметить, что при merge ошибка точно также возникнет. Но как справедливо заметил автор, по истории git-а в случае merge будет легче разобраться как и почему так получилось. Вроде об этом и статья.
Ну по сути ребейз здесь приянут за уши. Все что вы делеаете это переносите патч в другую ветку. Сооственно без какой либо проверки, и да он проходит. Я еще не встречал что бы кто то так пользовался ребэйзом, но так тоже можно делать. Если делать по нормальному, пример скрипта выше, то этой проблемы не будет, так как будет конфликт.
Если коротко то сейчас это выглядит так:
1. есть код в котором последнее состояние: 2*2=4
2. есть патч который заменяет 2 и 4 на 3 и 5
3. если такой патч применить, он сооствественно пройдет без конфликтов, и результат будет 2*3=5
И это не потому что ребэйз плохой, это потому что им изначально не верно воспользовались + неверное описание комитов.
Если коротко то сейчас это выглядит так:
1. есть код в котором последнее состояние: 2*2=4
2. есть патч который заменяет 2 и 4 на 3 и 5
3. если такой патч применить, он сооствественно пройдет без конфликтов, и результат будет 2*3=5
И это не потому что ребэйз плохой, это потому что им изначально не верно воспользовались + неверное описание комитов.
В вашем примере скрипта делается абсолютно то же самое, только формат записи не такой, как у меня. Посмотрите внимательней.
Почему не такой? Все тоже самое в столбик, только нету слов сумма и произведение, вот добавил и их, ничего не изменилось:
Обновленный скрипт
git init
touch test.txt
echo -e "сумма\n2\n+\n2\n=\n4" > test.txt
git add test.txt
git commit -m "2+2=4"
git checkout -b my
echo -e "сумма\n2\n+\n3\n=\n5" > test.txt
git add test.txt
git commit -m "2+3=5"
git checkout master
echo -e "произведение\n2\n*\n2\n=\n4" > test.txt
git add test.txt
git commit -m "2*2=4"
git checkout my
git rebase master
«сумма\n2\n+\n2\n=\n4» != «сумма\n2\nи\n2\n=\n4»
Я специально подбирал формат записи такой, чтоб конфликтов не было.
Я специально подбирал формат записи такой, чтоб конфликтов не было.
Не поленился, подправил ваш скрипт, и запустил. Конфликтов нет, всё как в статье.
Исправленный скрипт
git init
touch test.txt
echo -e "сумма\n2\nи\n2\n=\n4" > test.txt
git add test.txt
git commit -m "2+2=4"
git checkout -b my
echo -e "сумма\n2\nи\n3\n=\n5" > test.txt
git add test.txt
git commit -m "2+3=5"
git checkout master
echo -e "произведение\n2\nи\n2\n=\n4" > test.txt
git add test.txt
git commit -m "2*2=4"
git checkout my
git rebase master
Спасибо, я уже тоже сам проверил, действительно нету, уж учень мало информации что бы он был. Но пример конечно сильно искусственный, на практике вероятность его встретить очень мала. А история от мержа будет не красивой, сложной при просмотре, не говоря уже о том что если понадобится перенести историю в виде патчей на другое дерево исходных кодов.
Как вы видите, всего один символ и у нас выкидывает конфликт.
Но мораль rebase vs merge ясна, спасибо за терпеливость и помощь с форматом! :)
Как вы видите, всего один символ и у нас выкидывает конфликт.
Но мораль rebase vs merge ясна, спасибо за терпеливость и помощь с форматом! :)
Я бы не стал утверждать, что вероятность мала. На практике изменения Пети и Васи будут разделены не 1 символом, а десятками строк, а скорее и вообще в разных файлах будут. И конфликта скорее всего также не будет.
У меня прям сейчас открыт репозиторий, в котором однозначно при rebase вместо merge была бы масса «битых» ревизий.
Некрасивая история при merge — это конечно аргумент. Только возникает вопрос на подумать. Что мы хотим от истории: достоверности и информативности или красоты?
Кстати, cherry-pick коммитов при merge — совсем не сложное дело, если коммиты сгруппированы в отдельные законченные понятные ветки: берешь все коммиты ветки и переносишь. А с rebase надо ещё разобраться, с какого коммита нужная функциональность начинается, и на каком заканчивается.
У меня прям сейчас открыт репозиторий, в котором однозначно при rebase вместо merge была бы масса «битых» ревизий.
Некрасивая история при merge — это конечно аргумент. Только возникает вопрос на подумать. Что мы хотим от истории: достоверности и информативности или красоты?
Кстати, cherry-pick коммитов при merge — совсем не сложное дело, если коммиты сгруппированы в отдельные законченные понятные ветки: берешь все коммиты ветки и переносишь. А с rebase надо ещё разобраться, с какого коммита нужная функциональность начинается, и на каком заканчивается.
Имел ввиду, перенос патчей на полностью новое дерево, ни как не связанное с текущим репозиторием. Вероятность такого события где то равна проблеме с ребэйзом.
> А с rebase надо ещё разобраться, с какого коммита нужная функциональность начинается, и на каком заканчивается.
Обычно в комитах есть номера тикетов, к чему относится та или иная фича/баг, поиск не вызывает особых проблем. Кстати что происходит, когда замерженную фичу надо обновить по каким либо причинам?
> А с rebase надо ещё разобраться, с какого коммита нужная функциональность начинается, и на каком заканчивается.
Обычно в комитах есть номера тикетов, к чему относится та или иная фича/баг, поиск не вызывает особых проблем. Кстати что происходит, когда замерженную фичу надо обновить по каким либо причинам?
Открывается ветка с тем же именем, выполняются коммиты, ветка вливается в master (или куда принято). Всё то же самое, что и при rebase, только коммиты останутся сгруппированы в отдельной небольшой ветке (фича целиком окажется в 2 ветках: основной и дополнительной).
Кстати, на сколько я понимаю, выборку коммитов, созданных в конкретной ветке, удобнее всего делать в Mercurial: там в коммите сохраняется имя ветки. Возможно пользователи этой DVCS расскажут подробнее.
Кстати, на сколько я понимаю, выборку коммитов, созданных в конкретной ветке, удобнее всего делать в Mercurial: там в коммите сохраняется имя ветки. Возможно пользователи этой DVCS расскажут подробнее.
Если вам надо просто отменить слияние, то вы просто откатываете (revert, ни в коем случае не прочие манипуляции с историей) изменение, в котором слияние было произведено (если, конечно, первый родитель правильный: если делать слияние feature -> master, а не наоборот с последующим fast-forward merge. Документация предостерегает, что при повторном слиянии надо сначала откатить изменение с предыдущим откатом изменений и только затем делать повторное слияние).
С Mercurial вы действительно можете узнать, какой ветке принадлежало изменение. А можете и не узнать, если вместо веток использовались закладки (с некоторыми оговорками — то же, что и ветки в Git). Или отдельные каталоги вместо веток. Или программист поленился задать имя ветки (одна ветка с двумя головами — нормальная ситуация, иногда удобно). В общем, зависит от степени разгильдяйства программистов и мер защиты от оного. Как правило, сделать выборку действительно легче.
Для Git где‐то вроде лежит сложный скрипт на perl, который делает то же самое, с некоторой вероятностью даже давая верный результат. При использовании модели git flow при отсутствии отклонений от неё особых проблем с определением ветки не возникает.
С Mercurial вы действительно можете узнать, какой ветке принадлежало изменение. А можете и не узнать, если вместо веток использовались закладки (с некоторыми оговорками — то же, что и ветки в Git). Или отдельные каталоги вместо веток. Или программист поленился задать имя ветки (одна ветка с двумя головами — нормальная ситуация, иногда удобно). В общем, зависит от степени разгильдяйства программистов и мер защиты от оного. Как правило, сделать выборку действительно легче.
Для Git где‐то вроде лежит сложный скрипт на perl, который делает то же самое, с некоторой вероятностью даже давая верный результат. При использовании модели git flow при отсутствии отклонений от неё особых проблем с определением ветки не возникает.
Спасибо.
Вообще, отмена слияний в git дело не хитрое, главное понять принцип. Вот кстати, хорошая статья об этом, может кому пригодится.
Хм. Есть всё-таки что-то пугающее в этом зоопарке видов веток, — он всегда меня останавливал, когда я думал на досуге изучить Mercurial.
Да, простой поиск по commit message выдаст все merge-коммиты нужной ветки. Я просто предполагал, что в Mercurial это может быть изящнее.
Например, при задаче: в репозиторий Foo сделать cherry-pick всех коммитов ветки feature из репозитория Bar. В git нужно будет отобрать нужные последовательности коммитов, и для каждой сделать cherry-pick. А в Mercurial?
Вообще, отмена слияний в git дело не хитрое, главное понять принцип. Вот кстати, хорошая статья об этом, может кому пригодится.
С Mercurial вы действительно можете узнать, какой ветке принадлежало изменение. А можете и не узнать, если вместо веток использовались закладки (с некоторыми оговорками — то же, что и ветки в Git). Или отдельные каталоги вместо веток.
Хм. Есть всё-таки что-то пугающее в этом зоопарке видов веток, — он всегда меня останавливал, когда я думал на досуге изучить Mercurial.
При использовании модели git flow при отсутствии отклонений от неё особых проблем с определением ветки не возникает.
Да, простой поиск по commit message выдаст все merge-коммиты нужной ветки. Я просто предполагал, что в Mercurial это может быть изящнее.
Например, при задаче: в репозиторий Foo сделать cherry-pick всех коммитов ветки feature из репозитория Bar. В git нужно будет отобрать нужные последовательности коммитов, и для каждой сделать cherry-pick. А в Mercurial?
Например, при задаче: в репозиторий Foo сделать cherry-pick всех коммитов ветки feature из репозитория Bar. В git нужно будет отобрать нужные последовательности коммитов, и для каждой сделать cherry-pick. А в Mercurial?И в git, и в mercurial можно скормить команде cherry-pick (git) или transplant (mercurial, находится в одном из стандартных расширений) диапазон. В mercurial вместо диапазона можно написать что‐нибудь посложнее.
hg transplant -b {branch name}
или hg rebase --keep --source {branch name}
пересадит ветку. То же самое может сделать hg transplant 'branch(branch name)'
, но я не знаю, как всё это работает с уже применёнными изменениями. hg transplant
отказывается пересаживать родителя текущего изменения, git cherry-pick
не смущается, даже если его попросить пересадить HEAD. hg rebase -r {revisions}
, кстати, тоже работает, только отказывается если ревизии не связаны.Несколько более сложный пример: взять все ревизии из ветки foo, вносившие изменение в файл bar:
hg transplant 'branch(foo) and file(bar)'
.Хорошие примеры, спасибо!
* В первом примере с rebase не --source, а --base. В отличие от --source, делает то же, что и git rebase.
Можно и так. Только transplant — хорошее, запоминающееся имя (т.к. заимствованные слова на его базе всё время на слуху), а graft я как несколько раз видел, так и не запомнил. Судя по словарю является медицинским и садоводческим термином. И с двумя разговорными вариантами, не имеющими к цели команды отношения, — взятка/взяточничество и (брит.) работа. Кальк и заимствований на основе graft я ни разу не слышал, само слово без контекста «есть такая команда в Mercurial» — тоже.
забавно.
Вот такой
скрипт приводит к конфликту, а вот такой
нет. Подозреваю, что что-то нечисто с правилами автомерджа. Я всегда косо смотрел на его принципиальную осуществимость. Строчки строчками, а без контекста всё равно никак.
Вот такой
git init echo -e "2\n+\n2\n=\n4" > test.txt git add test.txt git commit -m "2+2=4" git checkout -b my echo -e "2\n+\n3\n=\n5" > test.txt git add test.txt git commit -m "2+3=5" git checkout master echo -e "2\n*\n2\n=\n4" > test.txt git add test.txt git commit -m "2*2=4" git checkout my git rebase master
скрипт приводит к конфликту, а вот такой
git init echo -e "+\n2\n2\n=\n4" > test.txt git add test.txt git commit -m "2+2=4" git checkout -b my echo -e "+\n2\n3\n=\n5" > test.txt git add test.txt git commit -m "2+3=5" git checkout master echo -e "*\n2\n2\n=\n4" > test.txt git add test.txt git commit -m "2*2=4" git checkout my git rebase master
нет. Подозреваю, что что-то нечисто с правилами автомерджа. Я всегда косо смотрел на его принципиальную осуществимость. Строчки строчками, а без контекста всё равно никак.
Всё верно. При префиксной нотации конфликта нет, я просто записал её словами.
На мой взгляд, никаких проблем с правилами мерджа нет.
В первом случае перед гитом стоит задача «найди строку „2“ между „+“ и „=“ и замени её на „3“», и не может её выполнить, т.к. нужные строки выглядят как «2,*,2,=,4» (вместо ожидаемого "+" стоит "*").
А во втором — «найди строку „2“ между „2“ и „=“ и замени её на „3“», и успешно её решает, т.к. строки выглядят как "*,2,2,=,4".
«4» на «5» меняется в обоих случаях без конфликта.
На мой взгляд, никаких проблем с правилами мерджа нет.
В первом случае перед гитом стоит задача «найди строку „2“ между „+“ и „=“ и замени её на „3“», и не может её выполнить, т.к. нужные строки выглядят как «2,*,2,=,4» (вместо ожидаемого "+" стоит "*").
А во втором — «найди строку „2“ между „2“ и „=“ и замени её на „3“», и успешно её решает, т.к. строки выглядят как "*,2,2,=,4".
«4» на «5» меняется в обоих случаях без конфликта.
честно говоря, не вижу никакого оправдания такой дискриминации польской нотации (или такого фаворитизма инфиксной).
Не сочтите за пеар, написал вот тут свои выводы о.
Не сочтите за пеар, написал вот тут свои выводы о.
ну т.е. технически вы правы — проблем с правилами мерджа нет, они едины. Однако то, что они выполняют сематически эквивалентную замену в одном случае и не выполняют в другом — общая и вобщем нерешаемая проблема, которая отлично проиллюстрирована вашим примером.
Вообще, можно написать стратегию слияний, игнорирующую различные строки вокруг изменяемой, чтобы конфликт выдавался только если одна и та же строка была изменена в обеих ветках. Тогда конфликта не будет в обоих случаях.
Также как можно написать и стратегию, чтобы конфликты вылезали чаще, например всегда, когда изменен один и тот же файл.
По моей субъективной оценке, обе эти крайности окажут скорее негативное влияние на эффективность разработки среднестатистического проекта, нежели позитивное. Нужен компромисс, например тот, что есть сейчас.
Также как можно написать и стратегию, чтобы конфликты вылезали чаще, например всегда, когда изменен один и тот же файл.
По моей субъективной оценке, обе эти крайности окажут скорее негативное влияние на эффективность разработки среднестатистического проекта, нежели позитивное. Нужен компромисс, например тот, что есть сейчас.
А вот и результирующая картинка дерева во время ребэйза, где видно и формат записи, и то что конфликт, и то что конфликт именно на тех местах где и должен быть:


На мой взгляд, вы торопитесь с выводами, говоря что тут rebase притянут за уши, и что мы им не правильно пользуемся. Описанный мной сценарий довольно популярен, и является рекомендованным, например, при разработке Ruby on Rails.
Если представить, что оба разработчика из моего примера не являются core members и не могут пушить в мастер, а только шлют пулл реквесты и при этом соблюдают рельсовый contribution guide, то именно так в жизни это и будет выглядеть. Шаги 4 и 7 будут выполнены через пулл реквесты.
Посмотрите, пожалуйста, внимательнее. Тут совсем не «Все что вы делаете это переносите патч в другую ветку». Тут классическая история, когда во время разработки feature2, мастер (естественно) ушел вперед. Поэтому чтобы влить feature2 назад в master, мы делаем сначала rebase, тем самым записывая изменения произведенные в feature2 поверх уже нового мастера. После этого шлем pull request (ну или мержим feature2 в мастер сами).
Если представить, что оба разработчика из моего примера не являются core members и не могут пушить в мастер, а только шлют пулл реквесты и при этом соблюдают рельсовый contribution guide, то именно так в жизни это и будет выглядеть. Шаги 4 и 7 будут выполнены через пулл реквесты.
Посмотрите, пожалуйста, внимательнее. Тут совсем не «Все что вы делаете это переносите патч в другую ветку». Тут классическая история, когда во время разработки feature2, мастер (естественно) ушел вперед. Поэтому чтобы влить feature2 назад в master, мы делаем сначала rebase, тем самым записывая изменения произведенные в feature2 поверх уже нового мастера. После этого шлем pull request (ну или мержим feature2 в мастер сами).
Это как раз пример того, что патч уже устарел. И если будет ревью, то в патче должы будут отказать и отправить назад на доработку.
Очень интересный пример. Не думал о такой возможности. Спасибо! :-)
Тяжёлый некропостинг, но эта статья всплыла как аргумент в свежем обсуждении на RSDN, поэтому прокомментирую.
1. Принципиальное: каждый, кто пользуется rebase, должен понимать, что это особенная разновидность мержа (в общем смысле) — что он получает результат, на который уже воздействует другая ревизия, чем та, на которой он до того основывался.
Это почти тавтология, пока не встаёт вопрос о том, что кто-то не хочет понимать последствия этого факта. А именно — что результат надо проверять заново.
Как минимум для этого надо проглядеть глазами, что получилось, и проверить тестами. Будут это ручные тесты, автотесты или что-то ещё — уже вопрос местного workflow, хотя автотесты, конечно, лучше всего, и форсированные, как через CI средство; а также peer review, без которого тоже разработка чего-то серьёзного имеет слишком мало смысла.
Из этого кажется, что с rebase сложнее. Но:
2. В случае логического конфликта, проблемы с merge того же порядка, что и с rebase, а часто и серьёзнее.
Логический конфликт — это примерно как в этой статье, когда нет текстуального конфликта. Но и в случае текстуального конфликта может быть проблема: тот, кто будет решать конфликт, хоть его и увидит, может ошибиться в решении.
В статье это обойдено очень «хитро»:
>> — Ага, нашел. В коде «2*3=5», ещё бы оно работало. Так, это появилось, когда слили 2 ветки.
Так вот — кто "слили"? Это конкретное действие слияния, за которое кто-то несёт ответственность.
Что у автора статьи мерж — это какое-то событие уровня стихийного бедствия, за которое никто не отвечает. Стукнула молния, пожгла дом, оползень унёс, мерж сломал продукт — ok, никто не виноват, пусть страховая платит.
А это не так. Мерж — не зря отдельный коммит, у которого есть все данные для того, чтобы иметь его состояние и предков. По крайней мере в Git. И он должен быть проверен автотестами, ручными тестами, или что там ещё делается для этого, а если изменений немного — он и визуально должен быть проверен в затронутых местах.
(Да, часто это игнорируют. В продукте, на который я сейчас трачу основное время, именно так: конкретный коммит проверяется — Gerrit посылает его в Jenkins на автотесты; но, если потребовалось слияние, результат просто фиксируется в таком виде. Но у нас хотя бы ежесуточный прогон голов активных веток находит проблемы. За последний год мы ломали мержем дважды. Альтернатива хуже: мы могли бы разрешить отправку merge commits с тем, чтобы Gerrit принимал только fast-forward отправки, но это резко замедлило бы работу за счёт того, что каждый коммит шёл бы по несколько кругов. Так что мы сознательно тут допускаем послабления и оставляем время для разгребания проблем. Повторяю: сознательно.)
Этот мерж в статье — кто был ответственен за проверку результата? У него есть конкретный автор — Антон. У него были тесты в помощь? Вот с этого надо было начинать, а также с того, почему Антон мержит ветки с работой Пети и Васи (а не Петя или Вася, которые лучше помнят и знают, что там происходило...), и почему Васю в одной вселенной уволили, а Антона в другой — нет.
А если перестать прятать голову в песок и признать ответственность и за мержи тоже — то окажется, что они не имеют никакого мистического преимущества. И по сравнению с rebase:
1) Мержи маскируют реальный конфликт, откладывая его на потом.
2) Они усложняют диагностику и ретроспективу. Больше веток для поиска (git bisect имеет сложную политику для движения по нескольким веткам DAG). Сложнее читать диффы по сравнению с двумя и более родителями, чем с одним.
3) За счёт основывания на более ранних версиях (если не ограничено — может оказаться, что база для влитой ветки вообще многолетней давности), проблемы пунктов (1) и (2) могут усиливаться до фатальности. Например, может оказаться, что использованная foo() 5 лет назад и та foo(), что сейчас, вообще не имеют ничего общего, кроме названия. В случае rebase автор коммита получает хотя бы напоминание, что он основывается на свежем состоянии.
1. Принципиальное: каждый, кто пользуется rebase, должен понимать, что это особенная разновидность мержа (в общем смысле) — что он получает результат, на который уже воздействует другая ревизия, чем та, на которой он до того основывался.
Это почти тавтология, пока не встаёт вопрос о том, что кто-то не хочет понимать последствия этого факта. А именно — что результат надо проверять заново.
Как минимум для этого надо проглядеть глазами, что получилось, и проверить тестами. Будут это ручные тесты, автотесты или что-то ещё — уже вопрос местного workflow, хотя автотесты, конечно, лучше всего, и форсированные, как через CI средство; а также peer review, без которого тоже разработка чего-то серьёзного имеет слишком мало смысла.
Из этого кажется, что с rebase сложнее. Но:
2. В случае логического конфликта, проблемы с merge того же порядка, что и с rebase, а часто и серьёзнее.
Логический конфликт — это примерно как в этой статье, когда нет текстуального конфликта. Но и в случае текстуального конфликта может быть проблема: тот, кто будет решать конфликт, хоть его и увидит, может ошибиться в решении.
В статье это обойдено очень «хитро»:
>> — Ага, нашел. В коде «2*3=5», ещё бы оно работало. Так, это появилось, когда слили 2 ветки.
Так вот — кто "слили"? Это конкретное действие слияния, за которое кто-то несёт ответственность.
Что у автора статьи мерж — это какое-то событие уровня стихийного бедствия, за которое никто не отвечает. Стукнула молния, пожгла дом, оползень унёс, мерж сломал продукт — ok, никто не виноват, пусть страховая платит.
А это не так. Мерж — не зря отдельный коммит, у которого есть все данные для того, чтобы иметь его состояние и предков. По крайней мере в Git. И он должен быть проверен автотестами, ручными тестами, или что там ещё делается для этого, а если изменений немного — он и визуально должен быть проверен в затронутых местах.
(Да, часто это игнорируют. В продукте, на который я сейчас трачу основное время, именно так: конкретный коммит проверяется — Gerrit посылает его в Jenkins на автотесты; но, если потребовалось слияние, результат просто фиксируется в таком виде. Но у нас хотя бы ежесуточный прогон голов активных веток находит проблемы. За последний год мы ломали мержем дважды. Альтернатива хуже: мы могли бы разрешить отправку merge commits с тем, чтобы Gerrit принимал только fast-forward отправки, но это резко замедлило бы работу за счёт того, что каждый коммит шёл бы по несколько кругов. Так что мы сознательно тут допускаем послабления и оставляем время для разгребания проблем. Повторяю: сознательно.)
Этот мерж в статье — кто был ответственен за проверку результата? У него есть конкретный автор — Антон. У него были тесты в помощь? Вот с этого надо было начинать, а также с того, почему Антон мержит ветки с работой Пети и Васи (а не Петя или Вася, которые лучше помнят и знают, что там происходило...), и почему Васю в одной вселенной уволили, а Антона в другой — нет.
А если перестать прятать голову в песок и признать ответственность и за мержи тоже — то окажется, что они не имеют никакого мистического преимущества. И по сравнению с rebase:
1) Мержи маскируют реальный конфликт, откладывая его на потом.
2) Они усложняют диагностику и ретроспективу. Больше веток для поиска (git bisect имеет сложную политику для движения по нескольким веткам DAG). Сложнее читать диффы по сравнению с двумя и более родителями, чем с одним.
3) За счёт основывания на более ранних версиях (если не ограничено — может оказаться, что база для влитой ветки вообще многолетней давности), проблемы пунктов (1) и (2) могут усиливаться до фатальности. Например, может оказаться, что использованная foo() 5 лет назад и та foo(), что сейчас, вообще не имеют ничего общего, кроме названия. В случае rebase автор коммита получает хотя бы напоминание, что он основывается на свежем состоянии.
Кое-какие замечания всё же по комментарию.
Во-первых, математически rebase vs. merge конечно же разное. Линейность истории, обеспечиваемая первым никак не гарантируется последним.
Во-вторых, чтобы слияние было отдельным изменением в истории — это надо форсировать.
В-третьих, и пожалуй главных, надо отличать публичные ветки от опубликованных, если первые — просто публично доступные, то вторые подразумевают неломание истории.
Учитывая вышесказанное, модель разработки может быть такой, что мейнтенер проекта делает только слияния, то девелоперы в своих топик-ветках спокойно делают смену родительского дерева через
P.S. Может быть пересечёмся в этом году — пообщаемся более детально за пивком :)
Во-первых, математически rebase vs. merge конечно же разное. Линейность истории, обеспечиваемая первым никак не гарантируется последним.
Во-вторых, чтобы слияние было отдельным изменением в истории — это надо форсировать.
В-третьих, и пожалуй главных, надо отличать публичные ветки от опубликованных, если первые — просто публично доступные, то вторые подразумевают неломание истории.
Учитывая вышесказанное, модель разработки может быть такой, что мейнтенер проекта делает только слияния, то девелоперы в своих топик-ветках спокойно делают смену родительского дерева через
git pull --rebase origin master
. Глобальное тестирование, понятное дело, работает поверх основной ветки. Разработчики могут в ручном режиме говорить интеграционным тестам прогнать их конкретную публичную (если проект закрытый, то в рамках компании) ветку.P.S. Может быть пересечёмся в этом году — пообщаемся более детально за пивком :)
привет :)
> математически rebase vs. merge конечно же разное. Линейность истории, обеспечиваемая первым никак не гарантируется последним.
Хм, вроде линейность тут и не требовалась?
> Во-вторых, чтобы слияние было отдельным изменением в истории — это надо форсировать.
Ну да. Или оно само, задетектив расхождение веток (собственно, при git pull так и получается — Вася успел закинуть свой коммит, у Пети свой локальный, он говорит pull — оно мержится). Здесь в статье вообще какой-то особый странный вариант получился — мержит кто-то третий, не проверив результат сразу, в результате надо искать последствия уже через фиг знает сколько времени. В нормальном месте, мне кажется, так не работает (см. мой комментарий — если проверяется, взорвалось бы ближайшей ночью, а если нет, то почему не проверяется?)
Вариант, как в Linux kernel, я не учитываю — он явно вне контекста статьи, и там rebase всё равно форсирован(!), пока не принята финальная версия патчей.
> Учитывая вышесказанное, модель разработки может быть такой, что мейнтенер проекта делает только слияния,
И тогда ему отвечать за качество результата. Непроверенная нерабочая ветка перед слиянием — и как тогда он вообще работает?
> Глобальное тестирование, понятное дело, работает поверх основной ветки. Разработчики могут в ручном режиме говорить интеграционным тестам прогнать их конкретную публичную (если проект закрытый, то в рамках компании) ветку.
Ну у нас сейчас всё так и работает. Но прогон через плановый комплект тестов обязателен, чтобы изменение вообще было доступно к мержу в основную ветку (этот контроль возложен на Gerrit). Сводить тут всю нагрузку на одного мейнтейнера как-то неудобно.
> P.S. Может быть пересечёмся в этом году — пообщаемся более детально за пивком :)
Дождёмся конца карантинов и можно будет планировать :)
> математически rebase vs. merge конечно же разное. Линейность истории, обеспечиваемая первым никак не гарантируется последним.
Хм, вроде линейность тут и не требовалась?
> Во-вторых, чтобы слияние было отдельным изменением в истории — это надо форсировать.
Ну да. Или оно само, задетектив расхождение веток (собственно, при git pull так и получается — Вася успел закинуть свой коммит, у Пети свой локальный, он говорит pull — оно мержится). Здесь в статье вообще какой-то особый странный вариант получился — мержит кто-то третий, не проверив результат сразу, в результате надо искать последствия уже через фиг знает сколько времени. В нормальном месте, мне кажется, так не работает (см. мой комментарий — если проверяется, взорвалось бы ближайшей ночью, а если нет, то почему не проверяется?)
Вариант, как в Linux kernel, я не учитываю — он явно вне контекста статьи, и там rebase всё равно форсирован(!), пока не принята финальная версия патчей.
> Учитывая вышесказанное, модель разработки может быть такой, что мейнтенер проекта делает только слияния,
И тогда ему отвечать за качество результата. Непроверенная нерабочая ветка перед слиянием — и как тогда он вообще работает?
> Глобальное тестирование, понятное дело, работает поверх основной ветки. Разработчики могут в ручном режиме говорить интеграционным тестам прогнать их конкретную публичную (если проект закрытый, то в рамках компании) ветку.
Ну у нас сейчас всё так и работает. Но прогон через плановый комплект тестов обязателен, чтобы изменение вообще было доступно к мержу в основную ветку (этот контроль возложен на Gerrit). Сводить тут всю нагрузку на одного мейнтейнера как-то неудобно.
> P.S. Может быть пересечёмся в этом году — пообщаемся более детально за пивком :)
Дождёмся конца карантинов и можно будет планировать :)
Sign up to leave a comment.
Чем опасен rebase, или как получилось, что 2*3=5