ðīó §ó Ēó Ĩó Ūó §ó ŋ English version of this text here!
Volta e meia eu crio aliases que executam comandos com algumas opçÃĩes que eu uso com frequÊncia. Um deles ÃĐ o meu alias para git log --oneline.
O problema ÃĐ que quando eu executo esse comando, vÃĄrios commits aparecem na minha tela atÃĐ que ela fique completamente cheia, enquanto que a Última linha indica que existem ainda mais commits. Se eu apertar q, eu volto para a minha linha de comando, mas o commit mais recente de todos serÃĄ empurrado para fora da tela (isso acontece comigo porque o meu prompt ocupa duas linhas).
AlÃĐm desse problema especÃfico do meu prompt grande, eu normalmente tambÃĐm quero apenas os commits na branch em que estou agora. Uma maneira simples que passou pela minha cabeça seria git log --oneline main..HEAD (ou talvez usando main^). Isso parecia apenas que seria mais um alias pra minha coleçÃĢo, mas daà eu lembrei que tenho projetos com branch a main e projetos com branch a master.
Decidi, entÃĢo, praticar minhas habilidades de shell script e fazer um programinha que decidiria automaticamente entre usar main ou master.
Primeiramente
Antes de começar, deixe-me planejar tudo que serÃĄ feito de antemÃĢo.
Se eu fosse escrever pseudo cÃģdigo desse script ou resumÃ-lo em passos, como seria?
AlÃĐm disso, o que deveria acontecer se eu nÃĢo estiver em um repositÃģrio git?
Vamos pensar sobre essas coisas enquanto escrevemos um plano:
Temos que:
- Imprimir alguma mesangem de erro e sair com cÃģdigo nÃĢo zero caso nÃĢo estejamos em um repo git
- Descobrir se ou main ou master estÃĢo disponÃveis
- Executar
git log main^/master^..HEADse possÃvel - Imprimir alguma mensagem de erro e sair com cÃģdigo nÃĢo zero caso contrÃĄrio
1 - Verificar se estamos em um repositÃģrio git
Uma maneira simples, e nÃĢo muito boa, seria de apenas executar ls .git. Se sair 0, estamos num repositÃģrio git. Se nÃĢo, nÃĢo estamos.
Essa nÃĢo ÃĐ uma soluçÃĢo muito boa porque nÃĢo vai funcionar se estivermos dentro de um subdiretÃģrio de um repositÃģrio git. Temos que pensar em outra forma. Torcemos para que o git jÃĄ tenha uma maneira simples e fÃĄcil de verificar isso.
E certamente tem, jÃĄ que o git ÃĐ tÃĢo legal.
Uma pesquisa rÃĄpida no google me mostrou que o comando git rev-parse --git-dir pode ser usado para isso. man git rev-parse mostra mais detalhes, e a opçÃĢo --git-dir realmente parece uma boa opçÃĢo. Quando nÃĢo estamos em um repositÃģrio git, um erro serÃĄ impresso na tela e um cÃģdigo nÃĢo zero serÃĄ retornado, segundo o manual.
Eu nÃĢo estou realmente interessado na mensagem de erro, apenas no cÃģdigo de saÃda (zero ou nÃĢo zero).
Portanto, para verificar se estamos ou nÃĢo em um repositÃģrio git ao mesmo tempo que escondemos mensagens de erro, eu fiz assim:
#!/bin/sh
if [ ! "$(git rev-parse --git-dir 2> /dev/null)" ]; then
echo Not in a git repository
exit 1
fi
A sintaxe [ ... ] ÃĐ uma forma reduzida do comando test.
O ! vai inverter a verificaçÃĢo, isto ÃĐ, se o comando (nesse caso git rev-parse --git-dir) retornar 0, entÃĢo ele serÃĄ considerado "falso" ou que resultou em uma "falha".
O 2> /dev/null estÃĄ redirecionando a mensagem de erro para /dev/null. Isso ÃĐ uma maneira simples de apenas nÃĢo mostrar mensagens de erro na tela.
EntÃĢo basicamente o que isso estÃĄ fazendo ÃĐ verificando se o comando falhou. Se sim, significa que nÃĢo estamos num repositÃģrio git, entÃĢo uma mensagem de erro ÃĐ impressa e o script retorna 1. Do contrÃĄrio, nada acontece e podemos seguir para os nossos prÃģximos passos.
2 - Descobrir se main ou master existem
Podemos fazer isso usando o comando git branch e o comando grep, mas git rev-parse tambÃĐm pode fazÊ-lo.
A segunda opçÃĢo ÃĐ bastante simples, git rev-parse --verify <nome-da-branch> ÃĐ o suficiente (passe -q para suprimir mensagens de erro):
git rev-parse -q --verify main
Pra mudar um pouco, porÃĐm, vou fazer com o grep. Dessa forma, podemos aprender um pouquinho mais sobre regexes!
O comando git branch mostra todas as branches disponÃveis na sua mÃĄquina local, com a branch atual sendo precedida por um *.

Basicamente podemos usar o grep passando main ou master.
Existe um perigo, porÃĐm. Simplesmente executar:
git branch | grep main
vai retornar com sucesso se alguma branch contendo a palavra main existir.

Para resolver esse problema, podemos usar uma expressÃĢo regular melhor do que apenas main. O padrÃĢo que buscamos ÃĐ:
- Começo da linha
- Possivelmente um
*, mas poderia nÃĢo ter tambÃĐm - Espaços em branco (a quantidade depende se um * estava lÃĄ ou nÃĢo)
- A palavra main (ou master) exatamente
- Final da linha
Vamos fazer por partes:
- O regex para o começo da linha ÃĐ
^ - Nos regexes, um
*ÃĐ um quantificador, que significa "a coisa antes de mim entre 0 e infinitas vezes". Portanto, para encontrar literalmente um*, temos que escapÃĄ-lo:\*. AlÃĐm disso, para tornÃĄlo opcional, podemos usar o quantificador?, que significa "a coisa antes de mim entre 0 e 1 vezes". Portanto, o regex pra isso tudo seria\*? - Espaços em branco poderiam simplesmente ser um espaço ou
\s. Este Último tambÃĐm encontra coisas como tabulaçÃĩes e quebra de linhas. Como a quantidade de espaços pode variar, podemos usar o quantificador*, resultando em\s* - As palavras
mainoumasterpodem ser encontradas pelas palavras literais - O regex para o final de uma linha ÃĐ
$
Portanto, nosso regex para a branch main seria ^\*?\s*main$. Para a master, ÃĐ claro, ÃĐ extremamente similar.
Um ponto importante, apra usar alguns caracteres especiais no regex, o comando grep deve ser invocado com a flag -E (a opçÃĢo de regex extendido)!
O comando final para a branch main, entÃĢo, poderia ser:
git branch | grep -E '^\*?\s*main$'
Isso deve funcionar para nÃģs.
3 - Executar git log para a branch correta
Podemos fazer isso com uma sequÊncia de comandos if/elif. Se main existir, usamos main. Se nÃĢo, porÃĐm master existir, usamos master.
AtÃĐ poderÃamos guardar o resultado do comando acima (git branch com grep) em uma variÃĄvel e verificÃĄ-la. Dessa forma nÃĢo teremos que executar git branch duas vezes. PorÃĐm, tambÃĐm podemos passar diretamente o comando inteiro para o if, assim:
if [ "$(git branch | grep -E '^\*?\s*main$')" ]; then
git log main^..HEAD
exit 0
elif [ "$(git branch | grep -E '^\*?\s*master$')" ]; then
git log master^..HEAD
exit 0
fi
Esse trecho ÃĐ razoavelmente simples. à verificado se main existe usando aquele regex que definimos. Se existir, usamos o git log no range saindo de main (incluÃda, pois adicionamos o ^) atÃĐ HEAD. Daà saÃmos 0 para finalizar o script (essa saÃda 0 provavelmente nÃĢo ÃĐ necessÃĄria e nem muito boa, pois se existir algum bug em nossa lÃģgica/regex, o programa pode falhar silenciosamente, retornando 0 apesar de um erro!)
4 - Imprimir mensagem de erro e sair nÃĢo zero caso main/master nÃĢo sejam encontradas
Podemos simplesmente adicionar um comando else para o trecho acima:
if [ "$(git branch | grep -E '^\*?\s*main$')" ]; then
git log main^..HEAD
exit 0
elif [ "$(git branch | grep -E '^\*?\s*master$')" ]; then
git log master^..HEAD
exit 0
else
echo 'Neither main nor master branch found'
exit 2
fi
Mas funciona?
Podemos testar nosso script agora! (:
Eu salvei um arquivo chamado git-log-main-master com nosso script e dei a ele permissÃĢo de execuçÃĢo. Vamos testÃĄ-lo:

Isso foi legal. Mas serÃĄ que nÃĢo conseguimos fazÊ-lo melhor?
E se pudÃĐssemos passar flags do git log para nosso script?
Flags!
Isso ÃĐ bastante simples de se fazer. Em um shell script, a variÃĄvel especial $@ contÃĐm todos os argumentos passados ao script, entÃĢo podemos simplesmente adicionÃĄ-lo à s nossas linhas com "git log":
if [ "$(git branch | grep -E '^\*?\s*main$')" ]; then
git log main^..HEAD "$@"
exit 0
elif [ "$(git branch | grep -E '^\*?\s*master$')" ]; then
git log master^..HEAD "$@"
exit 0
else
echo 'Neither main nor master branch found'
exit 2
fi
Agora se passarmos um monte de flags, serÃĄ executado o git log com essas opçÃĩes:
Mas serÃĄ que nÃĢo podemos tornar esse script ainda mais legal de usar? E se passÃĄssemos um nome de uma branch pra usar ao invÃĐs de main ou master?
Isso com certeza pode ser feito. Quem sabe pudÃĐssemos assumir que o primeiro argumento ÃĐ ou um nome de branch ou uma flag, e usar as variÃĄveis posicionais especiais $1, $2 etc.
Talvez o comando pudesse ser git log $1^..HEAD $FLAGS, onde $FLAGS seriam todos os argumentos passados menos o primeiro.
Isso envolveria um tratamento melhor e verificaçÃĩes pra ver se todos os argumentos passados sÃĢo somente flags (daà poderÃamos manter main/master e $@) ou se um nome de branch foi passado. à certamente factÃvel, mas vou deixar a possibilidade pra outro post por enquanto... ð
Aliases...?
Como eu disse, costumo fazer aliases para comandos simples com flags que uso comumente. --oneline ÃĐ definitivamente uma das minhas opçÃĩes mais usadas com git log, e ÃĐ claro, eu fiz um alias:
alias gl='~/scripts/git-log-main-master --oneline'
Script no final das contas
#!/bin/sh
##############################
# Non zero exit codes:
# 1 - If not in a git repository
# 2 - If neither git branch main nor master is found
##############################
if [ ! "$(git rev-parse --git-dir 2> /dev/null)" ]; then
echo Not in a git repository
exit 1
fi
if [ "$(git rev-parse -q --verify main)" ]; then
git log main^..HEAD "$@"
exit 0
elif [ "$(git rev-parse -q --verify master)" ]; then
git log master^..HEAD "$@"
exit 0
else
echo 'Neither main nor master branch found'
exit 2
fi
Isso ÃĐ tudo por hoje
Ainda estou aprendendo o bÃĄsico de shell script, e deve haver maneiras melhores de se fazer isso. Qualquer comentÃĄrio e sugestÃĢo ÃĐ mais do que bem vindo! ð


Top comments (0)