Как стать автором
Обновить

Комментарии 26

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
ну это же не моя статья ).  Я в общем тоже rebase не использую. Но почитать как можно сделать мне показалось полезным. Тут каждый решает для себя.

rebase, особенно интерактивный, — это способ переписать историю: перенести коммиты, убрать, добавить, переименовать. С историей можно сделать всё что угодно.


Зачем переписывать историю — большой вопрос.


Кто-то считает, что история коммитов должна оставаться неизменной, потому что она показывает как развивался проект.


Кто-то (я, например) считает, что в публичный репозиторий должен попадать "причёсанный" код, где каждый коммит приносит какаю-то ценность проекту.


Я делаю по несколько десятков коммитов ежедневно просто, чтобы не потерять прогресс. Большая часть из них — это черновая работа, проверка версий и гипотез. Я считаю, что подобные вещи не приносят ценности проекту, поэтому из множества черновых коммитов в итоге я рибейсом собираю 1-2 осмысленных чистовых. По ним можно адекватно проследить историю развития проекта, а самое главное — их можно легко откатить.

Публичную историю лучше держать неизменной (точнее, не так, НУЖНО держать неизменной. Ваш тезис про rebase в пошаренный код ).


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


Или — в основной ветке решили переименовать глобальную сущность из foo в bar.
Может в отдельном коммите, может в "комбайне" других изменений (вы на это не влияете), но суть в том, что вы у в своей фиче тоже много где заюзали foo.


В первом случае — rebase -i, встаём на нужный коммит, делаем reset HEAD~ и делаем нужное количество коммитов. В одном изменения в глобальную либу, в другом — новые строчки от фичи и т.д. Делаем снова rebase -i, и коммиты с изменениями в глобал группируем и сливаем в один, если нужно. Остальные — по своему усмотрению. По итогу мухи и котлеты отделены, можно работать дальше!


Во втором случае — делаем то же переименование поверх своей ветки (тем же способом, что было сделано в основной. Например, через. IDE). Коммитаем, ребейзим. В результате переписывания получаем коммит с переименованием ровно тех моментов, что вы добавили (а те, что приплыли из основной ветки автоматически отбросятся, как совпадающие).


Ну и третий момент — то же, что и первый, только более глобально. Накодили три сотни коммитов, а в ветке хочется оставить пару десятков. Rebase -i и вперёд.
Итоговую ветку можно заребейзить или смержить, это уже неважно. Главное, историю предварительно очистили от мусора.

Я пользуюсь регулярно и в разных кейсах.
Изредка даже форс пуш бывает.

Инструмент, как обычно, не виноват.

Один из примеров полезности rebase:


Есть очень-очень крупный проект (master) и есть свой личный форк (fork) проекта с личными изменениями, которые, по каким-то причинам, не могут быть приняты (либо не хочется отправлять) в апстрим. Проект большой (десятки-сотни тысяч коммитов), личные изменения — максимум 10-20 коммитов.


Пришло время подтянуть свежие обновления из крупного проекта. Есть два варианта:


1) можно замержить masterfork
2) можно сделать rebase


В случае «1» наши фиксы со временем перемешиваются с коммитами из апстрима и через пару лет уже будет трудно разобраться, что происходит.


В случае «2» у нас всегда в ветке fork сверху те 10-20 коммитов с нашими фиксами и ситуация всегда ясна, конфликты проще разрешить при значительных изменениях в апстриме.


Для облегчения ситуации можно при каждом обновлении из апстрима создавать новую ветку fork-YYYYMMDD, чтобы после rebase хэши коммитов не менялись.

НЛО прилетело и опубликовало эту надпись здесь
Для облегчения ситуации можно при каждом обновлении из апстрима создавать новую ветку fork-YYYYMMDD, чтобы после rebase хэши коммитов не менялись.
После rebase хэши, всё-равно, поменяются, потому что объект коммита содержит в себе указатель на предка, и если тот меняется (а при rebase это всегда происходит), то и хэш всего коммита меняется. И последующих, аналогично.

Я имел в виду, чтобы старую ветку не трогать (чтобы в ней хэши коммитов не менялись), а создать новую и очередной rebase уже там делать. Пример:


1) есть наш форк 2-недельной давности (our-20200414) и новые изменения в master, поверх которых надо наши изменения положить


* 8ce46bb (HEAD -> master) 5
* cda3070 4
| * 95f2d51 (our-20200414) 5'
| * 07a2aee 4'
|/
* d8695a4 3
* 82dc170 2
* 59a32e9 1

2) делаем rebase


