Pull to refresh

Comments 123

Другими словами у пользователей GIT нет именованных бранчей, поэтому для того, чтобы дерево коммитов оставалось читабельным, им приходится ужимать бранчи до одного коммита.
Теряя атомарность изменений? Как-бы плохой выход.
Конечно есть именованные бранчи, но коммиты у ним не привязаны и могут из одной именованной ветки кочевать в другую. Для меня самым большим прорывом в использовании git'а стало осознание этого факта. Теперь у меня локально в два раза больше бранчей, чем в репозитории, везде разные коммиты — и я совершенно не переживаю по поводу «красоты» этих коммитов. Когда приходит время комиттить в апстрим, я собираю нужные коммиты в одну ветку и «причёсываю» их.

С одной стороны пока я занимаюсь разработкой мне не надо думать о «красоте» коммитов и дереве истории. Я просто пишу код. А когда мне надо комитить, мне не надо думать о коде, я делаю красивую историю. Разделение этих двух разных по своей природе задач просто спасение для меня.
Конечно есть именованные бранчи, но коммиты у ним не привязаны
Об этом и речь — глядя на коммит, нельзя сказать к какому бранчу он относится. Т. е. к какой фиче-фиксу как вариант. Сообщение слишком специфично и не содержит такой информации, так как фича одна, а коммитов много. Возможно из-за этого и приходится впоследствии шаманить с коммитами, чтобы по ним было понятно, к чему они собственно относятся.

При наличии настоящих именованных бранчей (как в DVCS на букву М) необходимости что-то делать с кучей мелких коммитов в одном бранче нет.
если мержить с использованием ключа --no-ff — поведение меркуриала и гита совпадает… зато гит умеет сам автокоммитить результаты автомерджей!)))
Редактирование комитов нужно прежде всего для того чтобы каждый changeset содержал набор красивых и завершенных изменений.
За счет этого можно делать комиты очень часто не парясь насчет каждого комита, а перед пушем изменений — все поправить.
Пример линии комитов:
1) Added extension point for new cool feature
2) Implemented new feature // Данный комит содержит баг
3) debug
4) typo
6) some other stuff
5) temp
7) fix for stupid bug

Если вы зальете такие комиты в любую системы контроля версий то ничего хорошего в итоге не будет
В гите вы имеете возможность поправить историю и привести ее к виду:

1) Added extension point for new cool feature
2) Implemented new feature // данный комит содержит код фичи без бага т.к. фикс был залит сверху

Таким образом можно просто удалить ненужные изменения (дебаги и посторонние изменения) и оставить только ценные
Другими словами пользователи GIT редактируют историю «потому что могут».

Касательно вашего примера — коммиты с такими сообщениями действительно держать в истории неприятно. Даже во временном бранче. Особенно если некоторые из них не компилируются или не проходят тесты или приводят к ошибке во время выполнения. Если разрабатывать и коммитить в таком стиле, то GIT с его вольностями по отношению к истории кажется более предпочтительным.
А как иначе — не коммитить «потому что некрасиво»? Нет уж, пусть лучше локально будет некрасиво, зато я в любой момент времени смогу проследить историю дебага без извращений.
Иначе — это когда историю редактировать нельзя. В этом случае имеет смысл руководствоваться как минимум двумя правилами для коммитов: писать содержательное сообщение, которое описывает ключевые моменты, и делать коммит законченным, чтобы приложение хотя бы компилировалось. Эти условия не мешают коммитить часто и при этом, глядя на их историю, не возникает желания ее переписать.
Условие «делать коммит законченным» мешает коммитить часто, и местами — мешает сильно. Бывают моменты, когда в коде натуральные КРОВЬ КИШКИ РАСЧЛЕНЕНКА на протяжении целого дня, а то и нескольких. Следуя вашим правилам — в таком случае разработчику остается уповать только на историю, которую хранит ИДЕ (если хранит).

Условие «писать содержательное сообщение» несколько противоречит правилу «коммитить часто» — ну в самом деле, что мне написать в коммит месседж, когда я переключаю контекст, а из текущих изменений — одна измененная константа, скажем?

Плюс к этому правило «коммитить часто» порождает множество коммитов на каждую мелкую фичу, которые сильно захламляют историю. Я это в полный рост наблюдаю сейчас, когда перетянул последний проект на гит с свна и в силу недостатка опыта у народа с гитом мы пока придерживаемся самых элементарных гайдлайнов.
Бывают моменты, когда в коде натуральные КРОВЬ КИШКИ РАСЧЛЕНЕНКА на протяжении целого дня, а то и нескольких.
Такое может быть, если не задумываться об атомарных изменениях. Да, для этого нужно изменить подход к программированию, разбивать на мелкие задачи и фокусироваться на них. Но это реально и лично мне это не мешает, а наоборот — помогает сосредоточиться. До этого у меня тоже часто были подобные ситуации — делаю какие-то мелкие изменения, они тянут за собой все больше и больше кода и не успеешь обернуться, как вся система сломана и непонятно, за что хвататься, чтобы привести все в порядок. Я стараюсь до такого не доводить, фокусируясь на законченных коммитах и небольших не калечащих изменениях.

что мне написать в коммит месседж, когда я переключаю контекст, а из текущих изменений — одна измененная константа, скажем?
Писать, что изменена такая-то константа. Можно еще добавить — зачем. Это лучше, чем temp, wasd, 111 и прочие.

