Conceitos básicos para utilização do git/github.
O git é um versionador de arquivos, ou seja, ele funciona conservando um histórico de modificações (ou versões) dos arquivos especificados. Além disso, acrescenta uma funcionalidade de criar ramificações no código, ou branches, o que facilita a divisão de trabalhos em uma equipe de programação e a garantia de funcionamento do código principal.
O histórico do git funciona, inicialmente, de acordo com uma lista de commits, que guardam as modificações de determinada versão, e todas as informações sobre ela. Um commit pode ser definido como um ponto na história do repositório.
No ambiente Ubuntu, com o gerenciador de pacotes apt, basta rodar o comando:
~$ sudo apt install git -y
Assim os comandos git já estarão disponíveis para utilização.
init
:
Inicializa um repositório git na pasta atual. Utilização:
~/repos/repositório$ git init
Para executar todos os outros comandos você deve estar dentro de um repositório git. Portanto, certifique-se que está em um repositório.
status
:
Mostra o status dos arquivos do repositório atual. Mostra os arquivos nos estados untracked e tracked.
Obs:
Em um repositório git, cada arquivo pode estar em três estados possíveis, são eles:
untracked (não rastreado):
O estado padrão dos arquivos, quando ele é novo ou tem alterações em relação ao commit anterior (versão anterior). Ou seja, sempre que você criar ou modificar arquivos ele ficará no estado untracked.
tracked (rastreado) / staged (‘no palco’, ‘em foco’):
Após rodarmos o comando git add
(veja mais à frente) para um arquivo,
ele entrará nesse estado. Esse estado indica que o arquivo está pronto para
ser guardado no histórico do repositório. Ele é um estado intermediário para
os arquivos.
committed (comprometido):
Após rodarmos o comando git commit
(veja mais à frente) para os arquivos
rastreados no momento ele passará para o estado ‘commitado’. Esse estado
indica que o arquivo já está salvo no histórico do repositório e suas
modificações não serão perdidas.
O significado de cada estado ficará mais claro adiante.
log
:
Mostra o histórico de versões (commits) do repositório atual e suas informações: id do commit, autor e data. Utilização:
~/repos/repositório$ git log
add
:
Muda os arquivos não rastreados indicados para o estado staged, permitindo serem commitados mais à frente. Utilização:
Suponha que modificamos o arquivo ‘README.md’ e queremos colocar ele em staged:
~/repos/repositório$ git add README.md
Obs: quando queremos adicionar todos os arquivos do diretório atual podemos
rodar: git add .
, se você está na raiz do repositório todas as alterações
serão adicionadas (semelhante ao comando git add -A
/ git add --all
)
commit
:
Grava no histórico os arquivos em estado staged. Atenção: ele ignora os arquivos
fora do estado staged, então tenham certeza que os arquivos desejados estão
nesse estado (utilize o git status
).
Para gravarmos um commit devemos dar uma mensagem de commit para o comando, da seguinte forma:
~/repos/repositório$ git commit -m 'mensagem de commit' # '-m' vem de 'message'
Obs: como esse comando utiliza a entrada padrão não faz-se necessário o uso das aspas, mas as utilizamos para melhor organização.
Exemplo simples:
Digamos que queremos criar um repositório git chamado ‘repo’ e colocar o arquivo README.md com o conteúdo abaixo:
# Comandos básicos de git
Seguindo os passos:
~/repos$ mkdir repo # cria a pasta 'repo'
~/repos$ cd repo # entra na pasta
~/repos/repo$ git init # inicia o repositório git
~/repos/repo$ touch README.md # cria o arquivo 'README.md'
~/repos/repo$ echo '# Comandos básico de git' >> README.md
# adiciona o texto indicado ao final do arquivo (pesquise sobre o comando echo)
~/repos/repo$ git status # verificando o estado dos arquivos
~/repos/repo$ git add README.md # muda o arquivo para staged
~/repos/repo$ git commit -m 'adicionando o arquivo README'
# cria o ponto na história do repositório com o novo arquivo
~/repos/repo$ git status # confirmando o estado dos arquivos
~/repos/repo$ git log # verificando o histórico do repositório
Como já discutido anteriormente, em um repositório git existe a possibilidade da
criação de ramificações ou ‘galhos’ (branches). Com a utilização de branches
podemos desenvolver diferentes features (funcionalidades) paralelamente e ao
final somente adicionarmos essas modificações na branch main (veja o comando
merge
), a branch principal.
Para a manipulação das branches temos os seguintes comandos:
branch
:
Na verdade, um comando de múltipla utilidade, podendo:
criar branches
Suponha que queremos criar uma branch ‘branch-de-teste’, então rodamos:
~/repos/repo$ git branch branch-de-teste
deletar branches
Agora vamos deletar a branch anterior:
~/repos/repo$ git branch branch-de-teste -d # o '-d' indica '--delete' (deletar)
renomear a branch atual
Suponha que estamos na branch ‘branch1’ e queremos renomear para ‘branch2’:
~/repos/repo$ git branch -m branch2 # o '-m' indica '--move' (mover)
Esse comando funciona semelhante ao comando mv
de um terminal bashLike
checkout
:
Esse também é um comando com várias funções:
trocar entre branches
Suponha que estamos na branch ‘main’ e queremos mudar para a branch ‘backup’:
~/repos/repo$ git checkout -b backup
Obs: caso a branch não exista o comando se encarrega de criar a branch e trocar para ela imediatamente.
verificar commits passados
Quando queremos rever uma versão anterior do nosso repositório podemos copiar
o hash do commit e dar o checkout nele. Primeiramente pegamos o id/hash do
commit desejado usando o comando git log
.
Suponhamos que o hash do commit é ‘123456’ (pode ser tanto o hash de 6 dígitos quanto o completo), queremos ir para essa versão:
~/repos/repo$ git checkout 123456
Obs: nesse comando não devemos fazer alterações no commit, apenas vê-las,
caso precise modificar um commit devemos usar o comando rebase
.
merge
:
É o comando utilizado para mesclar duas, ou mais, branches. Imagine que um desenvolvedor estava criando uma nova funcionalidade na branch ‘feat/funcionalidade’ e deseja coloca-lá na branch principal. Assim, rodamos na branch main:
~/repo$ git merge feat/funcionalidade
Não é vital mas recomenda-se a criação de mensagens ao mesclar branches. Isso pode ser feito de forma semelhante quando fazemos um commit.
~/repo$ git merge feat/funcionalidade -m 'adicionando funcionalidade'
Obs1: após a mesclagem das branches podemos deletá-las sem perda alguma.
Obs2: existem casos em que existe conflito ao mergear branches. Quando isso acontece, o git tenta concertar automaticamente, porém, algumas vezes devemos fazer essas correções manualmente.
Obs3: quando não indicamos o tipo de merge o git se encarrega de simplificar
esse merge, existem 2 tipos de merge (fast-forward e 3way). O primeiro
se encarrega de mesclar em um único branch, sem conservar a estrutura de árvore,
e é a definida por padrão. Já a conserva a estrutura de árvore (por isso
three way). Para forcar o método 3way podemos rodar: git merge --no-ff
.
O funcionamento de um repositório git não depende fortemente de um servidor remoto como o Github, BitBucket ou GitLab, porém, em certas ocasiões é de grande utilidade um servidor remoto. Aqui utilizaremos o Github como padrão mas as rotinas serão semelhantes em qualquer outro serviço de hosting de git.
Para a manutenção do repositório remoto do git fazemos uso de 5 principais comandos, são eles:
remote
:
É utilizado para conectar ou desconectar um repositório local já existente ao seu respectivo repositório remoto. Suponha que queremos conectar o repositório local ‘repo’ ao repositório ‘repo’ no Github do usuário ‘user’.
Rodamos os seguintes passos:
~/repo$ git remote add origin https://github.com/user/repo.git
A partir daí o termo ‘origin’ será uma referencia para o repositório remoto.
E caso quisermos remover essa conexão rodamos:
~/repo$ git remote remove origin # o origin referencia o repositório remoto
clone
:
É comum já termos um repositório criado no github e precisamos clona-lo localmente, e é para isso que esse comando serve. Suponha que temos o repositório ‘repo’ do usuário ‘user’ no github, clonamos ele da seguinte maneira:
~$ git clone https://github.com/user/repo.git
Assim, é criada a pasta ‘repo’ no diretório atual e podemos entrar nela para utilizar os comandos do git.
push
:
Quando temos commits locais e precisamos passa-los para o remote repo podemos
fazê-lo usando o comando push
. Do inglês ‘empurrar’, esse comando empurra,
manda ou envia o histórico local para o remoto, sincronizando ambos.
Podemos rodar como indicado abaixo:
~/repo$ git push
Obs1: pode ocorrer de haver uma incompatibilidade entre os históricos local e remoto, o que pode ocorre por vários motivos, sendo o mais comum, a diferença de um commit local com um commit remoto.
Obs2: o parâmetro ‘-f’ pode forçar o push para o remoto, resolvendo o problema indicado acima. Entretanto, não é uma prática saudável a utilização desse parâmetro, evite.
pull
:
As vezes, também é necessário atualizar o repositório local com o conteúdo do
remoto. Isso pode ser feito ‘puxando’ o conteúdo do servidor remoto. Do inglês,
puxar, o comando pull
faz exatamente o que diz, ele pega o conteúdo remoto
e baixa localmente.
Rodamos ele assim:
~/repo$ git pull
Obs: assim como o comando push
, ele aceita o parâmetro ‘-f’ para forçar
a sobreposição, porém não é uma prática saudável.
fetch
:
E por fim, o comando fetch
sincroniza ambos os repositórios, o local e o remoto.
Ele dá pull e push deixando assim os repositórios sincronizados. Utilização:
~/repo$ git fetch
Quando temos um fork (uma ramificação) de um repositório e queremos mergear a branch main desse no original precisamos criar um pull request. O pull request é um pedido para mesclagem das branches que deve ser aprovado pelo responsável do repositório original. A criação desses pull requests pode ser feita diretamente do serviço de git remoto, nesse caso, o Github.
add --patch
É importante sempre manter nossos commits o mais limpos possível, seja nas suas
mensagens e até mesmo no agrupamento das modificações. Certas vezes modificamos
arquivos mas seus commits devem ser diferentes. O parâmetro patch
nos ajuda
a adicionar somente determinadas partes desse arquivo.
Imagine que modificamos um arquivo ‘index.html’, adicionando um header e um footer, cada um com seu conteúdo. Porém, como queremos manter nosso histórico de commits limpo devemos separar em dois commits. Podemos fazê-lo assim:
# escolhemos o 'patch'(ou remendo) desejado, o header, digamos e commitamos
~/repo$ git add --patch index.html
~/repo$ git commit -m 'feat: adicionado o header e seu conteúdo'
# novamente, escolhemos o patch desejado e commitamos
~/repo$ git add --patch index.html
~/repo$ git commit -m 'feat: adicionado o footer e seu conteúdo'
Assim, nosso histórico permanece mais limpo e de fácil entendimento.
commit --amend
Como dito antes, é importante manter nosso histórico de commits limpo! E o
parâmetro amend
nos ajuda nisso. Ele permite alterar o commit anterior, seja
adicionando modificações esquecidas ou mesmo mudar a mensagem de commit.
Suponha que esquecemos de commitar a modificação do arquivo ‘index.py’ e também existem um erro na mensagem de commit.
A mensagem anterior está como: ‘feat: adicionado conteudo ao arquivo’ e desejamos mudar para: ‘feat: adicionado conteúdo do arquivo index.py’
Podemos modificar assim:
~/repo$ git add index.py
~/repo$ git commit -m 'feat: adicionado conteúdo do arquivo index.py' --amend
reset
As vezes é necessário retroceder alguns commits ou mesmo tirar um arquivo do
stage. Podemos fazer essas operações com o comando git reset
.
Queremos tirar o arquivo ‘pasta/arquivo.txt’ do estado staged que foi adicionado por engano. Para isso rodamos:
~/repo$ git reset HEAD -- pasta/arquivo.txt
Obs1: esse comando recebe por padrão o commit para onde queremos resetar, e o ‘HEAD’ (cabeça) representa o ultimo commit. E em seguida recebe o arquivo desejado.
Podemos ainda voltar 3 commits atrás, por quais razões. Para isso rodamos:
~/repo$ git reset HEAD~3
Obs2: existem ainda dois parâmetros úteis, são eles: --soft
e --hard
.
O soft, é o padrão, reseta para o commit indicado mas conservando as modificações
criadas no caminho. Já o hard remove todas as alterações feitas.
rebase
Para a manipulação mais refinada dos commits anteriores podemos utilizar o
comando git rebase
. Com ele podemos editar os commits mais anteriores ainda,
seria como um commit --amend
mais refinado.
Existem diversas opções ao utilizar esse comando, alguns exemplos são:
Como existem muitos comandos para o rebase podemos roda-lo assim:
~/repo$ git rebase -i # '-i' ou '--interactive', de interativo
Teremos a manipulação interativa do rebase.