6

Я не понимаю, как использовать git-rebase , и я рассматриваю следующий пример.

Давайте запустим репозиторий в ~/tmp/repo:

$ git init

Затем добавьте файл foo

$ echo "hello world" > foo

который затем добавляется и фиксируется:

$ git add foo
$ git commit -m "Added foo"

Далее я запустил удаленный репозиторий. В ~/tmp/bare.git я побежал

$ git init --bare

Чтобы связать repo с bare.git я побежал

$ git remote add origin ../bare.git/
$ git push --set-upstream origin master

Далее, давайте ответим, добавим файл и установим апстрим для новой ветви b1:

$ git checkout -b b1
$ echo "bar" > foo2
$ git add foo2
$ git commit -m "add foo2 in b1"
$ git push --set-upstream origin b1

Теперь пришло время переключиться обратно к master и что-то там изменить:

$ echo "change foo" > foo
$ git commit -a -m "changed foo in master"
$ git push

На данный момент в master файл foo содержит измененный foo, в то время как в b1 это все еще привет мир. Наконец, я хочу синхронизировать b1 с прогрессом, достигнутым в master .

$ git checkout b1
$ git fetch origin
$ git rebase origin/master

В этот момент git st возвращает:

# On branch b1
# Your branch and 'origin/b1' have diverged,
# and have 2 and 1 different commit each, respectively.
#   (use "git pull" to merge the remote branch into yours)
#
nothing to commit, working directory clean

На этом этапе содержание foo в ветви b1 изменяется foo . Так что же означает это предупреждение? Я ожидал, что должен сделать git push , git предлагает сделать git pull ... Согласно этому ответу , это более или менее так, и в своем комментарии @FrerichRaabe прямо говорит, что мне не нужно делать тягу. Что тут происходит? В чем опасность, как действовать дальше? Как история должна быть последовательной? Какова взаимосвязь между описанным выше случаем и следующей цитатой:

Не перебазируйте коммиты, которые вы поместили в публичный репозиторий.

взято из Pro Git книги.

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

2 ответа2

7

Причина, по которой вы не хотите перебазировать коммиты, которые вы поместили в общедоступный репозиторий, заключается в том, что команда git-rebase меняет историю.

Теперь, что это значит и почему это плохо? Во-первых, я бы предложил прочитать этот раздел книги Git. Из этого вы узнаете, что коммиты состоят из указателя на объект дерева (снимок файла) и указателя на родительский коммит. Теперь, когда вы "изменяете историю", перебирая коммиты поверх новых коммитов, вы меняете родительский указатель коммитов, которые вы уже сделали, что, в свою очередь, меняет идентификатор ваших коммитов.

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

Вы можете увидеть все это, выполнив некоторые команды git-log во время выполнения вашего примера. Я запустил их прямо перед запуском команды rebase:

$ git log --pretty=oneline origin/master
9b077261d1619803213201d5c7cefb757eb66b67 Changed foo in master
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

$ git log --pretty=oneline origin/b1
63a57ef54e301314a9dab38de0cd9d88c59a5fba added foo2 in b1
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

$ git log --pretty=oneline b1
63a57ef54e301314a9dab38de0cd9d88c59a5fba added foo2 in b1
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

И теперь после выполнения ребазинга origin/master и origin/b1 одинаковы, а b1 теперь:

$ git log --pretty=oneline b1
6687c64c37db0ee21a4d87e45d6ccb0913b8686d added foo2 in b1
9b077261d1619803213201d5c7cefb757eb66b67 Changed foo in master
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

Вы заметите, что коммит "добавленный foo2 in b1" имеет другой идентификатор, чем в предыдущих командах журнала. Если вы фиксируете это изменение в своем публичном репо, у вас теперь есть два коммита, в которых выполняется одна и та же работа, и это вызывает проблемы.

Теперь предположим, что вместо перестановки b1 поверх master, вы просто слили master в b1, ваш журнал будет выглядеть так:

$ git checkout b1
$ git merge origin/master
$ git log --pretty=oneline b1
518eb2dc6b2da0ff43ddd6837332031cc00eaad1 Merge remote-tracking branch 'origin/master' into b1
9b077261d1619803213201d5c7cefb757eb66b67 Changed foo in master
63a57ef54e301314a9dab38de0cd9d88c59a5fba added foo2 in b1
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

Вы заметите дополнительный коммит, который представляет собой слияние двух предыдущих коммитов. Теперь этой историей можно поделиться, и все будут счастливы.

git-log --graph также может помочь пролить дополнительный свет на то, что происходит.

3

Поздно на вечеринке, но вот ответ для потомков:

git rebase предназначен для локального использования. Переписывает историю, что позволяет создать очень красивую "главную линию", но опасно в многопользовательской среде.

Если:

  • Вы единственный, кто использует удаленный репозиторий,
  • И вы используете его только в одном рабочем пространстве,

Тогда, возможно, имеет смысл сделать это, заставив переписать историю. Сделайте ребаз и затем:

git push -f origin remotebranch

Если какое-либо из этих предположений не было выполнено, вы можете сожалеть об этом действии :-)

Читайте больше в этом сообщении в блоге

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .