Quais são as diferenças entre a remoção remota do git, remoção do git, remoção do git --prune, etc

358

Minha situação é essa ... alguém que trabalha no mesmo repositório excluiu um ramo de seu repositório local e remoto ...

A maioria das pessoas que perguntou sobre esse tipo de problema no Stack Overflow ou em outros sites ainda tem o problema de ramificações em sua lista de ramificações de rastreamento remoto git branch -ana parte inferior:

* master
  develop
  feature_blah
  remotes/origin/master
  remotes/origin/develop
  remotes/origin/feature_blah
  remotes/origin/random_branch_I_want_deleted

No entanto, na minha situação, o ramo que não deveria estar lá é local:

* master
  develop
  feature_blah
  random_branch_I_want_deleted
  remotes/origin/master
  remotes/origin/develop
  remotes/origin/feature_blah

Quando eu faço qualquer um dos seguintes, ele não é removido localmente:

$ git prune

Eu também tentei:

$ git remote prune origin
$ git fetch --prune

Informações mais úteis: quando eu verifico, git remote show originé assim que fica:

* remote origin
Fetch URL: utilities:homeconnections_ui.git
Push  URL: utilities:homeconnections_ui.git
HEAD branch: master
Remote branches:
 master                        tracked
 develop                       tracked
 feature_blah                  tracked
 other123                      tracked
 other444                      tracked
 other999                      tracked
Local branches configured for 'git pull':
 develop                      merges with remote develop
 feature_blah                 merges with remote other999
 master                       merges with remote master
 random_branch_I_want_deleted merges with remote random_branch_I_want_deleted
Local refs configured for 'git push':
 develop         pushes to develop     (local out of date)
 master          pushes to master      (up to date)
 feature_blah    pushes to feature_blah(up to date)

Observe que está apenas na seção intitulada Local branches configured for 'git pull':

Por quê?

gogogadgetinternet
fonte
git branch -d the_local_branch
krsteeve
11
Obrigado, mas estou curioso para saber por que isso pode ter ocorrido.
Gogogadgetinternet 20/11/2013
Havia uma sutil diferença quando se trata de hierarquia ramo ( x/y): foi corrigido (ver minha resposta abaixo )
VonC

Respostas:

665

Não culpo você por ficar frustrado com isso. A melhor maneira de olhar é essa. Há potencialmente três versões de cada filial remota:

  1. A ramificação real no repositório remoto
    (por exemplo, repo remoto em https://example.com/repo.git , refs/heads/master)
  2. Seu instantâneo dessa ramificação localmente (armazenado em refs/remotes/...)
    (por exemplo, repositório local refs/remotes/origin/master)
  3. E uma filial local que pode estar rastreando a filial remota
    (por exemplo, repositório local refs/heads/master)

Vamos começar com git prune. Isso remove objetos que não estão mais sendo referenciados, mas não remove referências. No seu caso, você tem uma filial local. Isso significa que há um ref nomeado random_branch_I_want_deletedque se refere a alguns objetos que representam o histórico desse ramo. Portanto, por definição, git prunenão será removido random_branch_I_want_deleted. Realmente, git pruneé uma maneira de excluir dados acumulados no Git, mas que não estão sendo referenciados por nada. Em geral, isso não afeta sua visão de nenhum ramo.

git remote prune origine git fetch --pruneambos operam nas referências abaixo refs/remotes/...(eu as referirei como referências remotas). Não afeta as ramificações locais. A git remoteversão é útil se você deseja remover apenas referências remotas em um controle remoto específico. Caso contrário, os dois fazem exatamente a mesma coisa. Então, resumindo, git remote prunee git fetch --pruneopere no número 2 acima. Por exemplo, se você excluiu um ramo usando a GUI da web git e não deseja que ele apareça mais na sua lista local de ramos (git branch -r ), esse é o comando que você deve usar.

Para remover uma ramificação local, você deve usar git branch -d(ou -Dse não for mesclado em nenhum lugar). FWIW, não há comando git para remover automaticamente as ramificações de rastreamento local se uma ramificação remota desaparecer.

John Szakmeister
fonte
22
Isso faz um trabalho melhor ao abordar a questão geral, explicando as diferenças pertinentes. Ele também responde a perguntas adicionais que eu tive da acima.
Gogogadgetinternet 20/11/2013
14
Este comando mostra uma lista de todas as ramificações locais que não possuem uma ramificação remota correspondente. Você poderia tubulação isso xargs git branch -D, mas nota que os novos ramos que você criou, mas nunca empurrado para o servidor seria excluído, assim pisar com cuidado: git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}'
Jason Walton
4
@Seed Não, não. :-( Ele só exclui os refs monitoramento remoto locais Eu apenas verifiquei isso com a versão 2.7.0..
John Szakmeister
11
@ BlueRaja-DannyPflughoeft Tenha cuidado com essa abordagem. Por exemplo, dependendo de como você faz suas ramificações estáveis, elas podem parecer mescladas na ramificação principal e você acaba removendo-as. Não é uma grande perda aqui, pois você não os está removendo do servidor, mas se você tivesse alguma configuração especial definida para isso, isso seria perdido quando a ramificação fosse excluída.
John Szakmeister
11
@Cloud Não é inteiramente verdade. As referências podem ser compactadas (consulte o packed-refsarquivo na .gitárea), portanto, não é necessariamente uma questão simples de excluí-las via explorador de arquivos. Melhor usar os comandos para garantir que ambos sejam resolvidos corretamente.
John Szakmeister
55

git remote prunee git fetch --prunefaça o mesmo: excluindo os árbitros nos ramos que não existem no controle remoto, como você disse. O segundo comando se conecta ao controle remoto e busca suas ramificações atuais antes da remoção.

No entanto, ele não toca nos ramos locais que você efetuou check-out, que você pode simplesmente excluir com

git branch -d  random_branch_I_want_deleted

Substitua -dpor -Dse o ramo não for mesclado em outro lugar

git prune faz algo diferente, elimina objetos inacessíveis, aqueles commits que não são alcançáveis ​​em nenhuma ramificação ou tag e, portanto, não são mais necessários.

CharlesB
fonte
11
Eu sei que parece óbvio, mas git pruneprocura não apenas ramos e tags, mas todos os outros árbitros também.
Então, no meu caso, por que o git podar funciona? Porque ele não se importa com filiais locais, mas com referências remotas? Obrigado pela informação concisa.
Gogogadgetinternet
@hvd Que tipo de referências existem além de ramos e tags?
precisa
@gogogadgetinternet yes exatamente. (supondo que você quis dizer git remote prune)
charlesb
4
Na IMO, a convenção de nomenclatura git de usar "ameixa" para coleta de objetos e limpeza de referência é onde a confusão se instala. Mas essa é apenas uma das muitas intrigas da interface do usuário, no git. :-)
torek
14

