Migrar vários repositórios svn para um único repositório git

4

Queremos migrar do svn para o git permanentemente para poder usar os melhores recursos do git em termos de ramificação e colaboração.

Nosso atual repositório svn se parece com isso

svnrepo/
   frontend/
      trunk
      branches/
         ng/
         ...
      tags/
         1.x
         ...
   backend/
      trunk
      branches/
         ng/
         ...
      tags/
         1.x
         ...

O layout de trabalho é que nós verificamos o projeto do front-end e dentro dele, criamos uma pasta de back-end e finalizamos o projeto de back-end.

Agora queremos migrar para o git e desistir da divisão entre frontend e backend (em termos de projetos separados), porque isso nos dá mais problemas do que vantagens. Queremos que ambos estejam em um único repositório git.

Eu queria usar o svn2git para a conversão. Infelizmente o último desenvolvimento aconteceu em um branch, e não no trunk, mas acho que isso não deveria ser um problema para o svn2git. Então, o novo layout do repositório git deve ficar assim:

/            => svnrepo/frontend/branches/ng
/backend     => svnrepo/backend/branches/ng

Onde = > significa "migrado / convertido de".

Para a conversão, não é necessário convertermos todas as tags e ramificações do svn repository over to git. Isso não é importante para nós. O importante, no entanto, é que temos o histórico completo de todos os commits para todos os arquivos no diretório branches / ng, voltando à ramificação a partir do trunk e todos os commits que aconteceram no trunk antes disso. E queremos que todos esses commits estejam com o layout mencionado em um único repositório git. Isso é possível? E como faríamos isso?

Eu já pesquisei com o google e também em stackoverflow 1 , 2 mas não conseguiu encontrar uma solução exata para o nosso problema.

    
por Shyru 20.08.2012 / 14:09

3 respostas

2

Uma solução seria gerar cada um dos repositórios separadamente com svn2git ou apenas git svn (é uma pequena ferramenta que já foi construída no git), e então conectá-los com git filter-branch .

  1. Clona cada repositório svn individualmente.
  2. No repositório que você quer ser root, adicione os outros repositórios como remotos e busque suas ramificações que você deseja mesclar para esse repositório (você receberá avisos já que as ramificações não têm histórico comum; isso é esperado). / li>
  3. Execute git filter-branch nesses novos ramos, usando um filtro de índice para gerar um novo subdiretório para eles.
  4. Mesclar as ramificações filtradas em master (ou qualquer ramificação desejada) no repositório raiz. A história completa seria preservada.

O comando para o passo 3 seria algo como isto:

git filter-branch --index-filter '
    git ls-files -s |
    perl -pe "s{\t\"?}{$&newsubdir/}" |
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
    mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE
' HEAD

A mágica, e toda vez que eu tenho que fazer isso, parece um pouco mágica, é a declaração perl . git filter-branch está filtrando o índice a cada confirmação e prefixando todos os caminhos de blob (ou seja, alterando os caminhos de arquivo da árvore de trabalho) com 'newsubdir'. Você pode ter que experimentar para obter os caminhos exatamente certos. Algumas lições aprendidas de alguém que está percorreu este caminho antes:

  • Faz backup de tudo. git filter-branch é um histórico destrutivo. Depois de alterá-lo, você não poderá alterá-lo facilmente. Faça backup de todas as cópias do repositório que você está usando. Nada é pior do que terminar uma operação complexa e descobrir que você perdeu um / no caminho.
  • Rotule tudo. A menos que você tenha alguma habilidade séria; você não vai conseguir acertar da primeira vez. Roteiro cada passo individual como você concluí-lo, de modo que a execução de qualquer um deles é fácil. Além disso, se você descobrir que uma semana depois você errou uma bandeira, você pode replicar em instantes.
  • Gaste R $ 20 em uma instância de computação de cluster no EC2. git filter-branch é extremamente intensivo em CPU. Um filtro de índice em um histórico profundo pode levar horas para ser executado em seu ambiente local, mas uma fração desse tempo em um cluster da AWS exemplo. Claro, eles custam um pouco mais de $ 2 por hora, mas você só vai precisa de um por algumas horas. Salve-se de dor e use os scripts que você escreveu em hardware que tornam a operação trivial. Custa o preço de um bom almoço.
por 20.08.2012 / 14:33
0

Uma das soluções é converter os dois repositórios de projetos SVN em 2 repositórios Git e, em seguida, adicionar um repositório Git como um Submodule Git de outro.

Para converter seu repositório SVN em repositórios Git, você pode usar qualquer script baseado em git-svn ou SubGit . Com a última ferramenta você executa um único comando

$ subgit install path/to/svn/repository

Os repositórios git convertidos estarão no caminho / para / svn / repository / git.

Em seguida, você configura um acesso aos dois repositórios Git e adiciona um como submódulo de outro:

$ git clone <frontend_GitURL> frontend
$ git co
$ cd frontend
$ git submodule add -b ng <backend_GitURL> backend
    
por 20.08.2012 / 14:39
0

Tudo o que posso pensar é que isso vai exigir alguma invasão extrema, a menos que svn2git (do qual eu sou não um especialista) nativamente apóie isso de alguma forma.

O problema é que um commit do frontend é completamente independente de um commit no backend . Não há nenhuma maneira real de dizer qual commit irá mapear até qual commit em um único repositório. Isso nos deixa com apenas uma opção real: o histórico consistirá em dois ramos sendo mesclados, o que representa a história do projeto original e, depois que eles forem mesclados, o novo ramo será o "melhor modelo".

A partir de agora, presumiremos que você tem frontend na ramificação svn-frontend importada e backend na ramificação svn-backend importada, e ambas contêm seu próprio histórico.

O primeiro problema é corrigir svn-backend para estar no diretório backend/ :

git checkout svn-backend
git filter-branch --index-filter '
  git ls-files -s |
  perl -pe "s{\t\"?}{$&newsubdir/}" |
  GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
  mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' HEAD

(Veja esta documentação , e oe responder por @Christopher)

Agora, a menos que estes de alguma forma contenham o mesmo commit que uma base (improvável a menos que svn2git crie algum commit base pré-definido ou algo assim ...), nós temos que fazer um. Não importa qual branch você está começando.

git symbolic-ref HEAD refs/heads/svn-base
rm .git/index
git clean -dxf

O Git não pode rastrear diretórios vazios. Eu nunca testei para ver se isso se aplica ao diretório raiz, mas minha suposição não é, portanto, menos crie um arquivo ignorar git vazio e confirme:

touch .gitignore
git add .gitignore
git commit -m "Base for SVN branches"

Vamos reescrever o histórico:

git rebase svn-base svn-frontend
git rebase svn-base svn-backend

Estamos quase terminando. Vamos criar o ramo mestre agora. Se já existe:

git update-ref master "$head"

Caso contrário:

git branch master

Vamos conferir:

git checkout master

Finalmente, a mesclagem:

git merge svn-backend

É uma boa ideia marcar as ramificações antigas e excluí-las:

git checkout svn-frontend
git tag svn-frontend
git branch -d svn-frontend
git checkout svn-backend
git tag svn-backend
git branch -d svn-backend
git checkout master
git branch -d svn-base
    
por 20.08.2012 / 14:39