Enviando um repositório Git existente para o SVN

385

Venho fazendo todo o meu trabalho no Git e pressionando para o GitHub. Fiquei muito feliz com o software e o site, e não quero mudar minhas práticas de trabalho neste momento.

Meu orientador de doutorado está pedindo a todos os alunos que mantenham seu trabalho em um repositório SVN hospedado na universidade. Eu encontrei toneladas de documentação e tutoriais sobre como baixar um repositório SVN existente no Git, mas nada sobre enviar um repositório Git para um repositório SVN novo. Espero que haja alguma maneira de fazer isso com uma combinação de git-svn e um novo ramo, rebasing e todos esses termos maravilhosos, mas sou um novato no Git e não me sinto confiante com nenhum deles.

Desejo então executar apenas alguns comandos para enviar confirmações ao repositório SVN quando eu escolher. Desejo continuar usando o Git e ter apenas o espelho do repositório SVN que está no Git.

Serei a única pessoa que se comprometerá com o SVN, se isso fizer alguma diferença.

cflewis
fonte
11
Nota: você provavelmente perderá seus carimbos de data originais ao fazer isso. As novas datas serão baseadas na hora da importação para o Subversion.
Nobar
Para aqueles que procuram informações mais atuais alguns podem encontrar o útil seguinte pós durante a sua pesquisa para automação: deliciousbrains.com/deploying-wordpress-plugins-travis
Josh Habdas

Respostas:

403

Eu também precisava disso, e com a ajuda da resposta de Bombe + algumas brincadeiras, consegui que funcionasse. Aqui está a receita:

Importar Git -> Subversion

1. cd /path/to/git/localrepo
2. svn mkdir --parents protocol:///path/to/repo/PROJECT/trunk -m "Importing git repo"
3. git svn init protocol:///path/to/repo/PROJECT -s
4. git svn fetch
5. git rebase origin/trunk
5.1.  git status
5.2.  git add (conflicted-files)
5.3.  git rebase --continue
5.4.  (repeat 5.1.)
6. git svn dcommit

Após o nº 3, você receberá uma mensagem enigmática como esta:

Usando um nível mais alto de URL: protocol:///path/to/repo/PROJECT => protocol:///path/to/repo

Apenas ignore isso.

Quando você executa o número 5, pode ter conflitos. Resolva-os adicionando arquivos com o estado "não imerso" e retomando a rebase. Eventualmente, você estará pronto; sincronize novamente com o repositório SVN, usandodcommit . Isso é tudo.

Mantendo repositórios sincronizados

Agora você pode sincronizar do SVN para o Git, usando os seguintes comandos:

git svn fetch
git rebase trunk

E para sincronizar do Git para o SVN, use:

git svn dcommit

Nota final

Você pode tentar isso em uma cópia local, antes de aplicar em um repositório ativo. Você pode fazer uma cópia do seu repositório Git para um local temporário; basta usar cp -r, pois todos os dados estão no próprio repositório. Em seguida, você pode configurar um repositório de teste baseado em arquivo usando:

svnadmin create /home/name/tmp/test-repo

E verifique uma cópia de trabalho usando:

svn co file:///home/name/tmp/test-repo svn-working-copy

Isso permitirá que você brinque com as coisas antes de fazer alterações duradouras.

Adendo: Se você errar git svn init

Se você executar acidentalmente git svn initcom o URL errado e não for inteligente o suficiente para fazer um backup do seu trabalho (não pergunte ...), não poderá simplesmente executar o mesmo comando novamente. No entanto, você pode desfazer as alterações emitindo:

rm -rf .git/svn
edit .git/config

E remova a seção de [svn-remote "svn"]seção.

Você pode executar git svn initnovamente.

Troelskn
fonte
2
Boa resposta. Isso também atrapalha as datas de confirmação?
de Drew Noakes
4
Boas perguntas - Infelizmente, também não sei a resposta. Este é mais um guia prático do que achei funcionar. Eu não entendo completamente todos os detalhes. Em relação às datas de confirmação, acho que você poderia fazer um teste e descobrir. Lembre-se de que você pode iniciar um repositório svn local (baseado em fs), para testar as coisas.
troelskn
10
Eu segui essas mesmas etapas usando "git rebase --onto trunk --root" no lugar da etapa 5 e tive muito mais sucesso. Apenas um punhado de conflitos de mesclagem a serem resolvidos, em vez de toneladas.
Kubi
5
No meu caso, essa sequência não funcionou. Sempre uma mensagem "Não é possível determinar as informações de SVN upstream do histórico HEAD" era exibida. Portanto, não é possível comprometer.
Fedir RYKHTIK
2
Deixa pra lá, tentei ser inteligente e omiti o -s, pois não queria seguir a configuração padrão do SVN (trunk / branches / tags /). Não foi possível fazê-lo funcionar sem isso.
22612 James McMahon
33

