Plugin para GitExtensions que exibe branches hierarquicamente em estrutura de árvore, mostrando branches filhas.
Versão atual: 1.0.132
TreeOfLife
· · ← frutos dourados (apical)
/ ← bifurcação apical
|
· /|\ · ← galho nível 3 + frutos
/ |
·/ |/ \· ← galho nível 2 + frutos
\ | /
· |/ · ← galho nível 1 + frutos
⊙ ← "coração da vida" (fruto dourado central)
|
·/|\· ← raízes nível 1 + 2
|
───────────── ← dentro de um círculo verde
Elemento Cor Detalhe
Círculo verde-escuro (#145A29) fundo verde-claro #E8F5E9
Tronco verde-escuro 2 px, caps arredondados
Galhos (3 níveis) verde-escuro 1.5 → 0.9 px, afinando para o topo
Raízes (2 pares) verde-escuro 1.2 → 0.9 px
Frutos/folhas dourado (#D4A017) círculos 1.5 px nos galhos, 1.0 px nas raízes
Coração central dourado + borda verde círculo 2.2 px em (16,15)
- Janela não-modal que permanece aberta em paralelo ao GitExtensions
- Árvore dividida em três seções fixas: LOCAL, REMOTES e TAGS
- LOCAL e REMOTES combinam ancestralidade (parentesco real por commits / organização GitFlow) com agrupamento por caminho (
/): dentro de cada nível pai, nomes com/viram nós-pasta. Ex.:feature/testeaparece como pastafeature→ folhateste, erelease/2026comorelease→2026. Quandofeature/*é filha dedevelop, ficadevelop→feature→teste - TAGS também agrupa por
/(sem ancestralidade) - LOCAL, REMOTES e TAGS exibe
(nenhuma branch local encontrada)quando não há branches - A janela abre centralizada na tela (horizontal e vertical)
- Janela redimensionável com botões Minimizar, Maximizar e Fechar padrão do Windows (
Sizable) - A janela é independente do GitExtensions: minimizar o GitExtensions não afeta a janela ZimerfeldTree
- Carregamento assíncrono: ao abrir, a janela exibe o esqueleto imediatamente e depois mostra um painel de progresso centralizado ("Carregando dados do repositório") com barra de porcentagem (0→100%) enquanto lê os dados do repositório em background; a árvore é populada apenas ao final
- Montagem da hierarquia otimizada: o cálculo de parentesco entre branches usa um único
git log --allpara construir o grafo de commits em memória e determina os pais via BFS — complexidade O(commits) em vez do anterior O(N² × subprocesso), eliminando o gargalo em repositórios com dezenas ou centenas de branches - Overlay em toda atualização: o painel de progresso aparece sempre que a árvore é recarregada — abertura inicial, checkout, nova branch, merge, rename, delete, GitFlow, refresh manual e troca de repositório
- Lista de passos (somente leitura): o overlay exibe uma lista acumulativa de cada etapa executada ("Carregando branches locais…", "Calculando hierarquia…", etc.) — cada passo é adicionado à lista conforme é iniciado, permitindo acompanhar o progresso em detalhe. A lista é dimensionada para exibir todos os 8 passos de uma vez, sem barra de rolagem vertical. Após o último passo ("Concluído."), o overlay permanece visível por 1 segundo antes de fechar, para o usuário conseguir ver a conclusão
- Botão Cancelar no overlay: permite abortar o carregamento a qualquer momento (o cancelamento ocorre entre as etapas git, preservando os dados anteriores na árvore)
- Formulário bloqueado durante carregamento: todos os campos e botões ficam desabilitados enquanto o overlay está ativo e são reativados ao término (ou ao cancelar)
- Botão "Fechar" centralizado horizontalmente na parte inferior da janela (atalho: tecla Esc)
- Linha "Working Directory:" no topo da janela contém:
- Label fixo
Working Directory: - ComboBox (somente seleção) populado automaticamente com todos os repositórios listados no dashboard do GitExtensions (lido de
%APPDATA%\GitExtensions\GitExtensions\GitExtensions.settings) e quando novo repositório é criado - Label
Branch: <nome>mostrando a branch em checkout no repositório exibido
- Label fixo
- Selecionar outro repositório no dropdown recarrega a árvore para aquele repositório
- A lista do combo é recarregada automaticamente sempre que o GitExtensions troca de repositório
- A branch atual aparece destacada com texto em negrito e cor de seleção do sistema (
[nome]) - Seções da árvore mostram contadores:
LOCAL (N),REMOTES (N),TAGS (N) - Status bar inferior mostra:
Local: N | Remoto: N | Tags: N
- Campo de pesquisa filtra branches em todas as seções simultaneamente
- Filtro preserva nós pai que possuem filhos correspondentes
Exibidos acima da árvore quando há uma branch em checkout:
- Pull — executa
git pull --tags: traz commits da branch rastreada e todas as tags do remoto, garantindo que tags de releases criadas em outras máquinas apareçam na seção TAGS - Push / Push ↑N — abre o diálogo nativo de Push do GitExtensions (remote, URL, branch destino e opções avançadas); o contador
↑Nmostra quantos commits locais ainda não foram enviados ao remoto - Commit / Commit (N) — abre a janela de Commit nativa do GitExtensions; o contador
(N)só aparece quando há alterações pendentes; sem alterações o botão e o item do menu de contexto mostram apenasCommit - Após cada Push, Pull ou Commit (seja pelos botões ou pela janela principal do GitExtensions), a árvore é atualizada automaticamente e os contadores dos botões (
↑N,↓N,(N)) são recalculados - GitFlow — abre a janela de operações GitFlow; disponível a qualquer momento, independentemente do estado do painel de aviso
- O estado de expansão/recolhimento de cada nó é salvo automaticamente por Working Directory
- Ao abrir o plugin ou ao atualizar a árvore, a estrutura é restaurada exatamente como estava na última sessão
- Estado gravado em
%APPDATA%\GitExtensions\ZimerfeldTree.treestate.json - Salvamento com debounce de 500 ms para não gerar I/O excessivo durante expansões rápidas
- Primeira abertura de um repositório usa o comportamento padrão: LOCAL totalmente expandido, REMOTES e TAGS com apenas a raiz expandida
- Durante filtro ativo, todos os nós são expandidos automaticamente para mostrar os resultados
- O plugin verifica se a hierarquia real (por parentesco de commits) respeita as regras do GitFlow:
master/mainna raiz,developfilho demaster, e branchesfeature/*,release/*ehotfix/*nos pais esperados - Quando detecta que a hierarquia está fora da condição de GitFlow, ele aplica automaticamente a organização GitFlow na árvore e exibe o aviso correspondente
- Nesse estado, o botão do painel de aviso mostra "Restaurar hierarquia real" — clicar nele volta a exibir a ancestralidade real do git
- A escolha manual do botão é respeitada e só é reavaliada ao trocar de repositório (ou reabrir a janela)
- A árvore é recarregada automaticamente ao:
- Trocar de branch (checkout)
- Trocar de repositório na UI do GitExtensions
- Inicializar/reabrir um repositório
- Botão Atualizar para recarga manual
Cada item possui um ícone 16×16 embutido na DLL (gerado em Resources/ctx-*.png):
| Ícone | Item | Disponível para |
|---|---|---|
| ✅ verde | Commit (N) | Sempre — abre a janela de Commit do GitExtensions; N = nº de alterações pendentes |
| → azul | Checkout | Local, remota, tag |
| Y teal | Nova branch daqui… | Local, tag |
| V roxo | Mesclar na branch atual | Local |
| ↑ laranja | Rebase na branch atual | Local |
| ✏️ âmbar | Renomear… | Local |
| ✖ vermelho | Excluir… | Local, remota, tag |
| graph | GitFlow… | Branch (local/remota/tag) |
| ◤◥◣◢ | Expandir tudo | Sempre |
| ◣◢◤◥ | Recolher tudo | Sempre |
| ↻ azul | Atualizar | Sempre |
O item Commit mostra entre parênteses a quantidade de mudanças pendentes na working tree (arquivos staged, modificados e não rastreados), recalculada toda vez que o menu é aberto. Ao clicar, abre a janela de Commit nativa do GitExtensions no processo já em execução, de modo que todos os plugins de Commit Templates (ex.: Zimerfeld: Auto-resumo) já estejam carregados e visíveis no dropdown. Quando o repositório exibido no ZimerfeldTree divergir do repositório ativo no GitExtensions, a janela é aberta via novo processo como fallback.
- Ao fechar a janela GitFlow, a janela ZimerfeldTree é reposicionada automaticamente ao centro da tela
- Após um Start bem-sucedido, o painel "Manage existing branches" é pré-selecionado automaticamente no mesmo Type e na branch recém-criada — válido para feature, release, hotfix, bugfix e support
- Após qualquer botão da janela GitFlow (Start, Publish, Track, Update, Finish) concluir com sucesso, a árvore da janela ZimerfeldTree é atualizada imediatamente (mesmo com a janela GitFlow ainda aberta) e o foco permanece na janela GitFlow — o refresh roda por trás do diálogo modal sem roubar o foco
- Checkout + revelar a branch afetada: após cada botão, o plugin faz
git checkoutda branch afetada e, na árvore, expande os nós da seção LOCAL até alcançá-la e a seleciona. Para Start/Publish/Track/Update a branch afetada é a própria (<prefixo><nome>); para Finish (a branch é removida) o plugin revela a branch resultante atual (ex.:develop), sem refazer checkout
No painel Start branch da janela GitFlow, além de tipo e nome, há a opção based on::
- Por padrão o dropdown fica desabilitado e usa
developcomo base (comportamento padrão dogit flow ... start) - Ao marcar o checkbox based on:, o dropdown é habilitado e lista as branches locais, permitindo iniciar a nova branch a partir de outra — por exemplo, uma feature filha de outra feature pai
- A base escolhida é passada ao comando:
git flow feature start "<nome>" "<base>" - Nome padrão de release: ao selecionar o tipo release, o campo de nome é preenchido automaticamente com a convenção
yyyyMMddHHmm(ex.:202605311230), gerando branches comorelease/202605311230; o preenchimento só ocorre quando o campo está vazio, nunca sobrescrevendo digitação manual
O painel foi adaptado ao git-flow-next, que não possui o comando pull nem as flags -S/-p do finish:
-
Publish —
git flow <tipo> publish "<nome>": envia a branch para o remoto -
Track —
git flow <tipo> track "<nome>": cria uma branch local que rastreia a branch remota correspondente (útil para branches iniciadas por outra pessoa) -
Update —
git flow <tipo> update "<nome>": traz as mudanças da branch pai (ex.: develop) para a branch -
Finish —
git flow <tipo> finish [-k] [--no-fetch] [-m "<nome>"] "<nome>": mescla de volta e remove a branch; o checkbox Keep branch after finish adiciona-ke o checkbox No fetch (--no-fetch) evita a busca remota- Antes de executar o finish, o plugin executa automaticamente
git fetchpara manter as branches de rastreamento locais sincronizadas com o remoto e evitar divergências; o fetch é omitido quando No fetch está marcado - Mensagem da tag automática (
release/hotfix): como esses tipos criam uma tag anotada, o plugin passa-m "<nome>"(a própria versão como mensagem). Sem isso, o git-flow abriria um editor para a mensagem da tag — o que, no processo não-interativo do plugin, resultava emfatal: no tag message?e abortava o finish. O-mtambém é aplicado no retry da auto-resolução de "merge in progress" - Auto-resolução de "merge in progress": quando o finish falha com
a merge is already in progress for branch '<tipo>/<nome>', o plugin aborta o estado travado e retenta o finish original automaticamente. A recuperação tem três níveis: ①git flow <tipo> finish --abortpara limpar o lock do git-flow; ②git merge --abortpara limpar oMERGE_HEADdo git; ③ recuperação de deadlock — o git-flow-next mantém um arquivo de estado persistente (.git/gitflow/state/*.json) que sobrevive mesmo quando o git não temMERGE_HEAD; nesse caso os dois aborts falham e o plugin remove o arquivo órfão diretamente. Após a limpeza, volta à branch original e retenta o finish - A janela GitFlow mantém o foco após cada comando executado
- Antes de executar o finish, o plugin executa automaticamente
-
Finish de
release— fluxo completo automático: quando o tipo éreleasee o checkbox No fetch não está marcado, o painel executa automaticamente em sequência (com as saídas anexadas à janela de resultado):git push <remote> release/<nome>— envia a release para o remoto antes do finish, evitando o errofatal: couldn't find remote ref release/<nome>gerado pelo git-flow ao buscar a branch remotagit flow release finish [-k] -m "<nome>" "<nome>"(o-mfornece a mensagem da tag anotada, evitando o errono tag message?)git push <remote> <master>(nome lido degitflow.branch.master)git push <remote> <develop>(nome lido degitflow.branch.develop)git push <remote> refs/tags/<nome>— envia a tag criada pelo finish ao remoto (o git flow só cria a tag localmente)git push <remote> --delete release/<nome>— remove a branch remota da release, somente se ela ainda existir: o plugin antes verifica comgit ls-remote --heads <remote> release/<nome>e, se a branch já tiver sido removida pelo git-flow durante o finish, pula o delete e registra uma nota amigável na janela de resultado, em vez de exibir o errounable to delete '...': remote ref does not existgit checkout <develop>
Ao concluir com sucesso, a seção TAGS da árvore é expandida automaticamente e o foco vai para o tag criado pelo finish.
O remote usado é
origin(ou o primeiro configurado quandooriginnão existe). Se um dos passos de push de master/develop falhar, o fluxo para naquele ponto e a mensagem de erro é exibida; o push da tag e a remoção da branch remota não interrompem o fluxo. -
O dropdown de branch lista apenas as branches locais do tipo selecionado, refletindo o que existe localmente; é recarregado após cada comando git flow (ex.: ao finalizar uma branch com Finish, ela é removida do dropdown automaticamente)
-
Ao abrir a janela, se a branch em checkout corresponder a um tipo do git flow (ex.:
feature/manage), o dropdown de tipo e o dropdown de branch já vêm pré-selecionados nesse tipo e nessa branch
Quando um comando git flow falha, o resultado é exibido na janela e um aviso é mostrado. Se o erro indicar uma branch base/produção ausente (ex.: couldn't find remote ref main, start point branch 'main' does not exist), a mensagem orienta a verificar as branches existentes e a configuração gitflow.branch.*, e sugere marcar No fetch quando a falha for ao buscar do remoto.
O plugin usa um ícone gerado em tempo de execução via GDI+ (sem imagens externas ou recursos embutidos). O design é uma Árvore da Vida simétrica dentro de um círculo:
- Círculo com fundo verde-claro e borda verde-escura
- Tronco central vertical (verde-escuro, cantos arredondados)
- Galhos em 3 níveis + bifurcação apical — cada nível mais fino e mais estreito
- Raízes em 2 pares curvos abaixo do tronco
- Frutos/folhas dourados nos extremos de cada galho e raiz
- "Coração da vida" — fruto dourado central no tronco, representando a força vital
O ícone aparece:
- No menu Plugins do GitExtensions (16 × 16 px)
- Na barra de título da janela do plugin e na barra de tarefas do Windows (ICO multi-size: 32 + 16 px, formato PNG-encoded Vista+)
O arquivo TreeOfLifeIcon.cs contém toda a lógica de renderização. Não há dependências externas.
Cada nó da árvore recebe um ícone 16 × 16 px gerado em tempo de execução via GDI+ em NodeIcons.cs. Os tipos GitFlow têm ícones próprios:
| Branch | Ícone |
|---|---|
master / main |
imagem personalizada embutida (escudo dourado como reserva) |
develop |
imagem personalizada embutida (chave + martelo como reserva) |
| nó-pasta "feature" | imagem personalizada embutida (galho de branch; folha verde como reserva) |
feature/* (sub-nós) |
imagem personalizada embutida folha.png (folha verde como reserva) |
bugfix/* |
joaninha vermelha |
release/* |
imagem personalizada embutida (pacote/caixa marrom como reserva) |
hotfix/* |
extintor de incêndio vermelho |
support/* |
maleta de primeiros socorros |
Branches locais genéricas usam garfo laranja e nós de caminho usam pasta âmbar. As seções raiz (LOCAL, REMOTES, TAGS), o grupo de remote (ex.: origin), as branches remotas e as tags também usam imagens personalizadas embutidas (ver abaixo).
Vários nós usam imagens PNG embutidas na DLL, declaradas como <EmbeddedResource> condicionais no .csproj. Em tempo de execução, NodeIcons.LoadEmbedded lê o recurso pelo nome GitExtensions.ZimerfeldTree.Resources.<arquivo> e o redimensiona para 16 × 16 px com interpolação de alta qualidade.
| Nó | Arquivo | Reserva (glifo desenhado) |
|---|---|---|
| seção LOCAL | Resources/local.png |
monitor azul-aço |
| seção REMOTES | Resources/remotes.png |
nuvem azul-escura |
| seção TAGS | Resources/tags.png |
etiqueta/fita roxa |
grupo de remote (origin) |
Resources/origin.png |
nuvem azul |
| branch remota (filho de REMOTES) | Resources/remote-branch.png |
garfo verde |
| tag (filho de TAGS) | Resources/tag.png |
etiqueta teal |
master / main |
Resources/master.png |
escudo dourado |
develop |
Resources/develop.png |
chave de boca + martelo cruzados |
| nó-pasta "feature" | Resources/feature.png |
galho de branch |
feature/* (sub-nós) |
Resources/folha.png |
folha verde |
release/* |
Resources/release.png |
pacote/caixa marrom |
- O plugin permanece autocontido: as imagens viajam dentro da DLL, sem depender de arquivos externos na máquina do usuário.
- Cada
<EmbeddedResource>é condicional à existência do arquivo (Condition="Exists(...)"); se o PNG não existir no build, o recurso não é embutido e o nó usa o glifo desenhado de reserva — o build nunca quebra por falta da imagem. - Se o recurso estiver ausente ou ilegível em tempo de execução, o ícone cai automaticamente na reserva, preservando o comportamento anterior.
- Para trocar/adicionar uma imagem: coloque o PNG 16 × 16 em
src/GitExtensions.ZimerfeldTree/Resources/<arquivo>.pnge refaça o build.
- Duplo clique em qualquer branch → checkout da branch selecionada
- Enter → checkout da branch selecionada
- Botão direito → seleciona o nó e abre o menu de contexto
- A janela permanece aberta enquanto o GitExtensions está em uso
- Fechar a janela a destrói — necessário reabrir para recarregar dados
- Singleton: uma única instância por sessão do GitExtensions
- Foco persistente após ações: qualquer ação executada na janela (Pull, Push, Commit, Checkout, Nova branch, Merge, Rebase, Renomear, Excluir) devolve o foco à janela ZimerfeldTree ao concluir. Como a janela é independente (sem owner), notificar o GitExtensions para atualizar sua UI traria a janela principal do GitExtensions para frente; o plugin reativa a ZimerfeldTree logo em seguida. A única exceção é o botão GitFlow: ele abre a janela GitFlow (modal), que mantém o próprio foco enquanto estiver aberta
| Programa | Versão mínima | Download | Função |
|---|---|---|---|
| Git for Windows | qualquer | https://git-scm.com/download/win | Executa todos os comandos git (branch, checkout, pull, push, commit, tag…) |
| GitExtensions | 4.x (.NET 9) | https://github.com/gitextensions/gitextensions/releases | Aplicação host que carrega o plugin; fornece diálogos nativos de Commit, Push e Pull |
| Plugin ZimerfeldTree | — | C:\Program Files\GitExtensions\Plugins\ (build local ou release) |
O plugin em si |
Instalação do Git for Windows: baixar o instalador .exe e, na tela "Adjusting your PATH", selecionar "Git from command line and also from 3rd-party software".
Instalação do GitExtensions: baixar o instalador .msi da release 4.x; ele instala o .NET 9 Desktop Runtime automaticamente.
Atenção: GitExtensions 3.x (
.NET Framework 4.8) é incompatível — o plugin requernet9.0-windows.
| Programa | Download | Função |
|---|---|---|
| git-flow-next | https://github.com/nicola-gh/git-flow-next | Fornece o subcomando git flow (start, finish, publish, track, update) |
Por que git-flow-next e não git-flow clássico? O plugin usa os comandos update e track, ausentes no git-flow original e na edição AVH. Também trata o estado persistente .git/gitflow/state/*.json exclusivo do git-flow-next.
Instalação no Windows via Scoop:
scoop install git-flow-nextVerificar: git flow version
Configurar repositório: git flow init (aceitar padrões ou configurar prefixos manualmente).
| Programa | Download | Função |
|---|---|---|
| .NET SDK 9 | https://dotnet.microsoft.com/download/dotnet/9.0 | Compilar net9.0-windows |
| NuGet CLI | https://www.nuget.org/downloads | Gerar .nupkg (usado por build.ps1) |
cd C:\GitExtensions\ZimerfeldTree\tools .\install.ps1
- Copie
GitExtensions.Plugins.ZimerfeldTree.dllpara:C:\Program Files\GitExtensions\Plugins\ - Reinicie o GitExtensions
- O plugin aparece em Plugins → ZimerfeldTree
Install-Package GitExtensions.ZimerfeldTree -Source C:\NUGETO plugin agrupa branches usando o separador / do nome — não pelo histórico de commits do git. master e develop são irmãos porque nenhum deles contém /:
LOCAL
├── develop ← irmão
└── master ← irmão
Para que uma branch apareça como filha de outra, o nome deve conter /:
LOCAL
└── feature/
├── login ← feature/login
└── pagamento ← feature/pagamento
O git armazena refs como arquivos no sistema de arquivos. Se feature/login já existe como branch, tentrar criar feature/login/oauth resulta em erro:
fatal: cannot lock ref 'refs/heads/feature/login/oauth':
'refs/heads/feature/login' exists; cannot create 'refs/heads/feature/login/oauth'
Isso ocorre porque feature/login seria simultaneamente um arquivo (a branch) e um diretório (pai de oauth), o que é impossível no sistema de arquivos.
Solução: use prefixos distintos ou nomes irmãos:
| Intenção | Nomes que funcionam |
|---|---|
| Sub-tarefas de login | feature/login-oauth, feature/login-session |
| Agrupador sem branch real | feature/login/base + feature/login/oauth |
O gitflow define uma hierarquia fixa onde todas as branches feature/* derivam de develop e são irmãs entre si. Sub-features são geralmente tratadas com commits separados na mesma branch ou com branches irmãs de prefixo comum.
- Abra um repositório no GitExtensions
- Vá em Plugins → ZimerfeldTree
- A janela de hierarquia fica aberta ao lado — navega, filtra, faz checkout sem sair dela
# Incrementa versão, compila e gera .nupkg
# Execute como Administrador para também copiar o DLL para Plugins\
pwsh C:\NUGET\ZimerfeldTree\build.ps1O script:
- Lê a versão atual do
.nuspece incrementa obuild(major.minor.build) - Atualiza
.nuspece.csprojcom a nova versão - Compila em modo Release (
net9.0-windows) - Se for Administrador, copia o DLL para
C:\Program Files\GitExtensions\Plugins\ - Empacota o
.nupkgemC:\NUGET\ZimerfeldTree\
Delete o arquivo:
C:\Program Files\GitExtensions\Plugins\GitExtensions.Plugins.ZimerfeldTree.dll
O GitExtensions não é afetado pela remoção do plugin.
ZimerfeldTree/
├── src/
│ └── GitExtensions.ZimerfeldTree/
│ ├── ZimerfeldTreePlugin.cs # Ponto de entrada MEF (IGitPlugin)
│ ├── BranchHierarchyForm.cs # Janela WinForms principal
│ ├── BranchHierarchyService.cs # Lógica git: coleta, hierarquia, Git Flow
│ ├── BranchNode.cs # Modelos: BranchNode, RemoteGroupNode, BranchTag
│ └── GitExtensions.ZimerfeldTree.csproj
├── build.ps1 # Script de build e deploy
├── README.md # Este arquivo
└── GitExtensions.ZimerfeldTree.nuspec # Metadados do pacote NuGet