Переезд проекта с SVN на Git

    image
    Много лет подряд в качестве системы контроля версий для большого количества проектов использовали только SVN. Но наступил момент, когда количество разработчиков на одном из проектов заметно увеличилось, проект уже запущен в работу, и нужно как активно разрабатывать параллельно несколько фич, так и фиксить уже имеющиеся баги в оперативном режиме. Единый trunk в SVN не позволяет этого делать, а организация бранчей в нем же превращает жизнь разработчиков в ад. Поэтому было принято решение о переезде этого проекта с SVN на Git.

    1. Сервер для центрального репозитория


    Несмотря на то, что Git — система распределенная, это не отменяет необходимости наличия центрального репозитория, с которым в конечном итоге будут сихронизироваться все разработчики, а также с которого будут разворачиваться как тестовые сборки, так и будет производиться деплой на production. Поэтому нам необходим в первую очередь сервер. Поскольку проект коммерческий, то хранить исходники на чужих серверах как-то не очень хотелось, значит надо поднять свой сервер для хранения git-репозиториев. Сам сервер работает на Gentoo, поэтому нам нужно поставить на него весь необходимый софт.

    Здесь выбор особо не велик — gitosis либо gitolite. Поскольку gitosis уже не разрабатывается несколько лет как, то выбор пал на gitolite.

    1.1. Установка gitolite

    Ставим gitolite 3.03 на сервер:
    $ emerge gitolite
    

    При этом создается пользователь git, который и будет владеть всеми будущими репозиториями.

    1.2. Первичная настройка

    Теперь нам нужно сгенерировать rsa-ключ для доступа к админскому репозиторию (gitolite хранит все настройки в git-репозитории) и сохранить публичный ключ в общедоступном месте:
    $ ssh-keygen -t rsa
    $ cp ~/.ssh/id_rsa.pub /tmp/admin.pub
    

    После этого можно собственно инициализировать gitolite:
    $ su git
    $ cd
    $ mkdir -p bin
    $ gitolite/install -ln
    $ gitolite setup -pk /tmp/admin.pub
    

    1.3. Создание репозитория для проекта

    Сервер установлен, возвращаемся в своего пользователя и клонируем себе репозиторий с конфигами:
    $ cd
    $ git clone git@server:gitolite-admin.git
    

    Здесь и далее server — это hostname сервера, на котором установлен gitolite и хранятся репозитории.

    Открываем появившийся файл gitolite-admin/conf/gitolite.conf и добавляем в конец описание репозитория для нашего проекта (пока только с одним пользователем):
    repo project
        RW+          = javer
    

    После этого сохраняем наши изменения. Находясь в gitolite-admin, выполняем:
    $ git add .
    $ git commit -am "Repository for project added"
    $ git push origin master
    

    gitolite автоматически проинициализирует все репозитории, которые описаны в конфиге и еще не существуют.

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

    2. Импорт проекта из SVN


    Непосредственно преобразование SVN-репозитория в Git осуществляется с помощью команды
    $ git svn clone
    

    Для этого git должен быть собран с поддержкой perl.

    2.1. Определение стартовой ревизии

    Наш проект Project находится в SVN репозитории наряду с множеством других проектов. Поскольку номера ревизий сквозные на весь репозиторий, то находим в svn log первый коммит, который касается именно нашего проекта, и запоминаем. Это нужно для ускорения импорта, чтобы не сканировались все ревизии, начиная с первой. В нашем случае это ревизия 19815, поэтому к команде выше добавляется опция:
    -r19815:HEAD
    

    2.2. Соответствие SVN-пользователей с Git-пользователями

    Далее нам необходимо составить соответствие SVN-пользователей с будущими Git-пользователями, чтобы они при импорте успешно заменились. Для этого где-нибудь создаем файл authors примерно такого содержания:
    javer = javer <javer@domain.tld>
    developer1 = developer1 <developer1@domain.tld>
    ...
    

    Где user@domain.tld — это e-mail git-пользователя (в git-е каждый пользователь идентифицируется именем и электропочтой).

    Соответственно, к команде импорта добавляется опция:
    --authors-file=/path/to/authors
    

    2.3. Исключение ненужных файлов

    Едем дальше. В нашем проекте были случайные коммиты больших бинарных файлов, которые в новом репозитории нам не нужны. При импорте их можно исключить опцией:
    --ignore-paths="\.(avi|mov)$"
    

    2.4. Дополнительные опции

    Также нам нужен пользователь в SVN, от имени которого будет производиться доступ к репозиторию:
    --username javer
    

    Добавляем еще опцию --no-metadata, которая нужна для того, чтобы в логе коммитов в каждом комментарии не было добавлений вида:
    git-svn-id: svn://svn.domain.tld/repo/project/trunk@19815 e13dc095-444b-fa4e-8f24-06838a8318a5
    

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

    2.5. Клонирование проекта из SVN-репозитория

    Собираем все вместе и запускаем:
    $ cd
    $ mkdir project && cd project
    $ git svn clone -r19815:HEAD --authors-file=/path/to/authors --ignore-paths="\.(avi|mov)$" --username javer --no-metadata svn://svn.domain.tld/repo/project/trunk .
    

    Где svn://svn.domain.tld/repo/project/trunk — адрес trunk-а нашего проекта в SVN-репозитории.

    Начинается процесс клонирования, длительность которого зависит от количества коммитов и их объема. В нашем случае было около 4.5 тыс. коммитов, и на их клонирование понадобилось около двух часов.

    2.6. Исключение более ненужных файлов и каталогов

    По завершении клонирования в нашем каталоге project мы получаем полный клон проекта со всей историей коммитов. После клонирования может внезапно обнаружиться, что мы склонировали также какой-то каталог, который в новом репозитории нам не нужен, например, потому что мы выделим его в отдельный репозиторий. Удалить каталог и все упоминания о нем в истории можно так:
    $ git filter-branch --tree-filter 'rm -rf unneeded_directory' -f HEAD
    

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

    2.7. Удаление пустых коммитов

    В результате всех предыдущих действий мы получили наш склонированный проект со всей историей коммитов, среди которых теперь имеются пустые коммиты, то есть коммиты без единого измененного файла. Они появились в результате исключения некоторых файлов через опцию ignore-paths, либо же из-за последующей фильтрации через tree-filter. Для удаления таких пустых коммитов делаем:
    $ git filter-branch --commit-filter 'git_commit_non_empty_tree "$@"' HEAD
    

    Эта операция занимает примерно столько же времени, как и tree-filter.

    2.8. Пустые каталоги и svn:ignore

    Далее, нам необходимо сконвертировать бывшие svn:ignore в новые .gitignore. Это делается так:
    $ git svn create-ignore
    

    Не забываем, что git не хранит информацию о каталогах, только о файлах, поэтому во всех пустых каталогах нужно создать пустой файл .gitignore, после чего закоммитить все эти файлы:
    $ git add .
    $ git commit -am "Added .gitignore"
    

    2.9. Удаление упоминания об SVN

    Поскольку наш проект переезжает с SVN на Git окончательно, то удаляем всяческие упоминания об SVN:
    $ git branch -rd git-svn
    $ git config --remove-section svn-remote.svn
    $ git config --remove-section svn
    $ rm -rf .git/svn
    

    2.10. svn:externals

    В нашем проекте некоторые symfony-плагины, как кастомные, так и публичные, были подключены через svn:externals. Поскольку в git такой механизм отсутствует, будем использовать submodules для этого. С публичными плагинами проще:
    $ git submodule add git://github.com/propelorm/sfPropelORMPlugin.git plugins/sfPropelORMPlugin
    $ git submodule add git://github.com/n1k0/npAssetsOptimizerPlugin.git plugins/npAssetsOptimizerPlugin
    

    Со своими плагинами чуть сложнее — для них нужно создать отдельные репозитории аналогично описанному выше, после чего точно также подключить к нашему проекту:
    $ git submodule add git@server:customPlugin.git plugins/customPlugin
    

    После подключения submodules их необходимо склонировать в каталог проекта:
    $ git submodule update --init --recursive
    $ git commit -am "Added submodules: sfPropelORMPlugin, npAssetsOptimizerPlugin, customPlugin"
    

    2.11. Отправка локальной копии проекта на сервер

    Перед отправкой нашего проекта на сервер для ускорения этой операции оптимизируем его:
    $ git gc
    

    Подключаем к проекту наш новый репозиторий:
    $ git remote add origin git@server:project.git
    

    И, наконец, заливаем локальную копию проекта на сервер:
    $ git push origin master
    

    2.12. Обновление submodules в будущем

    Поскольку в отличие от svn:externals каждый submodule указывает на конкретный коммит, то при простом обновлении локальной копии через
    $ git pull
    

    содержимое submodules обновляться не будет. Их обновление производится следующим образом:
    $ git submodule update
    

    В случае, если были изменения:
    $ git submodule foreach git pull
    $ git commit -am "Updated submodules"
    


    3. Настройка прав доступа к репозиторию


    3.1. Пользовательские ключи

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

    3.1.1. Linux/Unix

    В случае Linux/Unix или Git bash под Windows это делается так:
    $ ssh-keygen -t rsa
    

    После чего полученный публичный ключ ~/.ssh/id_rsa.pub передается админу репозитория.

    3.1.2. Windows

    В случае Windows также можно воспользоваться puttygen, который можно скачать здесь: puttygen.

    Запускаем puttygen, нажимаем Generate, возим мышкой по окну, пока ключ не будет сгенерирован, потом в поле комментария указываем к чему этот ключ, вводим пароль доступа к ключу при необходимости. После этого копируем содержимое поля Public key, сохраняем в файл user.pub и передаем админу репозитория.

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

    Также в меню Conversions выбираем пункт Export OpenSSH key и сохраняем его в файл под названием C:\Users\USERNAME\.ssh\id_rsa, где USERNAME — имя вашего пользователя в системе. Этот ключ нам будет нужен при использовании git из командной строки.

    3.2. Настройка прав доступа

    Полученные на предыдущем шаге публичные ключи пользователей помещаем в админский репозиторий в каталог ~/gitolite-admin/keydir/ в файлы с названиями USERNAME.pub, где USERNAME — имя пользователя.

    Поскольку gitolite имеет достаточно широкие возможности по настройке, используем их для настройки прав доступа к репозиторию нашего проекта. Для этого редактируем файл ~/gitolite-admin/conf/gitolite.conf и приводим его к виду:

    @owners = javer
    @project_developers = user1 user2 user3
    @deploy = root@production

    repo project
    - master$ = @project_developers
    - refs/tags = @project_developers
    RW+ = @project_developers @owners
    R = @deploy

    Этим мы даем полный доступ для группы пользователей owners. Для группы project_developers — также полный доступ с возможностью создания своих веток, за исключением записи в ветку master и создания тегов. Для группы deploy, которая используется для деплоя на продакшн, разрешаем доступ только для чтения.

    В конце не забываем сохранить все изменения:
    $ git add .
    $ git commit -am "New users for project: user1, user2, user3..."
    $ git push origin master
    


    4. Установка и настройка на машинах разработчиков


    Серверная часть полностью готова, теперь остается установить и настроить git-клиент на машинах разработчиков.

    4.1. Linux/Unix


    Тут все просто — устанавливаем git с помощью своего любимого менеджера пакетов.

    После установки не забываем указать свое имя и e-mail, такие же, которые использовались при импорте из SVN:
    $ git config --global user.name "javer"
    $ git config --global user.email "user@domain.tld"
    

    4.2. Windows


    Здесь существует несколько различных клиентов, я пока остановился на TortoiseGit.

    Перед его установкой сначала нужно установить msysgit, желательно самой последней версии, несмотря на надпись Beta. Во время установки на вопрос об интеграции в систему я советую выбирать пункт Git Bash and Command prompt, чтобы можно было запускать git как из Git bash, так и с командной строки.

    После этого устаналиваем сам TortoiseGit. Я советую устанавливать последнюю стабильную версию, но не nightly-build.

    Теперь заходим в настройки TortoiseGit (правой клик по любому каталогу и TortoiseGit->Settings), находим там раздел Git и справа в блоке User Info вписываем свои имя и e-mail.

    5. Переезд завершен. Приступаем к работе


    Все, на этом процедура переезда с SVN на Git завершена.

    Каждый разработчик клонирует себе на машину проект и начинает с ним работать.

    Я бы посоветовал разработчикам ознакомиться с этими статьями:
    Метки:
    Поделиться публикацией
    Комментарии 51
    • 0
      А Github или Bitbucket в качестве сервера почему не захотели использовать?
      • +9
        Потому что в будущем планируем постепенно все проекты переводить на git, а у нас их порядка сотни. Плюс у каждого еще свои svn:externals, которые тоже превращаются в отдельные репы. При таком количестве репозиториев стоимость размещения на стороне растет экспоненциально. К тому же локальный сервер для разработки все равно есть, зачем ему простаивать.
        • +4
          почему не рассматривали mercurial?
          • 0
            Рассматривали, но каких-то значительных преимуществ против git для нас не нашли. А поскольку большая часть разработчиков так или иначе уже имела дело с гитом раньше, то выбор стал очевиден.
        • 0
          Стоимость размещения на bitbucket зависит от размера команды, кол-во репозиториев и место неограничено. Просто для информации, ваш выбор не критикую.
      • +2
        А разве tmp не очищается при перезагрузке? В любом случае, как-то неправильно в tmp хранить то, что постоянно нужно.
        • 0
          Для таких данных есть каталог /var
          • +1
            В том-то и дело, что этот ключ по тому пути больше не нужен. При запуске gitolite setup он этот ключ перекладывает себе в keydir/. Поэтому сразу после setup этот файл вообще можно удалить.
            • +1
              А зачем тогда «сохранить публичный ключ в общедоступном месте»?
              • +2
                Имелось ввиду временно сохранить по короткому пути, чтоб потом набирать меньше букв и чтобы gitolite смог его прочитать.
                • 0
                  Вы же gitolite запускаете от того же пользователя, от которого создаете файл. Это в большинстве случаев гарантирует, что он сможет прочитать этот файл.

                  Между фразами «в общедоступном месте» и «чтобы gitolite смог его прочитать» есть большая разница. Лучше в статье поправьте, а то получается совсем не тот смысл, который вы имели в виду.
          • –2
            А что насчет AD в gitolite?
            • +1
              Только ключи, если использовать gitolite второй версии. В третьей версии система модульная, можно взглянуть на документ sitaramc.github.com/gitolite/http.html. Просто стандартный метод аутентикации — ssh. При использовании smart http она перекладывается на веб-сервер, который может взаимодействовать с чем угодно для аутентикации пользователей.

              Резюмируя, gitolite не занимается аутентикацией, но только авторизаций: sitaramc.github.com/gitolite/auth.html.
            • +1
              Ставим gitolite 3.03 на сервер:
              $ emerge gitolite


              Понт засчитан!

              А вообще огромное спасибо за статью! В свое время долго промучился с настройкой gitosis (под тем-же дистрибутивом), а точнее с интеграцией его с redmine.
              Этой статьей вы мне не только открыли глаза на мою проблему с устаревшим gitosis, но и дали простое решение.

              Если не секрет, какой багтрекер интегрировали с gitolite и насколько это трудно?
              • +3
                Не автор статьи, но отвечу.

                Мы интегрировали redmine. Начиная с версии 0.9 и до текущей 1.4 все работает прекрасно. Из особенностей:
                • репозитории должны располагаться на одном сервере с redmine (либо можно клонировать на него специальных хуком при пуше)
                • у сервера, который будет запускать redmine должны быть права на доступ к директории с репозиториями
                • для удобства желательно добавить в репозитории хук, который будет сообщать redmine об изменениях (иначе redmine прийдется постоянно обращаться к репозиториям)


                В качестве сервера для запуска использовали fastcgi-обертку в lighttpd и redmine/public/dispatch.fcgi.

                Если будет интересно и на хабре это не описано, могу написать статью об этом.
                • +1
                  Используем Redmine. Он с коробки поддерживает Git, репозиторий подключался по инструкции: www.redmine.org/projects/redmine/wiki/RedmineRepositories#Setting-up-a-mirror-repository-shortcut-tracking-branches

                  Обновление локальной копии и подтягивание в redmine производится по крону с некоторой периодичностью, пока этого хватает.
                  • +3
                    Можно использовать post-recieve/post-update хук такого вида:

                    #!/bin/bash
                    
                    REDMINE_KEY=
                    REDMINE_SERVER=
                    
                    curl http://${REDMINE_SERVER}/sys/fetch_changesets?key=${REDMINE_KEY}
                    


                    Для этого, естественно, необходимо сгенерировать ключ и разрешить работу веб-сервиса управления хранилищами в настройках redmine.
                  • +2
                    Кстати, рекомендую заодно с gitolite посмотреть gitlabhq.
                  • +4
                    Хотелось бы добавить, что для сохранения директорий обычно в листовых создают .gitkeep, а не .gitignore, последний обычно используется по прямому назначению.

                    Также стоит упомянуть, что gitolite 3 ещё относительно сырой, Sitaram Chamarty его недавно выложил в публичный доступ. С другой стороны, поломать что-либо там довольно сложно, т. к. вся конфигурация и сами репозитории хранятся, как нормальные bare-repo.
                    • +1
                      Популяризации такого использования .gitignore способствует github:

                      Sometimes an empty .gitignore file is used as a placeholder for an empty path, for example to force git to generate a log/ path for your development environment to use.
                      ©github:help
                      • +3
                        Использование .gitkeep, как и .gitignore с этой целью — конвенции. Лично я предпочитаю использовать .gitignore по его прямому предназначению.

                        На эту тему можно глянуть stackoverflow.com/questions/7229885/gitignore-vs-gitkeep
                    • +1
                      В литературу можно добавить ещё Git Book: git-scm.com/book
                      • +1
                        Скажите, пожалуйста, какие преимущества может дать Git в плане merge'a функционала в основную ветку? Ведь как в SVN нужно разгребать конфликты после каждого merge'a (если они есть), зато эти конфликты видны сразу; так и в Git при push'e могут возникнуть конфликты, только в этом случае вы заливаете не маленькое изменение (как в SVN), а целую фичу. И не факт, что после этого push'a и исправления конфликтов предыдущая функциональность не разъедется…
                        • 0
                          После ручного мержа — тоже не факт. Тестирование никто не отменял.
                          • +1
                            Я и не спорю, просто вопрос возник после прочтения первого абзаца. Вот и хочу узнать, чем так хорош Git в плане merge'a кучи фич в одну ветку.
                          • +1
                            Ключевая разница в том, что в SVN, чтобы проверить работоспособность фичи в ветке в последних актуальных условиях (голова транка), нужно либо замержить ветку в транк, что в нашем случае не вариант, либо замержить транк в ветку, проверить работоспособность, после чего слияние с транком будет _очень_ болезненным, потому что теперь все изменения транка с момента создания ветки будут самостоятельными изменениями ветки, и в момент слияния ветки фичи с транком мы получим целую кучу конфликтов, потому что одни и те же файлы в тех же местах одновременно менялись и в ветке, и в транке.
                            • 0
                              Согласен, ветка на фичу — не выход из ситуации. Но чем плохо merge'ить в trunk всем разработчикам в процессе выполнения фич?
                              • +2
                                Объясню на примере. Например, программисту в процессе выполнения задачи нужна верстка какого-то блока. Чтобы передать работу верстальщику, ему нужно закоммитить частично рабочий код (ведь задача не закончена) в транк, чтобы его увидел верстальщик и мог что-то сделать. Внимание! С этого момента в транке находится частично работающий код.
                                Верстальщик может быть сейчас занят другой задачей либо выполнение необходимых правок требует значительного времени (дни). Теперь представим ситуацию, что другой программист в это время закончил выполнение задачи, его работа проверена и должна быть вылита на продакшн. Прямо сейчас. Либо же на продакшене был обнаружен критический баг, который требует немедленного исправления. А транк поломан, деплоить нельзя. Что делать?
                                • 0
                                  1) Как на вашем примере поможет Git? Программист зальет частично работающий код… куда? В копию репозитория верстальщика? Это возможно?
                                  2) Что делать? Заливать в trunk закомментаренный код. Верстальщик его раскомментарит, поработает. Когда закончит, зальет в trunk. Гемор, согласен. Возможно, ваш ответ на первый пункт прояснит ситуацию, как сделать без этого гемора.
                                  • +2
                                    1. Программист коммитит в свою ветку, пушит на сервер, верстальщик подключает себе эту ветку, сливает в локальную копию, и теперь они вместе работают в ветке фичи, попеременно коммитят и пушат в эту ветку, не затрагивая транк. Задача может выполняться неделями, ведь они никому не мешают, и одновременно взаимодействуют друг с другом, при этом нет никаких ожиданий и простоев.

                                    2. Программист внес изменения в 10 файлов, закоммитил в транк, чтобы у верстальщика работал тот функционал, для которого нужно сделать верстку. Добавим сюда, что программист не один, например их десять. Часть взаимодействует друг с другом, часть с верстальщиками. И все они закоммитили частично нерабочий код, чтобы напарник мог сделать свою часть. Теперь нужно срочно исправить баг. Начинать в логе судорожно искать коммиты, которые они сделали, проходить по кругу и делать опрос, у кого что работает, а что нет, поверить на слово не проверив, отменить изменения в сотнях файлов, при этом не ошибившись ни в одной строчке, чтобы внести на продакшн небольшое исправление, чтобы потом снова вернуть все назад? Это очень удобно)
                                    • 0
                                      Уже ясней. Тогда такой вопрос: отдельная ветка в SVN не решает проблему первого пункта аналогично Git?
                                      • +2
                                        Решает ровно до тех пор, пока эту ветку не надо будет окончательно тестить и вливать в транк, о чем я уже выше писал. Вот тогда и почувствуется разница между SVN и Git.

                                        И, да, к первому пункту я забыл добавить, что если оба разработчика живут под линуксом и у них настроена авторизация на компы друг друга, то возможно прямое подключение между двумя локальными репозиториями, в этом случае один будет удаленной копией для второго, и они могут взаимодействовать полностью локально, без необходимости частого пуша на сервер. И когда фича будет завершена, можно отредактировать историю, объединив, например, 100 коммитов с пустыми комментариями в 10-20 осмысленных, если конечно в этом будет необходимость. Ведь философия Git — коммить как можно чаще, чтобы в любой момент можно было вернуться к предыдущему микросостоянию, ведь именно для этого и нужна система контроля версий.

                                        За то недолгое время, как мы переехали на Git и добили раз и навсегда master до рабочего состояния, у меня еще ни разу не возникало мысли «сейчас нельзя задеплоить на продакшн, потому что...». Потому что причины больше нет. В мастере всегда рабочий код. А работа по разработке новых фич кипит пуще прежнего.
                                        • 0
                                          У нас эта тема тоже обсуждалась и мы пришли вот к какому затыку.
                                          Представим, что у нас есть рабочий trunk. Начинается разработка двух новых фич параллельно двумя разработчиками Алисой и Бобом.
                                          Допустим Алиса первая закончила свою фичу, заливает ее в trunk, в итоге мы имеем рабочую фичу в trunk'e. Вроде все пока нормально.
                                          И тут приходит очередь заливать свой код Бобу. Мало того, что он мог поменять код, который меняла и Алиса, так еще их изменения могут носить ортогональный характер! То есть либо Бобу надо изменять свою логику, либо менять логику кода Алисы. В итоге получаем, что все те разработчики, которые заливают после Алисы, должны разгребать конфликты от предыдущих разработчиков, так еще и заливают они не один маленький участок кода, а целую фичу! Ну протестирует Боб свой код. Как он может быть уверен, что он не сломает код Алисы, когда зальет в trunk?

                                          Надеюсь, проблему ясно описал. :)
                                          • 0
                                            Тут на помощь снова приходят локальные репозитории гита. Разработчик после завершения своей фичи может локально замержить свою ветку в мастер, если в процессе мержа будут проблемы — он сразу об этом узнает. И тут же он проверяет, все ли ок. Если нет — откатывает мерж и правит код, после чего снова мержит. Если код в мастере сильно поменялся за время выполнения задачи — лучше вообще сделать rebase относительно головы мастера, чтобы код гарантированно ничего не сломал после мержа в мастер, но это только если над фичей работает один человек. При этом все эти изменения происходят локально на машине разработчика, он никому не мешает, пока доводит все до ума.
                                            В результате всего этого разработчик в конечном итоге коммитит исправление в свою ветку, которое позволяет безболезненно замержить потом его ветку в мастер. Сам разработчик мержить в удаленный мастер не может, это порезано на уровне прав доступа к центральному репозиторию. Для этого есть техлид или другое доверенное лицо, ответственное за то, что будет вылито на продакшн.
                                            При этом в истории мастера мы видим чистые мержи фичи1, фичи2 и т.д., без коммитов вида «правка фичи2, чтобы она работала с фичей1».
                                            • 0
                                              > Тут на помощь снова приходят локальные репозитории гита. Разработчик после завершения своей фичи может локально замержить свою ветку в мастер, если в процессе мержа будут проблемы — он сразу об этом узнает.

                                              В меркуриале я бы наоборот мастер замержил в свою ветку. Потом бы протестировал и профиксил баги в результате мержа. И только потом свою ветку безболезненно замержил в мастер. В Гите делается по другому?
                                              • 0
                                                Нет, всё аналогично. Только смысл иметь два мержа подряд вместо одного? Хотя, в этом плане git лучше — он сделает fast-forward и будет всё равно, кто в кого влился.
                                                • +1
                                                  Если изменения в своей ветке были не большие, то конечно не имеет. Просто если мерж ломает программу, то ее лучше профиксить в своей ветке, чем в мастере. Пусть уж будет лишний коммит в своей ветке (пусть даже не рабочий), но зато в мастере будет только один коммит, причем профиксенный и рабочий.
                              • –2
                                в SVN, чтобы проверить работоспособность фичи в ветке в последних актуальных условиях (голова транка), нужно либо замержить ветку в транк, что в нашем случае не вариант, либо замержить транк в ветку, проверить работоспособность, после чего слияние с транком будет _очень_ болезненным, потому что теперь все изменения транка с момента создания ветки будут самостоятельными изменениями ветки

                                В транк, естественно, мержить не надо. Но не вижу причин не синхронизировать функциональную ветку с транком. Это основной совет как раз для безболезненного слияния с транком.
                                • 0
                                  потому что теперь все изменения транка с момента создания ветки будут самостоятельными изменениями ветки, и в момент слияния ветки фичи с транком мы получим целую кучу конфликтов
                                  Правильно я понимаю, что SVN просто «не помнит» о том, что он влил транк в ветку, в отличие от Git? Отсюда все дальнейшие проблемы?
                                  • 0
                                    Я думаю тут проблема в совокупности факторов. Во-первых, svn действительно не в курсе, что куда вливалось, для него это просто коммит каких-то изменений. Хотя в последних версиях он в комментариях обозначает, до какой ревизии было влито, но видимо это его не спасает. Плюс разница еще и в способах организации данных хранилища и способах мержа.
                                  • 0
                                    Использую «svnmerge.py merge -bs» для мержа в SVN между транком и веткой. Работает замечательно в обе стороны. Что я делаю не так?

                                    www.orcaware.com/svn/wiki/Svnmerge.py
                                • 0
                                  А вот если разработка в SVN продолжается? Грубо говоря форкается SVN проект под GIT. Форкнуть-то не проблема, но вот вот потом подтасиквать изменения из апстрима как? Вернее даже они подтаскиваются (в консоли что-то идёт), но вот заливать на пшерги нечего почему-то. Никак врубиться не могу. С Mercurial подобная схема работает на ура, а вот с гитом видимо какой-то нюанс не уяснил.
                                  • +1
                                    Пока апстрим свн-а не отключен, можно все изменения из него подтягивать через git svn rebase, при этом появляются все недостающие коммиты. Правда для этого нужно было делать git svn clone без опции --no-metadata, потому что git svn ищет последнюю подтянутую ревизию в комментариях истории коммитов.
                                    • 0
                                      Спасибо, попробую. Опцию не использовал.
                                      • 0
                                        Нашёл другой способ. После начального git svn clone, делаю git svn fetch , при этом внезапно (для меня) изменения стягиваются в ветку git-svn, с которой уже работаю как с обычной веткой в частности git merge git-svn.

                                        Столкнулся правда с тем, что если реп залить на гитхаб, то настройки giit-svn не сохраняются, нужно в клонах гитхабовского репа их ручками вписывать, но при этом всё довольно прозрачно работает.
                                    • 0
                                      GitLab не пробовали?
                                      • 0
                                        Уже в двух фирмах проводил ту же операцию, использовал:
                                        1) GitLab для визуализации реп
                                        2) авторизация по логину/паролю от GitLab, а не ключу
                                        3) во 2ой конторе GitLab ставился рядом с уже работающим gitolite
                                        • 0
                                          Флейм:
                                          • 0
                                            Простите, ошибся веткой.
                                            P.S. shoutout to habra usability :(
                                          • +1
                                            Нашел на сайтике способ, как сгенерировать список пользователей
                                            svn log -q | awk -F '|' '/^r/ {sub("^ ", "", $2); sub(" $", "", $2); print $2" = "$2" <"$2">"}' | sort -u > authors-transform.txt
                                            

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