Плюс к этому правило «коммитить часто» порождает множество коммитов на каждую мелкую фичу, которые сильно захламляют историю.
Вот. Подобное мышление — это уже следствие того, что редактирование истории становится частью рабочего процесса. Как пользователь меркуриала могу сказать, что у меня нет такого понятия, как захламление истории. Для ее организации я использую ветки и таги. Мне без разницы, сколько у меня коммитов в ветках, в большинстве случаем меня интересуют только последние.
У вас в команде принято ревьювить каждый патч или только конечный результат?
Сколько времени может пройти с момента первого показа фичи на ревью, до ее попадания в репозиторий?
Как часто вы смотрите в историю репозитория, чтобы понять зачем были сделаны те или иные изменения?
Кто у вас имеет право мержить фичи в репозиторий и если это делает сам разработчик, то ревьювит ли кто-то изменения которые были сделаны при мерже?
Ревью делается для конечного результата, а не для диффов в каждом коммите. Поэтому не важно, сколько там коммитов и сколько раз один кусок кода переписывался в них. Время на ревью в таком случае зависит от общего количества изменений и их сложности, а не от количества коммитов. В историю всего репозитория почти не смотрю. Смотрю на историю изменения отдельных модулей и файлов, если есть какие-то проблемы и нужно понять, почему они возникли.
>> Ревью делается для конечного результата, а не для диффов в каждом коммите.
Я описываю случай, когда этот метод не приемлем. Ревью одного логического изменения проще и как результат качественнее. Когда вы работаете над ядром операционной системы, хочется как можно меньше багов пропускать в основной репозиторий. При этом сложность кода достаточно высокая.
Согласен, что для мейнтейнеров ядра Linux сложившийся процесс является довольно удобным. Но не все пользователи GIT — разработчики ядра и далеко не всегда для манипуляций с комитами есть такая веская причина, а не простое желание сделать красиво. Если бы заголовок у поста был «Зачем разработчики ядра Linux редактируют свои коммиты», то возможно было бы меньше недопонимания с моей стороны и со стороны других отписавшихся здесь.
Да, согласен, надо было заголовок как-то конкретизировать.
Такое может быть, если не задумываться об атомарных изменениях. Да, для этого нужно изменить подход к программированию, разбивать на мелкие задачи и фокусироваться на них.


Это отличное правило в вакууме. В реальности бывают ситуации, когда рефакторинг на проекте в полмиллиона строк кода пару дней вообще не компилируется, а потом еще неделю тесты падают.

Писать, что изменена такая-то константа. Можно еще добавить — зачем.

Т.е. тратить на коммит месседж больше времени, чем на собственно изменения из этого коммита?

Вот. Подобное мышление — это уже следствие того, что редактирование истории становится частью рабочего процесса.

Это как раз наименее строгая из претензий. Большинство пользователей гита занимаются причесыванием истории на уровне веток, а не на уровне коммитов, и это вполне ОК. Просто в некоторых ситуациях затраты времени на причесывание коммитов экономят время на ревью и тогда они оправданы.

Лично я люблю гит именно за то, что он позволяет стрелять из сотен видов оружия и в тысячи разных точек на ноге.
Почему не возникает? Пишешь фичу, в процессе увидел баг в каком-то не относящемся к месту коде. Сделал stash изменений, пофиксил баг и закоммитил его, stash apply и продолжаешь. История в итоге такая:
[добавил фичу]->[фикс левого бага]->[добавил тесты к фиче]

Некрасиво. Отредактировал историю так, чтобы фикс фичи был отдельно, и сплющил фичу. В итоге история будет:
[фикс левого бага]->[добавил фичу + добавил тесты к фиче]

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

Каждый коммит имеет цепь предков, которая сходится к init-коммиту. В итоге образуется дерево коммитов, которым мы манипулируем. Имена бранчей (и не только) — это всего лишь указатели на листья этого дерева. Не даром все «master», «develop», «origin/master», «HEAD», «HEAD~2», «07abc92f» абсолютно взаимозаменяемы :)
Хорошо, но вы же понимаете что при разработке проекта средней сложности все выглядит гораздо проще?
Как правило, грамотная структура веток поволяет всем работать с одним центральным репозиторием.

А возможность редактировать коммит это возможность стрелять себе в ногу.
Когда ее нет, чувствуешь себя ущемленным, но после пары выстрелов это чувство проходит.
Есть разные подходы. В описанном здесь, главная проблема найти мейнтейнера. Если такой энтузиаст нашелся, то подход работает очень эффективно. Коммиты редактируют только разработчики в своих репозиториях. В центральном репозитории ничего не меняется, только добавляется. Мне этот подход нравится тем, что не приходится тратить лишних усилий во время ревью. Меньше трудозатрат, больше вероятность, что ваши патчи посмотрит кто-то еще.
Знакомо. Когда готовил патчи для ragel, тоже делал git reset и группировал изменения по коммитам, чтобы было удобно ревьюить.
Ну не знаю, reset — это уже крайняя мера. Резделять коммиты через add -i гораздо сложнее чем объединять через rebase -i. Так что лучше при девелопменте перед любой сменой контекста редактирования делать микрокоммит — потом можно будет выкинуть ненужные, а нужные — объединить.
Да, я немного слукавил. Конечно я стараюсь делать микрокомиты, конечно я стараюсь разбивать патчи сразу по идеям, но иногда все равно возникают коммиты, которые приходится сплитить.
На самом деле, проблема просто в том, что мейнтейнер смотрит, какие изменения патч вносит в _мастер_.
А надо просто посмотреть, какие изменения произошли _в ветке разработчика_. Crucible, к примеру, это позволяет делать безо всяких проблем.

