-
Notifications
You must be signed in to change notification settings - Fork 2
02 Hands On (Git workflow)
O segundo hands on consiste em:
- Criar repositórios
- Adicionar ficheiros à staging area
- Experimentar o comando
git status
- Fazer commit
- Usar o comando git diff
- Remover snapshots da staging area
- Reverter um commit
Para criar um repositório vazio usamos o comando $git init
. Este comando deverá ser executado na pasta onde se querer inicializar o repositório. Essa pasta pode estar vazia, ou pode já ter ficheiros e sub-diretórios.
O que o comando faz é criar um sub-diretório oculto, .git
, (para ser correto, em alguns sistemas operativos o .git
é um ficheiro, também oculto!) e é aqui onde o git armazena os dados, ou seja, é o repositório em si.
O repositório começa sempre vazio! Mesmo que o diretório onde o comando é executado contenha ficheiros, o git não irá adicioná-los automaticamente ao repositório, tudo é considerado inicialmente como untracked.
$mkdir workshop # cria uma pasta vazia
$git init # inicializa o repositório
Estando o repositório criado, experimenta criar um ficheiro, como um simples ficheiro de texto, e experimenta adicioná-lo à staging area.
Para colocar ficheiros na staging area, usa-se o comando $git add
(vê a nota abaixo antes de executar).
💡 Experimenta também usar o comando $git status
que lista os ficheiros untracked, modificados e que fazem parte do snapshot para o próximo commit (estão atualmente pendentes na staging area). Para te familiarizares com este comando, experimenta correr o comando sempre que adicionas novos ficheiros na working directory, quando crias o snapshot na staging area com $git add
e quando faz commit.
Existem diversas formas de usar o comando $git add
que podem tornar a tarefa mais eficiente. Podes especificar os ficheiros que quer adicionar, podes usar patterns, ou usar parametros que automaticamente consideram ficheiros num estado especifico. Eis alguns exemplos:
$git add filename.txt # adiciona um ficheiro especifico
$git add *.c # adiciona ficheiros com extensão .c
$git add . # adiciona TUDO (new/modified/deleted)
$git add -A # adiciona TUDO (new/modified/deleted)
$git add -u # adiciona apenas ficheiros conhecidos, ou seja, aqueles que já surgiram em algum commit (tracked files -> modified/deleted)
💡 Se um ficheiro é colocado na staging area e posteriormente modificado, o novo conteudo não será considerado. Ou seja, após usar o add
com um ficheiro exemplo.txt
, o snapshot é criado com o estado do ficheiro naquele mesmo instante, e irá ser adicionado como tal ao repositório com o $git commit
. Por isso, o ficheiro até pode ser apagado e o commit irá funcionar na mesma. Mais à frente, iremos analizar como o git armazena os dados e isto ficará mais claro.
Agora que temos a primeira versão do ficheiro pronta e com o seu snapshot na staging area, podemos fazer commit. Fazer commit consiste em mover o snapshot criado na staging area para o repositório.
Os commits têm uma mensagem que o descreve, e é especificado no parametro -m
.
$git commit -m "First commit"
💡 Se te esqueceres da mensagem do commit, o git automaticamente abre um editor de texto na consola (nano
, vim
, ou outros, é configurável) que permite descrever o commit. Isto também é útil quando os commits merecem mensagens detalhadas. Podemos usar uma abordagem de no primeiro pagrafo dar um titulo, curto e que sumarize bem o que foi alterado, e depois noutro paragrafo descrever em detalhes as modificações, o porquê, etc.
🤔 Mas, se o comando commit
apenas move o snapshot da staging area para o repositório, qual a necessidade destes dois passos (stage .. commit)? Podemos ver o ato de staging como uma preparação daquilo que realmente queremos fazer. Sem a staging area, seria muito fácil fazer commit de coisas por engano, o que implicaria reverter commits e outras operações que podem ser perigosas no sentido de haver perda de dados. Assim, primeiro trabalhamos nesta área intermédia, staging area, e quando tivermos a certeza que queremos registar o novo snapshot no repositório, fazemos commit (havendo sempre a possibilidade de reverter, mas há certos cuidados a ter!). Além disso, na staging area temos a flexibilidade de apenas criar snapshots de ficheiros em particular, ou até ir ao detalhe de para cada ficheiro apenas fazer stage a alguns blocos desse ficheiro 🔥 Outro uso frequente, em contextos como programação, é o facto de termos a segurança de que uma vez criado o snapshot, ele não será apagado/modificado a menos que se volte a correr $git add
. Isto dá a flexibilidade ao programador de fazer experiências no código. Se chegar a also que funciona, faz $git add
. Se eventualmente as coisas ficarem partidas, pode não só fazer commit do que funcionava, como repôr o estado da working directory. Quantas vezes quiseste fazer undo no teu editor de texto e não conseguiste chegar à versão que querias?! Além disso, sejamos sinceros, fazer Ctrl+Z 153 vezes, com alterações em vários ficheiros às quais temos que estar atentos para não exceder os Undos, não é propriamente divertido.
Após esta motivação para a existência da staging area, ainda assim haverão casos em que estamos a fazer coisas muito simples, e não queremos perder tempo a executar dois comandos. Pois bem, o comando commit
permite fazer os dois passos, adicionar à staging area e commit, num só comando. Contudo, este comando apenas considera ficheiros já conhecidos (novos ficheiros/untracked files são ignorados).
$git commit -a -m "commit description"
💡 Experimenta usar o comando $git log
antes e após fazer commits. Este comando permite ver todos os commits (alcançaveis através do último commit. Quando aprenderes o conceito de branch, ficará claro em que situações um commit não pode alcançar outro commit).
Uma pequena ilustração do que é suposto fazer neste hands on, usando somente o terminal, num ambiente Linux!
- Inicializar um repositório
$mkdir workshop # creates an empty folder 'workshop'
$cd workshop # changes the current folder
$git init
Initialized empty Git repository in /mnt/c/Users/fabio/Documents/workshop/.git/
- Criar um ficheiro de examplo, explorar o
$git status
e criar um snapshot.
$touch f1.txt # creates empty file
$ls
f1.txt
$git status
On branch master
Initial commit
Untracked files:
(use "git add <file>..." to include in what will be committed)
f1.txt
nothing added to commit but untracked files present (use "git add" to track)
$git add f1.txt
- Fazer
commit
, continuar a explorar o$git status
e usar$git log
.
$git status
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: f1.txt
$git commit -m "Added first empty file"
[master (root-commit) cfbb95e] Added first empty file
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 f1.txt
$git log
commit cfbb95e5cef2aca23774cd59e2a20d48d6fca69a
Author: Fabio Gaspar <[email protected]>
Date: Sun Mar 18 12:01:10 2018 +0000
Added first empty file
- Modificar o ficheiro, voltar a fazer commit. Uma vez mais, continuando a experimentar os comandos
status
elog
.
# Let's modify the file and had the most original phrase you ever seen
$cat > f1.txt
This is the first paragraph!
$git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: f1.txt
no changes added to commit (use "git add" and/or "git commit -a")
# Ok, I KNOW WHAT I AM DOING! I will skip manually staging by using `commit -a`
$git commit -a -m "Wrote first line"
[master bdaded3] Wrote first line
1 file changed, 1 insertion(+)
$ git log
commit bdaded3d1aa1fc919d237073db5fd8c2078aae48
Author: Fabio Gaspar <[email protected]>
Date: Sun Mar 18 12:02:13 2018 +0000
Wrote first line
commit cfbb95e5cef2aca23774cd59e2a20d48d6fca69a
Author: Fabio Gaspar <[email protected]>
Date: Sun Mar 18 12:01:10 2018 +0000
Added first empty file
O comando git diff
permite comparar secções do git (working directory, staging area), commits, branches 🔜 etc.
# compara working directory com a staging area. Ilustra o que iria ser adicionado à staging area com o comando add
$git diff
# compara o conteúdo da staging area com um commit (se omitido, considera o ultimo commit)
$git diff --cached <commit>
# compara dois commits
$git diff <commit> <commit>
# compara o conteúdo na working directory com um commit
$git diff <commit>
Temos dois commits, com base na demo anterior, um com SHA1 bdad...
e outro com SHA1 cfbb9...
Normalmente usar-se-ia o SHA1 completo, mas basta usar o suficiente para que o git consiga distinguir objetos (objetos? Wait for it... 🔜).
$git diff bdad cfbb9
diff --git a/f1.txt b/f1.txt
index 77d7734..e69de29 100644
--- a/f1.txt
+++ b/f1.txt
@@ -1 +0,0 @@
-This is the first paragraph!
$git diff cfbb9 bdad
diff --git a/f1.txt b/f1.txt
index e69de29..77d7734 100644
--- a/f1.txt
+++ b/f1.txt
@@ -0,0 +1 @@
+This is the first paragraph!
Repara como inverter a ordem dos commits nos argumentos produz o resultado inverso. No primerio comando, $git diff bdad cfbb9
, estamos a comparar o segundo commit, onde adicionei a linha This is the first paragraph!
, com o primeiro commit, onde simplesmente adicionei o ficheiro f1.txt
, vazio. Portanto, a forma de interpretar o output é, se eu me posiconar no commit bdad
, e caminhar para o commit cfbb9
, o que é que muda? Efetivamente, incialmente tenho uma linha no ficheiro f1.txt
, mas ao avançar para o commit cfbb9
ele estava vazio, então a linha é apagada. Se inverter o sentido, e seguir a ordem temporal, ou seja, indo do primeiro commit para o segundo, então eu comecei com um ficheiro vazio, e depois adicionei conteúdo. Então no output vemos o +
, indicando o que foi adicionado.
Por vezes adicionamos ficheiros à staging area acidentalmente, ou reparamos num erro do conteúdo, e como tal, não queremos fazer commit do snapshot criado.
Existem dois cenários possíveis:
- Apagar o snapshot de um ou mais ficheiros da staging area, mas não discartar as modificações na working directory.
- Apagar o snapshot, e repôr o(s) ficheiros (discarta as alterações na working directory)
Para o primeiro caso, usa-se o comando $git reset <file>
. Podemos especificar um ou mais ficheiros, mas se não indicarmos nada, o git faz unstage de todos os ficheiros.
Para o segundo caso, usa-se $git checkout -- <file>
. Aqui o parametro <file>
é orbigatório.
💡 --
??? ಠ_ಠ O --
é tipicamente uma expressão da shell, para indicar o fim de comandos de opção, normalmente começados por -
. No exemplo do checkout
, imaginemos que temos um branch chamado master e um ficheiro master. O checkout vai interpretar master como uma branch, ou como um ficheiro? O --
poderá ser usado para remover esta ambiguidade. Quando se tratam de branches, nunca se usa o --
, mas se for um ficheiro, deve ser usado. No geral, é uma regra válida para diversos comandos, mas nada melhor que consultar o manual para ter a certeza, já que há comandos que ou apenas lidam com ficheiros ou apenas branches, não havendo margem para ambiguidades.
Para reverter um commit, usa-se o comando $git revert <commit>
. O commit pode ser identificado pelo seu SHA1 (ou parte dele), podendo este ser obtido através do $git log
. Existe ainda uma notação simbólica HEAD
mas que ainda não foi abordada. Por agora, apenas interessa que o HEAD
referencia sempre o último commit
, apesar de não ser uma verdade absoluta.
Ao contrário do que se possa pensar, o git não vai apagar o commit. Vai criar um novo commit que reverte o commit especificado. E por sua vez podemos reverter o commit que reverteu o commit que............. (╯°□°)╯︵ ┻━┻
💡 Antes de usar revert
, é necessário garantir que não existem alterações na working directory. O $git stash
é um comando útil aqui.