Fast forward git что это

Режим fast-forward в Git

April 17, 2015

Слияние ( merge ) ветвей является весьма распространенной и обыденной операцией, выполняемой в системе Git. Однако, в некоторых случаях Git выполняет слияние ветвей в режиме fast-forward. Но что такое режим fast-forward и чем он отличается от обычного режима слияния ветвей.

Давайте разберем этот вопрос на конкретном примере. Допустим, при работе в репозитории мною была создана ветка speedup из текущей ветки master :

Поработав некоторое время в этой ветке, я создал несколько commit ‘ов (три коммита, три белых кружка на рисунке):

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

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

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

В ситуациях подобного рода система Git всегда старается применить режим fast-forward, если это возможно. Однако, такое поведение Git можно легко изменить на режим no fast-forward и сделать его поведением системы по умолчанию.

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Внимательно посмотрите на приведенный выше рисунок. На нем хорошо видно, что некоторые слияния ( merge ) веток могут быть выполнены в режиме fast-forward. Но своеобразный подход GitHub к слиянию веток привел к тому, что линейная история коммитов была превращена в нечто похожее на рисунок железнодорожного пути.

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

Режим no fast-forward хранит всю информацию о слияниях веток. Такой подход может оказаться запутанным и сложным, если необходимо прочитать историю коммитов.

С другой стороны, режим fast-forward хранит не всю информацию о слияниях веток. Такой подход более простой для чтения, но в этом случае становится неочевидным история веток проекта.

Источник

Тонкости благополучного git-merge

Вступительное слово

Основными командами пользовательского уровня для ветвления в Git являются git-branch, git-checkout, git-rebase, git-log и, конечно же, git-merge. Для себя я считаю git-merge зоной наибольшей ответственности, точкой огромной магической энергии и больших возможностей. Но это достаточно сложная команда, и даже достаточно длительный опыт работы с Git порой бывает недостаточным для освоение всех ее тонкостей и умения применить ее наиболее эффективно в какой-либо нестандартной ситуации.

Попробуем же разобраться в тонкостях git-merge и приручить эту великую магию.

Здесь я хочу рассмотреть только случай благополучного слияния, под которым я понимаю слияние без конфликтов. Обработка и разрешение конфликтов — отдельная интересная тема, достойная отдельной статьи. Я очень рекомендую так же ознакомиться со статьей Внутреннее устройство Git: хранение данных и merge, содержащей много важной информации, на которую я опираюсь.

Анатомия команды

Если верить мануалу, команда имеет следующий синтаксис:

По большому счету, в Git есть два вида слияния: перемотка (fast-forward merge) и «истинное» слияние (true merge). Рассмотрим несколько примеров обоих случаев.

«Истинное» слияние (true merge)

Мы отклоняемся от ветки master, чтобы внести несколько багов улучшений. История коммитов у нас получилась следующая:

Выполним на ветке master git merge feature :

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

А теперь посмотрим информацию о коммите (M):

Мы видим двух родителей, объект-дерево, соответствующее данному состоянию файлов репозитория, а так же информацию о том, кто виновен в коммите.

Посмотрим, куда ссылается указатель master:

Действительно, он теперь передвинут на коммит (M).

Squash и no-commit

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

Позже, в случае выполнения «классического» git merge feature можно исправить это. Тогда история примет следующий вид:

Перемотка (fast-forward merge)

Рассмотрим другой случай истории коммитов:

Все как и в прошлый раз, но теперь в ветке master нет коммитов после ответвления. В этом случае происходит слияние fast-forward (перемотка). В этом случае отсутствует коммит слияния, указатель (ветка) master просто устанавливается на коммит Y, туда же указывает и ветка feature:

Стратегии слияния

Стратегия resolve

Здесь C — общий коммит двух веток, дерево файлов, соответствующее этому коммиту, принимается за общего предка. Анализируются изменения, произведенные в ветках master и feature со времен этого коммита, после чего для коммита (M) создается новая версия дерева файлов в соответствии с пунктами 4 и 5 нашего условного алгоритма.