Veja como o fizemos funcionar:

Clone seu repositório Git em algum lugar da sua máquina.

Abra .git / config e adicione o seguinte (em Mantendo um espelho SVN somente leitura de um repositório Git ):

[svn-remote "svn"]
    url = https://your.svn.repo
    fetch = :refs/remotes/git-svn

Agora, em uma janela do console, digite:

git svn fetch svn
git checkout -b svn git-svn
git merge master

Agora, se ele quebrar aqui por qualquer motivo, digite estas três linhas:

git checkout --theirs .
git add .
git commit -m "some message"

E, finalmente, você pode se comprometer com o SVN:

git svn dcommit

Nota: Eu sempre rasco essa pasta depois.

Alex Rouillard
fonte
6
+1 isso realmente funcionou para mim (não têm tronco / base qualquer que seja), em contraste com outras respostas que continuou dandoUnable to determine upstream SVN information from HEAD history.
Stijn
2
Eu tentei essa técnica, mas ela não importou o histórico. Btw, "git merge master" é agora "git merge - mestre de histórias não-relacionadas-anteriores"
Berend de Boer
11
Se você deseja absolutamente substituir o conteúdo do SVN pelo git, use git merge -s recursive -Xtheirs --allow-unrelated-histories master.
user1475814
28

Usar git rebasediretamente perderá o primeiro commit. O Git trata diferente e não pode refazê-lo.

Existe um procedimento que preservará o histórico completo: http://kerneltrap.org/mailarchive/git/2008/10/26/3815034

Vou transcrever a solução aqui, mas os créditos são para Björn.

Inicialize o git-svn:

git svn init -s --prefix=svn/ https://svn/svn/SANDBOX/warren/test2

O --prefix fornece ramificações de rastreamento remoto como "svn / trunk", o que é legal porque você não recebe nomes ambíguos se chamar a ramificação local apenas como "trunk". E-s é um atalho para o layout padrão de troncos / tags / ramos.

Busque o material inicial do SVN:

git svn fetch

Agora, procure o hash do seu commit raiz (deve mostrar um único commit):

git rev-list --parents master | grep '^.\{40\}$'

Em seguida, obtenha o hash do tronco vazio commit:

git rev-parse svn/trunk

Crie o enxerto:

echo <root-commit-hash> <svn-trunk-commit-hash> >> .git/info/grafts

Agora, o "gitk" deve aparecer svn/trunkcomo o primeiro commit no qual sua ramificação principal se baseia.

Tornar o enxerto permanente:

git filter-branch -- ^svn/trunk --all

Solte o enxerto:

rm .git/info/grafts

O gitk ainda deve aparecer svn/trunkna ancestralidade do mestre.

Linearize seu histórico sobre o tronco:

git svn rebase

E agora "git svn dcommit -n" deve lhe dizer que ele irá se comprometer com o tronco.

git svn dcommit
Peter Mortensen
fonte
Você pode explicar como essa técnica é diferente da acima, de forma mais clara.
cmcginty
3
Quando tento "git rev-parse svn / trunk", ele relata uma revisão desconhecida ou o caminho que não está na árvore de trabalho.
23612 Adam Ness
Esta é a única resposta que funcionou para mim, exceto as etapas que o git filter-branch e o drop graft não eram necessários: fiz uma reformulação após criar o enxerto e git svn dcommit.
Fc7
8

Crie um novo diretório no repositório Subversion para o seu projeto.

# svn mkdir --parents svn://ip/path/project/trunk

Mude para o seu projeto gerenciado pelo Git e inicialize o git-svn.

# git svn init svn://ip/path/project -s
# git svn fetch

Isso criará uma única confirmação porque o diretório do projeto SVN ainda está vazio. Agora, refaça tudo o que foi confirmado git svn dcommite você deverá concluir. Isso vai atrapalhar seriamente suas datas de confirmação.

