# 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](/files/-MZKLeZq_bJBN_4KT1T3)

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

![Menu de Configurações de Desenvolvedor](/files/-MZKNV6T9qbdZQifN1pc)

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

![](/files/-MZKNpbDqrcbDV7JPTyy)

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.

![](/files/-MZKO9laGIhw84V83BH0)

![](/files/-MZKOPnplk7mQwixuQDS)

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](/files/-MZKOu68JX6PS6Am-cfe)

#### 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](/files/-MZKQChzLiMHgDB0v_WM)

![Criação do Repositório Superlists Vazio](/files/-MZKQJse96iH4mr2aqoS)

![URL de Acesso ao Repositório](/files/-MZKRP7tKWFGvZC2MiUG)

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](/files/-MZKTpEI3Eiw3ZFAUiUo)

#### 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](/files/-MZKW0lgB491BjprPC87)

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

![](/files/-MZKWEnVFQuiybal-GBK)

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

![Tela Inicial com o README.md](/files/-MZKWS5uAVZ6bYqqFHn7)

#### 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://aurimrv.gitbook.io/tdd-python/3-desenvolvimento-dirigido-por-teste/3-3-controle-de-versao-do-projeto.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