Стратегия recursive

Для иллюстрации этой стратегии позаимствуем пример из статьи Merge recursive strategy из блога «The plasticscm blog»:

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Итак, у нас есть две ветки: main и task001. И так вышло, что наши разработчики знают толк в извращениях: они слили коммит 15 из ветки main с коммитом 12 из ветки task001, а так же коммит 16 с коммитом 11. Когда нам понадобилось слить ветки, оказалось, что поиск реального предка — дело неблагодарное, но стратегия recursive с ее конструированием «виртуального» предка нам поможет. В результате мы получим следующую картину:

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

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

Стратегия octopus

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

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

Стратегия ours

Не следует путать стратегию ours и опцию ours стратегии recursive.

Стратегия ours — более радикальное средство.

Стратегия subtree

Для иллюстрации данной стратегии возьмем пример из главы Слияние поддеревьев книги «Pro Git».

Добавим в наш проект новые удаленный репозиторий, rack:

Ясно, что ветки master и rack_branch имеют абсолютно разные рабочие каталоги. Добавим файлы из rack_branch в master с использованием squash, чтобы избежать засорения истории ненужными нам фактами:

Теперь файлы проекта rack у нас в рабочем каталоге.

Источник

Что такое «git push problem: non fast forward»

Данная мини-заметка в первую очередь является ответом на вопрос. Так как мой аккаунт read-only, то вот такой вот способ ответа. «А жизнь-то налаживается!» ©

Первый вывод после прочтения вопроса и ответов — не делайте так, как предложил defuz. Он не понимает суть проблемы, и если вы сделаете как им предложено — скорее всего, вы потеряете данные.
Второй: alekciy тоже не совсем прав, но тут шансов на потерю данных гораздо меньше. Почти никаких.
Ну и третий: блин, ну когда же люди поймут, что владеть используемым инструментом это реально необходимо? Читайте документацию!

Скажу сразу, многие детали и «внутренности» работы git’а опущены либо упрощены, но не в ущеб пониманию. Именно появление понимания и цель статьи, и если оно у вас уже есть, и вы уверены, что оно верное 🙂 — то ничего нового для вас тут не будет. Если же понимания пока нет — то поехали!

Для начала, надо выяснить, что такое fast-forward. Это просто один из вариантов (методов, стратегий) выполнения операции merge. Он возможен только в том случае, если текущий коммит (тот, к которому будет происходить слияние, обозначим его А), является предком «сливаемого» коммита (тот, с которым будет происходить слияние, обозначим его Б). Графически это выглядит так (х — просто другие, не важные нам коммиты):

Если история коммитов выглядит вот так:

то выполнить слияние от А к Б методом fast-forward не получится — А не является предком для Б. Точно так же, в этой ситуации нельзя выполнить слияние с помощью метода fast-forward от Б к А. Но, любое из слияний «от В к А» и «от В к Б» — можно, и при выполнении одного из этих слияний git (по умолчанию) будет использовать именно метод fast-forward.

Теперь следующая вещь, которую необходимо уяснить — это что такое push. Ключевых моментов в этой операции два. Первый: это не просто передача данных о ваших коммитах «на ту сторону», а ещё и обязательная последующая операция merge «на той стороне». И второй: «та сторона» для операции merge будет использовать только fast-forward. Почему всё именно так?

Ну, с первым очевидно. Чтобы поместить что-то в историю, нужно создать новый объект-коммит в истории (который будет потомком текущего) и изменить указатель на последний коммит в ветке (который называется HEAD), на этот новый коммит. По большому счёту, для этого есть только две операции: commit и merge (слияние, которое в большинстве случаев будет приводить к появлению нового коммита). Очевидно, на сервере коммиты «вручную» никто не делает. И если на сервер просто передать вашу историю (новые коммиты), то это никак не изменит HEAD. Так что делать merge с вашими новыми коммитами сервер просто вынужден — у него просто нет другого варианта изменить HEAD.