Если внесённые изменения всех устраивают, то ветка разработчика мержится в мастер — с отсмотром merge-commit-а при необходимости.
В ядре есть люди, кто посылает pull request-ы, но разбивают свои изменения таким же образом. Потому что ревьювить много разных изменений одним куском, очень трудно. А когда они правильно оформлены, разбиты по идеям, отсортированы, то смотреть на них приятнее, понимать быстрее, проверять проще.
А что мешало их с самого начала правильно оформлять?
У меня не получается, у моих коллег тоже. А вы программируете? Если да то на чем и в каких проектах?
Программирую. На JavaScript. В очень больших.
Если бы на вопрос в заголовке темы был дан ответ «потому что не могут правильно коммитить с первого раза», то ок, у меня бы не было претензий.
А в посте рассказывается про проблемы организации приёма патчей, которые, якобы, принуждают делать rebase. А вот это как раз следствие неправильной организации ревью, и лечить её ребэйзами, на мой взгляд, принципиально неправильно.
rebase переносит МОИ изменения поверх ИХ изменений. Соответсвенно ревьювить то, что недавно прошло ребейз гораздо удобнее, чем просто набор чьих-то изменений. Особенно удобно, когда вы занимаетесь ревью постоянно. Каждый коммит для вас выглядит продолжением предыдущего. И это, намой взгляд, принципиально правильно.
Попробуйте правильно вмержить ветку, которая опирается на коммит недельной давности. Если Вы работаете с большими проектами, то Вы несомненно получите много радости от повторного исправления багов, повторного тестирования фич и много чего еще повторного.
> rebase переносит МОИ изменения поверх ИХ изменений.

Да, тем самым нарушая хронологическую последовательность коммитов и производя странные технические коммиты. Это и есть причина, по которой мы не используем ребэйзы.

> Соответсвенно ревьювить то, что недавно прошло ребейз гораздо удобнее, чем просто набор чьих-то изменений.

Зачем ревьювить какие-то чужие изменения?
Ещё раз: это проблема _правильной организации ревью_.
Грубо говоря: функциональные коммиты в нормальном воркфлоу перемешаны с техническими мерж-коммитами, но используемый инструмент ревью не умеет отличать одно от другого.

Неправильно решение: переписать историю коммитов с помощью ребэйза
Правильное решение: использовать нормальный инструмент ревью, который скроет мерж-коммиты.

> Попробуйте правильно вмержить ветку, которая опирается на коммит недельной давности.

Пробовал много раз. Гит прекрасно это делает.

> Если Вы работаете с большими проектами, то Вы несомненно получите много радости от повторного исправления багов, повторного тестирования фич и много чего еще повторного.

Вы что-то делаете неправильно.
Ребейз нужен вам, держвать патчи которые не прошли ревью поверх всех изменений, который уже попали в основной репозиторий. В основном репозитории никаких ребейзов, никто не делает. Мне кажется я начинаю понимать, о чем вы пытаетесь сказать. Не забудте, что у нас еще есть условие, что в основной репозиторий коммитят только избранные и у них нет времени мержить каждое изменение в отдельности.
Конечно, не делает. Это что-то меняет?

> Не забудте, что у нас еще есть условие, что в основной репозиторий коммитят только избранные и у них нет времени мержить каждое изменение в отдельности.

И не надо. Надо мержить ветки. Ветка прошла ревью — смержить её в мастер.
Да нету ветки, есть патчи. И никто не будет разгребать 10 ваших временных патчей и вникать что там.
1. Патчи должны быть подготовлены и разбиты по условно фичам
2. Комиты должны быть более целостные, что бы легко можно было откатить их в случае чего, а не выискивая ваши 10 патчей для одной фичи.
> Да нету ветки, есть патчи.

Ну так в том и проблема. Ветки — это ключевая фишка гита, без них он просто svn с редактированием истории.

> 2. Комиты должны быть более целостные, что бы легко можно было откатить их в случае чего, а не выискивая ваши 10 патчей для одной фичи.

Мерж ветки одним коммитом и происходит, его и откатывать, если что.
Простите, вы с ветками работали вообще?
У себя на проекте так и делали.
Кстати, есть на хабре статься «Удачная модель ветвления для Git».
Там именно так и предлагается — на фичу создавать ветку, а затем ее мержить.

Я, признаться, не работал в крупных командах. Поэтому никакого ревью коммитов в таком виде, чтобы пришлось формировать и отсылать патчи, я не проходил.

Но работал в одном месте с svn — там мы делали ответвления для задач, в эти ветки все коммитили (старались коммитить логически законченными и обязательно собирающимися кусками), а затем последним коммитом делали мерж из мастера в ветку задачи.
Затем тим лид переключался на эту ветку, убеждался что все работает как надо, смотрел диффы начального и конечного состояния ветки, делал ревью кода, если что — давал ЦУ по исправлениям.
В конце концов в мастер брэнч мержилось последнее состояние ветки задачи и проходило это гладко т.к. из мастера последние изменения там уже были и конфликты были решены.
Недостатки:
— мержить сложнее — ты работаешь над одной задачей, а мержишь все, что накопилось в мастере, и если есть конфликты — не всегда понятно как сохранить и те, и те изменения. Приходилось конечно же общаться с другими разработчиками и тратить на это время.
— много работы тим лиду по любой даже мелкой задаче.
Достоинства:
— очень чистая история.
— всегда понятно где искать изменения фичи.
— код всегда собирается.
— нет конфликтов при мержах в мастер.

