Что: be0132b6d3f1508ca0b33b9499bb6b9535615d3f
Когда: 2020-05-23 14:46:24+03:00
Темы: git
Про простой workflow в git >Какой workflow нужно использовать, если я хочу просто отслеживать свежие >изменения в remote репозитории >и чтобы при этом держать свои локальные патчи >(отдавать их наверх не собираюсь). Одному человеку вот написал моё видение workflow для этого случая: ------------------------ >8 ------------------------ >Вот склонировал я репозиторий, периодически делаю "git pull" - всё хорошо. А >как комиттить свои локальные патчи? В интернете пишут - в master ничего не >коммитить (WTF, это моя копия master). А тогда как? Workflow в git-е тьму всяких используют и единого или рекомендуемого нет. Но это и так очевидно. Очень сильно разнятся советы по использованию git-а в зависимости от человека который его будет использовать: опытный или нет например. В одной компании где я работал, git cherry-pick или git rebase -i -- были табу, запрещены к использованию в одном отделе. Потому что в нём работали не очень опытные с git-ом разработчики, а git штука мощная и позволяет коверкать и изменять историю множеством способов и эти разработчики её коверкали так, что фиг потом разберёшься что как и откуда шло/росло и как посмотреть разницу и историю между рядом правок. В умелых же руках все эти rebase, cherry-pick -- к месту творят хорошие вещи. Я не пользовался Mercurial, но знакомые с ним и с git, говорят, что в Mercurial сложнее выстрелить себе в ногу, но он и менее гибок/мощен -- git же наоборот. >git-rebase - Reapply commits on top of another base tip > >Не, я точно не хочу такую хрень. Где-то в гите перемудрили. По-твоему это >понятно звучит? [...] Дока в нём не везде и не всегда понятна -- это точно. Но в ней (git-rebase) вот например есть такая картинка: Assume the following history exists and the current branch is "topic": A---B---C topic / D---E---F---G master From this point, the result of either of the following commands: git rebase master git rebase master topic would be: A'--B'--C' topic / D---E---F---G master которая, по моему, очень понятно объясняет суть rebase. >А нельзя просто мержить из мастера в свою ветку, без rebase и прочих >модификаций истории? 1) Ваш вариант: вы постоянно находитесь на ветке master. Сделали в ней свой коммит. А дальше, если сделаете git pull, то, так как верхушка вашей ветки не совпадает с верхушкой origin/master ветки, потому что появился ваш коммит, то git не может просто переставить указатель вашего master на коммит на который указывает origin/master (это называется fast forward). Git вынужден будет сделать "искусственный" (чисто технически это обычный коммит конечно же, просто он неявно создан не человеком) merge-коммит в котором будут два родителя: origin/master коммит и ваш коммит. git pull это на самом деле синоним к: $ git fetch $ git merge origin/master предполагая что текущая master ветка прописана в .git/config что связана с origin/master и предполагая что fetch автоматически подставляет "origin" remote. По умолчанию это всё так. Так вот команда merge, по умолчанию, делает fast-forward (если может), в противном случае merge-commit. Например ваш master указывает на commit2, а origin/master (который обновляется после git fetch) на commit 3 commit2 <- master commit1 commit3 <- origin/master commit2 commit1 Находясь в master ветке, делая git merge origin/master, git просто передвинет/обновит master указатель на commit3, сравнявшись с origin/master. Если же в вашем master есть ваш коммит, то fast-forward невозможен и будет создан промежуточный искусственный коммит: MYcommit <- master commit2 commit1 станет после merge origin/master: "Merge branch origin/master" | +-------+ | | | commit3 MYcommit | | commit2 commit2 | | commit1 в рандомно взятом репозитории git log покажет это так: * 903bdfe Update vendor 42wim/mm-go-irckit * 50a0ba8 Bump version * f8394da Merge branch 'master' of github.com:42wim/matterircd |\ | * ad4a8f0 readme: Add a "Guides" section and one guide (#194) * | f7cf55c (tag: v0.18.2) Release v0.18.2 |/ * a0ab000 Update vendor nlopes/slack * 755960e Bump version * 1886628 (tag: v0.18.1) Release v0.18.1 * 1aea3b8 Add support for mattermost 5.x Каждый раз когда вы будете делать git-pull, будут создаваться эти "merge branch" коммиты, так как в вашей ветке верхушка никогда не совпадает (хэш отличается) с origin/master. Проблема ли это? Зависит от. Так вы каждый раз видите когда сделали git pull. Если это делать каждые пять минут, то с появлением коммита в origin, у вас будет создаваться merge-коммит, объединяющий вереницу коммитов вашей истории и удалённой. На мой субъективный взгляд, чаще всего это делает историю некрасивой, загромождает ненужной для человека информацией (мусорными коммитами) и сложнее разобраться может быть как что откуда и как растёт. 2) Вариант с отдельной веткой и merge-ем: $ git checkout master $ git pull $ git checkout mybranch $ git merge master аналогичен 1), но у вас локально master ветка будет всегда полностью соответствовать ветке с origin. Хотя, по факту эта информация всё равно содержится в origin/master. 3) Вариант с rebase-ом вашей ветки на master: $ git checkout master $ git pull $ git checkout mybranch $ git rebase master rebase это простая операция: она берёт коммит(ы) и меняет их родителя. Как-бы отрывает и прикрепляет к другому. Безусловно все хэши всех эти коммитов поменяются (в вырезке из man-а они поэтому со штрихами). Но, тогда, вместо: * f8394da Merge branch 'master' of github.com:42wim/matterircd |\ | * ad4a8f0 readme: Add a "Guides" section and one guide (#194) * | f7cf55c (tag: v0.18.2) Release v0.18.2 |/ * a0ab000 Update vendor nlopes/slack если бы "readme: Add a "Guides"" коммит имел родителя не "Update vendor nlopes", а "Release v0.18.2", то тогда произошёл бы fast-forward и история превратилась в: * XXXXXXX readme: Add a "Guides" section and one guide (#194) * f7cf55c (tag: v0.18.2) Release v0.18.2 * a0ab000 Update vendor nlopes/slack и в вашем случае бы ваш mybranch всегда выглядел как: ваш коммит последний коммит master как например вот тут: * a4ceccf (HEAD -> links) uptodate * a51d32b webp * 7c53a13 (tag: 8.1, origin/master, master) Hide mmap imports, failing on Windows последний коммит в master это 7c53a13, а над ним два постоянно rebase-ящихся коммита ветки "links". На мой взгляд, это очень удобно для человека, для его восприятия. Но всегда зависит от. Rebase это изменение цепочки хэшей и моя ветка "links" после каждого rebase никогда не может быть запушена на удалённый репозиторий просто так, потому что fast forward невозможен. Если это лично ваша ветка/ваш репозиторий, то уверен что это не проблема. 4) Вариант с rebase-ом вашей master ветки (с вашим коммитом) на origin/master: $ git pull -r что равносильно: $ git fetch $ git rebase origin/master Так вы будете иметь master полностью совпадающий с origin/master, за исключением того, что его верхним коммитом будет ваш. На мой взгляд это самый удобный способ. Я вот использую suckless terminal (st), а в нём вся конфигурация зашивается на момент компиляции. Я просто делаю rebase своего master на удалённый и моя история всегда линейна, без ответвлений: * b5a4ee2 (HEAD -> master) My config * 92cc580 Coloured italics * 43a395a (tag: 0.8.3, origin/master, origin/HEAD) bump version to 0.8.3 * 72e3f6c Update XIM cursor position only if changed Но! При любом раскладе возможны конфликты в файлах (origin поправил тоже место что и вы). В случае с merge-коммитами, один раз разрешив конфликт, он как-бы зафиксирован и сохранён в истории и three way merge алгоритм его всегда учитывает и если в origin в том же месте снова что-то поменяется, то большая вероятность что three way merge автоматически всё сам разрулит. В случае с rebase, three way merge перестаёт работать. rebase, можно сказать, это просто: $ git checkout коммит-родитель $ git cherry-pick ваш-коммит $ git cherry-pick ваш-коммит2 [...] ну и дальше выставление текущего указателя ветки на то, что получилось. Никакой магии в rebase нет и его можно делать вручную cherry-pick-ами. Но когда делается cherry-pick, то это равносильно просто попытке применения какого-то патча. То есть это буквально two way merge, где отсутствует ancestor информация и если конфликт постоянно возникает в каком-то месте, то после каждого rebase это может происходить. Проблема ли это на практике и часто ли такое может быть? Зависит от. Я не помню когда последний раз с этим сталкивался. Но! С какой-то версии в git-е появилась технология rerere: man git-rerere, которая запоминает разрешения конфликтов и если встречает аналогичные конфликты, то автоматически применяет сохранённое разршение. Это в отдельной поддиректории в .git сохраняется всё и может быть удалено когда угодно. Достаточно просто включить rerere и он начнёт работать и он спасёт от постоянно одинаково разрешающихся конфликтов, которые могут во время rebase возникнуть. А также надо быть очень осторожным с git pull командой, если не уверены что remote репозиторий не делает форсированных (не fast-forward) обновлений. Например Gitlab по умолчанию вообще запрещает не fast-forward push в репозиторий, но всё зависит от настройки конкретного репозитория и людей в него пушающих. Если человек сделал git push --force, то у всех кто сделает git pull -- возникнет merge-коммит между их историей и насильно форсированно изменённой историей удалённого репозитория. Поэтому говорят что делать push --force чудовищно плохое действие. И ваш master после такого git pull уже никогда не будет совпадать с origin/master-ом. Тут только можно сделать (откатив все ваши изменения, конечно же): $ git reset --hard origin/master Лично я, *никогда* pull не использую, ну кроме личных репозиториев про которые помню, а всегда делаю fetch чтобы увидеть обновился ли репозиторий как fast-forward (и я могу сделать git merge origin/) или же нет, он был насильно изменён и тут... уже смотреть по месту надо что делать дальше. Если резюмировать, то лично я бы (сам бы так и делал) для вашего use-case советовал git pull --rebase в master ветке, где ваш коммит будет всегда наверху.
Сгенерирован: SGBlog 0.34.0