Второй момент тоже не сложный, но не сразу очевиден. Почему сервер, делая merge, использует только fast-forward? Ведь есть же куча других замечательных методов, тот же octopus, но их сервер не использует. Почему? Всё очень просто: fast-forward это единственный вариант, который не порождает новых коммитов (помимо тех, что уже добавили вы). Ведь сервер не может порождать новые коммиты сам, так как для создания коммита git требует указания автора — сервер может только хранить ваши коммиты. И что ещё главнее — этот метод (fast-forward) при слиянии никогда не даёт конфликтов: просто по природе самого метода fast-forward конфликты при слиянии не возможны. Для сервера это важно, так как в случае возникновения конфликта решать его будет некому. На то он и конфликт, чтоб его решал человек, потому как компьютер разобраться не смог.

Понимание этих базовых вещей должно быть достаточно для «прояснения» ситуации. Но как она может возникнуть?

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

Как же решить возникшую проблему? Вариантов два, и оба они приводят к тому, что А будет сливаться с таким коммитом (назовём его Х), для которго А будет предком. Как же этого добиться?

Вариант первый: слияние. Вам необходимо локально выполнить слияние вашего кода (Б) и того, который есть на сервере (А). Результатом слияния будет новый коммит в вашей локальной истории, тот самый Х — и сделать для него push сервер не откажется. Для убедительности — картинка:

Вариант второй: rebase. Rebase — это операция «переноса» части истории таким образом, чтобы изменить «корень» ветки, но не изменить саму ветку. Для наглядности — снова картинка. Начальное состояние — всё тот же рисунок 2, а состояние после rebase будет вот таким:

В полученном результате роль коммита Х выполняет Б — но это уже другой Б, скажем так, Б’. В этом легко убедиться, посмотрев commit-id до rebase и после. Но как уже было сказано, сама ветка (то есть содержимое коммитов, её составляющих), не поменяется — поменяются только commit-id. Но цель достигнута — можно делать push.

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

Есть и третий вариант решения, и именно его предложил defuz — forced push, например так:
git push origin +master

Он приведёт к тому, что сервер примет ваши коммиты, и безусловно изменит свой HEAD так, чтобы он указывал на последний из ваших коммитов — на Б. Таким образом, все «свои» коммиты, которых у вас нет (от В до А) сервер «забудет». Это и есть та самая потеря данных, о которой я говорил в начале. Тем не менее, иногда бывают случаи, когда это именно та операция, которая вам необходима. Но если у вас именно такой случай, и вы это понимаете, то вероятно эта статья вам уже давно не нужна.

Ну и чтоб уж расставить совсем все точки над и — по поводу ответа alekciy. Поскольку его вариант не использует forced push, то потери данных не будет. Но вполне очевидно, что после merge во-первых, нечего будет положить в stash (кроме новых файлов, которые ещё не под наблюдением git’а), а во-вторых, rebase уже не нужен (и если есть новые файлы — он обломится).

Надеюсь, этот материал прояснил происходящее и поможет решить возникшую проблему.

Источник

CS Visualized: полезные команды Git

Хотя Git является очень мощным инструментом, я думаю, что большинство людей согласятся, когда я скажу, что он также может быть … полным кошмаром 😐 Мне всегда было очень полезно визуализировать в своей голове, что происходит при работе с Git: как ветви, взаимодействуют, когда я выполняю определенную команду, и как это повлияет на историю в git? И почему мой коллега заплакал, когда я сделал полный reset на master, выполнив принудительный push к origin и переименовав в rimraf папку .git?

Я подумал, что эта статья будет идеальным вариантом для создания наглядных примеров наиболее распространенных и полезных команд! 🥳 Многие из описываемых мной команд имеют необязательные аргументы, которые вы можете использовать для изменения их поведения. В моих примерах я расскажу о поведении команд по умолчанию, не добавляя (слишком много) параметров конфигурации! 😄

