Mar 11, 2016

Showing mercurial current branch in ConEmu prompt

As a long time git user, I had a relatively hard time when I started using mercurial CVS; one of the things that I really missed (this one goes to the list of annoyances) was the lack of support for showing the current branch in the console. In the following screenshot you can see that GIT console does show me the current branch (in the example, ofx)


Considering that I have a terrible memory and usually have at least 5 ~ 6 consoles open at any given time, it is not hard to conclude that, from time to time, I simply forget which is the current branch in each console. 

Since mercurial do not provide any help (other than keeping issuing hg branch command all the time ;), it happened, not once, but many times, that I found myself working in one console under the assumption that I was operating on branch Foo when in fact I was working in a completely different one.

Some days ago I had the idea of using mercurial hooks to detect when I am changing branches and setting the console prompt accordingly, but that did not worked out very well. To be fair, mercurial hooks worked as expected; the problem was that my hook was run by hg.exe process which itself is spawned from the console when one type, for instance, hg up -r Foo to change to branch Foo and this process (hg.exe) would need to change the PROMPT of its parent console, which is not allowed (at least AFAIK).

My next step was check whether ConEmu (it is not a secret that I'm in love with ConEmu ;))  had some support to achieve my goal and bingo! ConEmu support some extended ANSI codes, more specifically $E ] 9 ; 7 ; ”cmd“$E\  which allows one to execute the command specified in the string (cmd) and use its output as part of the PROMP string. 

With that information, I just created the following batch file:

@echo off
IF NOT EXIST .hg GOTO NOT_A_MERCURIAL_REPO
hg branch
GOTO END

:NOT_A_MERCURIAL_REPO
@echo !repo
:END

and set the PROMP to:

SET PROMPT=$E[32m$E]9;8;"USERNAME"$E\@$E]9;8;"COMPUTERNAME"$E\$S$E[1D$S$E[92m$P$E[90m$E[38;5;14m $Q$G $E]9;7;"path_to_above.bat"$E\$E[90m$G$E[m$S

and voalá!



In the picture above you can see that when the current directory is not a mercurial repository the string "!repo" is shown (I'll probably change that later and in this case will show nothing); also, as soon as I initialized a local repository the PROMPT changed to indicate that I was in the default branch. 

Now, the only missing bit was to configure ConEmu to automatically set that prompt when it creates a new console, which can be done by specifying a custom batch file (in my case I created a file named ConEmuInit.cmd):




Basically I copied the original file (that can be found in "%ConEmuBaseDir%\CmdInit.cmd") and change it accordingly.

Now I am happier ;) The changes of making mistakes are small now :)

Edit:
If you are using CONEMU  Build 160609 or newer you'll need to include you bath in a white list in order to CONEMU to run it:



EDIT (06/Feb/2018)
The path set above is case sensitive! (I've spent 3 hours hunting ghosts) i.e, if you use D:\utils\branch.bat in the PROMPT variable, adding d:\utils\branch.bat will not work.
 
Happy coding

Leia este post em Português!

Mostrando a branch corrente (mercurial) no prompt do ConEmu

Uma das funcionalidades que sempre gostei no GIT é o fato do GitBash (o console do mesmo) mostrar o nome da branch corrente (no exemplo abaixo, o a branch corrente é a ofx):


Não é de se admirar que quando comecei a usar o mercurial este recurso (ou melhor, a falta do mesmo) me causou certa dificuldade / desconforto.

Considerando que tenho uma memória muito ruim e que facilmente costumo ter entre 5 ~ 6 consoles abertos, é fácil entender a razão do porque, por mais de uma vez, eu estar trabalhando em um console assumindo que estava em uma branch qualquer quando na realidade estava em uma branch completamente diferente (não preciso dizer que isso me deu bastante dor de cabeça).

Alguns dias atras tive a ideia usar o conceito de hooks do mercurial para detectar quando eu mudasse de branch e assim setar o prompt com o nome da mesma; infelizmente esta ideia não funcionou muito bem pois hooks do mercurial são executados pelo processo hg.exe que por sua vez é executado a partir do console e assim sendo minhã solução não era viável pois um processo não pode mudar variáveis de ambiente (no caso o prompt) do processo parente, ou seja, o processo que o iniciou  (muito menos do processo parente do processo parente como neste caso).

Minha próxima tentativa foi verificar se o ConEmu (meu console preferido) tinha algum suporte que me permitisse definir o prompt dinamicamente; felizmente ConEmu suporta uma sintaxe estendida de códigos ANSI, mais precisamente $E ] 9 ; 7 ; ”cmd“$E\  o qual permite executar um programa (especificado pela string cmd) e usar a saída padrão do mesmo como parte do prompt

Munido destas informações eu simplesmente criei o aquivo bat abaixo:

@echo off
IF NOT EXIST .hg GOTO NOT_A_MERCURIAL_REPO
hg branch
GOTO END

:NOT_A_MERCURIAL_REPO
@echo !repo
:END

e defini a variável de ambiente PROMP como:

SET PROMPT=$E[32m$E]9;8;"USERNAME"$E\@$E]9;8;"COMPUTERNAME"$E\$S$E[1D$S$E[92m$P$E[90m$E[38;5;14m $Q$G $E]9;7;"caminho_do_arquivo_acima.bat"$E\$E[90m$G$E[m$S

e voalá!



Como você pode ver na imagem acima, quando o diretório corrente é um repositório mercurial a string => nome_da_branch_corrente é mostrada no prompt (caso o diretório corrente não seja um repositório mercurial a string !repo é apresentada). 

Como último passo configurei o ConEmu para definir o prompt toda vez que um novo console / tab é criado (para tanto eu simplesmente copiei o conteúdo do arquivo "%ConEmuBaseDir%\CmdInit.cmd" par um aquivo chamado ConEmuInit.cmd e o editei de forma que o memo defina o prompt com o valor desejado):



Agora sim, certamente minas dores de cabeça diminuirão :)

Edit:

Se você estiver usando a versão 160609 ou mais nova do CONEMU então será necessário configurar o mesmo para que o batch que você criou possa ser executado (no meu caso este batch encontra-se em c:\utils\hg_current_branch_or_die.bat)



Happy coding

Read this post in English!