-
1. Primeiros Passos
- 1.1 Sobre Controle de Versão
- 1.2 Uma Breve História do Git
- 1.3 O que é Git?
- 1.4 A Linha de Comando
- 1.5 Instalando o Git
- 1.6 Configuração inicial do Git
- 1.7 Obtendo Ajuda
- 1.8 Resumo
-
2. Fundamentos do Git
-
3. Ramificação (Branching) no Git
-
4. Git no Servidor
- 4.1 Os Protocolos
- 4.2 Colocando o Git em um Servidor
- 4.3 Gerando a sua Chave Pública SSH
- 4.4 Configurando o Servidor
- 4.5 Git Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Opções de Hospedagem de Terceiros
- 4.10 Resumo
-
5. Git Distribuído
-
6. GitHub
-
7. Ferramentas do Git
- 7.1 Seleção de Revisão
- 7.2 Área de Stage Interativa
- 7.3 Fazendo Stash e Limpando
- 7.4 Assinando Seu Trabalho
- 7.5 Pesquisando (Searching)
- 7.6 Reescrevendo o Histórico
- 7.7 Reset Desmistificado
- 7.8 Merging Avançado
- 7.9 Rerere
- 7.10 Depurando com Git
- 7.11 Submódulos
- 7.12 Empacotando (Bundling)
- 7.13 Substituir (Replace)
- 7.14 Armazenamento de Credenciais
- 7.15 Resumo
-
8. Customizando o Git
- 8.1 Configuração do Git
- 8.2 Atributos do Git
- 8.3 Hooks do Git
- 8.4 Uma Política de Exemplo Imposta pelo Git
- 8.5 Resumo
-
9. Git e Outros Sistemas
- 9.1 Git como Cliente
- 9.2 Migrando para o Git
- 9.3 Resumo
-
10. Git Internals (Por Dentro do Git)
- 10.1 Encanamento e Porcelana (Plumbing and Porcelain)
- 10.2 Objetos do Git
- 10.3 Referências do Git
- 10.4 Arquivos de pacote (Packfiles)
- 10.5 Refspec
- 10.6 Protocolos de Transferência
- 10.7 Manutenção e Recuperação de Dados
- 10.8 Variáveis de Ambiente
- 10.9 Resumo
-
A1. Appendix A: O Git em Outros Ambientes
- A1.1 Interfaces Gráficas
- A1.2 Git no Visual Studio
- A1.3 Git no Visual Studio Code
- A1.4 Git no IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.5 Git no Sublime Text
- A1.6 Git no Bash
- A1.7 Git no Zsh
- A1.8 Git no PowerShell
- A1.9 Resumo
-
A2. Appendix B: Incorporando o Git nos seus Aplicativos
- A2.1 Linha de Comando do Git
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Appendix C: Comandos do Git
- A3.1 Configuração e Ajustes
- A3.2 Obtendo e Criando Projetos
- A3.3 Snapshots Básicos
- A3.4 Branches e Merges
- A3.5 Compartilhando e Atualizando Projetos
- A3.6 Inspeção e Comparação
- A3.7 Depuração
- A3.8 Patches
- A3.9 E-mail
- A3.10 Sistemas Externos
- A3.11 Administração
- A3.12 Comandos de Encanamento
9.1 Git e Outros Sistemas - Git como Cliente
O mundo não é perfeito. Normalmente, você não pode mudar imediatamente todos os projetos com os quais entra em contato para o Git. Às vezes, você está preso a um projeto usando outro VCS e gostaria que fosse o Git. Passaremos a primeira parte deste capítulo aprendendo sobre as maneiras de usar o Git como cliente quando o projeto no qual você está trabalhando estiver hospedado em um sistema diferente.
Em algum momento, você pode querer converter seu projeto existente para o Git. A segunda parte deste capítulo aborda como migrar seu projeto para o Git a partir de vários sistemas específicos, bem como um método que funcionará se não houver nenhuma ferramenta de importação pré-criada.
Git como Cliente
O Git oferece uma experiência tão agradável para os desenvolvedores que muitas pessoas descobriram como usá-lo em sua estação de trabalho, mesmo que o resto da equipe esteja usando um VCS totalmente diferente.
Há vários desses adaptadores, chamados "pontes" (bridges), disponíveis.
Aqui, abordaremos aqueles que você tem maior probabilidade de encontrar no mundo real.
Git e Subversion
Uma grande fração de projetos de desenvolvimento de código aberto e um bom número de projetos corporativos usam o Subversion para gerenciar seu código-fonte. Ele existe há mais de uma década e, na maior parte desse tempo, foi a escolha VCS de fato para projetos de código aberto. Também é muito parecido em muitos aspectos com o CVS, que era o garoto grande do mundo do controle de fonte antes disso.
Um dos ótimos recursos do Git é uma ponte bidirecional para o Subversion, chamada git svn.
Essa ferramenta permite que você use o Git como um cliente válido para um servidor Subversion, para que você possa usar todos os recursos locais do Git e, em seguida, faça um push para um servidor Subversion, como se você estivesse usando o Subversion localmente.
Isso significa que você pode fazer ramificações e mesclagens locais, usar a área de preparação, usar rebasing (refazagem) e cherry-picking e assim por diante, enquanto seus colaboradores continuam a trabalhar de maneira escura e antiga.
É uma boa maneira de introduzir o Git no ambiente corporativo e ajudar seus colegas desenvolvedores a se tornarem mais eficientes enquanto você faz lobby para obter a alteração da infraestrutura para suportar o Git completamente.
A ponte Subversion é a porta de entrada para o mundo do DVCS.
git svn
O comando base no Git para todos os comandos de ponte do Subversion é git svn.
Ele recebe vários comandos, portanto, mostraremos o mais comum ao passar por alguns fluxos de trabalho simples.
É importante observar que, quando você usa o git svn, interage com o Subversion, um sistema que funciona de maneira muito diferente do Git.
Embora você possa fazer uma ramificação e mesclagem local, geralmente é melhor manter seu histórico da forma mais linear possível ao refazer seu trabalho (rebase) e evitar interagir simultaneamente com um repositório remoto do Git.
Não reescreva o seu histórico e tente empurrá-lo novamente, e não o empurre para um repositório git paralelo para colaborar com outros desenvolvedores do Git ao mesmo tempo. O Subversion pode ter apenas um histórico linear, e confundi-lo é muito fácil. Se você estiver trabalhando com uma equipe, e alguns usarem SVN e outros usarem Git, certifique-se de que todos usem o servidor SVN para colaborar - fazer isso facilitará sua vida.
Configurando
Para demonstrar essa funcionalidade, você precisa de um repositório SVN típico em que tem acesso de gravação.
Se você deseja copiar esses exemplos, precisará fazer uma cópia gravável de um repositório de teste SVN.
Para fazer isso facilmente, você pode usar uma ferramenta chamada svnsync que vem com o Subversion.
Para acompanhar, você deve primeiro criar um novo repositório local do Subversion:
$ mkdir /tmp/test-svn
$ svnadmin create /tmp/test-svn
Em seguida, ative todos os usuários a alterarem os revprops – a maneira mais fácil é adicionar um script pre-revprop-change que sempre sai (exit) de zero:
$ cat /tmp/test-svn/hooks/pre-revprop-change
#!/bin/sh
exit 0;
$ chmod +x /tmp/test-svn/hooks/pre-revprop-change
Agora você pode sincronizar este projeto com a sua máquina local chamando svnsync init a partir de repositórios e para eles.
$ svnsync init file:///tmp/test-svn \
http://your-svn-server.example.org/svn/
Isso define as propriedades para executar a sincronização. Você pode clonar o código executando:
$ svnsync sync file:///tmp/test-svn
Committed revision 1.
Copied properties for revision 1.
Transmitting file data .............................[...]
Committed revision 2.
Copied properties for revision 2.
[…]
Embora essa operação possa levar apenas alguns minutos, se você tentar copiar o repositório original para outro repositório remoto em vez de um local, o processo levará quase uma hora, mesmo que haja menos de 100 commits. O Subversion deve clonar uma revisão por vez e depois inseri-lo em outro repositório - é ridiculamente ineficiente, mas é a única maneira fácil de fazer isso.
Primeiros Passos
Agora que você tem um repositório Subversion ao qual tem acesso de gravação, pode passar por um fluxo de trabalho típico.
Você começará com o comando git svn clone, que importa um repositório inteiro do Subversion para um repositório local do Git.
Lembre-se de que, se você estiver importando de um repositório do Subversion hospedado de verdade, precisará substituir o arquivo file:///tmp/test-svn aqui pela URL do seu repositório do Subversion:
$ git svn clone file:///tmp/test-svn -T trunk -b branches -t tags
Initialized empty Git repository in /private/tmp/progit/test-svn/.git/
r1 = dcbfb5891860124cc2e8cc616cded42624897125 (refs/remotes/origin/trunk)
A m4/acx_pthread.m4
A m4/stl_hash.m4
A java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
A java/src/test/java/com/google/protobuf/WireFormatTest.java
…
r75 = 556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae (refs/remotes/origin/trunk)
Found possible branch point: file:///tmp/test-svn/trunk => file:///tmp/test-svn/branches/my-calc-branch, 75
Found branch parent: (refs/remotes/origin/my-calc-branch) 556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae
Following parent with do_switch
Successfully followed parent
r76 = 0fb585761df569eaecd8146c71e58d70147460a2 (refs/remotes/origin/my-calc-branch)
Checked out HEAD:
file:///tmp/test-svn/trunk r75
Isso executa o equivalente a dois comandos – git svn init seguido de git svn fetch – na URL fornecida.
Isso pode demorar um pouco.
Se, por exemplo, o projeto de teste tiver apenas cerca de 75 commits e a base de código não for tão grande, o Git ainda deve verificar cada versão, uma por vez, e comitá-las individualmente.
Para um projeto com centenas ou milhares de commits, isso pode literalmente levar horas ou mesmo dias para terminar.
A parte -T trunk -b branches -t tags diz ao Git que esse repositório do Subversion segue as convenções básicas de ramificação e marcação.
Se você nomear o seu tronco (trunk), ramificações (branches) ou etiquetas (tags) de maneira diferente, poderá alterar essas opções.
Como isso é tão comum, você pode substituir toda a parte por -s, que significa layout padrão e implica todas essas opções.
O seguinte comando é equivalente:
$ git svn clone file:///tmp/test-svn -s
Neste ponto, você deve ter um repositório Git válido que importou suas ramificações (branches) e tags:
$ git branch -a
* master
remotes/origin/my-calc-branch
remotes/origin/tags/2.0.2
remotes/origin/tags/release-2.0.1
remotes/origin/tags/release-2.0.2
remotes/origin/tags/release-2.0.2rc1
remotes/origin/trunk
Observe como essa ferramenta gerencia as tags do Subversion como referências remotas (remote refs).
Vamos dar uma olhada no comando de encanamento do Git show-ref:
$ git show-ref
556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae refs/heads/master
0fb585761df569eaecd8146c71e58d70147460a2 refs/remotes/origin/my-calc-branch
bfd2d79303166789fc73af4046651a4b35c12f0b refs/remotes/origin/tags/2.0.2
285c2b2e36e467dd4d91c8e3c0c0e1750b3fe8ca refs/remotes/origin/tags/release-2.0.1
cbda99cb45d9abcb9793db1d4f70ae562a969f1e refs/remotes/origin/tags/release-2.0.2
a9f074aa89e826d6f9d30808ce5ae3ffe711feda refs/remotes/origin/tags/release-2.0.2rc1
556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae refs/remotes/origin/trunk
O Git não faz isso quando é clonado de um servidor Git; aqui está a aparência de um repositório com tags após um clone novo:
$ git show-ref
c3dcbe8488c6240392e8a5d7553bbffcb0f94ef0 refs/remotes/origin/master
32ef1d1c7cc8c603ab78416262cc421b80a8c2df refs/remotes/origin/branch-1
75f703a3580a9b81ead89fe1138e6da858c5ba18 refs/remotes/origin/branch-2
23f8588dde934e8f33c263c6d8359b2ae095f863 refs/tags/v0.1.0
7064938bd5e7ef47bfd79a685a62c1e2649e2ce7 refs/tags/v0.2.0
6dcb09b5b57875f334f61aebed695e2e4193db5e refs/tags/v1.0.0
O Git busca (fetch) as tags diretamente nas refs/tags, em vez de tratá-las como branches remotos.
Comitando Novamente no Subversion
Agora que você tem um diretório de trabalho, pode trabalhar um pouco no projeto e fazer com que os seus commits voltem a ser a montante (upstream), usando o Git efetivamente como um cliente SVN. Se você editar um dos arquivos e comitá-lo, terá um commit que existe no Git localmente que não existe no servidor Subversion:
$ git commit -am 'Adding git-svn instructions to the README'
[master 4af61fd] Adding git-svn instructions to the README
1 file changed, 5 insertions(+)
Em seguida, você precisa efetuar o push de sua mudança para montante (upstream).
Observe como isso muda a maneira como você trabalha com o Subversion – você pode fazer vários commits offline e, em seguida, fazer push em todos os servidores do Subversion de uma só vez.
Para fazer um push em um servidor Subversion, execute o comando git svn dcommit:
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
M README.txt
Committed r77
M README.txt
r77 = 95e0222ba6399739834380eb10afcd73e0670bc5 (refs/remotes/origin/trunk)
No changes between 4af61fd05045e07598c553167e0f31c84fd6ffe1 and refs/remotes/origin/trunk
Resetting to the latest refs/remotes/origin/trunk
Isso pega todos os commits que você fez no código do servidor do Subversion, faz um commit no Subversion para cada um e, em seguida, reescreve o commit do Git local para incluir um identificador exclusivo.
Isso é importante porque significa que todas as somas de verificação (checksums) do SHA-1 de seus commits mudam.
Em parte por esse motivo, não é uma boa ideia trabalhar com versões remotas de seus projetos com base no Git simultaneamente a um servidor Subversion.
Se você observar o último commit, poderá ver o novo git-svn-id que foi adicionado:
$ git log -1
commit 95e0222ba6399739834380eb10afcd73e0670bc5
Author: ben <ben@0b684db3-b064-4277-89d1-21af03df0a68>
Date: Thu Jul 24 03:08:36 2014 +0000
Adding git-svn instructions to the README
git-svn-id: file:///tmp/test-svn/trunk@77 0b684db3-b064-4277-89d1-21af03df0a68
Observe que a soma de verificação (checksum) SHA-1 que originalmente começava com 4af61fd quando você o comitou, agora começa com 95e0222.
Se você deseja fazer push no servidor Git e em um servidor Subversion, você tem que dar o push (dcommit) no servidor Subversion primeiro, porque essa ação altera os dados de seu commit.
Extraindo as Novas Mudanças (Pulling)
Se você estiver trabalhando com outros desenvolvedores, em algum momento um de vocês dará um push e, em seguida, o outro tentará enviar um conflito na mudança.
Essa alteração será rejeitada até que você junte os trabalhos.
Em git svn, isso se parece com isso:
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
ERROR from SVN:
Transaction is out of date: File '/trunk/README.txt' is out of date
W: d5837c4b461b7c0e018b49d12398769d2bfc240a and refs/remotes/origin/trunk differ, using rebase:
:100644 100644 f414c433af0fd6734428cf9d2a9fd8ba00ada145 c80b6127dd04f5fcda218730ddf3a2da4eb39138 M README.txt
Current branch master is up to date.
ERROR: Not all changes have been committed into SVN, however the committed
ones (if any) seem to be successfully integrated into the working tree.
Please see the above messages for details.
Para resolver essa situação, você pode executar o git svn rebase, que extrai todas as alterações no servidor que você ainda não possui e recria o trabalho que você tem sobre o que está no servidor:
$ git svn rebase
Committing to file:///tmp/test-svn/trunk ...
ERROR from SVN:
Transaction is out of date: File '/trunk/README.txt' is out of date
W: eaa029d99f87c5c822c5c29039d19111ff32ef46 and refs/remotes/origin/trunk differ, using rebase:
:100644 100644 65536c6e30d263495c17d781962cfff12422693a b34372b25ccf4945fe5658fa381b075045e7702a M README.txt
First, rewinding head to replay your work on top of it...
Applying: update foo
Using index info to reconstruct a base tree...
M README.txt
Falling back to patching base and 3-way merge...
Auto-merging README.txt
ERROR: Not all changes have been committed into SVN, however the committed
ones (if any) seem to be successfully integrated into the working tree.
Please see the above messages for details.
Agora, todo o seu trabalho está em cima do que há no servidor Subversion, para que você possa dcommit com êxito:
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
M README.txt
Committed r85
M README.txt
r85 = 9c29704cc0bbbed7bd58160cfb66cb9191835cd8 (refs/remotes/origin/trunk)
No changes between 5762f56732a958d6cfda681b661d2a239cc53ef5 and refs/remotes/origin/trunk
Resetting to the latest refs/remotes/origin/trunk
Observe que, ao contrário do Git, que exige a mesclagem de trabalho a montante que você ainda não possui localmente antes de poder realizar push, git svn faz isso apenas se as alterações entrarem em conflito (muito parecido com a forma como o Subversion funciona).
Se outra pessoa der push de uma mudança num arquivo e, em seguida, você der push em uma mudança de um outro arquivo, seu dcommit funcionará perfeitamente:
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
M configure.ac
Committed r87
M autogen.sh
r86 = d8450bab8a77228a644b7dc0e95977ffc61adff7 (refs/remotes/origin/trunk)
M configure.ac
r87 = f3653ea40cb4e26b6281cec102e35dcba1fe17c4 (refs/remotes/origin/trunk)
W: a0253d06732169107aa020390d9fefd2b1d92806 and refs/remotes/origin/trunk differ, using rebase:
:100755 100755 efa5a59965fbbb5b2b0a12890f1b351bb5493c18 e757b59a9439312d80d5d43bb65d4a7d0389ed6d M autogen.sh
First, rewinding head to replay your work on top of it...
Isso é importante de se lembrar, porque o resultado é um estado de projeto que não existia em nenhum de seus computadores quando você os empurrou (push). Se as alterações forem incompatíveis, mas não entrarem em conflito, você poderá ter problemas difíceis de diagnosticar. Isso é diferente de usar um servidor Git – no Git, você pode testar completamente o estado do sistema cliente antes de publicá-lo, enquanto no SVN você nunca pode ter certeza de que os estados imediatamente antes e depois do commit são idênticos.
Você também deve executar este comando para efetuar pull nas alterações do servidor Subversion, mesmo que não esteja pronto para se comprometer (comitar).
Você pode executar git svn fetch para obter os novos dados, mas git svn rebase busca (fetch) e atualiza seus commits locais.
$ git svn rebase
M autogen.sh
r88 = c9c5f83c64bd755368784b444bc7a0216cc1e17b (refs/remotes/origin/trunk)
First, rewinding head to replay your work on top of it...
Fast-forwarded master to refs/remotes/origin/trunk.
A execução de git svn rebase de vez em quando garante que seu código esteja sempre atualizado.
Você deve ter certeza de que seu diretório de trabalho esteja limpo ao executar isso.
Se você tiver alterações locais, deve fazer stash do trabalho ou criar um commit temporário antes de executar git svn rebase – caso contrário, o comando será interrompido se constatar que o rebase resultará em um conflito de merge.
Problemas do Branching do Git
Quando você se sentir confortável com um fluxo de trabalho do Git, provavelmente criará ramificações (branches) de tópicos, fará o trabalho e as mesclará (merge) nelas.
Se você estiver empurrando (pushing) para um servidor Subversion via git svn, convém refazer (rebase) seu trabalho em uma única filial de cada vez, em vez de mesclar as ramificações (branches).
O motivo para preferir o rebasing é que o Subversion tem um histórico linear e não lida com mesclagens como o Git, portanto o git svn segue apenas o primeiro pai ao converter os instantâneos (snapshots) em commits do Subversion.
Suponha que seu histórico seja o seguinte: você criou uma ramificação experiment, fez dois commits e depois mesclou-os no master.
Quando você usar dcommit, você verá uma saída parecida com esta:
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
M CHANGES.txt
Committed r89
M CHANGES.txt
r89 = 89d492c884ea7c834353563d5d913c6adf933981 (refs/remotes/origin/trunk)
M COPYING.txt
M INSTALL.txt
Committed r90
M INSTALL.txt
M COPYING.txt
r90 = cb522197870e61467473391799148f6721bcf9a0 (refs/remotes/origin/trunk)
No changes between 71af502c214ba13123992338569f4669877f55fd and refs/remotes/origin/trunk
Resetting to the latest refs/remotes/origin/trunk
A execução do dcommit em um branch com histórico mesclado (merge history) funciona bem, exceto quando você analisa o histórico do projeto Git. Ele não reescreveu nenhum dos commits efetuados no branch experiment – em vez disso, todas essas alterações aparecem na versão SVN do commit de consolidação (merge commit) único.
Quando outra pessoa clona esse trabalho, ela vê apenas o commit de consolidação (merge commit) com todo o trabalho esmagado, como se você executasse git merge --squash; eles não veem os dados de confirmação sobre de onde eles vieram ou quando foram confirmados.
Ramificação (Branching) do Subversion
Branching no Subversion não é o mesmo que branching no Git; se você puder evitar o uso, provavelmente é o melhor.
No entanto, você pode criar e comitar em branches no Subversion usando o git svn.
Criando um Novo Branch do SVN
Para criar um novo branch no Subversion, você roda o git svn branch [new-branch]:
$ git svn branch opera
Copying file:///tmp/test-svn/trunk at r90 to file:///tmp/test-svn/branches/opera...
Found possible branch point: file:///tmp/test-svn/trunk => file:///tmp/test-svn/branches/opera, 90
Found branch parent: (refs/remotes/origin/opera) cb522197870e61467473391799148f6721bcf9a0
Following parent with do_switch
Successfully followed parent
r91 = f1b64a3855d3c8dd84ee0ef10fa89d27f1584302 (refs/remotes/origin/opera)
Isso faz o equivalente ao comando svn copy trunk branches/opera no Subversion e opera no servidor Subversion.
É importante observar que não faz uma verificação nesse branch; se você comitar nesse ponto, esse commit irá para o trunk no servidor, não no opera.
Mudando os Branches Ativos
O Git descobre em que ramificação as suas comissões ocorrem, procurando a ponta (tip) de qualquer uma de suas filiais (branches) do Subversion em seu histórico – você deve ter apenas um e deve ser o último com um git-svn-id no histórico da sua ramificação atual.
Se você deseja trabalhar em mais de um branch simultaneamente, pode configurar branches locais no dcommit para branches específicos do Subversion, iniciando-os no commit de Subversion importado para essa branch.
Se você deseja uma filial (branch) opera na qual possa trabalhar separadamente, execute:
$ git branch opera remotes/origin/opera
Agora, se você deseja mesclar (merge) o seu branch opera no trunk (seu branch master), é possível fazê-lo com um git merge normal.
Mas você precisa fornecer uma mensagem descritiva de commit (via -m), ou a consolidação (merge) dirá “Merge branch opera” em vez de algo útil.
Lembre-se de que, embora você esteja usando o git merge para executar esta operação, o merge provavelmente será muito mais fácil do que no Subversion (porque o Git detectará automaticamente a base de consolidação (merge) apropriada para você), esse não é um normal commit do Git normal.
Você tem que levar (push) esses dados de volta a um servidor Subversion que não consegue lidar com um commit que rastreia mais de um pai; logo, depois de enviá-lo (push up), parecerá um único commit no qual esmaga todo o trabalho de outro branch sob um único commit.
Depois de mesclar (merge) um branch com outro, não é possível voltar com facilidade e continuar trabalhando nela, como é normalmente possível no Git.
O comando dcommit que você executa apaga qualquer informação que diga qual branch foi mesclado (merged), para que os cálculos base subsequentes da base de consolidação fiquem errados – o dcommit faz o seu resultado do git merge parecer como se tivesse rodado o comando git merge --squash.
Infelizmente, não há uma boa maneira de evitar essa situação – o Subversion não pode armazenar essas informações, portanto, você sempre será prejudicado por suas limitações enquanto o usar como servidor.
Para evitar problemas, exclua o branch local (neste caso, opera) depois de mesclá-lo ao trunk.
Comandos do Subversion
O conjunto de ferramentas git svn fornece vários comandos para ajudar a facilitar a transição para o Git, fornecendo algumas funcionalidades semelhantes às do Subversion.
Aqui estão alguns comandos que fornecem o que o Subversion costumava fazer.
Histórico em Estilo SVN
Se você está acostumado ao Subversion e deseja ver o seu histórico no estilo de saída SVN, execute git svn log para visualizar o seu histórico de commits na formatação SVN:
$ git svn log
------------------------------------------------------------------------
r87 | schacon | 2014-05-02 16:07:37 -0700 (Sat, 02 May 2014) | 2 lines
autogen change
------------------------------------------------------------------------
r86 | schacon | 2014-05-02 16:00:21 -0700 (Sat, 02 May 2014) | 2 lines
Merge branch 'experiment'
------------------------------------------------------------------------
r85 | schacon | 2014-05-02 16:00:09 -0700 (Sat, 02 May 2014) | 2 lines
updated the changelog
Você deve saber duas coisas importantes sobre o git svn log.
Primeiro, ele funciona offline, diferentemente do comando real svn log, que solicita os dados ao servidor Subversion.
Segundo, ele mostra apenas confirmações (commits) que foram submetidas ao servidor Subversion.
Os commits do Git local com as quais você não se comitou não aparecem; tampouco as comissões que as pessoas fizeram no servidor Subversion nesse meio tempo.
É mais como o último estado conhecido dos commits no servidor Subversion.
Anotações (Annotations) do SVN
Assim como o comando git svn log simula o comando svn log offline, você pode obter o equivalente de svn annotate ao executar git svn blame [FILE].
A saída se parece com esta:
$ git svn blame README.txt
2 temporal Protocol Buffers - Google's data interchange format
2 temporal Copyright 2008 Google Inc.
2 temporal http://code.google.com/apis/protocolbuffers/
2 temporal
22 temporal C++ Installation - Unix
22 temporal =======================
2 temporal
79 schacon Committing in git-svn.
78 schacon
2 temporal To build and install the C++ Protocol Buffer runtime and the Protocol
2 temporal Buffer compiler (protoc) execute the following:
2 temporal
Mais uma vez, ele não mostra os commits que você fez localmente no Git ou que foram empurrados (pushed) para o Subversion durante esse período.
Informações do Servidor SVN
Você também pode obter o mesmo tipo de informação que a opção svn info fornece ao executar a opção git svn info:
$ git svn info
Path: .
URL: https://schacon-test.googlecode.com/svn/trunk
Repository Root: https://schacon-test.googlecode.com/svn
Repository UUID: 4c93b258-373f-11de-be05-5f7a86268029
Revision: 87
Node Kind: directory
Schedule: normal
Last Changed Author: schacon
Last Changed Rev: 87
Last Changed Date: 2009-05-02 16:07:37 -0700 (Sat, 02 May 2009)
É como no blame e no log, ele fica offline e está atualizado apenas na última vez em que você se comunicou com o servidor Subversion.
Ignorando O Que O Subversion Ignora
Se você clonar um repositório Subversion que possua propriedades svn:ignore configuradas em qualquer lugar, provavelmente você desejará configurar os arquivos .gitignore correspondentes para não comprometer acidentalmente arquivos que não deveriam.
O git svn possui dois comandos para ajudar com esse problema.
O primeiro é o git svn create-ignore, que cria automaticamente arquivos .gitignore correspondentes para você, para que o seu próximo commit possa incluí-los.
O segundo comando é git svn show-ignore, que imprime no stdout as linhas que você precisa colocar em um arquivo .gitignore para redirecionar a saída para o arquivo de exclusão (exclude) de projeto:
$ git svn show-ignore > .git/info/exclude
Dessa forma, você não joga lixo no projeto com arquivos .gitignore.
Esta é uma boa opção se você for o único usuário do Git em uma equipe de Subversion e os seus colegas de equipe não quiserem arquivos .gitignore no projeto.
Resumo do Git-Svn
As ferramentas git svn são úteis se você estiver preso a um servidor Subversion ou em um ambiente de desenvolvimento que exija a execução de um servidor Subversion.
Você deve considerá-lo como um Git prejudicado, no entanto, ou você terá problemas na tradução que podem confundir você e seus colaboradores.
Para evitar problemas, tente seguir estas diretrizes:
-
Mantenha um histórico linear do Git que não contenha os commits consolidados (merge) gerados pelo
git merge. Refaça (rebase) todo o trabalho que você faz fora do seu branch principal; não os consolide (merge). -
Não configure e nem colabore em um servidor Git separado. Possivelmente possua um para acelerar clones de novos desenvolvedores, mas não o empurre (push) para nada que não tenha uma entrada
git-svn-id. Você pode até adicionar um gancho (hook) depre-receiveque verifica cada mensagem de commit em busca de umgit-svn-ide rejeita push que contenha commits sem ela.
Se você seguir essas diretrizes, trabalhar com um servidor Subversion pode ser mais suportável. No entanto, se for possível mover para um servidor Git real, isso poderá ganhar muito mais para sua equipe.
Git e Mercurial
O universo DVCS é maior que apenas o Git. De fato, existem muitos outros sistemas nesse espaço, cada um com seu próprio ângulo sobre como fazer corretamente o controle de versão distribuído. Além do Git, o mais popular é o Mercurial, e os dois são muito semelhantes em muitos aspectos.
A boa notícia, se você prefere o comportamento do lado do cliente (client-side) do Git, mas está trabalhando com um projeto cujo código-fonte é controlado pelo Mercurial, é que há uma maneira de usar o Git como cliente de um repositório hospedado no Mercurial. Como a maneira pela qual o Git conversa com os repositórios do servidor se dá através de remotos, não deve surpreender o fato de que esta ponte (bridge) seja implementada como um ajudante remoto (remote helper). O nome do projeto é git-remote-hg e pode ser encontrado em https://github.com/felipec/git-remote-hg.
git-remote-hg
Primeiro, você deve instalar o git-remote-hg. Isso implica basicamente soltar o arquivo em algum lugar no seu caminho (path), da seguinte forma:
$ curl -o ~/bin/git-remote-hg \
https://raw.githubusercontent.com/felipec/git-remote-hg/master/git-remote-hg
$ chmod +x ~/bin/git-remote-hg
-
assumindo que
~/binestá no seu$PATH. O Git-remote-hg possui uma outra dependência: a bibliotecamercurialdo Python. Se você possui o Python instalado, isso é tão simples quanto:
$ pip install mercurial
Se você não tem o Python instalado, acesse https://www.python.org/ e faça a obtenção dele antes.
A última coisa que você precisará é do cliente Mercurial. Vá para https://www.mercurial-scm.org/ e o instale caso ainda não o tenha feito.
Agora você está pronto para agitar. Tudo que você precisa é de um repositório Mercurial no qual possa empurrar (fazer o push). Felizmente, todos os repositórios Mercurial podem agir dessa maneira, portanto, usaremos o repositório "hello world" que todo mundo usa para aprender o Mercurial:
$ hg clone http://selenic.com/repo/hello /tmp/hello
Primeiros Passos
Agora que temos um repositório "lado do servidor (server-side)" adequado, podemos seguir um fluxo de trabalho típico. Como você verá, esses dois sistemas são bastante semelhantes e não há muito atrito.
Como sempre no Git, primeiramente nós o clonamos:
$ git clone hg::/tmp/hello /tmp/hello-git
$ cd /tmp/hello-git
$ git log --oneline --graph --decorate
* ac7955c (HEAD, origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master, master) Create a makefile
* 65bb417 Create a standard 'hello, world' program
Você notará que trabalhar com um repositório Mercurial usa o comando padrão git clone.
Isso ocorre porque o git-remote-hg está trabalhando em um nível bastante baixo, usando um mecanismo semelhante a como o protocolo HTTP/S do Git é implementado (helpers remotos).
Como o Git e o Mercurial são projetados para que todos os clientes tenham uma cópia completa do histórico do repositório, este comando faz um clone completo, incluindo todo o histórico do projeto, e o faz com bastante rapidez.
O comando log mostra dois commits, os mais recentes sendo apontados por um monte de referências.
Acontece que alguns deles não estão realmente lá.
Vamos dar uma olhada no que realmente está no diretório .git:
$ tree .git/refs
.git/refs
├── heads
│ └── master
├── hg
│ └── origin
│ ├── bookmarks
│ │ └── master
│ └── branches
│ └── default
├── notes
│ └── hg
├── remotes
│ └── origin
│ └── HEAD
└── tags
9 directories, 5 files
O Git-remote-hg está tentando tornar as coisas mais idiomaticamente no formato Git-esque, mas sob o capô está gerenciando o mapeamento conceitual entre dois sistemas ligeiramente diferentes.
O diretório refs/hg é onde as referências remotas (remote refs) reais são armazenadas.
Por exemplo, o arquivo refs/hg/origin/branches/default é um arquivo de ref do Git que contém o SHA-1 que começa com “ac7955c”, que é o commit no qual a master aponta.
Portanto, o diretório refs/hg é como um falso refs/remotes/origin, mas tem a distinção adicional entre favoritos (bookmarks) e ramificações (branches).
O arquivo notes/hg é o ponto de partida de como o git-remote-hg mapeia hashes de commit do Git para IDs de conjunto de alterações do Mercurial.
Vamos explorar um pouco:
$ cat notes/hg
d4c10386...
$ git cat-file -p d4c10386...
tree 1781c96...
author remote-hg <> 1408066400 -0800
committer remote-hg <> 1408066400 -0800
Notes for master
$ git ls-tree 1781c96...
100644 blob ac9117f... 65bb417...
100644 blob 485e178... ac7955c...
$ git cat-file -p ac9117f
0a04b987be5ae354b710cefeba0e2d9de7ad41a9
Portanto, refs/notes/hg aponta para uma árvore, que no banco de dados de objetos do Git é uma lista de outros objetos com nomes.
O git ls-tree produz o modo, tipo, hash do objeto e o nome de arquivo para os itens em uma árvore.
Depois de cavar em um dos itens da árvore, descobrimos que, dentro dele, há um blob chamado “ac9117f” (o hash SHA-1 do commit apontado por master), com o conteúdo “0a04b98” (que é o ID do changeset do Mercurial na ponta (tip) do branch default).
A boa notícia é que não precisamos nos preocupar com tudo isso a maior parte do tempo. O fluxo de trabalho típico não será muito diferente do trabalho com um controle remoto no Git.
Há mais uma coisa que devemos atender antes de continuarmos: ignorações.
O Mercurial e o Git usam um mecanismo muito semelhante para isso, mas é provável que você não queira confirmar (comitar) um arquivo .gitignore em um repositório Mercurial.
Felizmente, o Git possui uma maneira de ignorar arquivos que são locais a um repositório no disco e o formato Mercurial é compatível com o Git; portanto, basta copiá-lo para:
$ cp .hgignore .git/info/exclude
O arquivo .git/info/exclude age como um .gitignore, mas não é incluído nos commits.
Fluxo de Trabalho
Vamos supor que fizemos algum trabalho e fizemos alguns commits no branch master e você está pronto para enviá-lo (push) ao repositório remoto.
Aqui está a aparência do nosso repositório no momento:
$ git log --oneline --graph --decorate
* ba04a2a (HEAD, master) Update makefile
* d25d16f Goodbye
* ac7955c (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Create a makefile
* 65bb417 Create a standard 'hello, world' program
Nosso branch master tem dois commits à frente da origin/master, mas esses dois commits existem apenas na nossa máquina local.
Vamos ver se alguém fez um trabalho importante ao mesmo tempo:
$ git fetch
From hg::/tmp/hello
ac7955c..df85e87 master -> origin/master
ac7955c..df85e87 branches/default -> origin/branches/default
$ git log --oneline --graph --decorate --all
* 7b07969 (refs/notes/hg) Notes for default
* d4c1038 Notes for master
* df85e87 (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Add some documentation
| * ba04a2a (HEAD, master) Update makefile
| * d25d16f Goodbye
|/
* ac7955c Create a makefile
* 65bb417 Create a standard 'hello, world' program
Como usamos o sinalizador (flag) --all, vemos as refs de “notes” usadas internamente pelo git-remote-hg, mas podemos ignorá-las.
O resto é o que esperávamos; origin/master avançou em um commit, e nosso histórico agora divergiu.
Ao contrário dos outros sistemas com os quais trabalhamos neste capítulo, o Mercurial é capaz de lidar com merges, portanto não faremos nada de extraordinário.
$ git merge origin/master
Auto-merging hello.c
Merge made by the 'recursive' strategy.
hello.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git log --oneline --graph --decorate
* 0c64627 (HEAD, master) Merge remote-tracking branch 'origin/master'
|\
| * df85e87 (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Add some documentation
* | ba04a2a Update makefile
* | d25d16f Goodbye
|/
* ac7955c Create a makefile
* 65bb417 Create a standard 'hello, world' program
Perfeito. Executamos os testes e tudo passa; portanto, estamos prontos para compartilhar o nosso trabalho com o restante da equipe:
$ git push
To hg::/tmp/hello
df85e87..0c64627 master -> master
Isso é tudo! Se você der uma olhada no repositório do Mercurial, verá que isso fez o que esperávamos:
$ hg log -G --style compact
o 5[tip]:4,2 dc8fa4f932b8 2014-08-14 19:33 -0700 ben
|\ Merge remote-tracking branch 'origin/master'
| |
| o 4 64f27bcefc35 2014-08-14 19:27 -0700 ben
| | Update makefile
| |
| o 3:1 4256fc29598f 2014-08-14 19:27 -0700 ben
| | Goodbye
| |
@ | 2 7db0b4848b3c 2014-08-14 19:30 -0700 ben
|/ Add some documentation
|
o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm
| Create a makefile
|
o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm
Create a standard 'hello, world' program
O conjunto de alterações (changeset) numerado 2 foi criado por Mercurial, e os conjuntos de alterações numerados 3 e 4 foram feitos por git-remote-hg, promovendo os commits efetuados pelo Git (push).
Ramificações e Favoritos (Branches e Bookmarks)
O Git possui apenas um tipo de ramificação (branch): uma referência que se move quando são feitos commits. No Mercurial, esse tipo de referência é chamado de "favorito (bookmark)" e se comporta da mesma maneira que uma filial (branch) do Git.
O conceito do Mercurial de uma "ramificação (branch)" é mais pesado.
A ramificação na qual um conjunto de alterações (changeset) é criado é gravada juntamente com o conjunto de alterações, o que significa que ele sempre estará no histórico do repositório.
Aqui está um exemplo de uma comissão efetuada no branch develop:
$ hg log -l 1
changeset: 6:8f65e5e02793
branch: develop
tag: tip
user: Ben Straub <ben@straub.cc>
date: Thu Aug 14 20:06:38 2014 -0700
summary: More documentation
Observe a linha que começa com “branch”. O Git não pode realmente replicar isso (e não é necessário; os dois tipos de ramificações (branch) podem ser representados como uma referência (ref) do Git), mas o git-remote-hg precisa entender a diferença, porque o Mercurial se importa.
Criar favoritos no Mercurial é tão fácil quanto criar ramificações (branches) do Git. Do lado do Git:
$ git checkout -b featureA
Switched to a new branch 'featureA'
$ git push origin featureA
To hg::/tmp/hello
* [new branch] featureA -> featureA
Isto é tudo que há nisto. Do lado do Mercurial, a aparência é a seguinte:
$ hg bookmarks
featureA 5:bd5ac26f11f9
$ hg log --style compact -G
@ 6[tip] 8f65e5e02793 2014-08-14 20:06 -0700 ben
| More documentation
|
o 5[featureA]:4,2 bd5ac26f11f9 2014-08-14 20:02 -0700 ben
|\ Merge remote-tracking branch 'origin/master'
| |
| o 4 0434aaa6b91f 2014-08-14 20:01 -0700 ben
| | update makefile
| |
| o 3:1 318914536c86 2014-08-14 20:00 -0700 ben
| | goodbye
| |
o | 2 f098c7f45c4f 2014-08-14 20:01 -0700 ben
|/ Add some documentation
|
o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm
| Create a makefile
|
o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm
Create a standard 'hello, world' program
Observe a nova tag [featureA] na revisão 5.
Eles agem exatamente como os branches do Git no lado do Git, com uma exceção: você não pode excluir um favorito (bookmark) do lado do Git (esta é uma limitação dos ajudantes remotos).
Você também pode trabalhar em um branch "pesado (heavyweight)" do Mercurial: basta colocar um branch no espaço de nome das branches:
$ git checkout -b branches/permanent
Switched to a new branch 'branches/permanent'
$ vi Makefile
$ git commit -am 'A permanent change'
$ git push origin branches/permanent
To hg::/tmp/hello
* [new branch] branches/permanent -> branches/permanent
Veja a aparência do lado do Mercurial:
$ hg branches
permanent 7:a4529d07aad4
develop 6:8f65e5e02793
default 5:bd5ac26f11f9 (inactive)
$ hg log -G
o changeset: 7:a4529d07aad4
| branch: permanent
| tag: tip
| parent: 5:bd5ac26f11f9
| user: Ben Straub <ben@straub.cc>
| date: Thu Aug 14 20:21:09 2014 -0700
| summary: A permanent change
|
| @ changeset: 6:8f65e5e02793
|/ branch: develop
| user: Ben Straub <ben@straub.cc>
| date: Thu Aug 14 20:06:38 2014 -0700
| summary: More documentation
|
o changeset: 5:bd5ac26f11f9
|\ bookmark: featureA
| | parent: 4:0434aaa6b91f
| | parent: 2:f098c7f45c4f
| | user: Ben Straub <ben@straub.cc>
| | date: Thu Aug 14 20:02:21 2014 -0700
| | summary: Merge remote-tracking branch 'origin/master'
[...]
O nome da ramificação (branch name) “permanent” foi gravada no conjunto de alterações (changeset) marcado 7.
Do ponto de vista do Git, trabalhar com qualquer um desses estilos de ramificação (branch styles) é a mesma coisa: basta fazer o checkout, comitar, fazer fetch, merge, puxar (pull) e dar push, como faria normalmente. Uma coisa que você deve saber é que o Mercurial não suporta a reescrita da história, apenas as acrescenta. Veja como é o nosso repositório Mercurial após um rebase interativo e um force-push:
$ hg log --style compact -G
o 10[tip] 99611176cbc9 2014-08-14 20:21 -0700 ben
| A permanent change
|
o 9 f23e12f939c3 2014-08-14 20:01 -0700 ben
| Add some documentation
|
o 8:1 c16971d33922 2014-08-14 20:00 -0700 ben
| goodbye
|
| o 7:5 a4529d07aad4 2014-08-14 20:21 -0700 ben
| | A permanent change
| |
| | @ 6 8f65e5e02793 2014-08-14 20:06 -0700 ben
| |/ More documentation
| |
| o 5[featureA]:4,2 bd5ac26f11f9 2014-08-14 20:02 -0700 ben
| |\ Merge remote-tracking branch 'origin/master'
| | |
| | o 4 0434aaa6b91f 2014-08-14 20:01 -0700 ben
| | | update makefile
| | |
+---o 3:1 318914536c86 2014-08-14 20:00 -0700 ben
| | goodbye
| |
| o 2 f098c7f45c4f 2014-08-14 20:01 -0700 ben
|/ Add some documentation
|
o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm
| Create a makefile
|
o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm
Create a standard "hello, world" program
Os conjuntos de alterações 8, 9 e 10 foram criados e pertencem à ramificação (branch) permanent, mas os antigos conjuntos de alterações ainda estão lá.
Isso pode ser muito confuso para seus colegas de equipe que usam o Mercurial; tente evitá-lo.
Resumo do Mercurial
O Git e o Mercurial são semelhantes o suficiente para que o trabalho além da fronteira seja indolor. Se você evita mudar o histórico ao ter saído da sua máquina (como geralmente é recomendado), talvez nem saiba que o outro lado é um Mercurial.
Git e Perforce
O Perforce é um sistema de controle de versão muito popular em ambientes corporativos. Existe desde 1995, o que o torna o sistema mais antigo abordado neste capítulo. Como tal, foi desenvolvido com as restrições de sua época; assume que você esteja sempre conectado a um único servidor central e apenas uma versão seja mantida no disco local. Certamente, seus recursos e restrições são adequados a vários problemas específicos, mas há muitos projetos que usam o Perforce, onde o Git funcionaria melhor.
Existem duas opções se você deseja misturar o uso do Perforce com o do Git. A primeira que abordaremos é a ponte “Git Fusion” (fusão com o Git) dos fabricantes do Perforce, que permite expor subárvores do seu depósito do Perforce como repositórios Git de leitura e gravação. O segundo é o git-p4, uma ponte do lado do cliente (client-side bridge) que permite usar o Git como cliente Perforce, sem exigir nenhuma reconfiguração do servidor Perforce.
Git Fusion
O Perforce fornece um produto chamado Git Fusion (disponível em https://www.perforce.com/manuals/git-fusion/), que sincroniza um servidor Perforce com repositórios Git no lado do servidor.
Configurando
Em nossos exemplos, usaremos o método de instalação mais fácil para o Git Fusion, que é o download de uma máquina virtual que executa o daemon do Perforce e o Git Fusion. Você pode obter a imagem da máquina virtual em https://www.perforce.com/downloads e, assim que o download for concluído, importe-o para o seu software de virtualização favorito (usaremos o VirtualBox).
Ao iniciar a máquina pela primeira vez, ele solicita que você personalize a senha de três usuários do Linux (root, perforce e git) e forneça um nome de instância, que pode ser usado para distinguir esta instalação de outras na mesma rede.
Quando tudo estiver concluído, você verá isto:
Você deve tomar nota do endereço IP mostrado aqui, para utilizarmos posteriormente.
Em seguida, criaremos um usuário do Perforce.
Selecione a opção “Login” na parte inferior e pressione Enter (ou digite um SSH na máquina) e faça o login como root.
Então, use esses comandos para criar um usuário:
$ p4 -p localhost:1666 -u super user -f john
$ p4 -p localhost:1666 -u john passwd
$ exit
O primeiro abrirá um editor VI para personalizar o usuário, mas você pode aceitar os padrões digitando :wq e pressionando Enter.
O segundo solicitará que você digite uma senha duas vezes.
Isso é tudo o que precisamos fazer com um prompt do shell (shell prompt), portanto, saia da sessão.
A próxima coisa que você precisa fazer para acompanhar é dizer ao Git para não verificar os certificados SSL. A imagem do Git Fusion vem com um certificado, mas é de um domínio que não corresponde ao endereço IP da sua máquina virtual, de modo que o Git rejeitará a conexão HTTPS. Se for uma instalação permanente, consulte o manual do Perforce Git Fusion para instalar um certificado diferente; para nossos propósitos de exemplo, isso será suficiente:
$ export GIT_SSL_NO_VERIFY=true
Agora podemos testar se tudo está funcionando.
$ git clone https://10.0.1.254/Talkhouse
Cloning into 'Talkhouse'...
Username for 'https://10.0.1.254': john
Password for 'https://john@10.0.1.254':
remote: Counting objects: 630, done.
remote: Compressing objects: 100% (581/581), done.
remote: Total 630 (delta 172), reused 0 (delta 0)
Receiving objects: 100% (630/630), 1.22 MiB | 0 bytes/s, done.
Resolving deltas: 100% (172/172), done.
Checking connectivity... done.
A imagem da máquina virtual vem equipada com um projeto de amostra que você pode clonar.
Aqui estamos fazendo um clone por HTTPS, com o usuário john que criamos acima; o Git solicita as credenciais para essa conexão, mas o cache de credenciais nos permitirá ignorar essa etapa em todas as solicitações subsequentes.
Configuração do Fusion
Depois que você tiver o Git Fusion instalado, precisará ajustar a configuração.
Isso é muito fácil de fazer usando o seu cliente (client) favorito do Perforce; basta mapear o diretório //.git-fusion no servidor Perforce para a sua área de trabalho.
A estrutura do arquivo se parece com isto:
$ tree
.
├── objects
│ ├── repos
│ │ └── [...]
│ └── trees
│ └── [...]
│
├── p4gf_config
├── repos
│ └── Talkhouse
│ └── p4gf_config
└── users
└── p4gf_usermap
498 directories, 287 files
O diretório de objects é usado internamente pelo Git Fusion para mapear objetos do Perforce para o Git e vice-versa, para que você não precise mexer com nada lá dentro.
Existe um arquivo p4gf_config global neste diretório, bem como um para cada repositório - esses são os arquivos de configuração que determinam como o Git Fusion se comporta.
Vamos dar uma olhada no arquivo na raiz:
[repo-creation]
charset = utf8
[git-to-perforce]
change-owner = author
enable-git-branch-creation = yes
enable-swarm-reviews = yes
enable-git-merge-commits = yes
enable-git-submodules = yes
preflight-commit = none
ignore-author-permissions = no
read-permission-check = none
git-merge-avoidance-after-change-num = 12107
[perforce-to-git]
http-url = none
ssh-url = none
[@features]
imports = False
chunked-push = False
matrix2 = False
parallel-push = False
[authentication]
email-case-sensitivity = no
Não entraremos no significado dessas bandeiras (flags) aqui, mas observe que este é apenas um arquivo de texto no formato INI, muito parecido com o qual o Git usa para configuração.
Esse arquivo especifica as opções globais, que podem ser substituídas por arquivos de configuração específicos do repositório, como repos/Talkhouse/p4gf_config.
Se você abrir este arquivo, verá a seção [@repo] com algumas configurações diferentes dos padrões globais.
Você também verá seções parecidas com esta:
[Talkhouse-master]
git-branch-name = master
view = //depot/Talkhouse/main-dev/... ...
Esse é um mapeamento entre um branch do Perforce e um branch do Git.
A seção pode ser nomeada com o que você preferir, desde que o nome seja único.
git-branch-name permite que você converta um caminho (path) de depósito que seria incômodo no Git para um nome mais amigável.
A configuração view controla como os arquivos Perforce são mapeados no repositório do Git, usando a sintaxe padrão do view mapping.
Mais de um mapeamento pode ser especificado, como neste exemplo:
[multi-project-mapping]
git-branch-name = master
view = //depot/project1/main/... project1/...
//depot/project2/mainline/... project2/...
Dessa forma, se o seu mapeamento normal da área de trabalho incluir alterações na estrutura dos diretórios, você poderá replicá-lo com um repositório Git.
O último arquivo que discutiremos é o users/p4gf_usermap, que mapeia os usuários do Perforce para usuários do Git, no qual você pode nem precisar dele.
Ao converter de um conjunto de alterações do Perforce (Perforce changeset) em um commit do Git, o comportamento padrão do Git Fusion é procurar o usuário do Perforce e usar o endereço de e-mail e o nome completo armazenado nele para o campo autor/commiter do Git.
Ao se converter da outra forma, o padrão é pesquisar no usuário do Perforce com o endereço de e-mail armazenado no campo do autor (author field) do commit do Git e enviar o conjunto de alterações (changeset) como esse usuário (com a aplicação das permissões).
Na maioria dos casos, esse comportamento servirá perfeitamente, mas considere o seguinte arquivo de mapeamento:
john john@example.com "John Doe"
john johnny@appleseed.net "John Doe"
bob employeeX@example.com "Anon X. Mouse"
joe employeeY@example.com "Anon Y. Mouse"
Cada linha está no formato <user> <email> "<full name>" e cria um mapeamento de usuário único.
As duas primeiras linhas mapeiam dois endereços de email distintos na mesma conta de usuário do Perforce.
Isso é útil se você criou commits do Git sob vários endereços de email diferentes (ou se alterou os endereços de email), mas deseja que eles sejam mapeados para o mesmo usuário do Perforce.
Ao criar um commit do Git a partir de um conjunto de alterações do Perforce, a primeira linha correspondente ao usuário do Perforce é usada para obter informações da autoria no Git.
As duas últimas linhas mascaram os nomes e os endereços de e-mail de Bob e Joe para os commits do Git que são criados. Isso é bom se você quiser abrir o código de um projeto interno, mas não quiser publicar seu diretório de funcionários em todo o mundo. Observe que os endereços de e-mail e os nomes completos devem ser únicos, a menos que você queira que todos os commits do Git sejam atribuídas a um único autor fictício.
Fluxo de Trabalho
O Perforce Git Fusion é uma ponte de mão dupla (two-way bridge) entre o Perforce e o controle de versão do Git. Vamos dar uma olhada em como é trabalhar no lado do Git. Vamos supor que tenhamos feito o mapeamento no projeto “Jam” usando um arquivo de configuração como o mostrado acima, que podemos fazer um clone assim:
$ git clone https://10.0.1.254/Jam
Cloning into 'Jam'...
Username for 'https://10.0.1.254': john
Password for 'https://john@10.0.1.254':
remote: Counting objects: 2070, done.
remote: Compressing objects: 100% (1704/1704), done.
Receiving objects: 100% (2070/2070), 1.21 MiB | 0 bytes/s, done.
remote: Total 2070 (delta 1242), reused 0 (delta 0)
Resolving deltas: 100% (1242/1242), done.
Checking connectivity... done.
$ git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/rel2.1
$ git log --oneline --decorate --graph --all
* 0a38c33 (origin/rel2.1) Create Jam 2.1 release branch.
| * d254865 (HEAD, origin/master, origin/HEAD, master) Upgrade to latest metrowerks on Beos -- the Intel one.
| * bd2f54a Put in fix for jam's NT handle leak.
| * c0f29e7 Fix URL in a jam doc
| * cc644ac Radstone's lynx port.
[...]
Da primeira vez em que você fizer isso, pode demorar algum tempo. O que está acontecendo é que o Git Fusion está convertendo todos os conjuntos de alterações aplicáveis no histórico do Perforce em commits do Git. Isso acontece localmente no servidor, portanto, é relativamente rápido, mas, se você tem muito histórico, ainda pode demorar algum tempo. As buscas subsequentes fazem a conversão em incrementos; por isso, se sentirá mais parecida com a velocidade nativa do Git.
Como você pode ver, o nosso repositório se parece exatamente com qualquer outro repositório Git com o qual você possa trabalhar.
Existem três filiais e o Git criou utilmente uma branch master local que rastreia a origin/master.
Vamos trabalhar um pouco e criar alguns novos commits:
# ...
$ git log --oneline --decorate --graph --all
* cfd46ab (HEAD, master) Add documentation for new feature
* a730d77 Whitespace
* d254865 (origin/master, origin/HEAD) Upgrade to latest metrowerks on Beos -- the Intel one.
* bd2f54a Put in fix for jam's NT handle leak.
[...]
Temos duas comissões novas. Agora vamos verificar se alguém está trabalhando:
$ git fetch
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://10.0.1.254/Jam
d254865..6afeb15 master -> origin/master
$ git log --oneline --decorate --graph --all
* 6afeb15 (origin/master, origin/HEAD) Update copyright
| * cfd46ab (HEAD, master) Add documentation for new feature
| * a730d77 Whitespace
|/
* d254865 Upgrade to latest metrowerks on Beos -- the Intel one.
* bd2f54a Put in fix for jam's NT handle leak.
[...]
Parece que alguém sim!
Você não saberia disso a partir desta visão, mas o commit 6afeb15 foi de fato criado utilizando um cliente Perforce.
Ele parece apenas mais um commit sob o ponto de vista do Git, que é exatamente o ponto em questão.
Vamos ver como o servidor Perforce lida com um merge de commit:
$ git merge origin/master
Auto-merging README
Merge made by the 'recursive' strategy.
README | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git push
Counting objects: 9, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 917 bytes | 0 bytes/s, done.
Total 9 (delta 6), reused 0 (delta 0)
remote: Perforce: 100% (3/3) Loading commit tree into memory...
remote: Perforce: 100% (5/5) Finding child commits...
remote: Perforce: Running git fast-export...
remote: Perforce: 100% (3/3) Checking commits...
remote: Processing will continue even if connection is closed.
remote: Perforce: 100% (3/3) Copying changelists...
remote: Perforce: Submitting new Git commit objects to Perforce: 4
To https://10.0.1.254/Jam
6afeb15..89cba2b master -> master
O Git acha que deu certo.
Vamos dar uma olhada no histórico do arquivo README sob o ponto de vista do Perforce, usando o recurso de gráfico de revisões (revision graph) do p4v:
Se você nunca viu essa visão antes, pode parecer confuso, mas ela mostra os mesmos conceitos que em um visualizador gráfico (graphical viewer) em relação ao histórico do Git.
Estamos examinando o histórico do arquivo README, portanto, a árvore de diretórios no canto superior esquerdo mostra apenas esse arquivo à medida que ele surge em várias ramificações (branches).
No canto superior direito, temos um gráfico visual de como as diferentes revisões do arquivo estão relacionadas, e a visão geral deste gráfico está no canto inferior direito.
O resto da exibição (view) é entregue à exibição de detalhes para a revisão selecionada (2 neste caso).
Uma coisa a se notar é que o gráfico é exatamente como o do histórico do Git.
O Perforce não possuía um branch nomeado para armazenar os commits 1 e 2, portanto, ele fez um branch “anonymous” no diretório .git-fusion para retê-lo.
Isso também acontecerá nos branches do Git nomeados que não correspondam a um branch do Perforce nomeada (e você pode mapeá-los para um branch do Perforce usando o arquivo de configuração posteriormente).
A maior parte disso acontece nos bastidores, mas o resultado final é que uma pessoa na equipe pode estar usando o Git, outra pode usar o Perforce e nenhum deles saberá sobre a escolha do outro.
Resumo do Git-Fusion
Se você tem (ou consegue obter) acesso ao seu servidor Perforce, o Git Fusion é uma ótima maneira de fazer o Git e o Perforce conversarem um com o outro. Há um pouco de configuração envolvida, mas a curva de aprendizado não é muito acentuada. Esta é uma das poucas seções deste capítulo nas quais os avisos sobre o uso de todo o poder do Git não serão exibidas. Isso não quer dizer que o Perforce ficará satisfeito com tudo o que você jogar sobre ele - se você tentar reescrever o histórico que já foi enviado (pushed), o Git Fusion irá rejeitá-lo - mas o Git Fusion se esforça muito para se parecer com algo nativo. Você pode até usar submódulos Git (embora pareçam estranhos para os usuários do Perforce) e consolidar os branches (merge) (isso será gravado como uma integração no lado do Perforce).
Se você não conseguir convencer o administrador do seu servidor a configurar o Git Fusion, ainda há uma maneira de usar essas ferramentas juntas.
Git-p4
O Git-p4 é uma ponte de mão dupla entre Git e o Perforce. Ele é executado inteiramente no seu repositório Git, portanto você não precisará de nenhum tipo de acesso ao servidor Perforce (além das credenciais do usuário, é claro). O Git-p4 não é uma solução tão flexível ou completa quanto o Git Fusion, mas permite que você faça a maior parte do que deseja sem ser invasivo no ambiente do servidor.
|
Note
|
Você precisará da ferramenta |
Configurando
Por exemplo, estaremos executando o servidor Perforce do OVA Git Fusion como mostrado acima, mas contornaremos (bypass) o servidor Git Fusion e acessaremos diretamente o controle de versão do Perforce.
Para usar o cliente de linha de comando p4 (do qual o git-p4 depende), você precisará definir algumas variáveis de ambiente:
$ export P4PORT=10.0.1.254:1666
$ export P4USER=john
Primeiros Passos
Como qualquer coisa no Git, o primeiro comando é o clone:
$ git p4 clone //depot/www/live www-shallow
Importing from //depot/www/live into www-shallow
Initialized empty Git repository in /private/tmp/www-shallow/.git/
Doing initial import of //depot/www/live/ from revision #head into refs/remotes/p4/master
Isso cria o que em termos do Git é um clone "raso (shallow)"; apenas a última revisão do Perforce é importada para o Git; Lembre-se, o Perforce não foi projetado para fornecer todas as revisões a todos os usuários.
Isso é suficiente para usar o Git como um cliente do Perforce, mas, para outros propósitos, não é.
Quando terminar, temos um repositório Git totalmente funcional:
$ cd myproject
$ git log --oneline --all --graph --decorate
* 70eaf78 (HEAD, p4/master, p4/HEAD, master) Initial import of //depot/www/live/ from the state at revision #head
Observe como há um remoto (remote) “p4” para o servidor Perforce, mas todo o resto parece um clone padrão. Na verdade, isso é um pouco enganoso; na verdade, não há nada remoto (remote) lá.
$ git remote -v
Não há remotos neste repositório.
O Git-p4 criou algumas referências (refs) para representar o estado do servidor e elas parecem com referências remotas do git log, mas não são gerenciadas pelo próprio Git e não podem sofrer o comando push.
Fluxo de Trabalho
Tudo bem, vamos trabalhar um pouco. Vamos supor que você tenha feito algum progresso em um recurso (feature) muito importante e esteja pronto para exibi-lo ao resto de sua equipe.
$ git log --oneline --all --graph --decorate
* 018467c (HEAD, master) Change page title
* c0fb617 Update link
* 70eaf78 (p4/master, p4/HEAD) Initial import of //depot/www/live/ from the state at revision #head
Realizamos duas novas comissões e estamos prontos para enviar ao servidor Perforce. Vamos verificar se alguém estava trabalhando hoje:
$ git p4 sync
git p4 sync
Performing incremental import into refs/remotes/p4/master git branch
Depot paths: //depot/www/live/
Import destination: refs/remotes/p4/master
Importing revision 12142 (100%)
$ git log --oneline --all --graph --decorate
* 75cd059 (p4/master, p4/HEAD) Update copyright
| * 018467c (HEAD, master) Change page title
| * c0fb617 Update link
|/
* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head
Parece que estavam, e a master e a p4/master divergiram.
O sistema de ramificações (branching system) do Perforce não é nada como o do Git, portanto o envio de um commit consolidado (merge commits) não faz nenhum sentido.
O Git-p4 recomenda que você refaça os (rebase) seus commits, e até acompanha um atalho para fazer isso:
$ git p4 rebase
Performing incremental import into refs/remotes/p4/master git branch
Depot paths: //depot/www/live/
No changes to import!
Rebasing the current branch onto remotes/p4/master
First, rewinding head to replay your work on top of it...
Applying: Update link
Applying: Change page title
index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Você provavelmente pode dizer pela saída, mas o git p4 rebase é um atalho para o git p4 sync seguido do git rebase p4/master.
É um pouco mais inteligente que isso, especialmente ao trabalhar com várias filiais (branches), mas essa é uma boa aproximação.
Agora, nossa história é linear novamente e estamos prontos para contribuir com nossas alterações de volta no Perforce.
O comando git p4 submit tentará criar uma nova revisão no Perforce para cada commit do Git que esteja entre a p4/master e a master.
Sua execução nos leva ao nosso editor favorito, e o conteúdo do arquivo se parece com isto:
# A Perforce Change Specification.
#
# Change: The change number. 'new' on a new changelist.
# Date: The date this specification was last modified.
# Client: The client on which the changelist was created. Read-only.
# User: The user who created the changelist.
# Status: Either 'pending' or 'submitted'. Read-only.
# Type: Either 'public' or 'restricted'. Default is 'public'.
# Description: Comments about the changelist. Required.
# Jobs: What opened jobs are to be closed by this changelist.
# You may delete jobs from this list. (New changelists only.)
# Files: What opened files from the default changelist are to be added
# to this changelist. You may delete files from this list.
# (New changelists only.)
Change: new
Client: john_bens-mbp_8487
User: john
Status: new
Description:
Update link
Files:
//depot/www/live/index.html # edit
######## git author ben@straub.cc does not match your p4 account.
######## Use option --preserve-user to modify authorship.
######## Variable git-p4.skipUserNameCheck hides this message.
######## everything below this line is just the diff #######
--- //depot/www/live/index.html 2014-08-31 18:26:05.000000000 0000
+++ /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/index.html 2014-08-31 18:26:05.000000000 0000
@@ -60,7 +60,7 @@
</td>
<td valign=top>
Source and documentation for
-<a href="http://www.perforce.com/jam/jam.html">
+<a href="jam.html">
Jam/MR</a>,
a software build tool.
</td>
Este é basicamente o mesmo conteúdo que você veria na execução da opção p4 submit, exceto o material no final onde o git-p4 o incluiu de forma prestativa.
O Git-p4 tenta honrar com as suas configurações do Git e do Perforce individualmente, quando for necessário fornecer um nome para o commit ou o seu changeset (conjunto de alterações), mas em alguns casos, será preciso substituí-lo.
Por exemplo, se a confirmação (commit) do Git importada foi escrita por um contribuinte que não tem uma conta de usuário no Perforce, você ainda pode querer que o changeset pareça que ele que o escreveu (e não você).
O Git-p4 importou generosamente a mensagem do commit do Git como o conteúdo desse changeset do Perforce; portanto, tudo o que precisamos fazer é salvar e sair duas vezes (uma para cada commit). A saída (output) resultante do shell será parecida com isto:
$ git p4 submit
Perforce checkout for depot path //depot/www/live/ located at /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/
Synchronizing p4 checkout...
... - file(s) up-to-date.
Applying dbac45b Update link
//depot/www/live/index.html#4 - opened for edit
Change 12143 created with 1 open file(s).
Submitting change 12143.
Locking 1 files ...
edit //depot/www/live/index.html#5
Change 12143 submitted.
Applying 905ec6a Change page title
//depot/www/live/index.html#5 - opened for edit
Change 12144 created with 1 open file(s).
Submitting change 12144.
Locking 1 files ...
edit //depot/www/live/index.html#6
Change 12144 submitted.
All commits applied!
Performing incremental import into refs/remotes/p4/master git branch
Depot paths: //depot/www/live/
Import destination: refs/remotes/p4/master
Importing revision 12144 (100%)
Rebasing the current branch onto remotes/p4/master
First, rewinding head to replay your work on top of it...
$ git log --oneline --all --graph --decorate
* 775a46f (HEAD, p4/master, p4/HEAD, master) Change page title
* 05f1ade Update link
* 75cd059 Update copyright
* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head
O resultado é como se tivéssemos feito um git push, que é a analogia mais próxima ao que realmente aconteceu.
Note que durante esse processo todo commit no Git se transforma em um changeset no Perforce; se você quiser esmagá-los em um único changeset, poderá fazê-lo com um rebase interativo antes de executar a opção git p4 submit.
Note também que as hashes de SHA-1 em todos os commits enviados (submitted) como changesets foram alteradas; isto é devido a uma linha a mais ao final de cada commit no qual o git-p4 foi convertido:
$ git log -1
commit 775a46f630d8b46535fc9983cf3ebe6b9aa53145
Author: John Doe <john@example.com>
Date: Sun Aug 31 10:31:44 2014 -0800
Change page title
[git-p4: depot-paths = "//depot/www/live/": change = 12144]
O que acontecerá se você tentar enviar um commit consolidado (merge commit)? Vamos tentar. Eis a situação na qual nos encontramos:
$ git log --oneline --all --graph --decorate
* 3be6fd8 (HEAD, master) Correct email address
* 1dcbf21 Merge remote-tracking branch 'p4/master'
|\
| * c4689fc (p4/master, p4/HEAD) Grammar fix
* | cbacd0a Table borders: yes please
* | b4959b6 Trademark
|/
* 775a46f Change page title
* 05f1ade Update link
* 75cd059 Update copyright
* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head
O histórico no Git e no Perforce diverge após a 775a46f.
O lado do Git possui duas comissões, logo em seguida, um merge de commits com o head no Perforce e então um outro commit.
Vamos tentar enviar isso no topo de um changeset único do lado do Perforce.
Vejamos o que acontece se tentarmos os enviar agora:
$ git p4 submit -n
Perforce checkout for depot path //depot/www/live/ located at /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/
Would synchronize p4 checkout in /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/
Would apply
b4959b6 Trademark
cbacd0a Table borders: yes please
3be6fd8 Correct email address
A bandeira (flag) -n é um atalho para a --dry-run, a qual tenta informar o que ocorreria se o comando enviar (submit) rodasse em caráter definitivo.
Neste caso, parece que estaríamos criando três changesets do Perforce, as quais correspondem a três commits de não-mesclagem que ainda não existem no servidor do Perforce.
Parece exatamente o que procuramos, vejamos como ele se tornará:
$ git p4 submit
[…]
$ git log --oneline --all --graph --decorate
* dadbd89 (HEAD, p4/master, p4/HEAD, master) Correct email address
* 1b79a80 Table borders: yes please
* 0097235 Trademark
* c4689fc Grammar fix
* 775a46f Change page title
* 05f1ade Update link
* 75cd059 Update copyright
* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head
Nossa história se tornou linear, exatamente como se a tivéssemos refeito a base (rebased) antes de submeter (que é de fato e exatamente o que aconteceu). Isso significa que você pode estar livre para criar, trabalhar, descartar e unificar os branches do lado do Git, sem receios de que a sua história se torne de alguma maneira, incompatível com o Perforce. Se você puder fazer isso (rebase), poderá contribuir para um servidor do Perforce.
Ramificação (Branching)
Se o seu projeto no Perforce tem múltiplas branches (ramificações), a sua sorte não esgotou; o git-p4 pode os administrar a fazer a impressão de um modo no qual se perceba como o Git. Digamos que o seu depósito do Perforce seja exposto da seguinte forma:
//depot
└── project
├── main
└── dev
E vamos dizer que você possua um branch dev, no qual tenha uma especificação de visão que se pareça a esta:
//depot/project/main/... //depot/project/dev/...
O Git-p4 pode detectar automaticamente a situação e fazer a coisa certa:
$ git p4 clone --detect-branches //depot/project@all
Importing from //depot/project@all into project
Initialized empty Git repository in /private/tmp/project/.git/
Importing revision 20 (50%)
Importing new branch project/dev
Resuming with change 20
Importing revision 22 (100%)
Updated branches: main dev
$ cd project; git log --oneline --all --graph --decorate
* eae77ae (HEAD, p4/master, p4/HEAD, master) main
| * 10d55fb (p4/project/dev) dev
| * a43cfae Populate //depot/project/main/... //depot/project/dev/....
|/
* 2b83451 Project init
Observe o especificador “@all” no caminho (path) do depósito; que diz a git-p4 para clonar não apenas o changeset mais recente nessa sub-árvore (subtree), como a todos os changesets que em algum momento já mexeram naqueles caminhos. Isto se aproxima mais aos conceitos do Git em um clone, mas, se você estiver trabalhando num projeto de longo histórico, pode levar algum tempo.
A bandeira (flag) --detect-branches diz ao git-p4 que utilize as especificações dos branches do Perforce a fim de mapear as refs de Git nas branches.
Se os mapeamentos não estiverem presentes no servidor Perforce (onde sua utilidade no Perforce é de um modo perfeitamente válido), você pode dizer ao git-p4 do que os mapeamentos das branches se tratam e que você obterá o mesmo resultado:
$ git init project
Initialized empty Git repository in /tmp/project/.git/
$ cd project
$ git config git-p4.branchList main:dev
$ git clone --detect-branches //depot/project@all .
Ao setar a variável de configuração da git-p4.branchList em relação a main:dev informará a git-p4 que ambas as ramificações (branches) “main” e “dev” se tratam da mesma, onde a segunda em questão seja a filha da primeira.
Se nós usarmos git checkout -b dev p4/project/dev no momento e fizermos algumas comissões, o git-p4 será esperto o suficiente a destinar o branch correto, quando estivermos com o git p4 submit.
Infelizmente, o git-p4 não pode misturar clones rasos (shallow clones) com as múltiplas ramificações (branches); caso você tenha um enorme projeto e deseja trabalhar com mais de um branch, você deverá usar a função git p4 clone uma vez a cada branch ao qual você quer destinar as suas submissões.
Ao se criarem as branches de integração, você terá de fazer o uso de um cliente Perforce (Perforce client). O git-p4 só pode sincronizar e se destinar aos branches os quais estão em existência, e a cada changeset em nível linear a seu tempo. Se você unir (merge) dois branches no Git, e tentar enviar o novo changeset, tudo o que for estarão gravadas é um punhado de alterações nos arquivos; os metadados acerca do qual the sobre os branches que se envolvam nas perdas destas integrações.
Resumo do Git e do Perforce
O Git-p4 possibilita ao uso com o workflow do Git acompanhado de um servidor Perforce, e ele o faz muito bem. Porém, é importante recordar que o Perforce se responsabiliza da fonte de seus projetos, e que você apenas usa o Git de maneira local. Apenas tenha muito cuidado ao dividir as suas comissões (commits) no Git; caso você possua um acesso remoto ao qual as outras pessoas usam, não submeta (push) nenhum commit onde os mesmos não tenham sido enviados previamente a um servidor do Perforce.
Se você desejar que eles se unam (mix) de forma livre no controle das fontes (source control) de clientes (clients) entre o Perforce e o Git, e se for possível o convencimento do seu administrador de servidores à instalação dele, o Git Fusion faz do uso do Git num controle de versão clientelar da mais alta linha (first-class), em favor de um servidor de Perforce.