> For the complete documentation index, see [llms.txt](https://aurimrv.gitbook.io/tdd-python/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://aurimrv.gitbook.io/tdd-python/3-desenvolvimento-dirigido-por-teste/3-3-controle-de-versao-do-projeto.md).

# 3.3 Controle de Versão do Projeto

Sistemas de controle de versão se popularizaram e são ferramentas de fundamental importância na gerência de projetos e gestão de configurações de software.

Um dos mais populares atualmente é o [Git](https://git-scm.com/), que conta com diversas plataformas que o utiliza e oferece outras funcionalidades, como o [GitHub](https://github.com/), por exemplo, que oferece recursos de colaboração entre equipes.

Nesta seção, é apresentado como podemo utilizar o Git para controlar as versões do nosso software em desenvolvimento e integrar tudo no GitHub para facilitar a colaboração em nosso projeto.

#### Iniciando o repositório Git

Para que o Git possa controlar as alterações feitas em nossos arquivo é necessário indicar a ele qual será a pasta que irá corresponder ao nosso repositório local. No nosso exemplo, utilizaremos como base a pasta que contém o arquivo `manage.py`. Ela está localizada em `$HOME/superlists/superlists`.

Entretanto, primeiramente vamos mover nosso arquivo de teste (`functional_tests.py`) para essa pasta. Atualmente ele está localizado um nível abaixo, em `$HOME/superlists`. Em seguida, basta entrar na pasta que sera utilizada como nosso diretório de trabalho, e a qual desejamos colocar sobre controle de versão, para inicializar nosso repositório com o comando `git init`.

```bash
(superlists) tdd@mlp:~/superlists$ pwd
/home/tdd/superlists
(superlists) tdd@mlp:~/superlists$ mv functional_tests.py superlists/
(superlists) tdd@mlp:~/superlists$ cd superlists/
(superlists) tdd@mlp:~/superlists/superlists$ git init .
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint: 	git config --global init.defaultBranch <name>
hint: 
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint: 
hint: 	git branch -m <name>
Repositório vazio Git inicializado em  /home/tdd/superlists/superlists/.git/
(superlists) tdd@mlp:~/superlists/superlists$
```

O próximo passo é decidirmos o que iremos colocar sobre controle de versão. O conteúdo atual da pasta é esse:

```bash
(superlists) tdd@mlp:~/superlists/superlists$ ls
db.sqlite3  functional_tests.py  manage.py  superlists
```

#### Excluindo Arquivo do Controle de Versão

`db.sqlite3` é um arquivo de banco de dados e não queremos que ele seja mantido sobre controle de versão. Também vimos anteriormente o arquivo `geckodriver.log` , que é um arquivo de log de execução do Selenium, e também podemos descartar esse arquivo do controle de versão. Para fazer isso no Git, basta criarmos na raiz do repositório, um arquivo denominado especial denominado `.gitignore`. O seu conteúdo indica ao Git o que deve ser ignorado do controle de versão. Os comandos abaixo foram utilizados para a criação do arquivo `.gitignore`. Você também pode utilizar um editor de texto qualquer para essa mesma finalidade.&#x20;

```bash
(superlists) tdd@mlp:~/superlists/superlists$ echo "db.sqlite3" >> .gitignore
(superlists) tdd@mlp:~/superlists/superlists$ echo "db.sqlite3" > .gitignore
(superlists) tdd@mlp:~/superlists/superlists$ echo "geckodriver.log" >> .gitignore
(superlists) tdd@mlp:~/superlists/superlists$ cat .gitignore 
db.sqlite3
geckodriver.log
```

#### Adicionando Arquivos ao Controle de Versão

Decidido o que ignorar, os demais arquivos, em princípio, podem ser adicionados ao controle de versão. Para isso, usamos o comando `git add`, conforme abaixo. Para consultar o que foi adicionado ao controle de versão, basta usar o comando `git status`.

```bash
(superlists) tdd@mlp:~/superlists/superlists$ git add .
(superlists) tdd@mlp:~/superlists/superlists$ git status
No ramo master

No commits yet

Mudanças a serem submetidas:
  (utilize "git rm --cached <arquivo>..." para não apresentar)
	new file:   .gitignore
	new file:   functional_tests.py
	new file:   manage.py
	new file:   superlists/__init__.py
	new file:   superlists/__pycache__/__init__.cpython-310.pyc
	new file:   superlists/__pycache__/settings.cpython-310.pyc
	new file:   superlists/__pycache__/urls.cpython-310.pyc
	new file:   superlists/__pycache__/wsgi.cpython-310.pyc
	new file:   superlists/asgi.py
	new file:   superlists/settings.py
	new file:   superlists/urls.py
	new file:   superlists/wsgi.py
```

Como podemos observar, além dos arquivos `.gitignore`, `functional_tests.py` e `manage.py`, todos os arquivos do subdiretório `superlists` também foram incluídos mas, alguns deles não desejamos colocar sobre controle de versão, como é o caso dos arquivos da pasta `__pycache__`. Para isso, basta pedirmos ao Git que remova tais arquivos do controle de versão e adicionarmos os mesmos no arquivo `.gitignore`, conforme mostrado abaixo.

```bash
(superlists) tdd@mlp:~/superlists/superlists$ git rm -r --cached superlists/__pycache__
rm 'superlists/__pycache__/__init__.cpython-310.pyc'
rm 'superlists/__pycache__/settings.cpython-310.pyc'
rm 'superlists/__pycache__/urls.cpython-310.pyc'
rm 'superlists/__pycache__/wsgi.cpython-310.pyc'
(superlists) tdd@mlp:~/superlists/superlists$ echo "__pycache__" >> .gitignore 
(superlists) tdd@mlp:~/superlists/superlists$ echo "*.pyc" >> .gitignore
```

Verificando o status de nosso repositório tudo parece ok, exceto que como modificamos o arquivo `.gitignore`, ele precisa ser adicionado novamente ao repositório (linha 22) e, em seguida, as modificações precisam ser confirmadas com o comando `git commit` (linha 23).

```bash
(superlists) tdd@mlp:~/superlists/superlists$ git status
No ramo master

No commits yet

Mudanças a serem submetidas:
  (utilize "git rm --cached <arquivo>..." para não apresentar)
	new file:   .gitignore
	new file:   functional_tests.py
	new file:   manage.py
	new file:   superlists/__init__.py
	new file:   superlists/asgi.py
	new file:   superlists/settings.py
	new file:   superlists/urls.py
	new file:   superlists/wsgi.py

Changes not staged for commit:
  (utilize "git add <arquivo>..." para atualizar o que será submetido)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   .gitignore

(superlists) tdd@mlp:~/superlists/superlists$ git add .gitignore 
(superlists) tdd@mlp:~/superlists/superlists$ git commit -m "Repository Creation"
Author identity unknown

*** Please tell me who you are.

Run

  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.

fatal: unable to auto-detect email address (got 'tdd@mlp.(none)')
```

Caso se observe o erro acima, bast aexecutar os comando abaixo que será resolvido e repetir o comando de commit.

```
(superlists) tdd@mlp:~/superlists/superlists$ git config --global user.email "aurimrv@gmail.com"
(superlists) tdd@mlp:~/superlists/superlists$ git config --global user.name "Auri Vincenzi"
(superlists) tdd@mlp:~/superlists/superlists$ git commit -m "Repository Creation"
[master (root-commit) 4b686b6] Repository Creation
 8 files changed, 209 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 functional_tests.py
 create mode 100755 manage.py
 create mode 100644 superlists/__init__.py
 create mode 100644 superlists/asgi.py
 create mode 100644 superlists/settings.py
 create mode 100644 superlists/urls.py
 create mode 100644 superlists/wsgi.py

```

Observe que ao executar o comando `git commit` utilizamos o parâmetro `-m "<MENSAGEM>"`uma vez que toda confirmação exige uma mensagem explicando as alterações realizadas. Se o parâmetro -m for omitido, o Git irá abrir o editor de texto padrão para que uma mensagem seja digitada. Tais mensagens servem para orientar a equipe sobre quais alterações são propostas pelo referido `commit`.&#x20;

#### Sincronizando o Repositório Local com o Repositório Remoto

Até o momento, estamos utilizando a ferramenta Git para gerenciar as mudanças em nossa pasta local, mas podemos sincronizar nosso repositório local com repositórios remotos, na nuvem. Existem diversas alternativas para o caso do Git. Uma das mais populares é o [GitHub](https://github.com/). Nele é possível você ter uma conta gratuita para criação de repositórios.&#x20;

Desse modo, para que possamos sincronizar o repositório local com o remoto, é importante que você crie uma conta no [GitHub](https://github.com/) e, após isso, crie um *token* para permitir que o comando Git se comunique com seu repositório remoto no [GitHub](https://github.com/).&#x20;

Assumindo que você já tenha criado sua conta no GitHub, a criação do *token* pode ser feita clicando sobre sua foto na conta e escolhendo a opção *Settings*, conforme imagem abaixo:

![Configurações no GitHub](https://976726803-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MXihT3dLI0T6GoCW1P1%2F-MZKL_gTrqKfy2r3Foaj%2F-MZKLeZq_bJBN_4KT1T3%2Fgithub-01.png?alt=media\&token=8940410f-d4fb-404e-af4c-95da2ceda136)

Em seguida, no menu da lateral esquerda, selecione a opção *Developer Settings*.

![Menu de Configurações de Desenvolvedor](https://976726803-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MXihT3dLI0T6GoCW1P1%2F-MZKL_gTrqKfy2r3Foaj%2F-MZKNV6T9qbdZQifN1pc%2Fgithub-02.png?alt=media\&token=2cc6aeab-af77-48fe-8f44-afbf0affe1fd)

Dentre as opções exibidas, escolha a *Personal Access Token*, e clique em *Generate New Token*.

![](https://976726803-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MXihT3dLI0T6GoCW1P1%2F-MZKL_gTrqKfy2r3Foaj%2F-MZKNpbDqrcbDV7JPTyy%2Fgithub-03.png?alt=media\&token=d3f2907d-5547-428a-bcad-2a2c54968ae5)

Na janela que irá abrir você deve fornecer um nome qualquer do *token* (`TokenCMD` no exemplo abaixo) e escolher as permissões que o mesmo terá. Para a gestão dos repositórios, basta selecionar o primeiro grupo de permissões (`repo`) e, navegar até o final e clicar em *Generete token*, conforme telas a seguir.

![](https://976726803-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MXihT3dLI0T6GoCW1P1%2F-MZKL_gTrqKfy2r3Foaj%2F-MZKO9laGIhw84V83BH0%2Fgithub-04.png?alt=media\&token=8d03bf31-9307-47a5-8261-efe048df6803)

![](https://976726803-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MXihT3dLI0T6GoCW1P1%2F-MZKL_gTrqKfy2r3Foaj%2F-MZKOPnplk7mQwixuQDS%2Fgithub-05.png?alt=media\&token=c64cc46c-80a2-4aa1-a0bb-4324e06d97f0)

Feito isso, será apresentado o *token* a você. Copie e cole em um arquivo texto pois você não será mais capaz de visualizá-lo assim que sair dessa tela. Se esquecer o número do *token* terá que criar outro.

![Token de Acesso para o GitHub](https://976726803-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MXihT3dLI0T6GoCW1P1%2F-MZKL_gTrqKfy2r3Foaj%2F-MZKOu68JX6PS6Am-cfe%2Fgithub-06.png?alt=media\&token=bf7cc588-935d-47e1-a113-86f46ce1109d)

#### Criando Repositório Remoto

Para enviarmos o repositório local para o repositório remoto precisamos ainda criar um repositório vazio no GitHub. Para isso, basta clicar sobre sua foto e escolher a opção *Your repositories*. Em seguida, clicar no botão *New*, dar um nome para o repositório (`superlists` no exemplo), escolher a visibilidade desejada (`public` no exemplo) e clicar em *Create repository*.

![Acesso ao Repositórios](https://976726803-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MXihT3dLI0T6GoCW1P1%2F-MZKPB5nDX4XuXf6pCJt%2F-MZKQChzLiMHgDB0v_WM%2Fgithub-07.png?alt=media\&token=80244c09-a6cd-46c1-b16b-a42d618c3109)

![Criação do Repositório Superlists Vazio](https://976726803-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MXihT3dLI0T6GoCW1P1%2F-MZKPB5nDX4XuXf6pCJt%2F-MZKQJse96iH4mr2aqoS%2Fgithub-08.png?alt=media\&token=937df677-29bf-4dbc-bfb2-c0acd60f7388)

![URL de Acesso ao Repositório](https://976726803-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MXihT3dLI0T6GoCW1P1%2F-MZKPB5nDX4XuXf6pCJt%2F-MZKRP7tKWFGvZC2MiUG%2Fgithub-09.png?alt=media\&token=acece32b-7e09-4b11-9589-b8dca5fdbfe7)

Criado o repositório é importante anotar também a URL de acesso do mesmo, que aparece em destaque na imagem acima. No exemplo essa URL é a <https://github.com/aurimrv/superlists.git>.&#x20;

**Enviando Arquivos do Repositório Local para o Remoto**

Ao final da janela acima (não visível na figura), o GitHub apresenta as instruções para conectar um repositório local já existente com este que acabamos de criar. A sequência de comandos que ele sugere é essa:

```
// …or push an existing repository from the command line
git remote add origin https://github.com/aurimrv/superlists.git
git branch -M main
git push -u origin main
```

Desse mode modo, estamos prontos para enviar os arquivos do diretório local para o repositório remoto. Para isso, usaremos o comando `git push`, conforme ilustrado abaixo. Durante a execução desse comando precisamos da URL do repositório no GitHub, do nome do usuário no GitHub e do *token* de acesso que foi gerado.

O primeiro passo (linha 1) é adicionar os dados do repositório remoto à origem do nosso repositório. Em seguida, para enviar os dados do repositório local para o remoto utilizamos o comando da linha 2, `git push`.

Ao executar esse comando, o Git irá solicitar nosso login (`aurimrv` no exemplo) e a senha que deve ser o *token* criado anteriormente.&#x20;

```bash
(superlists) tdd@mlp:~/superlists/superlists$ git remote add origin https://github.com/aurimrv/superlists.git
(superlists) tdd@mlp:~/superlists/superlists$ git branch -M main
(superlists) tdd@mlp:~/superlists/superlists$ git push -u origin main
Username for 'https://github.com': aurimrv
Password for 'https://aurimrv@github.com': 
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 3 threads
Compressing objects: 100% (9/9), done.
Writing objects: 100% (11/11), 2.96 KiB | 607.00 KiB/s, done.
Total 11 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), done.
To https://github.com/aurimrv/superlists.git
 * [new branch]      main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.

```

A figura a seguir ilustra o repositório atualizado no GitHub com os arquivos que estavam apenas no nosso repositório local.

![Repositório Atualizado Após Push](https://976726803-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MXihT3dLI0T6GoCW1P1%2F-MZKTTLi8J0ZNvRbP4yY%2F-MZKTpEI3Eiw3ZFAUiUo%2Fgithub-10.png?alt=media\&token=4128ade2-827c-40c7-b69f-c62c7dac2fa4)

#### Alterando o Repositório Remoto

Como podemos observar na tela acima, no GitHub aparece uma mensagem de que não criamos o arquivo README.md, que explica o conteúdo do repositório. Para adicionar tal arquivo ao projeto, basta clicarmos no botão *Add a README* que podemos editá-lo dentro do próprio GitHub, conforme ilustrado a seguir.

![Edição do README.md](https://976726803-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MXihT3dLI0T6GoCW1P1%2F-MZKTtodH8w5eZSDxP--%2F-MZKW0lgB491BjprPC87%2Fgithub-11.png?alt=media\&token=d4cea062-1512-4222-839f-3a101ddd61b7)

Finalizada a edição, basta realizarmos a confirmação das mudanças (*Commit new file*) para que o arquivo seja salvo em nosso projeto.

![](https://976726803-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MXihT3dLI0T6GoCW1P1%2F-MZKTtodH8w5eZSDxP--%2F-MZKWEnVFQuiybal-GBK%2Fgithub-12.png?alt=media\&token=8b3a17f0-f3ca-4b82-b80f-cf10842f0ea6)

A tela a seguir agora exibe a descrição do projeto na tela inicial do repositório.

![Tela Inicial com o README.md](https://976726803-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MXihT3dLI0T6GoCW1P1%2F-MZKWN-ys_SGBP9TUVgb%2F-MZKWS5uAVZ6bYqqFHn7%2Fgithub-13.png?alt=media\&token=fe8fd605-5034-4a4e-850e-2ff724e74158)

#### Atualizando o Repositório Local com os Dados do Repositório Remoto

Uma vez que criamos o arquivo `README.md` remotamente, ele não está presente no nosso diretório local. Para atualizar nossa cópia local podemos utilizar o comando da linha 1 e manter tudo sincronizado (`git pull`).

{% code lineNumbers="true" %}

```bash
(superlists) tdd@mlp:~/superlists/superlists$ git pull
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 1016 bytes | 508.00 KiB/s, done.
From https://github.com/aurimrv/superlists
   4b686b6..010bb52  main       -> origin/main
Updating 4b686b6..010bb52
Fast-forward
 README.md | 3 +++
 1 file changed, 3 insertions(+)
 create mode 100644 README.md
```

{% endcode %}

A execução do `git status` conforma que os repositórios estão sincronizados e o arquivo README.md já está presente no repositório local.

```bash
(superlists) tdd@mlp:~/superlists/superlists$ git status
No ramo main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
(superlists) tdd@mlp:~/superlists/superlists$ ls -l
total 144
-rw-r--r-- 1 tdd tdd 131072 jun 22 06:18 db.sqlite3
-rw-rw-r-- 1 tdd tdd    145 jun 22 06:07 functional_tests.py
-rwxrwxr-x 1 tdd tdd    666 jun 22 06:13 manage.py
-rw-rw-r-- 1 tdd tdd    123 jun 22 10:44 README.md
drwxrwxr-x 3 tdd tdd   4096 jun 22 06:17 superlists
```

Para evitar conflitos em repositórios é importante manter a sincronização. Desse modo, uma das práticas recomendadas no desenvolvimento é a realização de *commits* frequentes.