С Git'ом уже на другом предприятии работали сначала банально в мастере, затем я стал создавать ветки для задач, а затем сделали отдельные ветки мастер, релиз и еще именные ветки, в которых работали + держали последние изменения мастера. Когда фича готова и влита в ветку разработчика, ее можно мержить в мастер (но только по желанию тим лида). Именные ветки были нужны т.к. работали мы сразу на трех платформах и нужно было тестировать свой код + допиливать по необходимости. Юзать для этого общие ветки — зло.
Недостатки:
— много веток. Если долго работал в своей — мастер уже может не смержиться. Натыкался на это. Гит просто ругался что все слишком сложно. Мерж прерывался на середине и все тут. Проще было тогда удалить свою ветку и создать ее заново с последнего коммита в мастере.
— плохо следили за содержанием коммитов. Я еще старался, а вот некоторые ребята такие фишки валили, что хотелось выпрыгнуть в окно. Вроде стартанул ветку, из нее выросла еще одна, в нее замержился мастер, все свелось к 1й ветке и т.п… Но при этом половина коммитов не собарается вообще.

Достоинства:
— для кроссплатформ программирования удобно что есть своя ветка, в которой ты работаешь.
— мержи ветками — удобно т.к. если комиты идут правильно — всегда можно откатиться на коммит раньше мержа новой фичи или фикса и сразу все понятно.

Как-то недостатков больше :)

Короче, в целом согласен с вами. Ветки удобнее, чем ребэйзы и пересылки патчей.
Может быть это последствия привычки хранить версии кода в мелких архивах? :)
Мерж прерывался на середине и все тут.

Вообще-то он ждал пока вы разрешите конфликт, чтобы продолжить мерж
Да, мейнтейнер хочет получать патчи относительно свежего хеда, чтоб мержить ничего в большинстве случаев не надо было, т е вы перед посылкой должны сделать rebase в своей ветке.
И зачем он этого хочет? Чем ему патч милее мержа?
Потому что «ни у кого» нету доступа к репозиторию на запись. Таким образом нету возможности мержить ветки.
В смысле? У мейнтейнера есть. Какая ему разница, патч накатить или смержить ветку?
Ну он так и делает, накатывает ваши патчи в отдельную ветку и мержит.
Разница только в том, что накатить кучу непонятных комитов или же пару комитов разбитых по фичам или еще чему либо. В кратце, если у нас есть два комита фича1 и фича2, и он их принял и смержил, и потом оказалось что фича2 все таки с багом, проще откатить один комит фича2, чем выискивать ваши 10комитов и их по одному откатывать.
При мерже создаётся технический коммит, его и откатывать. В чем проблема?

Фактически, ваше утверждение можно переформулировать так: в гите с ветками всё плохо, не делайте мерж, потом задолбаетесь откатывать. Это, разумеется, не так.
Такая, что при ребейзе коммитером конфликты мержит коммитер, а при мерже мейнтейнером конфликты придется мержить мейнтейнеру, а это явно не его забота. Ему нужно проревьювить ченжи не в вакууме, а относительно актуального хеда.
А если коммитер сделал git pull origin/master и разрешил конфликты сам?
Если branch.autosetuprebase не стоит (или pull делается без ключика --rebase), то мы получим наихудший вариант из возможных (с которым я долго и не всегда успешно борюсь в своих проектах) — origin/master будет смержен в локальный master, а значит бранч тэг перескочит на соседнюю ветку и история станет менее читабельной (мастер больше не будет идти ровно).

Если пулл сделан с ребейзом — по факту ничем не отличается от приведенного в топике кейса.
Произошла путаница. Разницы между патчем и веткой нет никакой. В ветки патчи должны быть относительно свежего хеда, чтоб не пришлось разруливать конфликты.
Патч ему милее потому что он его просматривает в своем любимом почтовике и если он ему нравится то сразу его заносит в git просто хоткем, а если нет то сразу отписывает свои замечания предложения. А с веткой ему что делать? Просматривать изменения в браузере или себе клонировать? У него сотни, а у некоторых тысячи разработчиков и у каждого свои репозитории, а в них десятки, сотни веток. Мрак…
Если патчи таковы, что их можно отсмотреть в почтовике, то нет никакой нужды в ревью, ветках, ребэйзах и прочей фигне. Можно прям в мастер и коммитить.

Мы вроде про серьёзные проекты говорим.

И да, мне кажется, вы сейчас написали «ветки зло, не пользуйтесь ими».
> Если патчи таковы, что их можно отсмотреть в почтовике

Так и запишем: Linux Kernel, GCC, Clang, LLVM — не серьёзные проекты.
> Да, тем самым нарушая хронологическую последовательность коммитов и производя странные технические коммиты. Это и есть причина, по которой мы не используем ребэйзы.

зачем вообще кому то знать что свой локальный комит я создал неделю назад, а не сегодня? Все равно комит будет в истории только тогда, когда ревьювер его одобрит и смержит в главную ветку.