Merging

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

Один из способов получить изменения из одной ветви в другую — выполнить git merge имя коммита! Есть два типа слияний, которые может выполнять Git: fast-forward или no-fast-forward 🐢

Давайте посмотрим на различия!

fast-forward merge можно использовать, когда текущая ветвь не имеет дополнительных коммитов по сравнению с веткой, с которой мы объединяемся. Git … ленив и сначала попытается выполнить самый простой вариант: fast-forward! Этот тип слияния не создает новый коммит, а скорее объединяет коммит (ы) в ветви, которую мы объединяем прямо в текущей ветви 🥳

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Хорошо, если ваша текущая ветвь не имеет каких-либо дополнительных коммитов по сравнению с веткой, с которой вы хотите объединиться, но, к сожалению, это случается редко! Если мы закоммители изменения в текущей ветви, которых нет в ветви, с которой мы хотим объединиться, git выполнит объединение без no-fast-forward.

При no-fast-forward merge Git создает новый merging commit в активной ветви. Родительский коммит указывает на активную ветвь и ветвь, которую мы хотим объединить!

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Теперь у нас новый коммит 9e78i 🎉Ветка master теперь содержит все изменения, которые мы внесли в ветку dev.

Merge Conflicts

Хотя Git хорошо решает, как объединять ветви и добавлять изменения в файлы, он не всегда может самостоятельно принять это решение. 🙂 Это может произойти, когда две ветви, которые мы пытаемся объединить, имеют изменения в одной строке в одном файле, или если одна ветка удалила файл, который изменила другая ветка, и так далее.

В этом случае Git попросит вас помочь решить, какой из двух вариантов мы хотим сохранить! Допустим, что в обеих ветках мы отредактировали первую строку в файле README.md.

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Если мы хотим объединить dev с master, это приведет к конфликту слияния: какой заголовок должен быть оставлен Hello! или Hey!?

При попытке объединить ветви, Git покажет вам, где произошел конфликт. Мы можем вручную: удалить изменения, которые не хотим сохранять, сохранить изменения, снова добавить измененный файл и зафиксировать изменения 🥳

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Хотя конфликты слияний часто довольно раздражают, они определенно нужны: Git просто должен предполагать, какие изменения мы хотим сохранить.

Rebasing

Мы только что увидели, как можно применить изменения из одной ветви в другую, выполнив git merge. Другой способ добавить изменения из одной ветви в другую — выполнить git rebase.

git rebase копирует коммиты из текущей ветви и помещает эти скопированные коммиты поверх указанной ветви.

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Теперь все изменения, которые были сделаны в основной ветке master, добавлены в ветку dev! 🎊

Основная разница по сравнению с merging заключается в том, что Git не будет пытаться выяснить, какие файлы сохранить и не сохранить. В ветке, которую мы обновляем, всегда есть последние изменения, которые мы хотим сохранить! Таким образом, вы не столкнетесь ни с какими конфликтами слияния, и у вас будет хорошая линейная история Git. (То есть, в этом случае всегда используются только последние изменения)

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

Rebasing отлично подходит, когда вы работаете над вашей веткой, а основная ветвь была обновлена. Вы можете получить все обновления в свою ветку, которые предотвратят будущие конфликты слияний! 😄

Interactive Rebase

Перед rebasing коммитов мы можем их изменить! 😃 Мы можем сделать это с помощью interactive rebase. Interactive rebase также может быть полезен для ветки, над которой вы сейчас работаете, и хотите изменить некоторые коммиты.

Есть 6 действий, которые мы можем выполнить над коммитами, которые мы rebasing:

Таким образом, мы можем получить полный контроль над нашими коммитами. Если мы хотим удалить коммит, мы можем просто drop его.

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Или, если мы хотим объединить несколько коммитов вместе, чтобы получить более чистую историю, нет проблем!

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Interactive rebasing дает вам большой контроль над коммитами, которые вы пытаетесь rebase, даже в текущей активной ветке!