No caso de alguém estar interessado. Aqui está um rápido script de shell que removerá todas as ramificações locais que não são rastreadas remotamente. Uma palavra de cautela: Isso eliminará qualquer ramo que não seja rastreado remotamente, independentemente de ter sido mesclado ou não.

Se vocês encontrarem algum problema com isso, avise-me e eu o corrigirei (etc. etc.)

Salve-o em um arquivo chamado git-rm-ntb(chame o que for) PATHe execute:

git-rm-ntb <remote1:optional> <remote2:optional> ...

clean()
{
  REMOTES="$@";
  if [ -z "$REMOTES" ]; then
    REMOTES=$(git remote);
  fi
  REMOTES=$(echo "$REMOTES" | xargs -n1 echo)
  RBRANCHES=()
  while read REMOTE; do
    CURRBRANCHES=($(git ls-remote $REMOTE | awk '{print $2}' | grep 'refs/heads/' | sed 's:refs/heads/::'))
    RBRANCHES=("${CURRBRANCHES[@]}" "${RBRANCHES[@]}")
  done < <(echo "$REMOTES" )
  [[ $RBRANCHES ]] || exit
  LBRANCHES=($(git branch | sed 's:\*::' | awk '{print $1}'))
  for i in "${LBRANCHES[@]}"; do
    skip=
    for j in "${RBRANCHES[@]}"; do
      [[ $i == $j ]] && { skip=1; echo -e "\033[32m Keeping $i \033[0m"; break; }
    done
    [[ -n $skip ]] || { echo -e "\033[31m $(git branch -D $i) \033[0m"; }
  done
}

clean $@
D.Mill
fonte
Obrigado! Nice contato com a saída de cor ;-)
BVengerov
2
$ (Git branch -d $ i) não seria mais seguro excluir somente ramificações mescladas?
User2012677
Tão útil muito obrigado !!!
Robin Hartland
As opções mais seguras são discutidas em stackoverflow.com/questions/7726949/…
Michael Freidgeim
13

Observe que uma diferença entre git remote --prunee git fetch --pruneestá sendo corrigida, com commit 10a6cc8 , por Tom Miller ( tmiller) (para git 1.9 / 2.0, primeiro trimestre de 2014):

Quando temos um ramo de rastreamento remoto chamado " frotz/nitfol" de uma busca anterior, e o upstream agora possui um ramo chamado "** frotz " **, fetchfalha ao remover " frotz/nitfol" com a " git fetch --prune" do upstream.
O git informa o usuário a usar " git remote prune" para corrigir o problema.

Assim: quando um repo upstream possui uma ramificação ("frotz") com o mesmo nome que uma hierarquia de ramificação ("frotz / xxx", uma possível convenção de nomenclatura de ramificação ), git remote --prunefoi bem-sucedida (na limpeza da ramificação de rastreamento remoto do seu repo) , mas git fetch --pruneestava falhando.

Não mais:

Mude a forma como " fetch --prune" funciona movendo a operação de remoção antes da operação de busca.
Dessa forma, em vez de avisar o usuário sobre um conflito, ele o corrige automaticamente.

VonC
fonte