Pull to refresh

Comments 44

Нужно только добавить замечание, что все это годится только, если история локальная или в приватном репо. То, что мы опубликовали наружу — все, мы не имеем права изменять (потому что кто-то на том же гитхаб, например, мог сделать форк и при переписывании истории все ломается).

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

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

В дополнение статье:

Просто так залить свою исправленную историю коммитов не получится. Сервер не примет. Решается использованием git push -f (force push). Тогда часть истории на сервере будет переписана. Но очень сильно и неприятно удивятся прочие разработчики при попытке сделать git pull, т.к. их локальная история очень сильно расходится с серверной. Тоже решаемо локально, но это потеря времени.

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

Про "сделать очень быстро, пока другие не заметили" — надо понимать, что этими самыми "другими" может быть, например, хук-скрипт, который зеркалит ветку репозитория или его весь целиком на другой ресурс. Например, команда живёт на гитлабе, а зеркало — на гитхабе, куда все коммиты из мастера зеркалит CI-скрипт. В этом случае "пока другие не заметили" уже не прокатит. Мы однажды с таким столкнулись и обнаружили, что на гитхабе уже не клоны коммитов, а мерж-коммиты (история разделилась, FF-слияние перестало работать)

Я бы сказал, что это просто недоработка зеркалирующего скрипта. Чтобы учесть возможность изменения истории, нужно пушить как-то так:
git push -f mirror origin/branchname:branchname

ну, да, наверное, но при этом мы еще мину замедленного действия под себя закладываем.

Если случайно ветки разъедутся и некто перепишет локальную историю — она без вопросов уедет на Удаленный сервер. Что в случае публичного репо ломает всю модель работы любых коллабораторов

Ломает или нет — зависит, очевидно, от модели работы. Коллабораторы вполне могут работать и с переписыванием истории, если они к ней готовы.

Как правило — это не так. По крайней мере для публичных или полупубличных проектов.

Не-не, тогда результат push -f окажется в паблике. Так в паблике оказалась лишь небольшая цепочка мерж-коммитов, это не то, чтобы хорошо, но гораздо лучше переписывания истории (предположим, у кого-то тоже скрипт, который сделит за пабликом, пуллит оттуда коммиты себе в локальную копию и собирает/релизит. А тут вдруг мы сделали push -f… ) Мы просто сделали у себя обратный мерж — и всё починилось.

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

Ну вот в данном конкретном случае это не так. Пользователей — неопределённый "широкий круг", которым незачем эти странные "осведомления". А "определённая ветка" — это, внезапно, мастер.


Чуть детальнее — проект на гитлабе, где мы вольны делать с ветками что угодно (и push -f вполне нормальный рабочий инструмент; вся ветка живёт обычно ради автоматизированного тестирования). Это внутренняя кухня, которая никого снаружи не касается. А вот ветка master — публичная. Из закрытого проекта на гитлабе она зеркалится в открытый проект на гитхабе, где её все видят как одну из немногих веток проекта. Поэтому на мастер-ветку у нас действуют особые правила — там никогда ничего не переписываем, никаких push -f, никаких commit --amend. Синхронизация делается скриптом CI который как раз делает git pull/git push, и ещё дополнительно проверяет ветку по имени, чтобы не опубликовать случайно какую-то другую. Придумывать что-то сложнее надобности нет, покуда за несколько лет работы был лишь единственный инцидент с джуном, который сделал --amend.


И да, есть отдельная процедура для зеркалирования "в обратную сторону" — на случай приёма pr с гитхаба. Но там проще, покуда целевым репозиторием гитлаба пользуются только "осведомлённые пользователи".

Скорее все что попало в master изменять не следует. Фиче ветки вполне можно. Хранить ветку исключительно на локальный машине чревато потерей всей работы при сбое накопителя. Лично терял код, над которым работал 2 недели. Больше не хочу.


Но даже частичный ребейз мастера можно разрулить без слёз с использованием fork-point.

fork-point? не слышал ) Но это и неудивительно — git слишком мощный инструмент


Хранить ветку исключительно на локальный машине чревато потерей всей работы при сбое накопителя

несомненно. Но никто не мешает отгружать код в свой личный форк. А потом из него уже лить в основной репозиторий. Это на самом деле решает очень много проблем. Если подытожить есть принципиально несколько моделей разработки:


  1. вся разработка в одном репе. Есть выделенные ветки вроде master/dev, куда льются изменения и которые общие. Остальные ветки — фиче-ветки, над которыми работают обычно индивидуальные разрабы. Касательно того как запретить создавать из фиче-ветки новую фиче-ветку — вообще без идей.
  2. есть основная репа с каким-то набором долгоживущих веток (master/dev, может по версиям ПО). При необходимости доработок — разработчик форкает репо и разрабатывает в нем. Как только работа завершена — вливает все изменения в исходный репо через MR/PR. Заодно такой подход избавляет от ада с CI/CD — у каждого он получается свой ) и сломать его нереально физически. Ну, и посмотреть "секретные" переменные не получится разрабу в основном репо. На самом деле гитлаб очень подталкивает к такой модели, потому что в нем только репозитории являются 1st class сущностями… На каждый чих — по новому репо.
Касательно того как запретить создавать из фиче-ветки новую фиче-ветку — вообще без идей.

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

Лично терял код, над которым работал 2 недели.

Стоит добавить, что покуда не сработал git gc, потерянные коммиты можно найти в git reflog

reflog не спасет от ssd trim к сожалению

Эм, ну, я даже не знаю, что сказать. Это же gc совершенно разного уровня. И трим не ломает же поведение рефлога…

Это я про свою ситуацию. trim swap файла на расположенного на f2fs порушил всю файловую систему.