Resetting

Может случиться так, что мы допустили изменения, которые мы не хотели позже. Может быть, это коммит WIP или коммит, в котором есть ошибки! 🐛 В этом случае мы можем выполнить git reset.

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

Soft reset

soft reset перемещает HEAD к указанному коммиту (или индексу коммита по сравнению с HEAD), не избавляясь от изменений, которые были внесены в коммиты позже!

Допустим, мы не хотим сохранять коммит 9e78i, в который был добавлен файл style.css, и мы также не хотим сохранять коммит 035cc, в который был добавлен файл index.js. Однако мы хотим сохранить недавно добавленные файлы style.css и index.js! Идеальный вариант использования для этого soft reset.

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

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

Hard reset

Иногда мы не хотим сохранять изменения, внесенные некоторыми коммитами. В отличие от soft reset, нам не нужно больше иметь к ним доступ. Git должен просто сбросить свое состояние обратно туда, где он был в указанном коммите: это даже включает изменения в вашем рабочем каталоге и промежуточных файлах! 💣

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Git отменил изменения, которые были внесены в 9e78i и 035cc, и сбросил свое состояние до того, где он был при commit ec5be.

Reverting

Другой способ отменить изменения — выполнить git revert. Отменяя определенный коммит, мы создаем новый коммит, который содержит отмененные изменения!

Допустим, в ec5be добавили файл index.js. Позже мы на самом деле понимаем, что больше он не нужен, и мы не хотим чтобы это изменение было внесено этим коммитом! Давайте вернем коммит ec5be.

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Отлично! Коммит 9e78i отменил изменения, внесенные коммитом ec5be. Выполнение git revert очень полезно для того, чтобы отменить определенный коммит, не изменяя историю ветки.

Cherry-picking

Что бы получить из определенной ветки изменения, которые нам нужны в нашей активной ветке, мы можем использовать команду cherry-pick! Выбирая коммит cherry-pick, мы создаем новый коммит в нашей активной ветке, который содержит изменения, внесенные коммитом cherry-pick.

Предположим, что коммит 76d12 в ветке dev добавила изменение в файл index.js, и нам нужно получить их в нашей основной ветке master.

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Круто, ветка master теперь содержит изменения, которые внес 76d12!

Fetching

Если у нас есть удаленная ветка Git, например ветка на Github, может случиться так, что удаленная ветвь имеет коммиты, которых нет в вашей текущей ветки!

Мы можем получить эти изменения локально, выполнив git fetch в удаленной ветке! И это никак не повлияет на вашу локальную ветку: fetch просто загружает новые данные.

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

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

Pulling

Хотя git fetch очень полезен для получения удаленной информации о ветке, мы также можем выполнить git pull. git pull — это две команды в одной: git fetch и git merge. Когда мы извлекаем изменения из источника, мы сначала извлекаем все данные, как мы делали с помощью git fetch, после чего последние изменения автоматически объединяются в локальную ветвь.

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Круто, теперь мы отлично синхронизированы с удаленной веткой и имеем все последние изменения! 🤩

Reflog

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

git reflog — очень полезная команда для отображения журнала всех выполненных действий! Это включает в себя merges, resets, reverts: в основном, любые изменения в вашей ветке.

Если вы допустили ошибку, вы можете легко исправить ее, сбросив HEAD на основе информации, которую нам предоставляет reflog!

Fast forward git что это. Смотреть фото Fast forward git что это. Смотреть картинку Fast forward git что это. Картинка про Fast forward git что это. Фото Fast forward git что это

Мы видим, что последнее действие было перенесено в reflog!

У Git так много полезных команд, что я не могу их всех охватить в этой небольшой статье! Я знаю, что есть много других команд, которые я не успел осветить прямо сейчас — дайте мне знать, какие ваши любимые / самые полезные команды, и я расскажу о них в другом посте!

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *