Корзинка с сюрпризами — cпасите наши push'и

    TLDR: Сделал набор скриптов автоматизации миграции Bitbucket репозиториев с Mercurial на Git.

    В один непрекрасный день мой любимый хостинг репозиториев Bitbucket объявил о скором прекращении поддержки репозиториев Mercurial в пользу Git, после чего все Mercurial репозитории будут удалены.

    image

    На Bitbucket у меня много приватных репозиториев Mercurial, терять их не хотелось, так же, как и их историю коммитов. Переезжать на другой хостинг — также не вариант — к Bitbucket я привык. Сами Bitbucket, как ни странно, не сподобились сделать in-place конвертор. Они даже не написали никакой пошаговой инструкции по конвертированию, отсылая всех на форум своего коммьюнити — с довольно таки иезуитской формулировкой (We are happy to support your migration, and you can find a discussion about available options in our dedicated Community thread) — мол, мы рады переезду ваших репозиториев под Git, а как это сделать — обсудите сами на форуме. Впрочем, в том же посте они оставили ссылки на пару конвертеров hg-fast-export и hg-git.

    Первый из них — отдельный скрипт на Python, второй — плагин, для работы с Git репозиториями непосредственно из Mercurial. В Google нашлось также некоторое количество примеров того, как другие решали задачу перетаскивания Mercurial под Git: вот, вот, и вот. В них также использовался либо hg-fast-export, либо hg-git. Второй был для меня уже проверенным решением — hg-git я раньше иногда пользовался, когда коммитил на Github из Mercurial — пока не понял, что в Git, как это ни странно, лучше все таки коммитить из Git же. Так что для своих целей я выбрал hg-git.

    Тут приведен Powershell-скрипт для автоматизации миграции репозитория, который и стал отправной точкой моего решения, но для Linux мне пришлось переписать его на Bash. Кроме того, оригинальный скрипт только делает миграцию в локальный Git-репозиторий, и я добавил туда возможность запушивания содержимого сконвертированного Git-репозитория в репозиторий на Bitbucket. Вот как выглядит работа полученного скрипта:

    $ ./convert_repo.sh lebedevsergey advertisements_parser
    2a51eee7ade0
    requesting all changes
    adding changesets
    adding manifests
    adding file changes
    added 1 changesets with 5 changes to 5 files
    updating to branch default
    5 files updated, 0 files merged, 0 files removed, 0 files unresolved
    Initialized empty Git repository in /home/serge/project/_probes/hg2git/src/advertisements_parser-git/
    pushing to ../advertisements_parser-git
    searching for changes
    adding objects
    added 1 commits with 2 trees and 5 blobs
    error: Could not remove config section 'remote.origin'
    Branch master set up to track 

    Теперь у меня было готовое решение, но оно требовало ручных действий — перед его запуском надо было предварительно создать на Bitbucket Git-репозиторий, чтобы было куда пушить результат. Поглядев на свои 100500 подлежащих конверсии репозиториев, я понял, что так я долго буду их обрабатывать, хотелось бы чтобы скрипт сам создавал Git репозитории для сконвертированных, а еще лучше — сам получал список моих Mercurial репозиториев и работал по нему. Это явно выходило за рамки умения чистого Mercurial, тут требовался функционал самого Bitbucket, а точнее — Bitbucket API.

    Для работы с Bitbucket API есть готовые библиотеки для разных языков программирования, в частности, для Python — по-видимому официальный SDK (не путать с лежащим на самом Bitbucket давно заброшенным, но когда-то не менее официальным SDK). Есть также Bitbucket API клиенты для Java, NodeJS и PHP. Последний я и выбрал.

    Однако, при его детальном изучении оказалось, что там не реализовано получение списка репозиториев пользователя и создание нового репозитория. К счастью, создатели библиотеки предусмотрели возможность расширения своих классов, сделав закрытые методы protected, так что, унаследовав класс их API-клиента, можно было добавить свои методы, делающие то что мне нужно:

    class ExtendedClient extends Client
    {
        /**
         * @return \Bitbucket\Api\Repositories
         */
        public function repositories()
        {
            return new ExtendedRepositories($this->getHttpClient());
        }
    }
    
    class ExtendedRepositories extends Repositories
    {    
        public function listWorkspace(string $workspaceName, array $params = [])
        {
            $path = $this->buildRepositoriesPath($workspaceName);
            return $this->get($path, $params);
        }
    
        public function create(string $workspaceName, string $repoName, array $params = [])
        {
            $path = $this->buildRepositoriesPath($workspaceName, $repoName);
    
            return $this->post($path, $params);
        }
    }
    

    После чего, я написал пару PHP-скриптов и еще один Bash-скрипт для их запуска, и через час уже мог любоваться на вкалывающий вместо меня компьютер:

    Checking repository: mysett
    Trying to create Git repository: git_mysett
    Created Git repository: git_mysett
    20abecfb36fe
    applying clone bundle from https://api.media.atlassian.com/file/4d5980dc-148f-400c-97f7-8067506778a5/binary?client=403e8d2f-6661-452a-8307-5c68f82c1a13&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3MiOnsidXJuOmZpbGVzdG9yZTpmaWxE2bUg7aRIzuKbjwOn5SF8IbGEVs33WHDVb-JYto
    adding changesets
    adding manifests
    adding file changes
    added 27 changesets with 56 changes to 41 files
    finished applying clone bundle
    searching for changes
    no changes found
    updating to branch default
    35 files updated, 0 files merged, 0 files removed, 0 files unresolved
    Initialized empty Git repository in /home/serge/project/hg2git/src/mysett-git/
    pushing to ../mysett-git
    searching for changes
    adding objects
    added 27 commits with 82 trees and 50 blobs
    error: Could not remove config section 'remote.origin'
    Counting objects: 159, done.
    Delta compression using up to 12 threads.
    Compressing objects: 100% (133/133), done.
    Writing objects: 100% (159/159), 12.95 MiB | 7.95 MiB/s, done.
    Total 159 (delta 0), reused 159 (delta 0)
    To git@bitbucket.org:lebedevsergey/git_mysett.git
     * [new branch]      master -> master
    Branch master set up to track remote branch master from origin.
    Checking repository: jazzz
    Trying to create Git repository: git_jazzz
    

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

    Готовый набор скриптов автоматизации миграции репозиториев находится тут

    P.S.: Пользуясь случаем, подниму риторический вопрос — почему Git, работающий временами с довольно таки неочевидной логикой, настолько популярнее гораздо более логичного Mercurial, в котором просто коммитишь, создаешь ветки, и, если что-то пошло не так, — откатываешь коммиты, не думая о том, что находится под капотом системы контроля версий, и об указателе на Head, а просто делая то, что нужно? На мой взгляд, это несправедливо, ведь очевидное лучше неочевидного, и очень печально, что поддержку Mercurial прекращает Bitbucket — один из столпов сопротивления мейнстриму в системах контроля версий.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 10

      –1
      было интересно, спасибо)
        +2

        Печально, мне меркуриал тоже больше нравился :(

          +1

          А вот у меня беда. У меня репозитории с сабрепами на меркуриале. И совершенно непонятно как их конвертировать, с сохранением связей.

            +1

            На самом деле, все можно было написать на shell. И, да — тоже считаю mercurial удобными, чем git.

              –1
              Мне git понравился тем, что можно делать историю более линейной, без нелепого количества мёрж-коммитов. Или вот вы пишете:
              если что-то пошло не так, — откатываешь коммиты

              Как это «откатываешь»? Локально можно откатить, но что делать с центральным репозиторием? В этом случае нужно не «откатывать», а «накатывать» новый коммит, который откатывает изменения. В итоге у вас в истории остаётся сломанное ненужное состояние. А вот в гите вы просто перемещаете ветку на коммит назад, и всё, нету плохого коммита.

              Ситуация усложняется, когда вы сделали изменения, например, в release бранче, а надо было в develop. Здесь уже и «накатывание отката» не поможет, потому что при слиянии release в develop вы откатите изменения и в develop-е. И вот вам приходится снова какие-то ненужные коммиты делать.
                0

                В меркуриале можно откатить (revert) правки к версии за таким-то номером (Revision), и закоммитить это состояние следующим коммитом. Да, из истории коммитов это не потрешь, тем более, с удаленного репозитория. И, на мой взгляд — это хорошо — видны будут все перипетии работы над кодом. Лично мне не нравится то, что поклонники Git любят использовать возможность править историю коммитов.
                А ветки release и develop — да, лучше не путать.

                  –1
                  За все 6 лет, что я работаю в mercurial эти перипетии работы над кодом только мешали. И теперь, когда я делаю откат в git-е, я точно так-же не задумываюсь о том, что там происходит под капотом. Переношу ветку на новое место, и всё так же очевидно.
                    0
                    видны будут все перипетии работы над кодом

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


                    Лично мне не нравится то, что поклонники Git любят использовать возможность править историю коммитов.

                    Это не прерогатива поклонников Git, поклонники mercurial всегда хвалились MQ, Evolve и подобными расширениями.

                  +1

                  У гитхаб есть импортёр с конвертером. А так как Атлассиан поступили очень гадко, то перенести всё на гитхаб показалось не такой плохой идеей. Перенес личные проекты и проекты компании на гитхаб, теперь платим им, а не битбакету.

                    –1
                    почему Git, работающий временами с довольно таки неочевидной логикой, настолько популярнее гораздо более логичного Mercurial

                    Может потому что как раз с логикой там всё в порядке, а если вы к чему-то привыкли не значит что это единственный и тем более единственно верный вариант того как может быть?


                    Мой опыт работы с mercurial начался с непозволительно медленной для VCS последнего поколения работы с большим репозиторием, сильного удивления от закрытых веток и в итоге попадания в multihead состояние, которое я с ходу не разобрался как разрешить, на том и закончился. Просто как пример того что оценка очевидности субъективна.


                    откатываешь коммиты, не думая о том, что находится под капотом системы контроля версий, и об указателе на Head, а просто делая то, что нужно?

                    Так работают с любой VCS, когда не требуют от неё быть другой VCS.


                    один из столпов сопротивления мейнстриму в системах контроля версий

                    О как. А какой смысл в этом сопротивлении? Последствий кроме усложнения взаимодействия разработчиков я не вижу.

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

                    Самое читаемое