А есть вариант в процессе rebase разделить один commit на несколько? Частая задача, перед merge в мастер — прибираюсь в свой ветке и вижу слишком большой commit, который можно было бы разделить на несколько, но в момент когда он создавался я почему-то это не сделал.
А есть вариант в процессе rebase разделить один commit на несколько?

в целом — да.

Можно.
1. Всё тот же git rebase -i
2. В редакторе у нужного коммита ставите e (edit)
3. Ребейз остановится на этом коммите.
4. Делаете ваши чёрные дела и продолжаете rebase (git rebase --continue).
Делаете ваши чёрные дела

А именно: делаете reset, создаете из получившихся изменений сколь угодно много коммитов и продолжаете ребейз.

более того, после reset можно выполнить git add --patch и построчно выбирать, что включить в следующий коммит

Два варианта.


  1. Интерактивный ребейс, ставим буковку e (edit) на тот коммит, который хотим поделить. Продолжаем ребейс, он останавливается на этом коммите. Там делаем reset HEAD~ — и оказываемся на родительском коммите, но со всеми изменениями в дереве. Дальше можно интерактивным коммитом (или пачкой таких коммитов) коммитать изменения одно за другим. По окончании процесса rebase --continue — и оставшиеся изменения (если такие остались) будут зафиксированы в исходном коммите, который делили.

Недостаток — эти частичные коммиты сложно тестировать. В дереве есть ВСЕ изменения, поэтому если вы, например, закоммитали отдельно работу с новой переменной и забыли туда же закоммитать её объявление/определение, это обнаружится не сразу.


  1. Интерактивный ребейс, ставим буковку e (edit) на родительский коммит к тому, который хотим поделить. Продолжаем ребейс. Он останавливается на запрошенном коммите. Дальше сравниваем его со следующим и берём выбранные изменения. В терминах сред IDE Intelij — это "compare with local" — где открывается список различающихся файлов, каждый из которых можно ткнуть и получить двухсторонний diff, а потом копировать изменения из сравниваемого файла (изменения из которого надо поделить) с текущим. Здесь всё легче, покуда рабочее состояние файлов — это родительский коммит + некоторые (выбранные) изменения из следующего. Можно попробовать собрать, прогнать тесты — и если всё ок, коммитать, потом вносить следующую пачку изменений и т.д. Так же можно сравнивать не только со следующим, но с любым другим (например, последние (условно) 42 коммита были на тему "попробовал… не получилось… откатил… снова попробовал… вот она, окончательная версия". Можно начать ребейс от старта этого челленджа, и творить аккуратную историю, сравнивая с конечным коммитом, где уже "всё хорошо". Так промежуточные пробные эксперименты вообще никак не будут видимы, можно будет составить последовательность коммитов, которые прямо и целенаправленно реализуют конечную фичу.

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


Если довести такой ребейс до конца, так что ничего важного вообще не осталось (кроме, может быть, пары-тройки неудачных пробелов/табуляций) — после окончательного rebase --continue можно оставшуюся цепочку слить любым ненапряжным способом (accept-ours, accept-others и т.д.) — и потом просто выкинуть. Но тут лучше вообще делать не интерактивный rebase, а изначально ставить тэг на "окончательный" конец ветки, после чего делать reset hard на родительский коммит к первым изменениям и дальше вот так через compare with local сравнивать конечную цель с текущим состоянием кода. Тогда потом без всяких ребейсов удаляем тэг и всё.

закоммитали отдельно работу с новой переменной и забыли туда же закоммитать её объявление/определение, это обнаружится не сразу.
В этом случае можно в процессе добавления промежуточных коммитов пушить результат на сервер, где гоняется CI, и быть уверенным, что все тесты благополучно проходятся.

А можно локально прогнать каждый коммит потом через rebase --exec

Огромный мануал для операции, которая совершенно не нужна

Аргументируйте, пожалуйста — почему эта операция не нужна? Или нужна, но не через git rebase?

ну, и что это доказывает, что при работе с исходным кодом надо включать голову? Указанные примеры не доказывают, что нельзя (вообще, в принципе) использовать тот же rebase, например, для влития изолированного функционала. Очень даже можно.
Ну, и не рассмотрен пример — как ловить эти проблемы при помощи пайплайнов тестов...

Конечно можно, но не нужно. Лучше силы потратить на более полезные вещи.

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

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

Для достижения результата (рабочий код который зарабатывает деньги) переписывание истории не нужная операция.

это можно переформулировать и так — "для достижения результата — git вообще не нужен" или "вообще без разницы — чем там пользуются разработчики, лишь бы приносили деньги, но пускай они будут довольны"

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

Ну, не гит как таковой, а средства коллаборации. Те же системы аналоги — svn, mercurial. Пока ещё не сдохли. Где-то встречаю.

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


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

А вы не читали примечание от переводчика в самом начале этой статьи?

В ней просто и наглядно рассказывается об основных возможностях интерактивного rebase'а, что может стать отличным введением для тех, кто только начинает им пользоваться.

Ради справедливости, но не флейма — эта приписка ничего не означает. Я видел материалы, которые просто объясняют сложные вещи, но при этом не были нацелены на новичков ) и наоборот )

Ох, блин… вот что значит "издержки скорочтения"!.. Да, вы правы: я пропустил эту ремарку, спасибо что подсказали. Предыдущий мой комментарий уже не удалить, поэтому прошу считать неактуальным в части "было бы хорошо предупреждать". В части "переводить более сложные статьи" по-прежнему предлагаю переводчикам переводить не только популярные темы (увы, количество плюсиков на сложных темах меньше).

Sign up to leave a comment.