> Зачем ревьювить какие-то чужие изменения?
эм, у вас точно большой проект? :)
> зачем вообще кому то знать что свой локальный комит я создал неделю назад, а не сегодня?

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

>> Зачем ревьювить какие-то чужие изменения?
>эм, у вас точно большой проект? :)

Перечитайте ветку комментариев внимательно. Моё замечание относилось к реплике «rebase переносит МОИ изменения поверх ИХ изменений. Соответсвенно ревьювить то, что недавно прошло ребейз гораздо удобнее, чем просто набор чьих-то изменений.»
> Затем, что неделю назад состояние мастера было другим. После ребэйза уже нет возможности установить, что в тот момент представлял из себя мастер и зачем разработчик делал вот так.

Для этого и делается rebase, что бы патч был «свежий» и не важно что было неделю назад.
Если прошло прям так много времени или сильно много изменилось, и ревьювер тупил месяц, значит патч отправляется обратно, на обновление. Так как он может уже быть просто не актуальным.
Дело не в неактуальности. А в том, что за это время могло что-то поменяться — функционально или концептуально.

По нормальной истории коммитов это будет видно. Ага, здесь мы внедрили вот такую штуку, поэтому вот этот код отрефакторен и переписан.

А с неработающей историей будут оставаться артефакты из серии «а зачем Вася так странно написал».
Вы что то, или путаете, или не понимаете.
Какое еще время, если патч свежий, поверх всего дерева?
Почему история вдруг стала не работающей?
Потому что rebase перенес все мои коммиты поверх последнего коммита в мастере — так, как будто я их делал после него. А я их делал до.
Это не упоминая уже о том, что предлагается вообще через --interactive вообще историю похерить и сделать один/несколько больших коммитов — которые, быть может, выглядят красивее, но совершенно скрывают логику и порядок разработки.
Ну и что что вы делали их ДО. Суть вашего комита это новая фича, которая должна работать на новом дереве. Вы должны сделать ребэйз и удостоверится что ваш код все еще работает. Зачем заливать/отправлять не рабочий код?

> но совершенно скрывают логику и порядок разработки
Вы пишите одно потом другое.

Сейчас вы пишите, что хотите видеть как человек писал код (временные комиты), так сказать что бы понять его логику.

А чуть раньше вы писали, зачем нужны микрокомиты, если можно все сразу написать и скопом залить:
> А зачем коммит-то каждый раз? Боитесь винчестер сдохнет?
> Написал код — проверил — отладил.
> Далее через git add (который, между прочим, умеет добавлять отдельные строки) формируются коммиты.

Неувязочка…
> Ну и что что вы делали их ДО. Суть вашего комита это новая фича, которая должна работать на новом дереве. Вы должны сделать ребэйз и удостоверится что ваш код все еще работает. Зачем заливать/отправлять не рабочий код?

Зачем делать rebase, если можно делать merge? merge master от rebase master отличается только тем, что сохранит правильный хронологический порядок коммитов.

> Сейчас вы пишите, что хотите видеть как человек писал код (временные комиты), так сказать что бы понять его логику.

Я в своём проекте — хочу. Если кто-то не хочет, его право, можно сливать коммиты.
Но зачем делать rebase master — я так и не вижу ответа.

> Неувязочка…

Никакой неувязочки. История в гите мне нужна, чтобы видеть и понимать, когда и зачем происходили какие-то изменения. Чтобы, вернувшись через год к какому-то изменению, можно было понять, почему было сделано именно так.

Коммитить грязный, неработающий код чтобы зафиксировать какое-то промеждуточное состояние не надо. Но история коммитов должна отражать логику и порядок разработки, а не заменять собой objectives в ревью.
В случае ветки коммиты сгруппированы и при беглом просмотре видно, что с целевым коммитом связаны 3 до и 2 после. В случае постоянных ребейзов группировка коммитов по темам теряется, разве что номер таска в начало комментария добавлять.

Мне, честно говоря, не понятно чем ребейз лучше мержа мастера в ветку — с точки зрения интегратора это все тот же фаст-форвард.
Rebase, в сабже, в первую очередь нужен что бы удостоверится что ваш код/патч свежий, и работоспособен и патч зальется без проблем.
git merge master сделает абсолютно то же самое.
Хорошо, вы сделали мерж, патч не приняли, что дальше?
Исправить, еще раз смержить с мастером, послать на ревью.
Да блин, git rebase master от git merge master отличается только тем, что не искажает историю коммитов.
1. история локальных комитов никому не нужна, в ней нет ничего интересного, важен только результирующий, корректный патч.
2. вы смержили в мастер, потом что будете делать реверт или ресет мастера, возвращаться обратно в свою ветку, делать изменения, мержить обратно в мастер, тестировать еще раз теперь уже мастере и снова отправлять? При этом дублируя кучу работы каждый раз?
1. Ок, слепите коммиты в один. Зачем rebase master-то делать?
2. А с патчем-то чем проще?
Вы опять рассказываете, что ветки в гите зло и работать с ними невозможно. Это не так.
Кроме вас никто не говорит что ветки зло, через ветки собственно все и делается, только разница в том как это делается.

Вы упорно игнорируете все аргументы, как других собеседников так и собственно самой статьи.

