Давайте потренеруемся делать merge и rebase, чтобы понять разницу и научиться решать конфликты.
- Предположим, что есть задача написать скрипт, выводящий на экран параметры его запуска.
Давайте посмотрим, как будет отличаться работа над этим скриптом с использованием ветвления, мержа и ребейза.
Создайте в своем репозитории каталог
branching
и в нем два файлаmerge.sh
иrebase.sh
с содержимым:
#!/bin/bash
# display command line options
count=1
for param in "$*"; do
echo "\$* Parameter #$count = $param"
count=$(( $count + 1 ))
done
Этот скрипт отображает на экране все параметры одной строкой, а не разделяет их.
2. Создадим коммит с описанием prepare for merge and rebase
и отправим его в ветку мастер.
- Создайте ветку
git-merge
. - Замените в ней содержимое файла
merge.sh
на
#!/bin/bash
# display command line options
count=1
for param in "$@"; do
echo "\$@ Parameter #$count = $param"
count=$(( $count + 1 ))
done
- Создайте коммит
merge: @ instead *
отправьте изменений в репозиторий. - И разработчик подумал и решил внести еще одно изменением в
merge.sh
#!/bin/bash
# display command line options
count=1
while [[ -n "$1" ]]; do
echo "Parameter #$count = $1"
count=$(( $count + 1 ))
shift
done
Теперь скрипт будет отображать каждый переданный ему параметр отдельно.
2. Создайте коммит merge: use shift
и отправьте изменения в репозиторий.
- Вернитесь в ветку
master
. - Предположим, что кто-то, пока мы работали над веткой
git-merge
, изменилmaster
. Для этого изменим содержимое файлаrebase.sh
на следующее
#!/bin/bash
# display command line options
count=1
for param in "$@"; do
echo "\$@ Parameter #$count = $param"
count=$(( $count + 1 ))
done
echo "====="
В этом случае скрипт тоже будет отображать каждый параметр в новой строке.
3. Отправляем измененную ветку master
в репозиторий.
- Предположим, что теперь другой участник нашей команды
не сделал
git pull
, либо просто хотел ответвится не от последнего коммита вmaster
, а от коммита когда мы только создали два файлаmerge.sh
иrebase.sh
на первом шаге.
Для этого при помощи командыgit log
найдем хэш коммитаprepare for merge and rebase
и выполнимgit checkout
на него примерно так:git checkout 8baf217e80ef17ff577883fda90f6487f67bbcea
(хэш будет другой). - Создадим ветку
git-rebase
основываясь на текущем коммите. - И изменим содержимое файла
rebase.sh
на следующее, тоже починив скрипт, но немного в другом стиле
#!/bin/bash
# display command line options
count=1
for param in "$@"; do
echo "Parameter: $param"
count=$(( $count + 1 ))
done
echo "====="
- Отправим эти изменения в ветку
git-rebase
, с комментариемgit-rebase 1
. - И сделаем еще один коммит
git-rebase 2
с пушем заменивecho "Parameter: $param"
наecho "Next parameter: $param"
.
Мы сэмулировали типичную ситуации в разработке кода, когда команда разработчиков
работала над одним и тем же участком кода, причем кто-то из разработчиков
предпочитаем делать merge
, а кто-то rebase
. Конфилкты с merge обычно решаются достаточно просто,
а с rebase бывают сложности, поэтому давайте смержим все наработки в master
и разрешим конфилкты.
Если все было сделано правильно, то на странице network
в титхабе находящейся по адресу
https://github.com/ВАШ_ЛОГИН/ВАШ_РЕПОЗИТОРИЙ/network
будет примерно такая схема:
Сливаем ветку git-merge
в мастер и отправяем изменения в репозиторий, должно получиться без конфликтов:
$ git merge git-merge
Merge made by the 'recursive' strategy.
branching/merge.sh | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
$ git push
#!/bin/bash
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 223 bytes | 223.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
В результате получаем такую схему:
- А перед мержем ветки
git-reabse
выполним ееrebase
на мастер. Да, мы специально создали ситуацию с конфликами, что бы потренероваться их решать. - Переключаемся на ветку
git-reabse
и выполняемgit rebase -i origin/master
. В открывшемся диалоге должно быть два выполненных нами коммита, давайте заодно объединим их в один, указав слева от нижнегоfix
. В результате получаем что-то подобное:
$ git rebase -i origin/master
Auto-merging branching/rebase.sh
CONFLICT (content): Merge conflict in branching/rebase.sh
error: could not apply dc4688f... git 2.3 rebase @ instead *
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply dc4688f... git 2.3 rebase @ instead *
Если посмотреть содержимое файла rebase.sh
, то увидем метки оставленные гитом для решения конфликта:
cat rebase.sh
#!/bin/bash
# display command line options
count=1
for param in "$@"; do
<<<<<<< HEAD
echo "\$@ Parameter #$count = $param"
=======
echo "Parameter: $param"
>>>>>>> dc4688f... git 2.3 rebase @ instead *
count=$(( $count + 1 ))
done
Удалим метки, отдав предпочтение варианту
echo "\$@ Parameter #$count = $param"
сообщим гиту, что конфликт решен git add rebase.sh
и продолжим ребейз git rebase --continue
.
И опять в получим конфликт в файле rebase.sh
при попытке применения нашего второго коммита.
Давайте разрешим конфлик оставив строчку echo "Next parameter: $param"
.
Далее опять сообщаем гиту о том, что конфликт разрешен git add rebase.sh
и продолжим ребейз git rebase --continue
.
В результате будет открыт текстовый редактор предлагающий написать комментарий к новому объединенному коммиту:
# This is a combination of 2 commits.
# This is the 1st commit message:
Merge branch 'git-merge'
# The commit message #2 will be skipped:
# git 2.3 rebase @ instead * (2)
Все строчки начинающиеся на #
будут проигнорированны.
После сохранения изменения, гит сообщит
Successfully rebased and updated refs/heads/git-rebase
И попробуем выполнить git push
, либо git push -u origin git-rebase
чтобы точно указать что и куда мы хотим запушить.
Эта команда завершится с ошибкой:
git push
To github.com:andrey-borue/devops-netology.git
! [rejected] git-rebase -> git-rebase (non-fast-forward)
error: failed to push some refs to '[email protected]:andrey-borue/devops-netology.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Это произошло, потому что мы пытаемся перезаписать историю.
Чтобы гит позволил нам это сделать, давайте добавить флаг force
:
git push -u origin git-rebase -f
Enumerating objects: 10, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 12 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 443 bytes | 443.00 KiB/s, done.
Total 4 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:andrey-borue/devops-netology.git
+ 1829df1...e3b942b git-rebase -> git-rebase (forced update)
Branch 'git-rebase' set up to track remote branch 'git-rebase' from 'origin'.
Теперь можно смержить ветку git-rebase
в мастер без конфликтов.
Цель задания - попробовать на практике то, как выглядит решение конфликтов.
Обычно при нормальном ходе разработки выполнять rebase
достаточно просто,
что позволяет объединить множество промежуточных коммитов при решении задачи, чтобы
не засорять историю, поэтому многие команды и разработчики предпочитают такой способ.
Есть еще такой тренажер https://learngitbranching.js.org/, где можно потренероваться работе с деревом коммитов и ветвлений.