Git no macOS: "cannot lock ref" por causa de case sensitivity
Para quem é
Desenvolvedores usando macOS que enfrentam erros do Git ao fazer fetch/pull, como conflitos entre refs FIX/* e fix/* que não aparecem no Linux.
Sintoma
Erros semelhantes a:
error: cannot lock ref 'refs/remotes/origin/FIX/6357': is at <A> but expected <B>
! <hashA>...<hashB> FIX/6357 -> origin/FIX/6357 (unable to update local ref)
Causa (por que só acontece no macOS)
- No macOS, o volume APFS padrão é case-insensitive:
FIX/6357efix/6357são tratados como o mesmo caminho. - No Linux (ex.: Ubuntu, ext4), o filesystem é case-sensitive:
FIX/*efix/*coexistem normalmente. - Quando o repositório remoto tem branches/refs que diferem apenas por maiúsculas/minúsculas, o Git no macOS tenta salvar caminhos que colidem e falha ao atualizar/lockar as refs.
Como resolver
Caminho definitivo (recomendado)
- Trabalhar em um volume Case-Sensitive ou em ambiente Linux.
- Criar um disco virtual APFS Case-Sensitive e montar:
hdiutil create -size 50g -type SPARSEBUNDLE -fs 'Case-sensitive APFS' -volname DevCase ~/DevCase.sparsebundle
hdiutil attach ~/DevCase.sparsebundle
- Clonar e trabalhar dentro do volume:
mkdir -p /Volumes/DevCase/Projetos && cd /Volumes/DevCase/Projetos
git clone <SEU_REPO>
- Quando terminar:
hdiutil detach /Volumes/DevCase
Alternativa: usar Docker/VM Linux e manter o repositório dentro de um volume do container/VM (case-sensitive).
Workaround operacional (local)
Se você precisa continuar no volume case-insensitive, dá para mitigar filtrando do fetch as refs problemáticas (ex.: FIX/*) e limpando as refs locais conflitantes. No repositório local:
# 1) Não buscar mais branches em caixa alta FIX/*
git config --add remote.origin.fetch '^refs/heads/FIX/*'
# 2) Apagar as refs locais conflitantes
for ref in $(git for-each-ref --format='%(refname)' refs/remotes/origin/FIX); do
git update-ref -d "$ref"
done
# 3) Atualizar normalmente
git fetch --all --prune --prune-tags
git pull --ff-only
Observações:
- Isso não conserta o remoto; apenas evita o conflito local no macOS.
- Se o time padronizar o nome das branches (ver abaixo), o problema deixa de ocorrer.
Higiene no repositório (recomendado para o time)
- Padronize nomes de branches em minúsculas (ex.: use
fix/1234em vez deFIX/1234). - Evite criar branches que só diferem por maiúsculas/minúsculas.
Aliases recomendados (macOS)
Para agilizar o dia a dia, adicione ao seu ~/.zshrc:
# Fetch completo e limpo
alias gff='git fetch --all --prune --prune-tags'
# Pull seguro (fast-forward only)
alias gup='git pull --ff-only'
# Fetch + Pull na sequência
alias gfup='git fetch --all --prune --prune-tags && git pull --ff-only'
# Correção de refs conflitantes (FIX/* vs fix/*) + fetch/pull
gfix() {
if ! git config --get-all remote.origin.fetch | grep -qx '^\^refs/heads/FIX/\*'; then
git config --add remote.origin.fetch '^refs/heads/FIX/*'
fi
for ref in $(git for-each-ref --format='%(refname)' refs/remotes/origin/FIX); do
git update-ref -d "$ref"
done
git fetch --all --prune --prune-tags
git pull --ff-only
}
Uso sugerido:
# Quando notar o erro de "cannot lock ref" envolvendo FIX/*
gfix
# No dia a dia
gfup
FAQ rápido
- Posso resolver com
git config core.ignorecase? Não. Isso não “simula” case-sensitive, só influencia detecção de renomes. - Como desfazer o filtro do fetch? Use:
git config --unset-all remote.origin.fetch '^refs/heads/FIX/*'
- Por que no Ubuntu não acontece? Porque o filesystem é case-sensitive;
FIX/*efix/*são caminhos diferentes.