Bombe
fonte
Eu usei esta resposta com as instruções em hassox.blogspot.com/2007/12/using-git-with-svn.html Eu segui esses comandos e, em seguida, "#git branch -a" para ver o nome do tronco. Então: # git checkout -b local-svn trunk # git merge master # git svn dcommit Lembre-se de .gitignore no diretório .svn!
22909 cflewis
Como acabei de fazer a mesma operação, queria deixar explícito que já há algum tempo (janeiro '09), o git pode executar uma operação de rebase na confirmação raiz. Isso torna o processo muito mais simples do que um monte de artigos antigos indicam, ver comentários sobre its.arubything.com/2009/1/4/...
Louis Jacomet
O que a opção git svn init "-s" faz? Não vejo isso nas páginas de manual do git svn.
18711 Nathan
Leia a página de manual novamente, talvez pesquise "-s" porque está lá. É um alias para "--stdlayout".
Bombe
7

Git -> SVN com histórico completo de consolidação

Eu tinha um projeto Git e tive que movê-lo para o SVN. Foi assim que eu fiz, mantendo todo o histórico de confirmação. A única coisa que se perde é o tempo de confirmação original, já que o libSVN definirá o horário local quando o fizermos git svn dcommit.

Como:

  1. Tenha um repositório SVN para o qual queremos importar nossas coisas e cloná-lo com git-svn:

    git svn clone https://path.to/svn/repository repo.git-svn`
    
  2. Vá ali:

    cd repo.git-svn
    
  3. Adicione o controle remoto do repositório Git (neste exemplo, estou usando C: /Projects/repo.git ). Você deseja enviar para o SVN e dar o nome old-git:

    git remote add old-git file:///C/Projects/repo.git/
    
  4. Busque as informações da ramificação principal do repositório git antigo para o repositório atual:

    git fetch old-git master
    
  5. Faça o checkout da ramificação principal do controle remoto antigo-git em uma nova ramificação chamada antiga no repositório atual:

    git checkout -b old old-git/master`
    
  6. Rebase para colocar o HEAD em cima do old-git / master. Isso manterá todos os seus commits. O que isso faz basicamente é pegar todo o seu trabalho realizado no Git e colocá-lo em cima do trabalho que você está acessando no SVN.

    git rebase master
    
  7. Agora volte ao seu ramo principal:

    git checkout master
    

    E você pode ver que possui um histórico de consolidação limpo. É isso que você deseja enviar para o SVN.

  8. Envie seu trabalho para o SVN:

    git svn dcommit
    

Isso é tudo. É muito limpo, sem hackers, e tudo funciona perfeitamente fora da caixa. Aproveitar.

codingdave
fonte
Processo semelhante também detalhado em chani.wordpress.com/2012/01/25/…
TWiStErRob 2/17/17
Parece. No entanto, acho o caminho dela bastante confuso e o meu é muito mais curto (8 passos que 19/23). Talvez eu não esteja entendendo corretamente, mas acho que ela mistura os comandos svn e git em sua segunda bala.
Codigo
Ah, sim, a diferença é que seu processo importa o git para a raiz do repositório SVN, o seu importa para uma subpasta, é por isso que as poucas git svnpreparações são necessárias.
TWIStErRob 4/17/17
Na etapa 7, não deveria haver uma fusão de git antiga? Para mim, parece que você está fazendo um compromisso de mestre que não mudou ?!
Alexander Alexander
@Alexander usando o 'git rebase master', produzimos uma mesclagem de avanço rápido, que é uma mesclagem linear sem que um commit de mesclagem tenha dois pais. Queremos ter uma história linear aqui.
codingdave
4

Eu proporia uma instrução muito curta em 4 comandos usando SubGit . Veja este post para detalhes.

Dmitry Pavlenko
fonte
3

Eu precisava comprometer meu repositório Git existente em um repositório SVN vazio.

Foi assim que consegui fazer isso:

$ git checkout master
$ git branch svn
$ git svn init -s --prefix=svn/ --username <user> https://path.to.repo.com/svn/project/
$ git checkout svn
$ git svn fetch
$ git reset --hard remotes/svn/trunk
$ git merge master
$ git svn dcommit

Funcionou sem problemas. Espero que isso ajude alguém.

Como tive que me autorizar com um nome de usuário diferente para o repositório SVN (meu originuso de autenticação de chave pública / privada), tive que usar a --usernamepropriedade

Jay Linski
fonte
pode ser necessário instalar git-svnantes isso é possível, ver stackoverflow.com/questions/527037/git-svn-not-a-git-command
flexponsive
2

Se você quiser continuar trabalhando com o Git como seu repositório principal e apenas precisar "exportar" as revisões para o SVN de tempos em tempos, poderá usar o Tailor para manter o repositório SVN sincronizado. Ele pode copiar revisões entre diferentes sistemas de controle de origem e atualiza o SVN com as alterações feitas no Git.

Eu não tentei uma conversão de Git para SVN, mas para um exemplo de SVN -> SVN, veja esta resposta .

sth
fonte
2

Mais uma sequência que funcionou (com alguns comentários em cada etapa):

  1. Instalar git-svne subversionkits de ferramentas:

    sudo apt-get install git-svn subversion
    
  2. Mudar dentro do PROJECT_FOLDER

    cd PROJECT_FOLDER
    
  3. Crie o caminho do projeto no servidor Subversion (infelizmente o git-svnplugin atual tem um defeito em comparação com o TortoiseSVN). Não é possível armazenar o código-fonte diretamente no PROJECT_FOLDER. Em vez disso, por padrão, ele fará o upload de todo o código PROJECT_FOLDER/trunk.

    svn mkdir --parents protocol: /// caminho / para / repo / PROJECT_FOLDER / trunk -m "criando espaço reservado para o repositório git"

Este é o local onde trunkno final do caminho é obrigatório

  1. Inicialize o git-svncontexto do plug - in dentro da .gitpasta

    git svn init -s protocol:///path/to/repo/PROJECT_FOLDER
    

    Este é o lugar onde trunkno final do caminho é desnecessário

  2. Buscar uma Subversioninformação de repositório vazia

    git svn fetch
    

    Esta etapa está ajudando a sincronizar o servidor Subversion com o git-svnplug - in. É o momento em que o git-svnplug-in estabelece o remotes/origincaminho e o associa à trunksubpasta no lado do servidor.

  3. O rebase de confirmações antigas do Git aconteceu antes do git-svnenvolvimento do plug-in no processo (esta etapa é opcional )

    git rebase origin/trunk
    
  4. Adicione arquivos novos / modificados para confirmar (esta etapa é regular para atividades do Git e é opcional )

    git add .
    
  5. Confirme arquivos adicionados recentemente no repositório Git local (esta etapa é opcional e só é aplicável se a etapa 7 tiver sido usada):

    git commit -m "Importing Git repository"
    
  6. Enviando todo o histórico de alterações do projeto para o servidor Subversion:

    git svn dcommit
    
Oleg Kokorin
fonte
1

Você pode criar um novo repositório SVN. Exporte seu projeto Git (detalhando os arquivos .git). Adicione-o ao repositório SVN (inicializando o repositório com o que você tinha até agora no Git). Em seguida, use as instruções para importar repositórios SVN em um novo projeto Git.

Mas isso perderá seu histórico anterior do Git.

Vasil
fonte
1

Se você não precisar usar nenhum SVN específico e estiver usando o GitHub, poderá usar o conector SVN.

Mais informações estão aqui: Colaborando no GitHub com Subversion

elmariofredo
fonte
SVN o que? In ( "... qualquer SVN específico e ..." ). Versão SVN? Cliente SVN? Ou alguma outra coisa?
Peter Mortensen
1

Gostaria de compartilhar uma ótima ferramenta que está sendo usada na comunidade WordPress chamada Scatter

Plugins do Git WordPress e um pouco de dispersão da sanidade

Isso permite que os usuários possam enviar seu repositório Git para o wordpress.org SVN automaticamente. Em teoria, esse código pode ser aplicado a qualquer repositório SVN.

r109
fonte
O link parece estar efetivamente quebrado ( "O servidor no evansolomon.me está demorando muito para responder." ).
Peter Mortensen
1

Recentemente, tive que migrar vários repositórios Git para o SVN e, depois de tentar todas as soluções que pude encontrar, o que finalmente funcionou para mim foi o Mercurial (sim, usando um terceiro VCS). Usando este guia , criei o seguinte processo (no Linux, mas a idéia básica também deve funcionar no Windows).

  1. Os pacotes necessários:

    $ sudo apt-get install git subversion mercurial python-subversion
    
  2. O Mercurial precisa ser configurado adicionando o seguinte a ~/.hgrc:

    [extensions]
    hgext.convert=
    
  3. Crie alguns diretórios de trabalho temporários (eu tinha vários repositórios para migrar, então criei diretórios para as versões SVN e Git, para mantê-los separados):

    $ mkdir svn
    $ mkdir git
    
  4. Faça um repositório SVN local vazio:

    $ svnadmin create svn/project
    
  5. Clone o repositório Git existente:

    $ git clone server/path/project.git git/project
    
  6. Deixe o Mercurial fazer a sua coisa:

    $ hg convert --dest-type svn git/project svn/project
    
  7. Agora, o repositório SVN deve conter o histórico completo de consolidação, mas não com registros de data e hora originais. Se isso não for um problema, pule a próxima parte para a etapa 11.

  8. Com um pouco de trabalho, a data e hora de cada confirmação podem ser alteradas . Como meus repositórios são pequenos, era possível fazê-lo manualmente. Primeiro, crie um pre-revprop-changegancho no repositório SVN com o seguinte conteúdo, para permitir que a propriedade necessária seja modificada:

    #!/bin/bash
    exit 0;
    

    Este script deve ser tornado executável:

    $ chmod +x svn/project/hooks/pre-revprop-change
    
  9. O Mercurial criou uma cópia de trabalho do repositório SVN, chamada projeto -wc, portanto, alterne para ele e edite os tempos de confirmação:

    $ cd project-wc
    $ svn propedit svn:date --revprop -r 1
    

    Digite a data e a hora corretas (preste atenção aos fusos horários!) E salve. Você deve receber uma mensagem dizendo "Definir novo valor para a propriedade svn: date na revisão 1".
    Agora, enxágue e repita para todas as outras revisões.

  10. Opcionalmente, verifique o histórico de confirmação para garantir que tudo esteja correto:

    $ svn log -r 1:HEAD
    

    Volte um nível acima:

    $ cd ..
    
  11. Despejar o repositório:

    $ svnadmin dump svn/project > project.dump
    
  12. E carregue o dump no seu servidor Subversion. Feito!

Esse processo provavelmente também funcionaria diretamente entre repositórios remotos, mas achei mais fácil trabalhar com repositórios locais. Corrigir os tempos de consolidação foi muito trabalhoso, mas no geral o processo foi muito mais direto do que qualquer outro método que encontrei.

Indrek
fonte
1

existem três métodos:

  1. rebase: como as outras respostas

  2. commit id: encontre svn primeiro commit id e git primeiro commit id, faça eco em .git / info / grafts: echo "git_id svn_id}" > .git/info/graftsthengit svn dcommit

  3. confira cada commit do git, copie os arquivos para svn_repo, svn commit

demonstração do bash: demonstração do github

v1.x: use rebase e confirme o ID

v2.x: use arquivos de cópia e, em seguida, svn commit

张耀文
fonte
0

No meu caso, tive que iniciar um projeto limpo do SVN

$ Project> git svn init protocol://path/to/repo -s
$ Project> git svn fetch

adicione todas as fontes do seu projeto ...

$ Project> git add .
$ Project> git commit -m "Importing project sources"
$ Project> git svn dcommit
dangt85
fonte
0

Eu só quero compartilhar um pouco da minha experiência com a resposta aceita. Eu fiz todas as etapas e tudo estava bem antes de executar a última etapa:

git svn dcommit

$ git svn dcommit

Uso do valor não inicializado $ u na substituição (s ///) na /usr/lib/perl5/vendor_perl/5.22/Git/SVN.pm linha 101.

Uso do valor não inicializado $ u na concatenação (.) Ou string no /usr/lib/perl5/vendor_perl/5.22/Git/SVN.pm linha 101. refs / remotes / origin / HEAD: ' https://192.168.2.101/ svn / PROJECT_NAME 'não encontrado em' '

Encontrei o thread https://github.com/nirvdrum/svn2git/issues/50 e, finalmente, a solução que apliquei no arquivo a seguir na linha 101 /usr/lib/perl5/vendor_perl/5.22/Git/SVN.pm

Eu troquei

$u =~ s!^\Q$url\E(/|$)!! or die

com

if (!$u) {
    $u = $pathname;
} 
else {
       $u =~ s!^\Q$url\E(/|$)!! or die
      "$refname: '$url' not found in '$u'\n";
}

Isso corrigiu meu problema.

Pavel Slepiankou
fonte
-1

E se você não quiser comprometer todos os commit que fizer no Git, no repositório SVN? E se você apenas quiser enviar seletivamente confirmações pelo canal? Bem, eu tenho uma solução melhor.

Eu mantenho um repositório Git local onde tudo o que faço é buscar e mesclar no SVN. Dessa forma, posso garantir que estou incluindo todas as mesmas alterações que o SVN, mas mantenho meu histórico de consolidação separado do SVN por completo.

Em seguida, mantenho uma cópia local de trabalho SVN separada, que está em uma pasta separada. É o que eu faço confirmado no SVN e simplesmente uso o utilitário de linha de comando do SVN para isso.

Quando estou pronto para confirmar o estado do meu repositório Git local no SVN, simplesmente copio toda a bagunça de arquivos na cópia de trabalho local do SVN e o confirmo a partir daí usando o SVN em vez do Git.

Dessa forma, eu nunca tenho que fazer nenhum rebaseamento, porque rebasear é como base livre.

CommaToast
fonte