不使用 Fast-forward
也可以在 git merge 的時候,不使用 fast-forward 的方式來進行合併:
- 不使用 fast-forward 的話,把 2 個 branches 內容合併之後,會建立一個新的 m4 commit 節點
如何不使用 fast-forward?
只要在 指令之後加上 即可:
$ git checkout master
$ git merge —no-ff develop
Merge made by the ‘recursive’ strategy.
d1 | 0
d2 | 0
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 d1
create mode 100644 d2
1 |
$git checkout master $git merge—no-ff develop Merge made by the’recursive’strategy. d1| d2| 2files changed,insertions(+),deletions(-) create mode100644d1 create mode100644d2 |
為什麼不直接使用 fast-forward 要使用這種方式呢?
先來瞭解一下,有沒有 fast-forward,git log 看起來差別在哪:
有 fast-forward (develop 合併到 master 後):
d2
d1
m3
m2
m1
1 |
commit 932154590bde6dd8c7329439870ae893e8b6bdf4 Author: YMHuang <[email protected]> Date: Sat Apr 4 14:58:34 2015 +0800 d2 commit 129acb8246636279a28352f91a5c1a724a2d7a6d Author: YMHuang <[email protected]> Date: Sat Apr 4 14:57:11 2015 +0800 d1 commit adf8e08abdfe5d93acb24d9e0d43b89b4aa8d211 Author: YMHuang <[email protected]> Date: Sat Apr 4 14:45:17 2015 +0800 m3 commit 5a51d311899f0b8520d16b276291e80735bf90b8 Author: YMHuang <[email protected]> Date: Sat Apr 4 14:43:19 2015 +0800 m2 commit 09fd8ae6de5c0f718af12847d7c97364eb738fd2 Author: YMHuang <[email protected]> Date: Sat Apr 4 14:42:49 2015 +0800 m1 |
沒 fast-forward (develop 合併到 master 後):
Merge branch ‘develop’
d2
d1
m3
m2
m1
1 |
commit bc943817e068304f9de6285bd8c9f1a9a108e952 Merge: adf8e08 9321545 Author: YMHuang <[email protected]> Date: Sun Apr 5 12:56:40 2015 +0800 Merge branch ‘develop’ commit 932154590bde6dd8c7329439870ae893e8b6bdf4 Author: YMHuang <[email protected]> Date: Sat Apr 4 14:58:34 2015 +0800 d2 commit 129acb8246636279a28352f91a5c1a724a2d7a6d Author: YMHuang <[email protected]> Date: Sat Apr 4 14:57:11 2015 +0800 d1 commit adf8e08abdfe5d93acb24d9e0d43b89b4aa8d211 Author: YMHuang <[email protected]> Date: Sat Apr 4 14:45:17 2015 +0800 m3 commit 5a51d311899f0b8520d16b276291e80735bf90b8 Author: YMHuang <[email protected]> Date: Sat Apr 4 14:43:19 2015 +0800 m2 commit 09fd8ae6de5c0f718af12847d7c97364eb738fd2 Author: YMHuang <[email protected]> Date: Sat Apr 4 14:42:49 2015 +0800 m1 |
從上面的例子可以看到,如果沒有使用 fast-forward,能知道到哪些 commits 是從其他 branch 合併進來的,而且能在合併的時候加上 commit 訊息。
如果使用 fast-forward 合併,刪除被合併的 branch 後,原本的 commit 會不見嗎?
當然不會,以上面的例子來說,如果 branch 合併到 branch 後,要將 branch 刪除,只是會把指向 develop 的指標移除,所以不會讓 branch 中的 commits 節點也被移除掉。
Как использовать команду Git Merge?
Чтобы объединить изменения, полученные в предыдущем разделе, выполните следующую команду:
git merge
Если вы видите такой же результат, значит, вы успешно объединили изменения в своем локальном репозитории. На приведенном выше изображении третья строка показывает написанное Fast-forward (перемотка вперед). Это происходит потому, что это fast-forward merge (быстрое слияние), выполняемое git. Давайте посмотрим, что это такое.
Fast-Forward Merge
Fast-Forward Merge (быстрое слияние) в Git означает, что существует линейный путь от ветви, который отклоняется от ветви, к которой вы сливаетесь. Линейный путь означает, что не было никаких коммитов к главной ветви с тех пор, как ветвь объекта перешла в точку слияния.
На приведенном выше рисунке показано, что ветвь была отклонена, сделала три коммита, и за это время в главной ветви не было коммитов (зеленые точки). После трех коммитов я объединю ветвь функции в главную ветвь, что приведет к быстрой перемотке вперед.
Reflog
Все делают ошибки, и это совершенно нормально! Иногда может показаться, что вы испортили свой репозиторий Git настолько сильно, что просто хотите полностью его удалить.
— очень полезная команда для отображения журнала всех выполненных действий! Это включает в себя слияния, перезагрузки, возвраты: в основном, любые изменения в вашей ветке.
Если вы допустили ошибку, вы можете легко отменить ее, сбросив HEAD на основе информации, которую нам предоставляет reflog.
Скажем, мы на самом деле не хотели мержить origin ветку. Когда мы выполняем команду , мы видим, что состояние репозитория до мержа- . Давайте выполним , чтобы указать HEAD туда, где он был.
Продвинутые сценарии git merge
Параметр —no-commit
Бывают ситуации, когда история снимков (ряд команд commit) в ветке была сделана не очень осмысленно, то есть вы делали снимки состояний, которые не стоит сохранять в истории на всеобщем обозрении. Здесь то и пригодится параметр —no-commit. Он используется нечасто — только в том случае, если хочется уничтожить историю снимков ветки, так чтоб никто их никто никогда не увидел и к ним нельзя было вернуться.
Но сначала вернемся к вышеприведенным командам и рассмотрим, как работает merge без параметра:
git checkout master git merge hotfix
Первая команда (checkout) переведет нас на ветку master. Это значит, что наша папка на диске физически заполнятся версиями файлов из ветки master
А команда merge:
- перенесет в master все изменения ветки hotfix. То есть содержимое текущей папки изменится с учетом залитых изменений
- и сделает commit (если нет нет конфликтов). То есть сделает снимок полученного состояния и занесет его в историю. При этом история коммитов ветки hotfix будет сохранена и ее можно будет посмотреть.
Теперь рассмотрим, как работает команда merge с параметром.
Первый шаг такой же — переходим на ветку master, в которую надо залить изменения:
git checkout master
Но в этот раз допустим, что снимки в ветке hotfix были ненужными и мы хотим, чтоб их не было в истории. В этом случае надо добавить параметр —no-commit:
git merge --no-commit hotfix
Эта команда обновит текущие папки — перенесет в них все изменения ветки hotfix, но финального снимка сделано не будет. Мы сможем еще кое-что поменять в файлах и потом выполнить команду commit самостоятельно. При этом в историю попадет только финальный снимок. Будет видно, что ветка hotfix слита с веткой master, и все. А снимки ветки hotfix видны не будут.
Параметр —squash
Эта команда такая же, как предыдущая, разница будет только при просмотре истории. Перейдем на ветку master, в которую мы хотим залить изменения:
git checkout master
После выполнения команды:
git merge --squash hotfix
изменения появятся в текущих папках ветки master, аналогично тому, как это было после выполнения предыдущей команды с аргументом —no-commit. При этом финального снимка (commit) так же не произойдет.
Но затем после самостоятельного выполнения команды commit мы увидим в истории этот снимок как самый обычный снимок в ветке master. То, что ветка hotfix сливалась в ветку master, отображено в истории не будет.
Параметр —no-ff
Параметр —no-ff запрещает перемотку.
ff означает fast forward, то есть перемотка.
Во-первых объясню, что такое перемотка. Она случается при слиянии, если в той ветке, от которой мы ответвились, не было снимков после нашего ответвления. Называется это перемоткой, потому что технически никакого нового снимка в этом случае делать не надо, выполняя слияние двух предков. Наш снимок считается последним, и на него просто перематывается текущий указатель. Смотрите пример.
На картинке мы ответвились от С и сделали снимки X и Y. После снимка C в ветке master никто ничего не делал, и при слиянии указатель просто переместится на снимок Y:
master: A - B - C \ hotfix: X - Y
Еще раз обратите внимание, что в ветке master после ответвления от С снимков не появилось, именно поэтому перемотка и возможна. С помощью аргумента —no-ff можно запретить перемотку, а именно: сделать при слиянии новый отдельный снимок, хоть в нем и нет технической необходимости
Сделаем это:
С помощью аргумента —no-ff можно запретить перемотку, а именно: сделать при слиянии новый отдельный снимок, хоть в нем и нет технической необходимости. Сделаем это:
git merge --no-ff hotfix
На картинке это выглядит так:
master: A - B - C - (M) \ / hotfix: X - Y
Будет создан дополнительный снимок M и указатель переместится на него (хотя мог бы просто переместиться на Y, если б выполняли слияние без параметра —no-ff).
Когда именно стоит использовать параметр —no-ff сказать сложно — отчасти это вопрос вкуса
Некоторые любят делать отдельный снимок при любом важном слиянии
Далее рассмотрим git pull.
Создание новой ветви в командной строке
Программа Git в командной строке предлагает максимальную мощность и гибкость, но есть чему поучиться. Если вам удобно копаться в man-страницах и активно использовать Git, это отличный вариант.
Используйте команду git branch <branchname>, чтобы создать новую ветку с заданным именем:
Это ответвление от текущей ветви, поэтому убедитесь, что вы переключились на ту, от которой хотите перейти, прежде чем выполнять эту команду.
Вы можете перечислить все ветки и подтвердить, что новая была создана, с помощью git branch без каких-либо аргументов:
Вы можете увидеть дополнительную информацию, в том числе, какую ветвь отслеживает другая, используя флаг -vv :
Если вы попытаетесь создать ветку до первой фиксации, вы получите сообщение об ошибке, например:
Если вы попытаетесь создать ветку с уже существующим именем, вы получите сообщение об ошибке, например:
Команда git branch создает новую ветку, указывающую на тот же коммит, над которым вы сейчас работаете. Однако ваша рабочая копия по-прежнему будет указывать на основную ветку. Чтобы переключиться на новую ветку, которую вы только что создали, используйте git checkout :
Термин « проверка» может сбивать с толку, если вы привыкли к другим системам контроля версий; в Git проверка относится к переключению текущей активной ветки. Поскольку вы обычно хотите переключиться на новую ветку после ее создания, для всего процесса есть ярлык:
Эта команда означает «создать новую ветку под названием« dev »и немедленно переключиться на нее». Это эквивалент:
Фактически, вы даже можете использовать git checkout для создания ветки из любой другой, а не только из той, которая в данный момент проверена. Например, чтобы создать новую ветку с именем another , из ветки с именем dev :
GitLab
Поддержка Rebase Flow: ХОРОШО
Оценивая поддержку на https://gitlab.com, мы, по сути, оцениваем поддержку в продукте GitLab EE, на базе которого он реализован. Что же касается поддержки Rebase Flow в GitLab CE, то её там попросту нет.
Для понимания того, как именно организована поддержка Rebase Flow, взглянем на настройки проекта
Как вы видите, тут даже есть промежуточный вариант полулинейной истории, когда merge-коммиты остаются, но возможность принять pull request появляется только в том случае, если feature-бранч является линейным продолжением. Если выбран этот вариант с полулинейной историей или «Fast-forward merge», у нас появляется дополнительная возможность управления pull request’ом. А именно появляется кнопка «Rebase onto …», позволяющая сделать из feature-бранча линейное продолжение истории.
После чего можно без проблем принять pull request, который будет смержен без создания отдельного merge-коммита.
Более подробное описание этой функциональности можно посмотреть в документации (раз, два). Несмотря на то, что скриншоты в ней немного устарели, она не потеряла своей актуальности. На этом в принципе поддержка Rebase Flow заканчивается. То что она вообще есть — это, конечно, плюс, но в ней явно не хватает
- Возможности выбрать поведение для конкретного pull request’а. Да, перед принятием pull request’а можно поменять настройки самого проекта, но это не очень удобно.
- Возможности автоматического слияния коммитов feature-бранча в один.
Atlassian BitBucket Server (a.k.a. Atlassian Stash)
Поддержка Rebase Flow: УДОВЛЕТВОРИТЕЛЬНО
Я рассматриваю BitBucket v4.5.2 и, возможно, в будущих версиях ситуация изменится в лучшую сторону. Сейчас же с поддержкой в BitBucket Server несколько лучше, чем в его облачном брате. Если у вас есть доступ к администраторам, то вы можете их любезно попросить в файле поменять для вашего проекта/репозитория настройки мержа pull request’ов ()
- — настройка для конкретного репозитория в проекте .
- — настройка для конкретного проекта .
- — глобальное настройка для всего BitBucket Server.
Значения настроек могут быть следующими
- — никакого fast-forward. Это значение по умолчанию.
- –- при возможности, будет выполнен fast-forward мерж.
- — всегда fast-forward мерж. Вы просто не сможете смержить pull request, если это нельзя сделать линейно.
- — сливает все коммиты в один и не создает merge-коммита.
- — сливает все коммиты в один и не создает merge-коммита, но делает это только в том случае, если возможен fast-forward мерж.
Как вы видите, настройки достаточно гибкие, но есть две проблемы
- Нет web-интерфейса настроек, а обращения к администраторам сильно усложняют рабочий процесс.
- Нельзя выбрать поведение для конкретного pull request’а, минимальной настраиваемой сущностью является репозиторий.
Как только эти две проблемы будут устранены, поддержку Rebase Flow у BitBucket можно будет оценить на отлично. А пока …
Конфликт слияния
Допустим, файл index.html отредактировал кто-то еще в том же месте. Тогда при попытке выполнить команду merge мы получим сообщение:
$ git merge hotfix Auto-merging index.html CONFLICT (content): Merge conflict in index.html Automatic merge failed; fix conflicts and then commit the result.
Что же делать? Если открыть index.html, мы увидим, что Git его пометил примерно так:
<<<<<<< HEAD:index.html <div id="footer">contact : [email protected]</div> ======= <div id="footer"> please contact us at [email protected] </div> >>>>>>> hotfix:index.html
Надо удалить разметку в файле и оставить только один вариант спорной строки. После этого нужно добавить конфликтный файл в индекс:
git add index.html
И сделать снимок:
git commit
На этом все, конфликт будет разрешен.
Если же разрешать конфликты в данный момент некогда, можно отменить текущее слияние merge (чтобы выполнить его в следующий раз):
git merge abort
Merge
Наличие нескольких веток чрезвычайно удобно для того, чтобы новые изменения были отделены друг от друга, а также чтобы вы случайно не запушили несанкционированные или поврежденные изменения в продакшин. Как только изменения будут одобрены, мы хотим получить эти изменения в нашей прод ветке!
Один из способов получить изменения из одной ветки в другую — выполнить ! Есть два типа менж команд, которые может выполнять Git: fast-forward или no-fast-forward
Fast-forward ()
Fast-forward merge когда текущая ветка не имеет дополнительных коммитов по сравнению с веткой, которую мы мержим. Git у нас ленив и сначала попытается выполнить самый простой вариант: Fast-forward! Этот тип менжа не создает новый коммит, а скорее объединяет коммит(ы) в ветку, которую мы объединяем прямо в текущей ветке
Отлично! Теперь у нас есть все изменения, которые были сделаны в ветке , в ветке . Итак, что же такое no-fast-forward?
No-fast-foward ()
Хорошо, если ваша текущая ветка не имеет каких-либо дополнительных коммитов по сравнению с веткой, которую вы хотите смержить, но, к сожалению, это случается редко! Если мы зафиксировали изменения в текущей ветке, которых нет в ветке, которую мы хотим объединить, git выполнит объединение без fast-forward merge. При слиянии без fast-forward Git создает новый коммит мержа в активную ветку. Родительский коммит указывает на активную ветку и ветку, которую мы хотим объединить!
Merge конфликты
Хотя Git хорошо решает, как объединять ветки и добавлять изменения в файлы, он не всегда может принять это решение сам по себе. Это может произойти, когда две ветки, которые мы пытаемся смержить, имеют изменения в одной строке в одном и том же файле, или если одна ветка удалила файл, который изменила другая ветка, и так далее.
В этом случае Git попросит вас помочь решить, какой из двух вариантов мы хотим сохранить. Допустим, что в обеих ветках мы отредактировали первую строку в файле README.md.
Если мы хотим смержить в , это приведет к конфликту: хотите, чтобы заголовок был Hello! или hey!?
При попытке объединить ветки, Git покажет вам, где происходит конфликт. Мы можем вручную удалить изменения, которые не хотим сохранять, сохранить изменения, снова добавить файл и закоммитить изменения.
Reflog
Все делают ошибки, и это совершенно нормально! Иногда может показаться, что вы испортили свой репозиторий Git настолько сильно, что просто хотите полностью его удалить.
git reflog — очень полезная команда для отображения журнала всех выполненных действий! Это включает в себя merges, resets, reverts: в основном, любые изменения в вашей ветке.
Если вы допустили ошибку, вы можете легко исправить ее, сбросив HEAD на основе информации, которую нам предоставляет reflog!
Скажем, мы на самом деле не хотели объединять ветку source. Когда мы выполняем команду git reflog, мы видим, что состояние репо до слияния — HEAD@{1}. Давайте выполним git reset, чтобы указать HEAD туда, где он был на HEAD@{1}!
Мы видим, что последнее действие было перенесено в reflog!
У Git так много полезных команд, что я не могу их всех охватить в этой небольшой статье! Я знаю, что есть много других команд, которые я не успел осветить прямо сейчас — дайте мне знать, какие ваши любимые / самые полезные команды, и я расскажу о них в другом посте!
И как всегда, не стесняйтесь обращаться ко мне! (Оригинальная статья https://dev.to/lydiahallie/cs-visualized-useful-git-commands-37p1)
Spread the love
Как использовать команду Git Fetch?
Прежде чем использовать эту команду, давайте внесем некоторые изменения в наш удаленный репозиторий, чтобы мы могли извлечь их через локальный репозиторий.
Выполните следующие действия в своей учетной записи GitHub:
- Во-первых, откройте свою учетную запись GitHub и перейдите в репозиторий.
- Во-вторых, откройте Readme.md файл.
- В-третьих, нажмите значок редактирования, чтобы отредактировать файл.
- В-четвертых, отредактируйте описание файла.
- После этого прокрутите вниз, зафиксируйте изменения и нажмите кнопку commit changes (зафиксировать изменения).
- Изменения будут отражены в файле README.md.
Теперь, когда у нас есть некоторые изменения в удаленном репозитории, мы должны получить их в нашей локальной рабочей копии репозитория.
- Откройте Git bash и перейдите в свой рабочий каталог.
- Проверьте наличие чистого рабочего репозитория (никаких зафиксированных изменений).
- Наконец, выполните команду в Git Bash: git fetch
Последние две строки означают:
https://<repo_url> : URL-адрес репозитория.
e7b37f6..47b2bf6 : первый хэш-это хэш последней объединенной фиксации в локальном репозитории, а 47b2bf6-это новый хэш-код фиксации/изменения из удаленного репозитория.
Пользователь также может проверить коммиты и недавние действия с помощью команды git log.
Если на вашем экране отображается аналогичный вывод, как показано на рисунке выше, вы успешно извлекли изменения из удаленного хранилища.
3 ответа
Лучший ответ
Ваша ветвь, вероятно, расходится.
Когда вы делаете , выполняет слияние, которое часто создает коммит слияния. Поэтому по умолчанию извлечение из удаленного режима не является безопасной операцией: оно может создать новый коммит, которого раньше не было. Такое поведение может сбить пользователя с толку, потому что то, что, по его мнению, должно быть безвредной операцией загрузки, фактически изменяет историю коммитов непредсказуемым образом.
Чтобы этого избежать, нужно
С Git будет обновлять вашу ветку, только если она может быть «быстро продвинута» без создания новых коммитов. Если это невозможно сделать (если локальный и удаленный разделены), просто прерывается с сообщением об ошибке.
Вы можете настроить свой Git-клиент так, чтобы он всегда использовал по умолчанию, поэтому вы получите такое поведение, даже если вы забудете флаг командной строки:
Примечание. Флаг применяет изменения для всех репозиториев на вашем компьютере. Если вы хотите, чтобы такое поведение было только для репозитория, в котором вы находитесь, опустите флаг.
Взято из здесь
Это предупреждение было добавлено в Git 2.27, как указал Джо в своем ответе.
Вот как выглядит полное предупреждение:
Предупреждение представляет собой три команды в качестве параметров, все они подавляют предупреждение. Но они служат разным целям:
Это сохраняет поведение по умолчанию и подавляет предупреждение.
На самом деле это коммитируется поверх удаленной ветки, поддерживая одну ветвь как локально, так и удаленно (в отличие от поведения по умолчанию, когда задействованы две разные ветки — одна локальная, а другая удаленная — и для их объединения выполняется слияние ).
Это выполняется только в том случае, если локальная ветвь может быть перенаправлена. В противном случае он просто прерывается с сообщением об ошибке (и не создает никаких коммитов).
52
Qumber
23 Июл 2020 в 03:08
Imho — лучший. Причина моего ответа в том, что ребаз может перезаписать историю, а также может привести к потере некоторых файлов, если другой разработчик работает над той же веткой.
Но все они действительны.
1
sensorario
30 Июн 2020 в 08:23
Это новое предупреждение, добавлено в :
Чтобы удалить предупреждение, установите одно из предложенных значений в предпочтительное поведение по умолчанию для , если вы не задаете поведение в командной строке (используя , , `X3 `, ). Во всех случаях попытается выполнить перемотку вперед (Что такое быстрая пересылка git ?) объединить, если это возможно. Настройки управляют тем, что происходит, когда в вашей ветви есть изменения, но нет в удаленной ветви.
Это существующее поведение по умолчанию; установите это без предупреждения и без изменений в поведении; объединит удаленную ветку с вашей локальной.
Здесь попытается откатить ваши изменения поверх удаленной ветви. См. Когда мне следует использовать git pull —rebase? для получения более подробной информации о том, почему вы можете этого хотеть
Если слияние в ускоренном режиме невозможно, откажется продолжить. Разница между git pull —rebase и git pull — -ff-only цитаты:
31
Joe
30 Июн 2020 в 14:11
The basics of merging
The job of the git merge command is to integrate a code history that we’ve forked at some point. Let’s look deeper into how it works. First, let’s create a new branch and make some changes.
1 2 3 4 |
git checkout-bnew-branch echo»Additional line of code»>>README.md git add.README.md git commit-m»Added a line of code» |
We see the current state of the branch with git log:
Now, let’s move back to master and merge the changes:
1 2 |
git checkout master git merge new-branch |
Fast Forward Merge
One of the most important things about git merge, when compared to git rebase, is that merging creates a merge commit. This is not always the case, though. Let’s look into the git log after the above commit:
Since our new-branch is very simple, a fast forward merge occurred. It works by combining the histories of both branches. It can happen when there is a linear path from the current branch tip to the target branch. The above is the case since we haven’t committed anything to master before creating the new-branch.
In the third part of this series, we can learn that the branch is a reference.
If we dig a bit deeper, we can see that both master and the new-branch now point to the same commit. This is the case thanks to performing a fast-forward merge.
1 | git show-branch—sha1-namemaster |
1 | git show-branch—sha1-namenew-branch |
True merge
However, the above is not possible if our branches have diverged. To create such an example, let’s create a new branch but then make some changes to master.
1 2 3 4 5 |
git checkout-bfeature-b git checkout master echo»console.log(‘Feature A’)»>>feature-a.js git add.feature-a.js git commit-m»Added feature A» |
Now that our master includes some new changes let’s go back to feature-b.
1 2 3 4 |
git checkout feature-b echo»console.log(‘Feature B’)»>>feature-b.js git add.feature-b.js git commit-m»Added feature B» |
Now, let’s merge feature-b to master.
1 2 |
git checkout master git merge feature-b |
Once we do the above, Git fires up the text editor. The default depends on your system. In my case, it is Nano:
The editor opens because the merge results in creating a merge commit. Once we finalize the merge, we get the following:
In the above graph, we can observe at which point the master diverges into feature-b and when it comes together with the use of the merge commit.
Let’s look into the merge commit a bit more to understand it better.
1 | git cat-file-p73575db27f3c8a9d7492139013eae0e01193b880 |
In the second part of this series, we’ve learned that a parent of a commit is simply the previous commit. When we create a merge commit, it has multiple parents. Let’s inspect them:
1 | git cat-file-pff435740aad2a498294bd162e611f9a876c2b489 |
1 | git cat-file-p86251d7ea21d1b718e249ca83ae93dbdbb480e48 |
We can see that the parents of the merge commit are the tips of the branches involved.