Entendendo a diferença de ramificação entre SVN e Git

44

Sou usuário do SVN e agora estou aprendendo o Git.

No SVN, geralmente faço check-out na máquina local de um repositório, que inclui todas as ramificações do meu projeto e eu costumava selecionar a pasta da minha filial na qual me interessava e trabalhava lá.

Eu vejo uma diferença usando o Git.

Atualmente, estou clonando um repositório e clonando um ramo específico usando o gitk.

A pasta do projeto contém apenas o conteúdo desse ramo e não consigo ver todos os ramos como no SVN, o que é um pouco confuso para mim.

Não consigo encontrar uma maneira fácil de ver todas as ramificações no meu repositório local usando o Git.

  • Eu gostaria de saber se o processo Git que descrevi é "padrão" e, de certa forma, está correto ou está faltando alguma coisa.

  • Também gostaria de saber como lidar com um processo em que preciso trabalhar em duas ramificações ao mesmo tempo, caso, por exemplo, precise fazer um hotfix no master, mas também mantenha o conteúdo de outra ramificação.

  • O que é uma convenção de nome de recomendação para tornar as pastas que incluem a ramificação clonada do repositório no Git, por exemplo myproject-branchname?

Radex
fonte
8
Interessante - geralmente com o SVN, você só verificaria o tronco ou o ramo em que está trabalhando, mas entendo o que você quer dizer.
HorusKol
20
Você interrompeu o fluxo de trabalho no SVN, agora tenta espelhá-lo no Git (enquanto um bom fluxo de trabalho SVN se aplica apenas parcialmente ao Git). Melhor solução - esquecer todos SVN-hábitos e começar no Git a partir do zero
preguiçoso Badger
6
git cloneé mais parecido svnadmin hotcopyou svnrdump+ svnadmin loaddo que é svn checkout. Com git, você não solicitar pedaços do repositório; você copia o repositório inteiro e trabalha com ele localmente, enviando alterações para o repositório "fonte" quando e se lhe apetecer. Git e Subversion usam dois modelos completamente diferentes para controle de origem.
chepner
1
sobre a coisa hotfix, considere o usogit-flow
Bakuriu
5
@LazyBadger, não é um fluxo de trabalho interrompido se facilitar o desenvolvimento e fornecer valor. Não existe uma maneira correta de usar ramificações.
dietbuddha

Respostas:

67

Sou usuário do SVN e agora estou aprendendo o GIT.

Bem-vindo à gangue!

Reeducação SVN

Em SVN eu normalmente [...]

Espere um momento. Embora o CVS e o SVN e outro sistema de controle de versão tradicional (ou seja, centralizado) atendam (principalmente) ao mesmo objetivo que os sistemas de controle de versão modernos (ou seja, distribuídos), como mercurial e Git, será muito melhor aprender o Git desde o início, em vez de tentando transferir seu fluxo de trabalho SVN para o Git.

http://hginit.com ( vista sobre archive.org ) por Joel Spolsky (um dos próprios fundadores da Stack Exchange) é um tutorial para mercurial, não Git, mas é de zero-th capítulo, "Subversion Re-educação" é útil para pessoas de comutação longe de SVN para qualquer sistema de controle de versão distribuído, como ele diz o que conceitos SVN você tem que (temporariamente) un -Saiba (ou para stashlonge , como os usuários do Git poderia dizer) para ser capaz envoltório sua cabeça em torno do distribuída conceitos de sistema de controle de versão e fluxos de trabalho idiomáticos estabelecidos para trabalhar com eles.

Assim, você pode ler o capítulo zero e, em geral, apenas substituir a palavra "mercurial" por "Git" e, assim, preparar adequadamente você e sua mente para o Git.

A boa impressão

(Você pode ignorar isso por enquanto.) Enquanto mercurial e Git são muito mais semelhantes entre si do que para SVN, não são algumas diferenças conceituais entre eles, para algumas das declarações e conselhos em "Subversion Re-educação" se tornará tecnicamente errado ao substituir "mercurial" por "Git":

  • Enquanto o mercurial rastreia e armazena internamente os conjuntos de alterações, o Git rastreia e armazena internamente as revisões (ou seja, estados do conteúdo de uma árvore de diretórios), assim como o Subversion. Mas, além do Subversion, o Git realiza mesclagens observando as diferenças entre cada ramo envolvido, respectivamente, e uma revisão ancestral comum (uma verdadeira mesclagem de 3 pontos); portanto, o resultado é o mesmo do mercurial: mesclar é muito mais fácil e com menos erros. -prone do que no SVN.
  • Enquanto você pode ramificar no Git clonando o repositório (como é habitual no mercurial), é muito mais comum criar uma nova ramificação dentro de um repositório Git. (Isso ocorre porque as ramificações do Git são simplesmente (movendo) ponteiros para as revisões, enquanto as ramificações mercuriais são rótulos permanentes aplicados a todas as revisões. Esses rótulos permanentes geralmente são indesejados, portanto os fluxos de trabalho mercuriais geralmente funcionam clonando o repositório completo para desenvolvimento divergente.

No SVN, tudo é um diretório (mas você não deve necessariamente tratá-lo como tal)

Mas eu tenho interrompido você. Você estava dizendo?

No SVN, geralmente faço check-out na máquina local de um repositório, que inclui todas as ramificações do meu projeto e eu costumava selecionar a pasta da minha filial na qual me interessava e trabalhava lá.

Se, com isso, você quer dizer que fez check-out da pasta raiz do repositório SVN (ou qualquer outra pasta correspondente a mais do que a trunkou a uma única ramificação, por exemplo, no layout de repositório de SVN convencional, a branchespasta que contém todas as ramificações que não são de tronco) Ouso dizer que você provavelmente usou SVN errado (ish), ou pelo menos abusou um pouco do fato de que tronco, ramificações e tags são todos dobrados em um único espaço de nome (embora hierárquico), juntamente com diretórios nas revisões / base de código.

Embora possa ser tentador alterar várias ramificações SVN em paralelo, esse não é o AFAIK, como a SVN deve ser usada. (Embora não tenha certeza sobre quais desvantagens específicas funcionariam dessa maneira.)

No Git, apenas diretórios são diretórios

Cada clone de um repositório Git é ele próprio um repositório Git: Por padrão, ele obtém uma cópia completa do histórico do repositório de origem, incluindo todas as revisões, ramificações e tags. Mas manterá tudo do nosso jeito: o Git o armazena em um tipo de banco de dados, localizado na .git/subpasta oculta da pasta raiz do repositório .

Mas quais são os arquivos não ocultos que você vê na pasta do repositório?

Quando você é git cloneum repositório, o Git faz várias coisas. Aproximadamente:

  1. Ele cria a pasta de destino e nela a .git/subpasta com o "banco de dados"
  2. Ele transfere as referências (tags, ramificações etc.) do banco de dados do repositório de origem e faz uma cópia delas no novo banco de dados, dando a eles um nome ligeiramente modificado que os marca como referências "remotas".
  3. Ele transfere todas as revisões (ou seja, estados da árvore de arquivos) para as quais essas referências apontam, bem como todas as revisões para as quais essas revisões apontam direta ou transitivamente (seus pais e ancestrais) para o novo banco de dados e as armazenam, para que as novas referências remotas realmente aponte para algo que está disponível no novo repositório.
  4. Ele cria uma filial local que rastreia a revisão remota correspondente à filial padrão do repositório de origem (geralmente master).
  5. Ele verifica a filial local.

Essa última etapa significa que o Git consulta a revisão apontada por esse ramo e descompacta a árvore de arquivos armazenada lá (o banco de dados usa alguns meios de compactação e desduplicação) na pasta raiz do repositório. Essa pasta raiz e todas as suas subpastas (excluindo a .gitsubpasta especial ) são conhecidas como "cópia de trabalho" do seu repositório. É aí que você interage com o conteúdo da revisão / filial com check-out atualmente. Eles são apenas pastas e arquivos normais. No entanto, para interagir com o repositório em si (o "banco de dados" de revisões e referências), você usa gitcomandos.

Vendo ramificações Git e interagindo com ramificações Git

Atualmente, estou clonando um repositório e clonando um ramo específico usando o gitk.

A versão gitkque obtive não pode clonar repositórios. Ele só pode exibir o histórico do repositório, criar ramificações e fazer check-out de ramificações. Além disso, não há "clonagem" de um ramo. Você pode clonar apenas repositórios.

Você quis dizer que você clonar um repositório usando git clone ...e, em seguida, usando gitk, confira um ramo?

A pasta do projeto contém apenas o conteúdo desse ramo e não consigo ver todos os ramos como no SVN, o que é um pouco confuso para mim.

[...]

  • Eu gostaria de saber se o processo git que descrevi é "padrão" e, de certa forma, [...]

Sim, isso é bastante padrão:

  • Clone um repositório usando git clone ...
  • Confira o ramo em que você deseja trabalhar git checkout ...ou use uma ferramenta gráfica comogikt

Não consigo encontrar uma maneira fácil de ver todas as ramificações no meu repositório local usando o GIT.

  • [...] ou estou sentindo falta de smt.

Talvez:

  • você pode listar filiais locais com git branch
  • você pode listar ramificações remotas com git branch -rou todas as ramificações comgit branch -a
  • você pode usar gitkpara visualizar o histórico completo (todas as tags de filiais etc. que seu representante local conhece) invocando-o com

    gitk --all
    

Como trabalhar com várias ramificações em paralelo

  • Também gostaria de saber como lidar com um processo em que preciso trabalhar em duas ramificações ao mesmo tempo, caso, por exemplo, precise fazer um hotfix no master, mas também mantenha o conteúdo de outra ramificação.

Existem diferentes cenários aqui:

Uma nova alteração (ainda a ser criada) precisa ser aplicada a várias ramificações

Use este fluxo de trabalho:

  1. Crie um novo ramo a cpartir de uma revisão que já esteja na ancestralidade de todos esses ramos (por exemplo, a revisão que introduziu o bug quando a alteração for uma correção de bug) ou a partir de uma revisão que (incluindo todos os seus ancestrais) seja aceitável para ser introduzida em todos esses ramos.
  2. Faça e confirme a alteração nesse novo ramo c.
  3. Para cada filial bque precisa da mudança:

    1. Confira b:

      git checkout b
      
    2. Mesclar cem b:

      git merge c
      
  4. Remover ramo c:

    git branch --delete c
    

Uma mudança existente é necessária em uma ramificação diferente

(... mas sem as outras alterações feitas no local em que essa alteração reside)

  1. Confira a filial onde a alteração é necessária
  2. Escolha as revisões que estão fazendo a alteração, a fim de

Em uma ramificação a, você deseja alterar um ou alguns arquivos para o estado exato que eles têm em uma ramificação diferenteb

  1. Verificação de saída a
  2. Obtenha o conteúdo do arquivo da filial b:

    git checkout b -- path/to/a/file.txt path/to/another/file.cpp or/even/a/complete/directory/ ...
    

    (Além de git checkoutnão passar caminhos, isso não muda para ramificação b, apenas obtém o conteúdo do arquivo solicitado a partir dali. Esses arquivos podem ou já não existir a. Se existirem , serão substituídos pelo conteúdo b.)

Ao trabalhar em uma ramificação, você deseja ver como estão as coisas em outra ramificação

Confira o ramo em que você deseja trabalhar.

Então, por olhar para o outro ramo,

  • use ferramentas gráficas que permitem visualizar o conteúdo de revisões que não estão com check-out no momento (por exemplo, na gitktentativa de alternar os botões de opção de "patch" para "árvore")
  • ou clonar o repositório em um diretório temporário e verificar a outra ramificação lá
  • ou use git worktreepara criar um diretório de trabalho separado do mesmo repositório (ou seja, também usando o banco de dados no .git/diretório do seu repositório local atual), onde você pode verificar essa outra ramificação
das-g
fonte
Para o último fluxo de trabalho, stashé ideal.
CAD97
@ CAD97, não se você quiser continuar trabalhando no primeiro ramo, enquanto olha para o segundo para referência em paralelo . git stashé bom afastar o trabalho inacabado, por exemplo, para alternar entre ramificações e depois voltar.
das-g
1
"fluxos de trabalho mercuriais geralmente funcionam ao clonar o repositório completo para desenvolvimento divergente" - Ah, eu já ouvi falar disso, mas a maioria das pessoas que desejam ramos Git apenas usa marcadores em vez de clonar todo o repositório. É muito mais fácil
21417 Kevin
@ Kevin Isso pode ser. Há algum tempo atrás eu usei mercurial pela última vez, então talvez fosse diferente naquela época. (Ou talvez fosse apenas os fluxos de trabalho da comunidade eu usei-o com que fosse assim.)
das-g
Talvez o texto Hg Init "[...] porque você realmente deveria estar ramificando o caminho do Mercurial, clonando repositórios [...]" também deveria ser atualizado nesse sentido.
das-g
31

No SVN, geralmente faço check-out na máquina local de um repositório, que inclui todas as ramificações do meu projeto e eu costumava selecionar a pasta da minha filial na qual me interessava e trabalhava lá.

A menos que você verifique o diretório acima do tronco, no Subversion você geralmente não possui todas as ramificações disponíveis localmente. No Git, todo o conteúdo (confirmações, mensagens, ramificações etc.) é sempre clonado em todas as cópias.

Atualmente, estou clonando um repositório e clonando um ramo específico usando o gitk.

Não é possível, veja acima. Você pode git checkout branch-name, mas você não pode git clone something branch-name. No Subversion, ramificações são pastas. No Git, as ramificações são ponteiros para um commit.

Não consigo encontrar uma maneira fácil de ver todas as ramificações no meu repositório local usando o GIT.

Corra git branch. Por padrão, ele mostra apenas ramificações locais. Use git branch --remotepara ver ramificações remotas.

l0b0
fonte
4
Hum. "Por padrão, mostra apenas filiais locais." Parece que existem outros tipos de ramos que não são locais. Mas você também diz "No Git, todo o conteúdo (confirmações, mensagens, ramificações etc.) é sempre clonado em todas as cópias". . Acho isso um pouco confuso, porque se todos os ramos são clonados na sua cópia, quais são esses ramos não locais misteriosos? Talvez seja muito complicado responder em um campo de comentário.
pipe
2
@pipe Quando você confirma alterações, você faz isso criando uma confirmação local, que altera a confirmação para a qual o ramo está apontando. Se você deseja que outras pessoas obtenham essas alterações, é necessário enviá-las para o repositório remoto. Isso resulta na ramificação remota apontando para esse novo commit. Agora todo mundo pode puxar essas mudanças de lá e como resultado todos iriam eventualmente, obter uma cópia completa do repositório
Frozn
9
@ pipe Primeiro, você pode de fato clonar apenas um ramo usando --single-branch(mesmo apenas a ponta --depth 1). A diferença entre ramificações locais e remotas conhecidas pelo git é apenas um tipo de rótulo. As ramificações remotas são prefixadas com o nome da fonte remota (geralmente origin). As ramificações locais não têm esse prefixo. Mas, no final, os dados estão disponíveis localmente (a menos que você tenha feito --single-branchou algo semelhante). Quando você executa git checkout $branchnamecom uma ramificação para a qual não existe nenhuma ramificação local, mas remota, o git configura automaticamente uma ramificação local "rastreando" o controle remoto.
Jonas Schäfer
"A menos que você verifique o diretório acima do tronco" - Minha experiência é que isso é bastante comum, pois facilita a fusão. (Embora nós usamos um único ramo "ao vivo" em vez de marcas de versão, por isso geralmente equivale a apenas 2-3 cópias do código)
Izkata
1
@ Izkata "facilita muito a fusão", não é?
HorusKol
3

Como isso está marcado com , espero que minha falta de conhecimento sobre SVN seja negligenciável.

Atualmente, estou clonando um repositório e clonando um ramo específico usando o gitk.

Você está clonando todo o repositório remoto, não apenas uma ramificação específica.

O repositório é melhor imaginado como um banco de dados, portanto, você está criando um clone do estado atual do banco de dados remoto. Mas depois disso, você está trabalhando em sua própria cópia desse banco de dados; se você confirmar, alterará seu banco de dados local.

O fetchcomando é usado para manter o banco de dados local sincronizado com o remoto.

Geralmente a partir desse banco de dados local, você faz check-out de uma filial para trabalhar. Isso nada mais é do que um marcador interno para o git, onde seu trabalho atual começou.

Digamos que você esteja trabalhando em um repositório simples, onde não há ramificações além disso master, você pode dar uma olhada na .gitpasta para desvendar a "mágica":

Suponha que seu último commit (on master) foi 182e8220b404437b9e43eb78149d31af79040c66, você encontrará exatamente isso em cat .git/refs/heads/master.

git checkout -b mybranchDepois que você ramifica uma nova ramificação , você encontrará exatamente o mesmo ponteiro no arquivo cat .git/refs/heads/mybranch.

Ramos não passam de "ponteiros". O marcador "ativo" é chamado de HEAD.

Se você quiser saber onde HEADestá:

cat .git/HEADque diz ref: refs/heads/mybranch, por exemplo , que, por sua vez, aponta ( cat .git/refs/heads/mybranch) para um hash de confirmação78a8a6eb6f82eae21b156b68d553dd143c6d3e6f

As confirmações reais são armazenadas na objectspasta (como é um tópico próprio).

A pasta do projeto contém apenas o conteúdo desse ramo e não consigo ver todos os ramos como no SVN, o que é um pouco confuso para mim.

Não confunda o working directory"git-database" como um todo. Como eu disse acima, seu diretório de trabalho é apenas um instantâneo de (talvez) um subconjunto.

Digamos que você tenha ramificações diferentes, seu diretório de trabalho é dedicado a trabalhar somente nessa ramificação (embora você possa colocar o trabalho de lá em outro lugar).

Geralmente, se você deseja ver quais ramos são definidos para o projeto, você tem a possibilidade de

  • git branch para filiais locais
  • git branch --remote para filiais remotas
  • git branch -a para ambos

(ou git branch -v)

Como o git é um sistema de controle de versão distribuído, não é apenas possível, mas incentivado, criar diferentes ramificações localmente / remotas.

Meu fluxo de trabalho típico é:

  • ramificar um recurso ramificar
  • ramificar uma ramificação WIP (trabalho em andamento) dessa
  • trabalhe como quiser - mesmo se você confirmar após uma única linha; Não importa

Quando o recurso estiver concluído:

  • esmagar / retrabalhar a WIPramificação (com rebasing interativo) = fazer uma única confirmação a partir dessa
  • mescle a WIPramificação na ramificação de recursos e ofereça que (se você trabalha com o github, essa oferta seria chamada de "solicitação de recebimento") para integrar na ramificação estável (mestre).

Também gostaria de saber como lidar com um processo em que preciso trabalhar em duas ramificações ao mesmo tempo, caso, por exemplo, precise fazer um hotfix no master, mas também manter o conteúdo de outra ramificação.

Depende de como o seu projeto está estruturado:

Digamos que você tenha um mestre estável. E os recursos são desenvolvidos apenas a partir desse ramo estável - portanto, geralmente ele está atrás de um ramo de recurso. Então você teria um último commit no master que seria a raiz do ramo do recurso.

Em seguida, você faria um commit na ramificação principal e poderia decidir mesclar as duas ramificações ou fazer uma nova reformulação (que é uma espécie de mesclagem para usuários com necessidades avançadas, por assim dizer).

Ou você sempre pode fazer alterações nos galhos (por exemplo master) e selecioná-los em outros galhos.

O que é uma convenção de nome de recomendação para tornar as pastas que incluem a ramificação clonada do repositório no GIT, exemplo myproject-branchname

É com você.

Normalmente, você acaba com o nome dos repositórios.

Mas há ocasiões em que isso não é desejado:

por exemplo, você clona oh-my-zsh com git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh Here .oh-my-zshé explicitamente nomeado como o destino.

Thomas Junk
fonte
2

Git clone na verdade, clona o repositório inteiro, mas define seu diretório de trabalho como o padrão (geralmente mestre).

Você pode ver outras ramificações usando git branchpara ver ramificações locais ou git branch -rremotas e depois git checkoutmudar para uma ramificação existente ou criar uma nova com base na ramificação atual.

Você deve ler a documentação do git para obter mais detalhes, e o Atlassian também possui alguns bons artigos.

HorusKol
fonte
0

Ramos no git e svn são coisas fundamentalmente diferentes.

No svn, um ramo (ou uma tag) é um diretório no repositório.

No git, um branch (ou uma tag) é um ponteiro para um commit.

Com o svn, você pode se desejar fazer o checkout da raiz do repositório. Isso significa que você tem todas as ramificações e tags verificadas de uma só vez. Afirmar que esta não é a maneira normal de usar svn.

Uma ramificação git não é um diretório; portanto, não há como "verificar a raiz do repositório". Há algum suporte para várias árvores de trabalho, então eu acho que você pode montar um script para fazer check-out de todos os ramos ao mesmo tempo, se você realmente quiser, mas seria um pouco incomum pensar nisso.

Além disso, com o SVN, há exatamente um repositório. Com o git, todo desenvolvedor tem seu próprio repositório. Isso significa que os desenvolvedores podem trabalhar offline, mas também significa que diferentes desenvolvedores podem ter uma idéia diferente do que está em cada filial.

Em virtude de serem diretórios e em virtude do modelo de história linear simples do svn, os ramos do svn têm históricos robustos. Se você quiser saber o que estava na ramificação x na data y, pode facilmente fazer essa pergunta.

Os ramos Git, por outro lado, não têm realmente histórias. Existe o "reflog", mas é mais um mecanismo de recuperação de desastres do que uma história de longo prazo. Em particular, o reflog não pode ser lido remotamente e é desativado por padrão para repositórios nus.

Compromissos, é claro, têm histórias, mas essas histórias não são suficientes para responder à pergunta do "que estava no ramo x do relatório na data z".

Não consigo encontrar uma maneira fácil de ver todas as ramificações no meu repositório local usando o Git.

Você pode listar todas as ramificações locais, digitando "git branch".

Você pode listar todas as ramificações locais e remotas usando "git branch -a"

Também gostaria de saber como lidar com um processo em que preciso trabalhar em duas ramificações ao mesmo tempo, caso, por exemplo, precise fazer um hotfix no master, mas também mantenha o conteúdo de outra ramificação.

Algumas opções.

Você pode confirmar suas alterações no "outro ramo", alternar para mestre, fazer o seu trabalho lá e depois voltar.

Você também pode criar uma árvore de trabalho extra. Google "git-worktree" para obter detalhes sobre a sintaxe.

O que é uma convenção de nome de recomendação para tornar as pastas que incluem a ramificação clonada do repositório no Git, por exemplo, myproject-branchname?

Eu não acho que exista uma convenção estabelecida. Fazer o check-out de várias árvores de trabalho ao mesmo tempo é a exceção, não a regra.

Peter Green
fonte
2
esta resposta parece incompleta, por quê?
Gnat