GIT
Gestão de artefatos através de repositórios GIT
Conteúdo |
Conceitos, termos e abreviações
artefato | Qualquer produto do ciclo de produção do projeto, podendo ser arquivos eletrônicos de código-fonte, arquivos eletrônicos de código compilado, a reunião desses arquivos, arquivos de documentação ou mesmo objetos físicos, como manuais impressos ou discos de instalação |
repositório | Sistema de controle de versões de artefatos eletrônicos em que os itens de configuração eletrônicos são armazenados com a garantia de possibilidade de reversão a uma versão anterior do item |
SCV | Sistema de Controle de Versões |
baseline | Conjunto de artefatos aprovados e revisados que serão utilizados para atividades posteriores. É definida pela gerência de projeto como base para evoluções e desenvolvimento futuro |
branch | Linha de desenvolvimento paralela à linha principal do repositório de código-fonte e documentação |
tag | Rótulo de identificação de um conjunto de itens de configuração que representa um todo de interesse. Ordinariamente, as versões de release e as baselines são identificadas com tags para melhor referência |
trunk/master | Linha principal de desenvolvimento existente no repositório |
merge | Operação de integração de alterações de um determinado branch (ramo) ao trunk ou a outro branch |
release | Versão do sistema validada para instalação, seja em produção, seja para homologação. Conjunto de itens enviado a um tribunal ou a um departamento para instalação, que não deve conter o código-fonte do sistema. Cada release pode encerrar novas funcionalidades ou mudanças decorrentes de evolução do sistema |
commit | Submeter ao sistema de controle de versão alterações em um conjunto de arquivos |
checkout | Copiar uma versão dos arquivos do sistema de controle de versão. Geralmente esta operação copia a última versão dos arquivos e, em alguns SCVs, o checkout inclui metadados necessários para o próximo commit. |
Introdução
GIT é um sistema de controle de versão de documentos distribuído capaz de hospedar grandes projetos de código aberto como o Kernel Linux. Sua natureza descentralizada facilita a gestão projetos que têm várias equipes de desenvolvimento.
O presente documento tem como objetivo fornecer, de forma simplificada, o conhecimento necessário para gestão de artefatos através de repositórios GIT.
Público alvo
Este documento se destina aos envolvidos no desenvolvimento do sistema PJe, seja no próprio Conselho Nacional de Justiça, seja nas equipes de desenvolvimento formadas por órgãos ou tribunais participantes do projeto.
Este documento não se destina ao desenvolvimento feito dentro de fábricas de software terceirizadas, desde que, nas entregas de artefatos, essas fábricas apresentem o produto dentro das presentes especificações.
Sistemas de controle de versão
Softwares de controle de versão são sistemas que registram as alterações em um conjunto de arquivos ao longo do tempo de modo a permitir a recuperação de versões específicas quando necessário.
Além de registrar as alterações de um conjunto de arquivos, os sistema de controle de versão podem conter outras funcionalidades e são divididos em três categorias principais:
Sistemas de controle de versão local
O método mais utilizado pelos usuários para controle de versão é copiar os arquivos em outro diretório. Esta abordagem é comum devido a sua simplicidade, mas também muito suscetível a erros. É fácil esquecer em que diretório está e acabar removendo ou sobrescrevendo o arquivo errado. Para resolver estes problemas, os programadores desenvolveram Sistemas de Controle de Versão (SCV) locais que possuem um simples banco de dados contendo todas as alterações dos arquivos, conforme a Figura 3.1-1:
Um dos sistemas de controle de versão mais populares é o rcs, incluso em várias distribuições Linux e no sistema operacional Mac OSX. Essa ferramenta funciona basicamente mantendo conjuntos de mudanças em um formato especial no disco. Dessa forma, o sistema permite a recriação de qualquer arquivo em qualquer momento aplicando as mudanças registradas.
Sistemas de controle de versão centralizado
Quando existe a necessidade de colaboração de um grupo de pessoas sobre um conjunto de arquivos, os SCV locais passam a não atender. Para resolver esse problema, sistemas de controle de versão centralizados (SCVC) foram desenvolvidos. Tais sistemas, como CVS, SVN e Perforce, têm um único servidor que contém todos os arquivos versionados e um número de usuários que realizam checkout (isto é, copiam a última versão do arquivo) de um local central (Figura 3.2-1).
Essa configuração oferece uma séria de vantagens, especialmente sobre os SCV locais. Por exemplo, todos os usuários sabem, até certo ponto, o que outros usuários do mesmo projeto estão fazendo. Os administradores possuem um controle refinado sobre quem pode fazer o quê, o que torna a administração destes repositórios muito mais simples do que lidar com bancos de dados locais em cada cliente. No entanto, esta mesma configuração também tem algumas desvantagens sérias. O mais óbvio é o único ponto de falha que o servidor centralizado representa. Se esse servidor cair por uma hora, durante aquela hora ninguém poderá colaborar ou até mesmo salvar as alterações realizadas nos arquivos em que estavam trabalhando. Se o disco rígido do servidor central for corrompido, e nenhum backup tenha sido realizado, todos os dados guardados serão perdidos – todo o histórico do projeto, exceto alguns arquivos que estiverem guardados nos computadores clientes. SCV locais sofrem do mesmo problema – sempre que o histórico for guardado em um único lugar, corre-se o risco de perder tudo.
Sistemas de controle de versão distribuído
É no problema do único ponto de falha que os Sistemas de Controle de Versão Distribuídos entram em cena. Em tais sistemas como Git, Mercurial, Bazaar ou Dracs, os clientes possuem um espelho completo do conteúdo do servidor, em vez de uma única versão do arquivo. Assim, se o servidor ficar corrompido, qualquer usuário do sistema pode restaurar completamente o histórico de modificações do projeto, evitando que os dados sejam perdidos. Nesses sistemas, cada checkout é uma cópia completa do conteúdo do servidor. (Figura 3.3-1)
Além disso, esses sistemas lidam muito bem com colaboração de vários grupos de trabalhos no mesmo projeto. Essa característica permite o desenvolvimento de vários fluxos de trabalho que não são possíveis em sistemas centralizados, como modelos hierárquicos.
Versionamento com o GIT
Nesta Seção serão apresentados os conhecimentos básicos sobre ferramenta GIT tais como detalhes de funcionamento, comandos, fluxo de trabalho e ferramentas de apoio.
Conceitos básicos
A principal diferença entre o GIT e outros SCVs é a maneira como o GIT trabalha sobre os dados. Conceitualmente, a maioria dos SCVs (CVS, SVN, Perforce, Bazar, e assim por diante) armazenam as informações como uma lista de arquivos baseados em mudanças. Isto é, esses sistemas armazenam o conjunto de arquivos e uma lista de mudanças que cada arquivo sofreu ao longo do tempo, conforme a Figura 4.1-1.
Por outro lado, o GIT organiza os dados como um conjunto de “fotos instantâneas” de um mini sistema de arquivos. Toda vez que uma alteração é submetida, o GIT “tira uma foto” de todos os arquivos naquele momento e armazena referência da “foto”. Para ser mais eficiente, os arquivos que não foram alterados não são armazenados novamente, o GIT cria apenas um vínculo para estes arquivos e os grava em sua “foto” (Figura 4.1-2).
A maneira como o GIT armazena as informações o torna diferente de todos os outros SCVs. Essa diferença faz com que o GIT reconsidere praticamente todos os aspectos de controle de versão que os outros sistemas copiaram de gerações anteriores. O GIT pode ser considerado mais como um mini sistema de arquivos com “superpoderes” do que como um simples SCV. Exploraremos alguns destes “poderes” nas seções seguintes.
Todas as operações são locais
A maioria das operações no GIT só necessitam de arquivos e recursos locais para serem executadas – geralmente nenhuma informação de outro computador na rede é necessária. Uma vez que o usuário possui toda a história do projeto no seu disco local, a maioria das operações parecem instantâneas.
Por exemplo, para mostrar o histórico de um projeto, o GIT não precisa ir no servidor para obter as informações – ele simplesmente lê a informação diretamente do banco de dados local. Caso seja necessário comparar a versão atual de determinado arquivo com outra de um mês atrás, o GIT recupera o arquivo de 1 mês atrás e faz um cálculo de diferenças.
As operações locais permitem que usuários façam praticamente tudo mesmo estando offline. Em casa, no shopping ou durante uma viagem, usuários podem continuar seus trabalhos, realizar commits e, quanto possuir uma conexão de rede, submeter as alterações ao servidor.
GIT tem integridade
Toda informação no GIT tem um cálculo de checksum antes de ser armazenado e é referenciado por esse cálculo. Isso significa que é impossível alterar o conteúdo de qualquer arquivo ou diretório dentro do GIT sem que ele saiba. Esta funcionalidade foi construída no GIT em seus níveis mais baixos e é totalmente incorporada à sua filosofia. O usuário não perde informações, seja por transferência ou falha de disco, sem que o GIT seja capaz de detectá-la.
O mecanismo que o GIT usa para seu cálculo de checksum é chamado hash SHA-1. É uma frase de 40 caracteres composta de dígitos (0-9) e letras (a-f) e é calculada conforme a estrutura do diretório ou o conteúdo do arquivo armazenado. Um hash SHA-1 é algo parecido com isto:
24b9da6552252987aa493b52f8696cd6d3b00373
É bem provável que o usuário veja estes valores hash em todo o lugar no GIT, pois o sistema os utiliza constantemente. Na verdade, o GIT armazena todas as informações pelo hash do conteúdo e não pelo nome do arquivo.
GIT somente adiciona dados
Quando alguma ação é executada no GIT, praticamente em todas as situações isso se traduz em adição de informações ao seu banco de dados. É muito difícil fazer alguma coisa no sistema que não seja reversível ou que apague algum tipo de dado. Como em qualquer SCV, você pode perder ou estragar informações que não foram submetidas ainda, mas uma vez submetida ao GIT, é muito difícil perder, especialmente se o usuário submeter seu repositório a outro (servidor) regularmente.
Os três estágios
Esta seção deve ser lida com atenção. Os conceitos introduzidos aqui serão utilizados por todo manual. Os arquivos controlados pelo GIT têm três estados principais: commited, modificado ou staged. Commited significa que os arquivos estão armazenado com segurança no banco de dados do GIT. Modificado significa que houve mudanças nos arquivos mas não foram submetidos ainda. Staged significa que as alterações foram marcadas e serão submetidas no próximo commit.
Estes estados dividem um projeto GIT em três seções principais: o diretório GIT, o diretório de trabalho, e a área de staging (Figura 4.1.4-1).
O diretório do GIT é onde o GIT armazena o banco de dados de seu projeto. Esta é a parte mais importante do GIT, e é o que é copiado quando se clona o repositório de outro computador.
O diretório de trabalho é um checkout único de uma versão do projeto. Esses arquivos são retirados do banco de dados compactado do GIT e colocados no disco para que usuário possa modificá-los.
A área de staging é um simples arquivo, geralmente inserido no diretório do GIT, que armazena as informações que serão submetidas no próximo commit.
Embora muitos usuários do GIT utilizem a área de staging, o GIT permite que as modificações realizadas nos arquivos sejam submetidas diretamente, assim como é feito em outros SCVs.
Comandos
Nesta seção serão apresentados os comando básicos do GIT e a comparação com os comandos do SVN. Embora muitos usuários trabalhem com o GIT através de interfaces gráficas, os comandos devem ser conhecidos para uma melhor comunicação entre os membros da equipe.
Usuários que queiram trabalhar com o prompt de comandos podem usar o programa Terminal, caso utilizem o sistema operacional Mac OSX ou Linux. Caso utilizem o sistema operacional Windows, existe um Terminal que acompanha a instalação padrão do GIT. Essas ferramentas serão apresentadas com mais detalhes na Seção Ferramentas de apoio.
Todos os comandos da tabela abaixo devem ser executados no diretório de trabalho.
Comando | Descrição | Equivalente SVN |
git init | Inicializa um repositório GIT no diretório corrente | -- |
git clone <endereço> | Clona todo conteúdo de um repositório remoto para o diretório corrente e inicializa o repositório local | svn checkout <endereço> |
git add <arquivo> | Adiciona arquivos/diretórios para área de staging. Este comando também é utilizado para rastrear novos arquivos/diretórios | svn add <arquivo> |
git commit [–a] -m “<mensagem>” | Submete as alterações ao repositório local. Deve-se informar uma mensagem que justifique o commit. Muitas vezes as mensagens são utilizadas para integrar o GIT a sistemas de gestão como JIRA. O usuário deve informar na mensagem apóo número da issue para que o JIRA rastreie as modificações no projeto. O argumento -a informa o que o GIT deverá submeter todas as alterações de todos os arquivos e não somente aqueles que se encontram no status Staged | -- |
git rm <arquivo> | Remove um arquivo/diretório do diretório atual e informa que GIT deverá remover este arquivo no próximo commit | svn rm <arquivo> |
git mv <orig> <dest> | Move um arquivo de origem para um determinado destino. O comando pode ser utilizado também para renomear arquivos/diretórios | svn mv <orig> <dest> |
git reset | Reverte todas as modificações realizadas nos arquivos e ainda não submetidas | svn revert |
git branch <branch> | Cria um novo branch de nome <branch> | -- |
git checkout [-b] <branch> | Troca o branch de trabalho atual para o branch de nome <branch>. Caso o argumento -b seja informado o GIT cria um branch novo. Mais detalhes sobre branches no GIT serão apresentados na seção seguinte | svn switch <branch> |
git remote add <endereço> | Adiciona um link de um repositório remoto ao repositório local. Um repositório GIT pode conter vários repositórios remotos | -- |
git push <remote> <branch> | Submete todas as alterações armazenadas no repositório local ao repositório remoto de nome <remote> em um branch específico. O branch é automaticamente criado caso não exista no repositório remoto | svn commit |
git fetch | Copia todos os dados do repositório remoto de nome <remote> para o repositório local. As alterações não são incorporadas ao repositório local. Este comando é semelhante ao git clone, exceto que é executado em um repositório existente | -- |
git pull <remote> <branch> | Copia todas as mudanças do repositório remoto de nome <remote> e reintegra ao repositório local. Caso o argumento <branch> não seja informado, o comando é executado sobre o branch de trabalho atual | svn update |
Branching
Praticamente todos os SCV possuem algum suporte a ramificação (Branching). Ramificação significa que o usuário pode divergir da linha principal de desenvolvimento e continuar a fazer seu trabalho sem mexer com esta linha. No entanto, a maioria das ferramentas de controle de versão tornam o processo de ramificação lento e complicado, muitas vezes exigindo que o desenvolvedor crie uma nova cópia do diretório de código-fonte, o que pode levar um longo tempo para grandes projetos.
Muitos usuários referem-se ao modelo de ramificação do GIT como sua principal vantagem. Mais uma vez os desenvolvedores do GIT repensaram totalmente o modelo de forma a torná-lo diferente de todos os outros SCV. A maneira como o GIT trata ramificações é incrivelmente leve, fazendo com que as operações de ramificação e a alternância entre os ramos sejam praticamente instantâneas. Ao contrário dos outros SCV, o GIT incentiva o fluxo de trabalho através de branches e merges frequentes, que podem ocorrer várias vezes durante o dia. Entender e dominar esta característica dá ao usuário uma ferramenta poderosa e única que pode literalmente mudar sua forma de trabalho.
Para entender como o GIT trata ramificações, deve-se lembrar de como o GIT armazena informações. De acordo com seção anterior, o GIT não armazena os dados como um conjunto de mudanças ou deltas, mas sim como uma série de “fotos instantâneas”. Ao realizar um commit o GIT armazena um objeto contendo alguns metadados, zero ou mais ponteiros (hash) para os commits anteriores e um ponteiro para a “foto instantânea” dos arquivos (Figura 4.3-1).
Sendo assim, um branch no GIT é simplesmente um ponteiro móvel sobre um destes commits. O branch padrão no GIT chama-se master e cada commit realizado sobre ele move o ponteiro para frente (Figura 4.3-2).
Um novo branch não passa de um novo ponteiro móvel sobre o commit do ponteiro atual. A operação é realizada por meio do comando git branch <nome>.
O GIT possui também um ponteiro especial chamado HEAD para identificar em que branch o usuário está trabalhando. Quando um usuário troca de branch, através do comando git checkout <branch>, o ponteiro HEAD é movido para o branch de nome <branch> e o diretório de trabalho é restaurado de acordo com o branch atual. O usuário deve lembrar que o comando git branch cria apenas um novo branch, não comutando para o mesmo.
Suponha que o usuário deseje começar um trabalho no branch “testing”. Ele deverá trocar para o branch “testing”, alterar os arquivos que deseja e realizar um commit.
$ git checkout testing $ vim test.rb $ git commit -a -m “Commit de teste”
Desta forma, o GIT moverá o ponteiro HEAD para o branch “testing” e o ponteiro “testing” para o último commit realizado (Figura 4.3-5)
Suponha agora que o usuário resolveu continuar seu trabalho no branch “master”. Ele troca novamente para o branch “master”, modifica alguns arquivos e submete as alterações.
$ git checkout master $ vim main.rb $ git commit –a –m “Modificação no método main”
A Figura 4.3-6 expõe o estado final do repositório GIT. Como pode ser observado, as linhas de desenvolvimento divergiram. O usuário poderá permutar entre os branches quando quiser e continuar seu trabalho de forma paralela até que esteja pronto para fundi-los (merge). O processo de merging será apresentado em detalhes na próxima seção.
Merging
Para ilustrar como funciona o processo de merging no GIT será exposto um pequeno exemplo contendo uma série de passos que serão utilizados para definir o fluxo de trabalho apresentado na seção seguinte. Suponha um repositório de acordo a Figura 4.4-1.
O desenvolvedor resolveu iniciar o trabalho da uma demanda sob o nome Issue #53, registrada em algum sistema de gestão de demandas. Para ficar claro, o GIT não é associado a qualquer sistema de gerenciamento de demandas; no entanto, como a Issue #53 é um tópico focado que precisa ser desenvolvido, aconselha-se criar um branch para realizar o trabalho. Desta forma, antes de iniciar o trabalho, o desenvolvedor executou o comando checkout com o parâmetro –b com o objetivo criar e trocar para um novo branch.
$ git checkout -b Issue#53 Switched to a new branch "Issue#53"
A Figura 4.4-2 ilustra o resultado do comando.
O desenvolvedor modificou alguns arquivos e realizou um commit, movendo o ponteiro do branch Issue#53 para frente (Figura 4.4-3).
$ vim index.html $ git commit –a -m “Novo rodapé da página principal[Issue #53]”
Antes de concluir o trabalho, um problema grave surgiu no projeto e precisa ser corrigido imediatamente. O desenvolvedor não irá submeter o hotfix junto com a Issue em que estava trabalhando, logo deverá voltar ao branch master para aplicar a correção.
$ git checkout master Switched to branch "master"
Neste ponto, o diretório de trabalho do projeto está exatamente do jeito que era antes de começar a trabalhar na Issue #53, permitindo que se concentre na correção. Este ponto merece uma atenção especial: o GIT redefine todo o diretório de trabalho, removendo, substituindo ou adicionando arquivos, para ficar exatamente igual ao commit que o branch está apontando.
Agora o desenvolvedor pode começar a trabalhar no hotfix. Deve-se criar um branch hotfix e submeter as modificações até que esteja pronto para entrar em produção (Figura 4.4-4).
$ git checkout -b 'hotfix' Switched to a new branch "hotfix" $ vim index.html $ git commit -a -m 'Corrigido endereço de e-mail inválido' [hotfix]: created 3a0874c: "Corrigido endereço de e-mail inválido " 1 files changed, 0 insertions(+), 1 deletions(-)
Uma vez concluído, o desenvolvedor deve reintegrar o branch hotfix ao master para subir a alteração no ambiente de produção.
$ git checkout master $ git merge hotfix Updating f42c576..3a0874c Fast forward README | 1 - 1 files changed, 0 insertions(+), 1 deletions(-)
É importante notar a expressão “Fast forward” exibida durante o merge. Essa expressão indica que o GIT apenas moveu o ponteiro do branch master para frente, pois o commit do branch hotfix é um sucessor direto do commit referenciado pelo branch master (Figura 4.4-5).
Após subir a correção, o desenvolvedor pode voltar a trabalhar na Issue #53. Antes de continuar, é necessário remover o branch hotfix, pois uma vez que o branch master aponta para o mesmo commit do hotfix, o branch não será mais necessário. No GIT, um branch pode ser removido acrescentando o parâmetro –d ao comando git branch.
$ git branch –d hotfix Deleted branch hotfix (3a0874c).
Agora o desenvolvedor pode voltar ao trabalho na Issue #53 e concluir as modificações (Figura 4.4-6).
$ git checkout Issue#53 Switched to branch "Issue#53" $ vim index.html $ git commit -a -m 'Finalizado novo rodapé [issue 53]' [Issue#53]: created ad82d7a: " Finalizado novo rodapé [issue 53]" 1 files changed, 1 insertions(+), 0 deletions(-)
É importante notar aqui que o trabalho realizado no branch hotfix não está contido nos arquivos do branch Issue #53. Caso necessário, o desenvolvedor pode puxar as modificações reintegrando o branch master ao Issue#53 através do comando git merge master, ou esperar para subir as mudanças da Issue#53 quando reintegrá-lo ao master. Mais detalhes sobre o fluxo de trabalho serão apresentados na seção seguinte.
Supondo que o trabalho na demanda Issue#53 esteja completo, o branch Issue#53 deve ser reintegrado ao master. Dessa forma, o desenvolvedor irá reintegrar o branch Issue#53 assim como fez com o branch hotfix. Tudo o que deverá fazer é trocar para o branch que irá receber a reintegração (master) e então executar o comando git merge informando o branch que será reintegrado (Issue#53).
$ git checkout master $ git merge Issue#53 Merge made by recursive. README | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
Dessa vez, o merge foi um pouco diferente que o anterior, já que o histórico de desenvolvimento divergiu em algum commit passado e o GIT precisa realizar algum trabalho. Nesse caso, o GIT irá realizar uma fusão de três vias simples, usando os dois commits referenciados pelos branches e o ancestral em comum entre eles, conforme a Figura 4.4-7.
Em vez de simplesmente mover o ponteiro do branch master para frente, o GIT cria automaticamente gera um novo commit resultante do merge de três vias (Figura 4.4-8).
Após concluir a reintegração, o desenvolvedor pode remover com segurança o branch Issue#53 e fechar a demanda no sistema de controle de demandas.
$ git branch -d Issue#53
Dois pontos interessantes sobre o sistema de merge do GIT devem ser observados: ao contrário de outros SCVs, o GIT seleciona automaticamente o ancestral em comum, retirando esta responsabilidade do desenvolvedor; os commit resultantes de merge permitem que se faça um rastreamento completo de todas as mudanças do sistema, mesmo após a remoção dos branches intermediários.
Fluxo de trabalho
Após uma breve introdução sobre os principais conceitos e propriedades do GIT é necessário definir o fluxo de trabalho que será adotado por todos os colaboradores do CNJ e órgãos parceiros.
A definição do fluxo de trabalho tem como objetivo uniformizar o uso do GIT em conjunto com outras ferramentas, bem como facilitar a comunicação entre os integrantes de diferentes equipes de desenvolvimento.
O fluxo de desenvolvimento é iniciado com o cadastro de uma demanda no sistema de controle de demandas adotado. No caso do CNJ, o sistema adotado foi o JIRA e deve ser conhecido por todos os colaboradores. No JIRA, a palavra “issue” significa “demanda” e será utilizada no restante do documento.
Suponha que o projeto PJE esteja hospedado em um servidor GIT sob o endereço git.cnj.jus.br/pje.git. Antes de iniciar o trabalho, o colaborador terá de baixar o projeto para um repositório local através do comando git clone.
$ git clone git@git.cnj.jus.br:pje/pje.git Cloning into 'pje'... remote: Counting objects: 8539, done. remote: Compressing objects: 100% (4764/4764), done. remote: Total 8539 (delta 3693), reused 8484 (delta 3661) Receiving objects: 100% (8539/8539), 13.65 MiB | 2.62 MiB/s, done. Resolving deltas: 100% (3693/3693), done. Checking out files: 100% (7789/7789), done.
Uma vez concluída a importação, o usuário pode iniciar o trabalho. Basicamente o fluxo é definido por uma única regra:
Nunca, jamais submeta um commit no branch master
Esta regra, embora radical, foi estabelecida por dois motivos: (i) submeter commits no branch master impede que o usuário trabalhe em mais de uma issue ao mesmo tempo; (ii) o branch master, no repositório central, é configurado para receber commits somente dos gestores do projeto ou revisores de commits.
Suponha que uma issue de nome PJE-234 foi aberta e precisa ser resolvida imediatamente. O responsável pelo desenvolvimento terá de criar um branch com o nome “pje-234” e submeter commits neste branch até concluir o trabalho.
$ git branch –b pje-234 Switched to a new branch 'pje-234' $ vim arquivo1.java $ vim arquivo2.java $ vim arquivoM.java $ git commit –am “Conclusão da primeira tarefa na Issue pje-234” $ vim arquivo3.java $ vim arquivo4.java $ vim arquivoN.java $ git commit –am “Conclusão da segunda tarefa na Issue pje-234” $ vim arquivoO.java $ git commit –am “Issue pje-234 concluída”
Uma vez concluído, o desenvolvedor irá submeter o código ao servidor de origem através do comando git push.
$ git push origin pje-234 Counting objects: 5, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 349 bytes, done. Total 3 (delta 1), reused 0 (delta 0) To git@gitcnj:pje.git 4efa2aa..ccb648e pjeii-2233 -> pjeii-2233
Após a submissão do código, o desenvolvedor terá de abrir uma solicitação de merge de código através de uma interface de gestão do GIT. Essa solicitação será analisada pelos revisores de commit antes de ser integrada ao master do projeto. Uma vez aberta a solicitação, o desenvolvedor pode resolver a issue no JIRA.
Antes de iniciar o desenvolvimento da próxima issue, é aconselhável ter a última versão do branch master para evitar conflitos durante a reintegração do código. A atualização do código é realizada por meio do comando git pull.
$ git checkout master Switchet to branch 'master' $ git pull --rebase origin master
Fluxo com mais de um usuário por issue
Suponha que uma issue tenha uma subtarefa de alteração de banco de dados. O código submetido deve ser validado junto com os scripts de banco antes de serem reintegrados ao branch master. No entanto, o código e os scripts de alteração de banco de dados serão desenvolvidos por equipes distintas em momentos distintos.
Tome como exemplo a issue pje-234. Após o primeiro desenvolvedor submeter o código no branch pje-234 e solicitar o merge request, a equipe de banco de dados pode baixar todas as alterações e continuar o trabalho.
$ git fetch origin
Após baixar o código, é necessário criar um branch local com o mesmo nome e reintegrar os commits do branch remoto.
$ git checkout –b pje-234 Switched to new branch ‘pje-234’ $ git rebase remotes/origin/pje-234
Agora o repositório está pronto e a equipe de banco pode continuar o trabalho no branch de onde o desenvolvedor parou.
$ vim script.sql $ git commit –am “Scripts de banco de dados para Issue 234” $ git push origin pje-234
Ferramentas de apoio
Nesta seção serão apresentadas as ferramentas de apoio de trabalho com o GIT como interfaces gráficas e a interface de gestão GitLab. Instalação e configuração de tais ferramentas não fazem parte do escopo deste documento.
Clientes
Nesta seção serão tratados todos os comandos básicos do GIT e suas execuções em diferentes interfaces.
EGit
O EGit é um interface integrada ao Eclipse para trabalhar com repositórios GIT. Por adotar o padrão de conectores do Team, a interface possui muita semelhança com interfaces de outros SCVs como o Subversive ou Subversion, apenas mudando o nome de alguns comandos.
- Importando um projeto
Antes de importar um projeto, registre sua chave pública no repositório central conforme os passos descritos na seção Criando um par de chaves. Abra a tela de Preferências do Eclipse, selecione a opção General > Network Connections > SSH2 e adicione a chave privada através do botão Add Private Key.
Clique no bontão "Open Perspective" localizado no canto superior direito conforme e Figura 4.6.1.1-1
Selecione a perspectiva "Git Repository Exploring" de acordo com a Figura 4.6.1.1-2 e clique em "Ok"
Na perspectiva de repositórios Git selecione a opção "Clone a Git Repository" (Figura 4.6.1.1-3)
Preencha o campo URL conforme a Figura 4.6.1.1-4 e clique em "Next".
Selecione todos os branches do projeto e clique em "Next".
Selecione a pasta de destino e clique em "Finish" (Figura 4.6.1.1-6).
Após a conclusão da clonagem do repositório, mude para a perspectiva "J2EE" clique com o botão direito na área da aba "Project Explorer" e selecione a opção "Import > Import...". Conforme a Figura 4.6.1.1-7, selecione opção "Existing Maven Projects" e clique em "Next"
Clique em "Browse" e selecione o diretório de clonagem do repositório. Aguarde o reconhecimento do projeto e clique em "Finish" (Figura 4.6.1.1-7.1).
Após a importação do projeto é necessário associá-lo com o repositório Git local. Clique com o botão direito no projeto e selecione a opção "Team > Share Project". Selecione a opção "Git" e clique em "Next" conforme a Figura 4.6.1.1-7.1.
Marque as opções conforme a Figura 4.6.1.1-7.1 e clique em "Finish".
- Criando um branch
Quando a importação do projeto estiver concluída, o menu Team aparecerá com todos os comandos básicos do GIT (Figura 4.6.1.1-8).
Para criar um novo branch no repositório local, clique com o botão direito no projeto e selecione a opção Team > Switch To > New Branch. Informe o nome do branch, selecione as outras opções conforme a Figura 4.6.1.1-9 e clique em Finish.
Para trocar de branch apenas selecione o branch que deseja no menu Team > Switch To.
O usuário deve lembrar que todos os branches criados encontram-se no repositório local. O processo para criar um branch no servidor será apresentado mais a frente.
- Commit
Selecione a opção Team > Commit para realizar um commit no repositório local. Informe a mensagem que justifique o commit e selecione todos os arquivos que serão armazenados conforme a Figura 4.6.1.1-11. Caso queria que o commit seja submetido ao repositório central, selecione a opção “Push the changes to upstream”. Uma vez selecionada, o GIT criará um branch remoto com o mesmo nome do branch local e irá submeter o commit neste branch. Esta opção é um atalho para o comando git push.
- Submetendo código
Selecione a opção Team > Push to Upstream para submeter todas as alterações para o repositório remoto. Uma janela será exibida informando o resultado do comando (Figura 4.6.1.1-12).
- Atualizando o repositório
Selecione a opção Team > Fetch from Upstream. O comando irá baixar todas os dados do repositório remoto incluindo branches e tags. Uma janela será exibida informando o resultado do comando (Figura 4.6.1.1-13).
No entanto, as mudanças baixadas não foram reintegradas aos branches locais. Para realizar a reintegração, mude para o branch que deseja e selecione a opção Team > Rebase. Uma janela será exibida pedindo que informe o branch que deseja reintegrar. Selecione o branch que deseja da pasta “Remote Tracking” e clique em Rebase (Figura 4.6.1.1-14).
Um atalho para esta operação é o git pull e pode ser executado pela opção de menu Team > Pull. A atualização é geralmente executada em branches que têm colaboração de outros usuários, como o master e o stable.
TortoiseGIT
O TortoiseGIT é um port para o GIT do popular cliente para SVN chamado TortoiseSVN. Integrado ao Windows Explorer o cliente oferece uma interface simples e completa. Sua semelhança com o TortoiseSVN facilita a migração de usuários familiarizados com o SVN.
Importando um projeto
Antes de importar um projeto, registre sua chave pública no repositório central conforme os passos descritos na Seção Criando um par de chaves.
Para importar um projeto, clique com o botão direito no Windows Explorer e selecione a opção “Git Clone...” (Figura 4.6.1.2-1). Será exibida uma janela conforme a Figura 4.6.1.2-2 pedindo a URL do projeto, o diretório de destino e a chave privada de conexão. Preencha os dados conforme a figura e clique em OK.
- Criando um branch
Quando a importação do projeto estiver concluída, o menu TortoiseGit aparecerá com todos os comandos básicos do GIT (Figura 4.6.1.2-3).
Para criar um branch no repositório local, selecione a opção TortoiseGit > Create Branch.... Informe o nome do branch e selecione as outras opções conforme a Figura 4.6.1.2-4 e clique em Ok. Caso queira trocar para o novo branch criado, selecione a opção Switch to new branch.
Para trocar de branch, selecione a opção TortoiseGit > Switch / Checkout..., informe o branch que deseja usar (Figura 4.6.1.2-5) e clique em Ok. Selecione apenas os branches locais pois os remotos são atualizados de forma automática pelo GIT.
O usuário deve lembrar que todos os branches criados encontram-se no repositório local. O processo para criar um branch no servidor será apresentado mais a frente.
- Commit
Para realizar um commit no repositório local, selecione a opção Git Commit -> “<branch>”, onde <branch> é o ramo em que se está trabalhando. Informe a justificativa e selecione todos os arquivos que serão armazenados conforme a Figura 4.6.1.2-6.
- Submetendo código
Selecione a opção TortoiseGit > Push para submeter as modificações do branch atual para o repositório remoto. Informe, no campo Remote, o branch que deseja submeter as alterações e selecione as opções conforme a Figura 4.6.1.2-7.
- Atualizando repositório
Selecione a opção TortoiseGit > Fetch para baixar todas as modificações do repositório remoto. Selecione as opções conforme a Figura 4.6.1.2-8 e clique em Ok.
No entanto, as mudanças baixadas não foram reintegradas aos branches locais. Para realizar a reintegração e selecione a opção TortoiseGit > Rebase. Selecione o branch de destino (Branch) e origem (UpStream) e clique em Start Rebase (Figura 4.6.1.2-9).
Um atalho para esta operação é o git pull e pode ser executado pela opção de menu TortoiseGit > Pull. A atualização é geralmente executada em branches que têm colaboração de outros usuários, como o master e o stable.
Interface de gestão GitLab
O projeto GitLab é uma interface open source para administração de repositórios GIT com suporte a issues, wiki, controle de acesso, visualização de código e solicitações de merge(merge requests).
O GitLab pode ser considerado como uma ponte de comunicação entre todos os colaboradores do projeto. Por meio dele os desenvolvedores revisam código, discutem soluções, abrem chamados e compartilham conhecimento. Junto com o JIRA, o GitLab é uma ferramenta indispensável para coordenação do projeto.
Embora tenha uma extensa lista de funcionalidades, a maioria delas foge do escopo deste documento.
Criando um par de chaves
Linha de comando
A maioria dos servidores GIT usa o protocolo SSH como meio de comunicação padrão devido ao seu alto nível de segurança. O protocolo exige um par de chaves RSA (público e privado) para autenticar e autorizar um usuário no repositório.
Em ambientes UNIX (Linux e Mac OSX) basta executar o comando abaixo informando seu e-mail institucional no argumento <email> e responder as perguntas do aplicativo:
$ ssh-keygen –t rsa –C <email>
Eclipse
O Eclipse fornece também uma ferramenta de geração de chaves SSH. Entre na tela de preferência do Eclipse ("Window > Preferences") e digite "SSH2" no campo de busca conforme a Figura 4.6.2.1-0.
Selecione a aba "Key Management" e clique no botão "Generate RSA Key" (Figura 4.6.2.1-0.1).
Copie a chave pública, salve a chave privada em uma pasta e cadastre a chave pública no Gitlab conforme os passos mostrados mais adiante.
Tortoise Git
Em ambientes Windows, é necessário executar o aplicativo Puttygen que acompanha a distribuição do TortoiseGit. Ao iniciar o aplicativo, conforme Figura 4.6.2.1-1, selecione o tipo de chave SSH-2 RSA, clique em Generate e passe o mouse aleatoriamente na região delimitada pelo campo “Key” até que a barra de progresso esteja completa.
Coloque seu e-mail institucional no campo Key comment e clique em Save private key. O aplicativo irá perguntar se deseja salvar a chave sem uma senha; clique em Sim e salve a chave privada em algum diretório sob o nome id_rsa.ppk. Caso utilize o Eclipse, é necessário criar uma chave OpenSSH. Selecione a opção Conversions > OpenSSH key, clique em Sim e salve a chave no mesmo diretório da chave .ppk sob o nome id_rsa.
Não feche o aplicativo!
Gitlab
Após salvar a chave privada, é necessário cadastrar a chave pública no GitLab. Copie o conteúdo do campo Public key for pasting into OpenSSH authorized_keys file. Abra o navegador no endereço do Gitlab local, passe o mouse sobre o avatar à direita e selecione a opção My Profile (Figura 4.6.2.1-3).
Na página do perfil, selecione a opção SSH Keys e clique no botão Add new (Figura 4.6.2.1-4).
Na página de adição de chave, cole o conteúdo copiado no campo Key e clique em Save para registrar a chave pública (Figura 4.6.2.1-5).
Abrindo uma solicitação de merge
Para abrir uma solicitação de merge, selecione o projeto que deseja no menu “Projects” à direita (Figura 4.6.2.2-1).
Na página do projeto, selecione a opção “Merge Request” localizada ao lado do botão “Download” (Figura 4.6.2.2-2).
Na página de Merge Requests do projeto (Figura 4.6.2.2-3), clique em New Merge Request para criar uma solicitação de merge.
Selecione o branch de origem (From), o branch de destino (To), o título do merge request e o responsável conforme a Figura 4.6.2.2-4.
Aguarde e acompanhe as suas solicitações de merge no menu Merge Requests em sua página inicial. (Figura 4.6.2.2-5)
Gestão de configuração
Nesta seção serão apresentados os processos de gestão de configuração do projeto.
Responsabilidades
atualizar
A gerência de configuração é atividade exercida por três papéis, cujas responsabilidades são descritas na tabela abaixo.
Papel | Responsabilidades |
Gerente de configuração ou integrador de configuração | i. elaborar e ajustar este documento; ii. controlar as baselines dos produtos; iii. realizar a integração (merge) junto às equipes de desenvolvimento; iv. realizar auditorias para garantir o cumprimento das atividades de gerenciamento de configuração pelas equipes de trabalho; v. informar o status da configuração; vi. definir permissões de acesso aos repositórios; vii. gerar tags; viii. gerar branchs; ix. gerar builds; x. gerar releases. |
desenvolvedor | i. respeitar as definições deste documento quando da elaboração ou modificação dos artefatos afetados pela gerência de configuração (itens de configuração) a ele atribuídos; ii. realizar a integração (merge) com a gerência de configuração; iii. gerar builds a pedido da gerência de configuração; iv. gerar releases a pedido da gerência de configuração. |
Analista de sistema | i. respeitar as definições deste documento quando da elaboração ou modificação dos artefatos afetados pela gerência de configuração (itens de configuração) a ele atribuídos. |
Versionamento
Os documentos relacionados ao projeto PJe são mantidos nesta wiki, no testlink e no Redmine. O versionamento adotado para os documentos será o disponível nas ferramentas citadas. Os códigos fontes, assim como artefatos de banco de dados, são mantidos no GIT. Seguem direcionamentos a respeito do versionamento dos artefatos.
Regras de Versionamento
O esquema de numeração de versões adotado pelo CNJ é baseado no esquema adotado pela organização Apache Foundation. O esquema define que uma versão é composta por quatro números inteiros, MAJOR.MINOR.MICRO.PATCH onde:
MAJOR | Número principal da versão, somente alterado quando: a)há modificação de arquitetura do sistema, ainda que não tenha havido modificação da estrutura de dados; b)há modificação da estrutura de dados que demanda uma migração significativa de uma base para outra base de dados, não sendo suficiente a mera concretização de scripts de migração de dados entre tabelas de um mesmo banco de dados. Esse número deve ser 0 para a versão anterior à primeira. |
MINOR | Número menor de versão, modificado sempre que houver inclusão de um ou mais conjuntos de novas funcionalidades. Esse número deve iniciar em 0 e deve ser reiniciado quando da troca do número principal. |
MICRO | Número micro de versão, modificado sempre que liberada uma versão de correção de erros ou de comportamento esperado na versão do sistema. Esse número deve iniciar em 0 e deve ser reiniciado quando da troca do número intermediário ou do número principal. Para versões intermediárias ou principais novas, antes da homologação, esse número deverá ser acrescido do milestone de liberação (M1, M2, M3 etc.) até que a versão seja homologada, quando receberá o número menor. |
PATCH | Número de correção de versão, modificado sempre que liberada uma versão de correção de erros críticos do sistema. |
O CNJ convencionou também um ciclo de lançamento de versões MINOR baseado em metodologias ágeis. Estas versões são lançadas a cada 6 semanas e possuem um conjunto de modificações planejadas, incluindo novas funcionalidades e correções. Conforme a metodologia adotada, as versões MINOR passam por um período de estabilização até que estejam aptas para uso em produção. Após a estabilização da versão, o ciclo de manutenção é iniciado e versões MICRO são disponibilizadas até que a próxima versão MINOR esteja pronta.
A Figura 5.1-1 apresenta em detalhes como o processo ocorre. O repositório possui dois branches principais: o master, que recebe todas as modificações planejadas para a próxima versão, e o stable, que representa a versão atual em produção.
Quando a nova versão está prestes a ser lançada, é realizado um merge entre os branches master e stable. Neste mesmo momento, um branch para a versão atual é criado e mantido até que a nova versão estável esteja pronta para produção. Logo após o merge, inicia-se o período de estabilização da nova versão com o objetivo de corrigir eventuais bugs introduzidos pelas novas funcionalidades. Uma vez estável, uma tag é marcada e a versão está pronta para ser usada em produção e, após o lançamento da versão, inicia-se o ciclo de manutenção.
Nomes das versões do sistema
As versões intermediárias do sistema receberão o nome de município brasileiro iniciado na letra de referência da versão, estas na ordem alfabética, que não contenha espaços ou caracteres especiais, obtidos a partir do nome das unidades federativas, essas na ordem alfabética inversa. Assim, por exemplo, temos:
Versão | Letra de referência | Unidade Federativa | Município existente na letra de referência |
1.0 | A | Tocantins | Alvorada |
1.1 | B | Sergipe | Balbinos |
1.2 | C | São Paulo | Capela |
1.4 | D | Santa Catarina | Descanso |
2.0 | E | Roraima | Esmeralda |
Versões de bibliotecas ou projetos utilitários do sistema
As bibliotecas ou projetos utilitários do sistema receberão sua numeração seguindo as seguintes regras:
X.Y.Z
, onde:
X | Número principal da versão, a ser alterada quando:a) opcionalmente, os desenvolvedores incluíram na versão substanciais alterações que melhoram as funcionalidades existentes; b) obrigatoriamente, quando a nova versão não é compatível, em nível de interface, com a versão de produção atual. A compatibilidade em nível de interface existe quando, substituída uma versão por outra em um projeto que somente faz uso das interfaces públicas da biblioteca, não há erro de compilação. Esse número deve ser 0 para a versão anterior à primeira. |
Y | Número intermediário de versão, a ser alterado quando há acréscimos de funcionalidades em relação à versão anterior e não foi mantida a compatibilidade em nível de interface. Esse número deve iniciar em 0 e reiniciado quando da troca do número principal. |
Z | Número menor de versão, modificado sempre que liberada versão de correção de erros ou de comportamento esperado na versão do sistema. Esse número deve iniciar em 0 e reiniciado quando da troca do número intermediário ou do número principal. Para versões intermediárias ou principais novas, antes da homologação, esse número deverá ser acrescido do milestone de liberação (M1, M2, M3 etc.) até que a versão seja homologada, quando receberá o número menor. |
Versões de banco de dados
Os dumps de bancos de dados seguirão a nomenclatura da versão a que estão vinculadas, acrescido de descritor de seu conteúdo, quando necessário.
Exemplos:
- pjedb_1.0.1_treinamento.backup
- pjedb_1.0.1_treinamentobin.backup
- pjedb_1.0.1_limpa.backup
- pjedb_1.0.1_limpabin.backup
- pjedb_1.2.0_limpa.backup
No caso de scripts de bancos de dados, deverá ser seguida a seguinte regra de nomenclatura:
- pjescript_importacao_<versaodestino>_<descricao>.sql
- pjescript_conversao_<vorigem>-<vdestino>_descricao.sql
, onde a descrição é opcional.
Exemplos:
• pjescript_importacao_1.0.1_cep.sql
• pjescript_conversao_1.0.1-1.0.1.sql
• pjescript_importacao_1.0.1_cnae.sql
Repositórios distribuídos
O GIT tem sido amplamente adotado em projetos de código e desenvolvimento abertos por permitir a coordenação de várias equipes de desenvolvimento de forma simples e transparente.
A Figura 5.2-1 apresenta uma organização de repositórios distribuídos com três papeis distintos. A disposição é formada por um repositório central (CNJ Abençoado), sob responsabilidade dos líderes do projeto, e repositórios locais para cada equipe participante do desenvolvimento.
Cada um dos repositórios locais tem uma equipe formada por desenvolvedores, revisores de código e um ou mais gestores. O processo de desenvolvimento estende fluxo definido em Fluxo de trabalho, acrescentando os seguintes passos:
- Os desenvolvedores resolvem issues e submetem as alterações nos repositórios locais através de branches específicos e solicitações de merge
- Os revisores testam e aprovam as solicitações, reintegrando os branches ao master
- Uma vez revisado, testado e aprovado, o gestor do repositório local submete a alteração para o repositório central e aguarda aprovação dos gestores do projeto.
Essa disposição traz duas vantagens em relação aos SCVs centralizados: (i) as equipes de locais de desenvolvimento têm total liberdade de organização; (ii) os líderes do projeto possuem total controle sobre o código submetido.
Política de acesso
atualizar
O PJe encerra um desenvolvimento colaborativo intenso que envolve múltiplas fábricas de software e analistas de sistemas, muitas vezes não localizados no mesmo ponto geográfico brasileiro. Em razão disso e da intensa necessidade de controle do código fonte com vistas a evitar um desenvolvimento não controlado, o acesso aos artefatos do sistema deve ser bem controlado.
Deve-se identificar esses acessos segundo os seguintes papéis:
Papel | Sigla | Descrição |
Membro do CG | CG | Magistrado membro do comitê gestor nacional. |
Gerente geral | GG | Servidor diretamente ligado ao CNJ que gerencia o projeto. |
Gerente de configuração | GC | Servidor diretamente ligado ao CNJ que procura assegurar o respeito ao plano de gerência de configuração. |
Gerente de desenvolvimento | GD | Servidor diretamente ligado ao CNJ que lidera o desenvolvimento do sistema, assegurando que os desenvolvedores respeitem as regras de configuração, de desenvolvimento e os requisitos especificados. |
Gerente de qualidade | GQ | Servidor diretamente ligado ao CNJ que lidera as atividades de testes e homologação de uma versão do sistema. |
Analista de requisitos | AR | Pessoa, servidor ou não, responsável por receber, analisar e formalizar os requisitos de uma determinada funcionalidade do sistema. |
Analista de bancos de dados | ABD | Servidor responsável por avaliar a estrutura de bancos de dados do sistema, com vistas a assegurar sua otimização quando necessário. |
Desenvolvedor | DES | Pessoa, servidor ou não, responsável por implementar um determinado requisito ou caso de uso no sistema. |
Membro de grupo de requisitos | ER | Servidor especialista em um determinado tópico que dará insumos para a preparação dos requisitos e dos casos de uso pertinentes. |
Fábrica | FAB | Tribunal ou terceiro contratado responsável por desenvolver uma determinada funcionalidade ou manter um determinado branch. Cabendo a ela realizar os commits na periodicidade definida neste documento respeitadas as regras estabelecidas de desenvolvimento e qualidade. |
Tomando por base tais papéis, os acessos devem ser concedidos seguindo a seguinte tabela:
Recurso | CG | GG | GC | GD | GQ | AR | ABD | DES | ER | FAB |
Redmine | L | LG | LG | LG | LG | L | L | L | L | L |
/aplicacao | - | L | L | LG | L | L | L | LG | - | LG* |
wikipje - gerência | L | L | LG | L | L | LG | L | L | L | LG* |
wikipje - casos de uso | L | L | LG | L | LG | LG | L | L | L | LG* |
wikipje - configuração | L | LG | LG | L | LG | L | L | L | L | L |
wikipje - regrasdenegocio | L | L | LG | L | LG | LG | L | L | L | LG* |
wikipje - requisitos | L | L | LG | L | LG | LG | L | L | L | LG* |
/aplicacao/bancodedados/* | L | L | LG | LG | L | L | LG | L | L | LG* |
testlink | L | LG | LG | L | LG | LG | L | L | L | LG* |
, onde:
- L = leitura
- LG = leitura e gravação
- LG* = leitura e gravação limitadas ao branch afetado a essa fábrica, ou no trunk, se atuando no trunk, devendo ela assegurar que os papéis correspondentes internos, nesses casos, respeitem iguais regras de permissão.