git rebase для начинающих

    В продолжение статьи на тему что сказать git, чтобы он сделал то, что вам нужно и перед статьей как создать PR в чужой Open Source проект на GitHub думаю стоит полезным рассказать о том, что такое git rebase.

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

    Итак git работает с комитами. Каждый комит — набор изменений. У каждого комита есть уникальный hash. Когда происходит слияние веток посредством merge:

    # git merge "another_branch"
    

    то все комиты сохраняются — сохраняются комментарии комита, его hash + как правило добавляется еще один искусственный комит. При этом комиты могут чередоваться друг с другом. Это не всегда удобно. Допустим ваш комит решили откатить — выискивать в общем списке где ваш комит, а где не ваш не очень приятно. И вообще — в общей истории хочется видеть действительно важные изменения, а не «ой, я забыл поставить ;». Для того, чтобы несколько комитов склеивать в один можно использовать rebase. Хотя в интерфейсе GitHub есть кнопочка squash & commit — это когда вы создаете pull request (PR) из одной ветки в другую (как правило из вашей рабочей ветки в основную) и после прохождения всех формальностей можно нажать squash & commit, обновить комментарий и ваши изменения появятся в основной ветке как один комит.

    Другие варианты объединить несколько комитов в один
    Я раньше делал так
    # git checkout master && git pull && git branch -b <НоваяВетка> && git merge <СтараяВеткаГдеМногоКомитов> --squash

    Потом смотрел изменения в IDE, делал предкомитовые review, потом комитил и пушил на сервер.

    Из плюсов — можно весь код, все изменения посмотреть в IDE. Иногда бывает удобнее, чем через web UI, который на GitHub.


    Осторожно rebase может менять hash комита и приводить к конфликтам слияний, особенно если над одной веткой трудятся несколько человек.

    Хочу написать о двух случаях использования rebase:

    1. Когда изменения включаются из одну ветку в другую не посредством merge, а посредством rebase:

      # git rebase "another_branch"
      

      Это позволяет ваши локальные комиты поставить после всех комитов, которые были внесены в ветку «another_branch». Хэши ваших комитов изменятся.
    2. Когда можно руками отредактировать несколько ваших комитов — например склеить их, изменить коментарий:

      # git rebase -i {HEAD~_commit_count_|commit_hash}
      

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

    Итак вы все сделали в своей уютненькой веточки и решили поделиться этим комитом с миром, но мир хочет от вас только один комит. `git rebase -i ` запустит редактор и предложит отредактировать комиты (порядок следования комитов — сверху вниз в отличие от git log). Можно оставить комит как есть, можно изменить комментарий, можно склеить с предыдущим. Как правило ваш первый комит надо оставить как есть, а все остальные изменить на
    pick "commit_hash" "comment" → fixup "commit_hash" "comment".

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

    Но если процесс разработки был долог, то скорее всего вам приходилось делать merge основной ветки. И все ваши комиты перемешаются с общими комитами и склеивать ваши с не вашими будет задачей непростой. Поэтому перед тем, как делать `git rebase -i <>` стоит сделать `git rebase`. `git rebase` поставит все ваши комиты в конец списка всех комитов (в чем можно убедиться запустив `git log`) и после этого запустиь `git rebase -i <HEAD~Количесво_ваших_комитов>`, во всех строчках кроме первой заменить pick → {fixup|squash} и вуаля — у вас один комит.

    Если в процессе редактирования комитов `git rebase -i <>` вы как-то накосячили, то не стоит жать Control+C — exit code выхода из редактора git не волнует. Он просто возьмет файл и сделает все по нему. Просто удалите или закомментируйте все строчки в файле. git поймет, что вы ничего не хотели.

    После манипуляций с rebase потребуется push с опцией -F. Все это потому, что мы переписываем меняем историю комитов и git нас об этом честно предупреждает.
    # git push -f

    Пример использования git rebase
    Вдогонку напишу что у меня получилось. Дано — 2 ветки — master и b1. В процессе работы мы сделали комиты:

    Initial commit - ветка master - 2fbbe67
    b1.1 - ветка b1 - 85eac43
    master after b1.1 - ветка мастер b505f18
    b1.2 - ветка b1 - 2d7d4ea
    +1 - ветка master - 8dcef6c


    Комиты в master и b1 были сделаны независимо. Написал в один список, чтобы был понятен порядок в котором все делалось. Вот список комитов в каждом бранче:

    # git checkout master && git log
    8dcef6c "+1"
    b505f18 "master after b1.1"
    2fbbe67 "Initial commit"

    # git checkout b1 && git log
    2d7d4ea "b1.2"
    85eac43 "b1.1"
    2fbbe67 "Initial commit"


    Делаем git merge master в b1

    # git checkout b1 && git merge master && git log
    5383781 "Merge branch 'master' into b1"
    8dcef6c "+1"
    2d7d4ea "b1.2"
    b505f18 "master after b1.1"
    85eac43 "b1.1"
    2fbbe67 "Initial commit"


    Добавился новый синтетический комит

    Теперь делаем rebase

    # git checkout b1 && git rebase master && git log
    7f18e47 "b1.2"
    6fb80cb "b1.1"
    8dcef6c "+1"
    b505f18 "master after b1.1"
    2fbbe67 "Initial commit"


    Обратите внимание — наши локальные комиты встали в конец списка, номера комитов изменились, синтетический комит исчез.

    Ну и напоследок склеиваем наши комиты в один

    # git rebase -i HEAD~2


    Файл было

    pick 6fb80cb b1.1
    pick 7f18e47 b1.2


    Файл стало после нашего редактирования

    pick 6fb80cb b1.1
    fixup 7f18e47 b1.2


    Получилось

    # git checkout b1 && git log
    9062cd7 "b1.1"
    8dcef6c "+1"
    b505f18 "master after b1.1"
    2fbbe67 "Initial commit"

    Такие дела
    • +6
    • 49.3k
    • 8
    Share post

    Similar posts

    Comments 8

      +9

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

        0

        Можно не писать fixup, squash, rename и pick при интерактивном rebase. Достаточно указать первую букву

          0
          «Сам я в этой теме не очень разбираюсь и не люблю ее использовать» — потрясающее начало статьи.
            0

            Не пользуюсь никогда, так и не узнал из статьи зачем им пользоваться. Для сквошей делаю:


            git reset --soft '@{u}'
            git add -all
            git commit -m 'my comment'
              0
              Насколько я понимаю git reset --soft '@{u}' сработает только если не делать merge upstream ветки. Ну то есть делаешь ветку, делаешь в нее 100500 комитов, никаких merge. Потом склеил и отдал.
                0
                Почему? Пока что он у меня и с мерджами работал. Не будет интеллектуального резолва конфликтов? Ну дык надо будет смотреть что коммитишь потом, собствено вообще всегда надо сомтреть что коммитишь. А то многие добавляют всё и коммитят, а там куча мусора.
              +1

              Имх, за force push в публичный репозиторий руки отрывать надо.

                0
                Ну во-первых если в свою уютненькую веточку, то почему нет?
                Во-вторых, а почему нет. Я по ошибке вмержил бранч из 19 комитов вида «ай, забыл запятую и пробел». Ну простите, кому это интересно. Предупредил всех, зарибейзил, запушил. Все смеются все довольны.

              Only users with full accounts can post comments. Log in, please.