В очередной раз, с ребэйзом:
1. мы проверяем что патч работает нормально на свежем дереве.
2. наши комиты всегда наверху, нам не нужно их искать среди мастера.
3. нам не надо мержить в мастер еще не одобренный патч и лепить затем код поверх него. Если ваш патч не примут, а вы сделаете новый основанный на старом, у вас уже 20 комитов, которые «застряли».
4. в случае возврата патча, скажем он пролежал месяц, и стал не актуальным, мы снова сделаем ребэйз и без проблем протестируем его на свежем дереве и отправим снова.
Замените в своём алгоритме rebase на merge. Ничего не изменится, кроме того, что порядок коммитов сохранится.

Зачем rebase?
Очень странно что вы не видите разницы. Написано уже достаточно много, нужно лишь внимательно прочитать. А самое лучшее один раз попробовать и понять разницу.
Пока кажется, что это вы не понимаете что вам пишут. Судя по вашему
нам не надо мержить в мастер еще не одобренный патч

Вы считаете, что git merge master изменяет мастер.
Нет конечно, а вот это было похоже что да:
> Исправить, еще раз смержить с мастером, послать на ревью.
хотя возможно я не понял что имелось ввиду. Ну да ладно, суть проблемы не сильно изменилась, чуть стало проще конечно.
Вероятно, я не вижу разницы, потому что её нет. Накатывание, откатывание и синхронизация коммитов в гит выполняются одинаково, независимо от того, с фаст-форвардом или без.
Ни кто не предлагает мержить в мастер ветку! Предлагается мержить в ветку мастер.
Все 4 пункта сохраняются, но добавляется еще 1: не потеряна группировка коммитов.
Получим большой бардак и страшную ветвистую историю еще даже не отправив патчи. А так же не возможность что либо поменять в будущем, если патч не приняли.
Откуда невозможность помненять? Кто мешает повторно выполнить мерж мастера в ветку?
Вы считаете, что длинная svn-style история без группировок лучше истории с ветками? Чем же?
Нету возможности поменять старые комиты для вашей фичи, если патч не приняли. А мержить в свою ветку мастер можно сколько угодно. Бардак будет только нарастать.
Лично я бы откатил ветку на коммит назад (до мержа с матером), исправил и смержил с новым мастером.
Вы можете сказать, что тогда труд по первому мержу теряется. Да, это так, но ради вменяемой истории я на такое готов пойти.

А вообще проблема в безвозвратном удалении веток. Сохранялась бы информация о ветках ребейз бы не превращал историю в svn-style.
О какой истории вы говорите и почему она так важна?
К тому же merge в мастер не создаст merge комит, если никто еще ничего туда не комитил. Что вы делаете в таких случаях?
И что вы делаете если, мержить мастер в ветку надо часто?
Что я хотел бы получить я описывал.
К сожалению в git это в общем случае не возможно из-за «гениальной» идеи сделать ветки настолько легковесными (читай терять информацию о контексте коммита).

Я в таких случаях ни чего не делаю — (svn, tfs. git только на гитхабе и дома). Если бы не гитхаб — осваивал бы hg.

Если мержить надо часто, то такая попытка исправить «гениальность» веток работать не будет. Но ребейз как панацея (даже при сравнительно не больших — в пару десятков разработчиков — коммандах) кажется несколько не правильным.
Не в мастер же! Мастер остался где был. В ветку добавился 1 коммит.
1. История атомарных изменений (не всего бардака, а именно отдельных идеологически самостоятельных изменений) очень помогает. Не приходится разбираться одновременно с несколькими не связанными изменениями.
При мерже мастера в ветку интергатору надо выполнить все тот же фаст-форвард, при этом структура коммитов не убивается.
> Перечитайте ветку комментариев внимательно. Моё замечание относилось к реплике «rebase переносит МОИ изменения поверх ИХ изменений. Соответсвенно ревьювить то, что недавно прошло ребейз гораздо удобнее, чем просто набор чьих-то изменений.»

Все правильно написано. Делаем rebase что бы:
1. проверить что наш патч нормально накатится на мастер
2. у нас нет конфликтов с новым кодом
(даже если мы будем делать мерж, а не патч, у нас будет понятная, линейная история)

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

Далее по изменениям в этой ветке проводится ревью, если оно проходит — ветка мержится в мастер.
Ну, оно так и происходит. Именно это и описано в статье. Только ревью проходят изменения в виде патчей, но это сути не меняет. А вот если во время ревью находят баг, то любому придется делать ребейз и править патч.
Нет, не так. Моя схема сохраняет логику коммитов во времени, а описанная в статье — нет.
Проблема в управлении сложностью. Никому не интересно копаться в этой логике с экспериментами и их откатами. После реализации фичи ветка с фичей копируется, плющится и отправляется реквест. В репе разработчика остается подробная история с мержами, а в мастере — пара аккуратных коммитов.
Ок, допустим. Разработчик делает из своих 10 коммитов один.
rebase поверх мастера зачем?

Логику «редактируйте свои коммиты, чтобы получалась аккуратная история» я ещё понимаю.
Но зачем их при этом rebase-ить — не понимаю.
В статье где-то написано про rebase поверх мастера? git rebase -i — основное средство редактировать историю туда-сюда для того, чтобы другим участникам было понятнее, что ты делаешь.

Непосредственно git rebase master я делаю только когда нет конфликтов, для утрамелких патчей, иначе уже svn-like мерж получается, насколько я понимаю, то есть шаг назад по сравнению с git.
Нашел где это написано. После сплющивания патча rebase относительно мастера сделать легко и приятно, и это делает историю чище. При этом сами изменения все равно будут вмерживаться мейнтейнером (пока пулл-реквест провисит в очереди все равно мастер уйдет вперед), но в целом это будет слегка чище и уменьшает вероятность конфликтов.
Какая разница, сплющивать историю до rebase master или после? С чего она стала чище?
Как я уже выше писал, оформление коммитов и кодинг — разные по своей природе задачи. Вот у меня есть код, я его жёстко рефакторю — что-то заработало комичу. Потом всё заработало — время обновить юнит тест. После каждого пофиксенного юнит теста — коммит. Потом ещё функциональные тесты. Иногда ещё и sanity тест обновить надо. И от этого меня могут оторвать в любой момент, когда мне надо будет переключиться на другую задачу. Потому и коммичу так часто. Но в итоге всё сводится к нескольким (не обязательно одному) красивым коммитам. Например:
* user attributes caching
* unit tests for user attributes caching
* functional tests for user attributes caching

Отдельные комиты сами рассказывают историю. У нас несколько ревьюверов, технический директор посмотрит на список коммитов «ага, фунциональные тесты не забыл» и всё, не будет тратить больше своё время. Архитектор посмотрит только на первый коммит, он своё время на ревью всяких тестов не тратит. А коллега тест-инженер наоборот больше внимания уделит тестам — ему их поддерживать потом.

Причём если я какой баг в коде нашёл в процессе написания функциональных тестов и исправил, то фикс всё равно будет где-то внутри первого коммита. Удобно же! Экономит время других программистов. И моё, когда я сам — ревьювер.

На самом деле всё не так сложно как кажется. Я просто постоянно использую git commit --amend, так что редактировать историю через git rebase --interactive приходится только в сложных случаях. Зато когда приходится, как я счастлив что эта фича есть!
Так при чем здесь тогда история про сложности приёма патчей, описанная в посте?

> Вот у меня есть код, я его жёстко рефакторю — что-то заработало комичу. Потом всё заработало — время обновить юнит тест. После каждого пофиксенного юнит теста — коммит. Потом ещё функциональные тесты. Иногда ещё и sanity тест обновить надо.

А зачем коммит-то каждый раз? Боитесь винчестер сдохнет?
Написал код — проверил — отладил.
Далее через git add (который, между прочим, умеет добавлять отдельные строки) формируются коммиты.
Комитить каждый раз — как минимум удобнее работать:
1. легче потом разобрать
2. фиксируются какие то короткие шаги
3. легче делать новую фичу, что бы прямо сейчас не тратить на причесывание не нужных комитов и не создавать еще одну ветку для одной и тоже фичи.
4. в случае чего очень легко откатывать мелкие изменения, чем копаться в простыне, боясь поломать уже «рабочее».

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

Грамотная система ревью (ещё раз прорекламирую крусибле) избавляет от этой необходимости. В ревью выделяются нужные файлы и нужная пачка коммитов. Отсматривать при этом каждый атомарный коммит нет никакой необходимости.

Таким образом, и отслеживание изменений получается более удобным для всех сторон, и коммиты отражают логику разработки, а не логику подготовки ревью.
Что именно происходит в вашем случае при положительном ревью? Происходит мерж 10 патчей для одной фичи в основное дерево или они все склеиваются и лепится один комит?
Происходит мерж ветки в мастер.
У меня для вас есть совет. Попробуйте отдохнуть чуть-чуть и перечитать этот пост и комментарии к нему. Мне кажется Вы — умный человек, который сейчас в горячке спора не может услышать оппонента. Это случается со всеми, ничего зазорного в этом нет. Но на все ваши вопросы уже есть ответы здесь. Вы просто их не видите.

Вот например Вы спрашиваете (довольно агрессивно):
А зачем коммит-то каждый раз? Боитесь винчестер сдохнет?

А ведь непосредственно в моём комменте написано:
И от этого меня могут оторвать в любой момент, когда мне надо будет переключиться на другую задачу.

Это и есть ответ. Но Вы его не заметили, как и не заметили более сложных ответов, которые были здесь озвучены.
> Попробуйте отдохнуть чуть-чуть и перечитать этот пост и комментарии к нему.

Вот, перечитываю с утра. Прихожу к выводу, что хабр уже не торт.
Раньше было как-то лучше.

> И от этого меня могут оторвать в любой момент, когда мне надо будет переключиться на другую задачу.

Вообще говоря, это говорит о неверной организации процессов. Переключение контекстов очень дорогая операция, и начинать одну задачу, не закончив другую — это значит потратить на обе задачи вдвое больше времени.

Но, если уж так сильно хочется, то именно для таких ситуаций есть git stash.
Пока ещё торт — дискуссия на личности не перешла, спорящие способны слышать друг друга. И плюсы друг другу ставить.
То же, что мешает сразу софт без багов писать.
Плохая аналогия — она как лимон в лифте.
Git — целая наука. Не достаточно просто знать как послать изменения и принять их. Сколько живу — столько учусь! Спасибо за пост! А так вообще я на меркуриале сижу и в нем тоже есть возможности сделать partial commit, правда, пока не пользовался, все как-то и так гладко вроде выходит. Пока…
В git всё так же просто, как и в mercurial (работал с обеими системами). Техника с комбинированием истории позволяет коммитить на каждый чих, и немного причёсывать перед публикацией. На практике это очень удобно.

[offtop]
регулярные перепалки git vs. mercurial мне напоминают следующий анекдот:
Как-то теплым, летним утром шел мужик (1) по мосту и увидел другого мужика (2), стоящего на перилах и явно намеревающегося прыгнуть вниз. 1 — Остановись! Не делай этого! 2 — Почему? 1 — В жизни столько прекрасного ради чего стоит жить. 2 — Например? 1(задумавшись) — Ты вот верующий или атеист? 2 — Верующий. 1 — Я тоже. А ты христианин или еврей? 2 — Христианин 1 — Я тоже. А ты католик или протестант? 2 — Протестант. 1 — Я тоже. А ты приверженец епископальной церкви или баптист? 2 — Баптист. 1 — Ну и ну!!! Я тоже. А ты баптист церкви Бога нашего или баптист церкви Христовой? 2 — Баптист церкви Бога нашего. 1 — Я тоже. А ты баптист ортодоксальной церкви Бога нашего или реформированной? 2 — Реформированной. 1 — Ну просто невероятно! Я тоже. А ты баптист реформированной церкви Бога нашего 1879 года или баптист реформированной церкви Бога нашего 1915 года? 2 — 1915 года. 1 — Ух мерзкий еретик!!! — сказал первый мужик и столкнул второго вниз.

Зачем-то находится человек, которому очень хочется всказаться, что git (или mercurial) плохая система, и нужно пользоваться mercurial (или git).
[/offtop]
UFO just landed and posted this here
Интересно, как долго вы пользовались обеими системами?
UFO just landed and posted this here
Я работаю над фиксом. Делаю ветку, в нее коммичу по ходу разработки, каждые небольшое изменение. Когда фичу закончил, сливаю ветку в главное дерево. Этот мердж может просмотреть каждый, как один коммит, и если хочется, сделать из него патч (о, Боже, в 21м то веке).
При этом в истории есть ход моих мыслей, ошибки, и исправления. Если кто захочет, сможет посмотреть что было не так и не делать таких же ошибок.
История для истории, а не для того что бы дрочить и править ее что бы все красивенько было.
Никому не интересны атомарные коммиты типа: Fiexd dmub topy или изменение имени переменной $a -> $b. Для этого и надо переписывать историю, чтобы коммит был не глуп, а нес в себе изменение какой-то части функциональности. А полет мыслей в коммитах не интересен и запутывает, важен конечный, рабочий результат.
комит неикогда не глупее автора.
Если важен только результат, то можно дифами все делать, и вообще, зачем история?
Спасибо за статью.
Открыл для себя такую вещь как squash (сплющивание нескольких коммитов или целых ветов в один коммит)
Но сразу же возник большой вопрос, на который не смог найти в интернетах вразумительного ответа.
Ткните ссылкой, если уже где-то был ответ, но пока мне интересен ответ на следующий кейс:
Текущий workflow команды разрабов выглядит так:

git pull
git commit -am "фича1"
git commit -am "баг в фиче1"
git pull
git push
git commit -am "фича2"
git commit -am "новый баг в фиче1"
git commit -am "Всё, вот теперь точно финал"
git pull
git push


Внимание вопрос. Возможно ли сплющить эти 4 коммита к фиче1 в один коммит уже после того, как они были запушены в общую ветку и ничего не сломать при этом или уже поздно метаться и надо было раньше думать? :-)

К сожалению, раньше надо было думать :) Все что уже отправлено править «нельзя».
В добавлению к ответу vovkab:
Можно, но сломает работу всех людей, завязавшихся на эти коммиты.

Я подобное делал на своем проекте, когда был уверен, что ни кто больше с ним не работает.
Поясню чуть подробнее, git такое делать позволяет. Только надо будет делать git push -f, чтобы форсировать переписывание истории в апстрим. Но так делать не стоит, потому что все, кто сделал git pull между git push и git push -f, окажутся в «мертвом» ответвлении. И их изменения будет сложнее вернуть обратно в мастер. Опять же не невозможно, но вот не стоит красота коммитов тех сложностей.
Я предпочитаю делать

git fetch
git reset --hard origin/master

вместо

git pull

Поскольку я локальных изменений в мастере не держу, это гарантирует, что я получу master, идентичный удаленному, даже если его перезаписывали.
Я лучше буду отрывать руки тем, кто будет перезаписывать удалённые ветки :-)
Во-первых ветка origin/master — локальная, всего лишь копия серверной, ее легко вернуть на место используя git fetch. Во-вторых она не изменяется. Команда означает «установить HEAD в состояние origin/master», origin/master остается при своих.
Я тоже локальных изменений в локальном мастере не держу, но делаю так:
git fetch origin
git rebase origin/master

Результат тот же, но если вдруг по какому-то недосмотру у меня что-то оказалось в локальном мастере, git мне об этом скажет.

А ещё у меня есть форк каждого активного проекта. Так что даже git push не меняет настоящего мастера. Лишний шаг для большинства проектов, но мне удобно: ментальная закладка «просмотри изменения ещё раз».
Если очень надо, то можно. Мы переписывали историю, когда в репозиторий попали ненужные бинарники. Однако для некритичной ситуации это лучше не делать: доступ к этой функциональности есть только у админов.
Sign up to leave a comment.

Articles