git checkout -b our-20200428 master
git rebase --onto our-20200428 d8695a4 95f2d51
git branch -f our-20200428

3) получили:


* e2e5630 (HEAD, our-20200428) 5'
* f1ed7e7 4'
* 8ce46bb (master) 5
* cda3070 4
| * 95f2d51 (our-20200414) 5'
| * 07a2aee 4'
|/
* d8695a4 3
* 82dc170 2
* 59a32e9 1
* 87aa0fa “root”
rebase помогает держать свою собственную feature-ветку актуальной, периодически перестраивая её поверх актуального master/develop (при этом без лишних мержей). всегда так делаю для веток, которые принадлежат только мне и от которых никто не зависит
НЛО прилетело и опубликовало эту надпись здесь
тем, что история остается линейной.

И какое в этом преимущество?

Например, проще искать изменения, которые внесли регрессию. Да и, вообще, Оккам был не дурак.

Я в гите относительно недавно. А что за мерж, которые не создает коммитов? Что вы имели ввиду?

Возможно речь про fastforward, но он работает только когда ветка в которую вливает без изменений с момента ветвления.
Тогда и rebase не нужен. Да и вообще причём тут? Речь же шла о том, что есть фича-ветка и девелоп ушедший вперёд. Один говорит, что фичу надо ребейзить на голву девелопа периодически, а этот отвечат, что надо подмёрживать девелоп в фичу. При этом как-то делать это без коммита. Или чего-то не так понял, или он.
Согласен, похоже le1ic в чем то ошибся, ибо таких возможностей в гите без ребейза нет.
НЛО прилетело и опубликовало эту надпись здесь
Опишу свой сценарий. Есть некий проект, туда имеют возможность коммитить несколько человек, включая меня. Я замечаю опечатку в GUI, правлю, делаю коммит и хочу отправить его на сервер:
error: failed to push some refs to 'https://github.com/miranda-ng/miranda-ng'
hint: Updates were rejected because the remote contains work that you do not have locally. This is usually caused by another repository pushing to the same ref. You may want to first integrate the remote changes (e.g., 'git pull ...') before pushing again. See the 'Note about fast-forwards' in 'git push --help' for details.

Если я не сделаю «rebase after merge», то мой коммит (когда он таки улетит на сервер) будет выглядеть как две записи в истории:
Merge branch 'master' of github.com/miranda-ng/miranda-ng
Fix spelling

Первая из двух записей — информационный мусор, который никакой пользы остальным участникам не несёт. Он прилетит всем, кто подписан, например, по RSS на историю коммитов (некоторые пользователи, совершенно точно, подписаны и следят за ходом разработки). Зачем им это?

P.S. Возможно, я использую git как-то неправильно и ужасно, но я не программист, сложные и большие коммиты с разработкой новых фич не делаю, все мои правки сводятся к таким вот тривиальным исправлениям мелких и очевидных ошибок.
НЛО прилетело и опубликовало эту надпись здесь
Updates were rejected because the remote contains work that you do not have locally

1) git pull --rebase
2) проверить, что там подтянулось
3) git push

Выбор команд — дело абсолютно неважное в данной ситуации, потому что в реальности отработает оно одинаково, ваш вариант будет разве что быстрее =)
и ни одного слова зачем он вообще может быть нужен. Я например не знаю.

Поправить опечатку в сделанном ранее коммите, внести доработки, которые сразу не учел. То есть всё то, для чего нужен amend, только для предыдущих коммитов.


Помимо этого объединить промежуточные шаги в законченный коммит. Это работает немного наоборот. Когда вы не исправляете коммиты, вы держите все изменения незакоммиченными, и коммитите когда уверены что все сделано. Это создает дополнительную умственную нагрузку, особенно если надо проверить несколько вариантов реализации (и выбрать тот, который не последний) — ага, тут я поменял, надо еще в том файле поменять, в этом вот это убрать, а вот это не трогать, в этом было мало изменений, можно Ctrl+Z нажать несколько раз, все поменял, проверил, можно коммитить. С исправлением коммитов можно любой шаг коммитить, и при проверке очередной версии не держать все в голове, а просто вносить изменения, если что-то упусил, будет еще один коммит. Потом это все объединяется в 1 или несколько нормальных коммитов. "Наоборот" тут в том смысле, что не rebase дает что-то конкретное, а без rebase сложнее, но пока не попробуешь делать с rebase, это неочевидно.


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

По сути, rebase — это последовательность cherry-pick'ов, поэтому на каждом коммите может вознинкуть merge conflict. Когда-то тут на хабре я описал способ как, делая rebase, свести всё к единственному merge conflict'у.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий