quarta-feira, 26 de maio de 2010

Prompt colorido e com git branch

Meu terminal (gnome-terminal + bash) está assim no meu Ubuntu 9.10:






Eu alterei a fonte para Monaco 12px, troquei as cores e adicionei ao prompt o branch do repositório que eu estou (caso a pasta seja um repositório git).

E como isso foi feito?




Instalando a Fonte Monaco no Ubuntu



A instalação da fonte Monaco no Ubuntu pode ser feita da seguinte maneira:



sudo mkdir /usr/share/fonts/macfonts
sudo wget http://jorrel.googlepages.com/Monaco_Linux.ttf -O /usr/share/fonts/macfonts/Monaco_Linux.ttf
sudo fc-cache -f -v


O diretório /usr/share/fonts/macfonts será criado, a fonte Monaco_Linux.ttf será colocada em tal diretório e as configurações de fontes do sistema serão atualizadas. Muito simples!


(Créditos do post http://jorrel.blogspot.com/2007/11/monaco-on-ubuntu.html)



Como Adicionar o git branch ao Prompt


No terminal há uma variável de ambiente chamada PS1, ela guarda a mensagem que será apresentada ao usuário do terminal. Imagine a seguinte situação:


hugo@hugo-laptop:~$ echo $PS1
\u@\h:\w$

Isso significa que será mostrado na mensagem o usuário do sistema, um arroba, o hostname, dois pontos, o diretório atual e um cifrão. Em outras palavras, significa dizer que o usuário é identificado por \u, enquanto o hostname é \h e o diretório é \w.


Para personalizar as mensagens é simples, basta alterar o valor da variável PS1! Por exemplo:


hugo@hugo-laptop:~$ pwd
/home/hugo
hugo@hugo-laptop:~$ export PS1="prompt do \u:\w$ "
prompt do hugo:~$ pwd
/home/hugo
prompt do hugo:~$


A idéia é adicionar à mensagem o nome do branch do repositório git. Para saber o nome do branch no git basta um `git branch` e olhar a linha que começa com asterisco. Por exemplo:



hugo@hugo-laptop:~/should-dsl$ git branch
master
* matchers-as-functions
new_matchers_based_on_hamcrest
nsigustavo_master
rodrigomanhes
separate-matchers
hugo@hugo-laptop:~/should-dsl$


Se for desejado saber apenas o branch atual, podemos usar o grep pra fazer isso:


hugo@hugo-laptop:~/should-dsl$ git branch | grep ^\*
* matchers-as-functions
hugo@hugo-laptop:~/should-dsl$


E se quisermos apenas o nome do branch, sem o asterisco, podemos usar o sed pra remove-lo:


hugo@hugo-laptop:~/should-dsl$ git branch | grep ^\* | sed 's/\* //'
matchers-as-functions
hugo@hugo-laptop:~/should-dsl$

Agora já podemos adicionar o nosso branch à mensagem do terminal:


hugo@hugo-laptop:~/should-dsl$ branch=`git branch 2>/dev/null| grep ^\* | sed 's/\* //'`
hugo@hugo-laptop:~/should-dsl$ export PS1="\u@\h:\w($branch)$ "
hugo@hugo-laptop:~/should-dsl(matchers-as-functions)$

O redirecionamento de erros é pra tratar o caso do comando `git branch` dar um erro, ou seja, a pasta atual não ser um repositório git.



Mas o interessante é ter cores, não!?



Cores no Terminal


É possível usar cores nos terminais unix através de algumas sequências ASCII de cores. Há uma biblioteca, termcolor, que traz esse recurso pro python. Assim, é possível usá-la pra colorir o terminal. Mas no nosso caso vamos ser mais hardcores e lidarmos com as nossas sequências manualmente (porque queremos ser independentes!).



O padrão das sequências de cores é basicamente o seguinte: uma sequência dizendo que se inicia a sequencia, um modificador opcional, a cor, o texto e o finalizador de cor. O que eu quero dizer por modificador é um negrito, por exemplo.



A cor verde, por exemplo, é identificada pelo número 32. A sequência de cores se inicia sempre com um \033[ e sempre termina com um \033[m. Um texto colorido de verde no terminal pode ser feito assim:







Para quem não conseguir visualizar a imagem, o texto no terminal é:


hugo@hugo-laptop:~$ echo -e "\033[32mHello World (verde)\033[m"
Hello World (verde)
hugo@hugo-laptop:~$

Agora chegou a hora de juntar tudo num lugar só!



Mostrando o git branch colorido no prompt


Os comandos aqui usados já foram explicados, então o código segue:


function git_branch_name() {
git branch 2>/dev/null | grep -e '^*' | sed -E 's/^\* (.+)$/(\1) /'
}

function show_colored_git_branch_in_prompt() {
PS1="\[\033[01;32m\]\u@\h:\[\033[01;34m\]\w\[\033[31m\]\$(git_branch_name)\[\033[m\]$ "
}

show_colored_git_branch_in_prompt

Adicione este trecho ao ~/.bashrc e seja feliz!



Caso você queira ver as modificações sem ter que abrir outro terminal, use o comando source:


$ source ~/.bashrc



Peguei a dica nesse post: http://asemanfar.com/Current-Git-Branch-in-Bash-Prompt e alterei algumas coias. Os comentários são excelentes!



Espero que tenham gostado da dica ;-)




Updates:



  • Corrigido PS1 pra ser dinâmico, e não estático
  • Corrigida duplicação de `function`. Valeu Siminino!


11 comentários:

  1. Legal, uso algo parecido:

    http://github.com/ricobl/dotfiles/blob/master/bash/.bashrc#L83

    ResponderExcluir
  2. ae hugo!
    bem bacana a dica, uso uma forma mais simples:
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]$(__git_ps1 "(\033[1;31m%s\033[m)")\$ '
    a parte relevante é o:
    $(__git_ps1 "(%s)")
    que retorna (nomedobranch)
    sem nenhuma função extra.
    ah, aqui é o matias, do fisl e tal :D

    ResponderExcluir
    Respostas
    1. ÉSSA OPÇÃO É MUITO BOA MESMO:

      AINDA PODE COLAR NO no final do ARQUIVO .bashrc

      Excluir
  3. Hugo, fiz algumas alterações pra incluir o estado (alterado ou não) do branch corrente:

    function parse_git_dirty {
    [[
    $(git status 2> /dev/null | tail -n1) != "nothing to commit (working directory clean)" &&
    -e ".git"
    ]] && echo " *"
    }
    function git_branch_name() {
    git branch 2>/dev/null | grep -e '^*' | sed -E "s/^\* (.+)$/(\1$(parse_git_dirty))/"
    }
    function show_colored_git_branch_in_prompt() {
    PS1="\[\033[01;32m\]\u@\h:\[\033[01;34m\]\w\[\033[31m\]\$(git_branch_name)\[\033[m\]$ "
    }

    show_colored_git_branch_in_prompt

    ResponderExcluir
  4. Gostei :D
    Aproveitei pra modificar duas coisas: como ele acha as alterações e
    alterei pra mostrar o * em todas as pastas:

    function parse_git_dirty {
    test "$(git diff HEAD --name-only 2>/dev/null 2>&1)" && echo " *"
    }

    Esse $(git diff HEAD --names-only) mostra os nomes dos arquivos novos,
    alterados, removidos, renomeados e etc, mas que estão na staging area.
    O problema no meu caso, pelo menos, é que tem alguns arquivos que eu
    simplesmente ignoro mas não estão no meu .gitignore, aí ficava
    mostrando o asterisco quando não estava pra ser commitado.

    Valeu a dica Diogo, já até alterei e coloquei no meu .bashrc :D

    ResponderExcluir
  5. Hugo, aqui é o siminino, tava tentando por, mais tava dando error, daí eu reparei que tinha 2 function na seguinte linha:
    "function function git_branch_name() {"

    Não sei se fiz algo certo, mais eu tirei um function e funcionou.

    ResponderExcluir
  6. Fala Siminino!

    Estava errado mesmo! Valeu, já corrigi ;-)

    ResponderExcluir
  7. Curti Hugo !
    Muito bacana mesmo, vlw !

    ResponderExcluir
  8. Excelente, consegui fazer bem rápido e sem traumas!

    ResponderExcluir
  9. Para funcionar, sempre que eu abrir o terminal eu tenho que executar o source ~/.bashrc. Como faço para quando sempre abrir o terminal, essa configuração já seja aplicada?

    ResponderExcluir
    Respostas
    1. Anne Kelly,

      faça o seguinte:

      primeiro passo: copie isso: PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]$(__git_ps1 "(\033[1;31m%s\033[m)")\$ '

      segundo passo: abra o arquivo .bashrc

      terceiro passo: cole o final do arquivos, salve-o, saia do arquivo .bashrc e pronto!

      Se não aparecer nada de diferente feche o terminal e abra novamente!

      Excluir