O "git pull --all" pode atualizar todos os meus ramos locais?

473

Geralmente, tenho pelo menos três filiais remotas: mestre, preparação e produção. Eu tenho três filiais locais que rastreiam essas filiais remotas.

Atualizar todas as minhas filiais locais é entediante:

git fetch --all
git rebase origin/master
git checkout staging
git rebase origin/staging
git checkout production
git rebase origin/production

Eu adoraria poder fazer um "git pull-all", mas não consegui fazê-lo funcionar. Parece fazer uma "busca - tudo" e, em seguida, atualiza (avanço rápido ou mescla) o ramo de trabalho atual, mas não os outros ramos locais.

Ainda estou preso alternando manualmente para cada filial local e atualizando.

mpoisot
fonte
8
Deseja atualizar automaticamente as filiais de rastreamento local apenas em caso de avanço rápido? Ypu deve, merge becaue pode ter conflicst você teria que resolver ...
Jakub Narębski
34
Assumindo um custo conservador de US $ 300 em tempo de consultoria para mexer com isso, esse problema único custou às empresas US $ 23.242.800, usando uma contagem de visualizações de 77.476. Agora considere esta questão stackoverflow.com/questions/179123/… e todas as outras. Uau.
Luke Puplett
16
@ Lucas Você é a primeira pessoa que ouvi dizer como o tempo gasto tentando fazer o git fazer o que queremos custa às empresas. Essas coisas simples devem ser automáticas e devem ser tão simples que não preciso abrir um navegador para ler os fóruns, IMO.
Samuel
13
@LukePuplett Há quase 9 vezes mais perguntas sobre o git no SO em comparação com o Mercurial, e a maioria das primeiras parece ser "como faço <operação simples> no git?". Isso indica que o git é mal projetado, mal documentado, não intuitivo ou os três.
Ian Kemp
26
@IanKemp Não sei se é seguro fazer essa afirmação sem conhecer os dados demográficos da SO. Se o Mercurial não é tão usado aqui, ou se seus usuários usam outros fóruns para perguntar sobre isso, eu esperaria ver o mesmo resultado. :) Existem aproximadamente 51 vezes mais perguntas sobre Javascript em comparação com o Assembly - portanto, nem sempre é preciso julgar as ferramentas apenas por esses tipos de métricas.
danShumway

Respostas:

188

O comportamento para o qual você descreve pull --allé exatamente como o esperado, embora não seja necessariamente útil. A opção é passada para o git fetch, que busca todos os refs de todos os controles remotos, em vez do necessário; pulldepois mescla (ou, no seu caso, rebase) a ramificação única apropriada.

Se você quiser conferir outras filiais, precisará verificá-las. E sim, a mesclagem (e a reformulação) exige absolutamente uma árvore de trabalho, para que não possam ser feitas sem verificar os outros ramos. Você pode agrupar as etapas descritas em um script / alias, se quiser, embora eu sugira associar os comandos &&para que, caso um deles falhe, ele não tente seguir adiante.

Cascabel
fonte
2
Se você der um exemplo de linha de comando, eu votaria. Eu tenho esse problema no github. Eu criei uma filial na interface do usuário. Agora eu preciso do meu local para mostrar a filial. git pull - tudo; git branch ... argh ... o comando: git branch -a
mariotti
@ mariotti Depende do que você está tentando fazer, e isso não está claro no seu comentário. Talvez seja melhor fazer uma nova pergunta.
Cascabel 29/10
1
Ou @Jefromi .. dê um exemplo. Na verdade, eu concordo com você.
Mariotti #
3
@mariotti O objetivo desta resposta é que os comandos internos não fazem realmente o que o OP solicitou, portanto a sequência de etapas que eles tiveram é necessária. É possível automatizar essas etapas (veja, por exemplo, a resposta de John), mas elas precisam ser executadas. Portanto, se o que você está tentando fazer é exatamente o mesmo que o OP, não há realmente um exemplo a dar, e se você está tentando fazer algo diferente, deve fazer uma nova pergunta - é assim que o StackOverflow funciona! (E o seu comentário não é clara, mas meu melhor palpite é que você quer diferente algo do OP aqui, então sim, nova pergunta.)
Cascabel
Sim, algo um pouco diferente. Mas sua resposta foi perfeita para o contexto. E talvez eu não precise mais perguntar apenas por causa da sua resposta. Just: A resposta aceita usa o git-up, que é simplesmente uma interface para a linha de comando do git (presumo). Eu esperava que você explicitasse algumas linhas de comandos git. A resposta atual NÃO é git.
Mariotti #
206

Eu uso o syncsubcomando do hub para automatizar isso. Eu tenho alias git=hubno meu .bash_profile, então o comando que eu digito é:

git sync

Isso atualiza todas as ramificações locais que possuem uma ramificação upstream correspondente. Na página do manual:

  • Se a ramificação local estiver desatualizada, avance-a rapidamente;
  • Se a filial local contiver trabalho não enviado, avise-o;
  • Se a ramificação parecer mesclada e sua ramificação upstream foi excluída, exclua-a.

Ele também lida com as alterações não confirmadas ocultas / desimpedidas na ramificação atual.

Eu costumava usar uma ferramenta semelhante chamada git-up , mas não é mais mantida e git syncfaz quase exatamente a mesma coisa.

John
fonte
15
E o Windows?
Girafa Violeta 28/03
6
@ TrentonD.Adams confirma datas e datas de autor são conceitos diferentes. Uma nova alteração mudará a data de confirmação, mas não a data do autor (exceto em conflitos, onde a data do autor também muda). A data do autor reflete quando a árvore da confirmação foi criada e não deve ser alterada durante uma nova recuperação sem conflito. A data de confirmação é alterada porque a rebase sempre cria uma nova confirmação. Portanto, as datas de confirmação sempre estarão na ordem correta.
Dev
16
Para desativar o comportamento de rebaseamento automático do git-up, execute git config --global git-up.rebase.auto false.
Dan Loewenherz
18
@MaxYankov O rebasing do histórico compartilhado geralmente deve ser evitado, não há nada errado em rebater os commits locais durante um pull.
Dev
23
Reestruturar confirmações locais é reescrever o histórico e torná-lo mais simples do que realmente é. Com o rebase, você pode encontrar um código que foi mesclado automaticamente, mas não compila, ou pior, compila, mas não funciona. A mesclagem reconhece a maneira como você trabalhou: você implementou as alterações e as testou antes de incorporar as alterações de outras pessoas, e mesclar o commit é um ponto muito útil: esse é o local em que você garante que diferentes chatsets funcionem bem juntos. O rebaseamento faz com que pareça que esse processo nunca aconteça, o que simplesmente não é verdadeiro e é uma prática muito perigosa.
Max Yankov
39

Sei que esta pergunta tem quase 3 anos, mas me perguntei a mesma pergunta e não encontrei nenhuma solução pronta. Então, eu criei um script de shell de comando git personalizado.

Aqui está, o git-ffwd-updatescript faz o seguinte ...

  1. emite um git remote updatepara buscar as revs lates
  2. depois usa git remote showpara obter uma lista de ramificações locais que rastreiam uma ramificação remota (por exemplo, ramificações que podem ser usadas com git pull)
  3. depois, verifica com git rev-list --count <REMOTE_BRANCH>..<LOCAL_BRANCH>quantos commit a filial local está atrás do controle remoto (e vice-versa)
  4. se a filial local for 1 ou mais confirmada adiante, NÃO poderá ser encaminhada com rapidez e precisará ser mesclada ou reformulada manualmente
  5. se a ramificação local for 0 confirmada adiante e 1 ou mais confirmada atrasada, poderá ser encaminhada rapidamente por git branch -f <LOCAL_BRANCH> -t <REMOTE_BRANCH>

o script pode ser chamado como:

$ git ffwd-update
Fetching origin
 branch bigcouch was 10 commit(s) behind of origin/bigcouch. resetting local branch to remote
 branch develop was 3 commit(s) behind of origin/develop. resetting local branch to remote
 branch master is 6 commit(s) behind and 1 commit(s) ahead of origin/master. could not be fast-forwarded

O script completo deve ser salvo como git-ffwd-updatee precisa estar no PATH.

#!/bin/bash

main() {
  REMOTES="$@";
  if [ -z "$REMOTES" ]; then
    REMOTES=$(git remote);
  fi
  REMOTES=$(echo "$REMOTES" | xargs -n1 echo)
  CLB=$(git rev-parse --abbrev-ref HEAD);
  echo "$REMOTES" | while read REMOTE; do
    git remote update $REMOTE
    git remote show $REMOTE -n \
    | awk '/merges with remote/{print $5" "$1}' \
    | while read RB LB; do
      ARB="refs/remotes/$REMOTE/$RB";
      ALB="refs/heads/$LB";
      NBEHIND=$(( $(git rev-list --count $ALB..$ARB 2>/dev/null) +0));
      NAHEAD=$(( $(git rev-list --count $ARB..$ALB 2>/dev/null) +0));
      if [ "$NBEHIND" -gt 0 ]; then
        if [ "$NAHEAD" -gt 0 ]; then
          echo " branch $LB is $NBEHIND commit(s) behind and $NAHEAD commit(s) ahead of $REMOTE/$RB. could not be fast-forwarded";
        elif [ "$LB" = "$CLB" ]; then
          echo " branch $LB was $NBEHIND commit(s) behind of $REMOTE/$RB. fast-forward merge";
          git merge -q $ARB;
        else
          echo " branch $LB was $NBEHIND commit(s) behind of $REMOTE/$RB. resetting local branch to remote";
          git branch -f $LB -t $ARB >/dev/null;
        fi
      fi
    done
  done
}

main $@
muhqu
fonte
1
Obrigado por este script. É possível que alguém possa converter esse script em lote do Windows?
Saariko 2/12/12
@ Saariko por que você não quer usar o git em um shell normal do Windows? Se você usar algo como cygwin esse script deverá funcionar muito bem ... (embora eu não testei)
muhqu
@RyanWilcox obrigado, estou usando-o como todos os dias (de trabalho) ... ;-) você pode dar uma olhada nos meus arquivos de ponto para obter mais scripts e aliases relacionados ao git: github.com/muhqu/dotfiles
muhqu 25/02
@muhqu Estou tentando usar seu script e não sei por que ele funcionou pela primeira vez, mas não está funcionando "como esperado" no momento. Por exemplo, dê uma olhada nisso . Por que o mestre ainda está comprometido depois que eu executo seu script?
BPL
1
@muhqu Nas versões mais recentes do git, -t e -l não devem ser usados ​​juntos em uma git branchchamada. Eu removi o -l para alterar a chamada git branch -f $LB -t $ARB >/dev/null;e agora o script funciona como deveria.
Radek Liska
24

Não é tão difícil de automatizar:

#!/bin/sh
# Usage: fetchall.sh branch ...

set -x
git fetch --all
for branch in "$@"; do
    git checkout "$branch"      || exit 1
    git rebase "origin/$branch" || exit 1
done
Fred Foo
fonte
4
Provavelmente é melhor não usar aliases em scripts. Na verdade, isso também não busca nada, apenas refaz o conteúdo já buscado. Você deve alterar git rebase origin/$branchpara git pull, para que ele seja buscado no ramo de rastreamento apropriado (presumivelmente na origem) e seja mesclado ou reformulado conforme determinado pela configuração.
Cascabel
@ Jeffromi: eu tinha esquecido o fetch. Editou; recursos / correções extras, tudo o que for necessário para o OP.
Fred Foo
8
Eu ainda acho que você pode querer usar pull(ou verificar branch.<branch>.rebase), para não refazer acidentalmente um ramo que está configurado para puxar normalmente (mesclagem).
Cascabel
1
Considere usar em set -evez de || exit 1para fazer o intérprete sair no primeiro erro.
crishoj
18

Isso ainda não é automático, como eu gostaria que houvesse uma opção para - e deve haver alguma verificação para garantir que isso só ocorra em atualizações de avanço rápido (e é por isso que fazer um pull manualmente é muito mais seguro !!), mas ressalvas, você pode:

git fetch origin
git update-ref refs/heads/other-branch origin/other-branch

para atualizar a posição de sua filial local sem precisar fazer check-out.

Nota: você estará perdendo sua posição atual de ramificação e movendo-a para onde está a ramificação de origem, o que significa que, se você precisar mesclar, perderá dados!

Matt Connolly
fonte
1
Esta é exatamente a solução que eu estava procurando. Normalmente, não tenho alterações não enviadas em várias ramificações e só quero atualizar minhas várias ramificações locais para corresponder ao controle remoto. Esta solução é muito melhor do que o meu método usual de exclusão / nova verificação!
Dave Cavaleiro
1
combinado em um comando:git fetch origin other-branch:other-branch
fabb 22/09/18
12

Há muitas respostas aqui, mas nenhuma é usada git-fetchpara atualizar diretamente a referência local, o que é muito mais simples do que verificar filiais e mais segura do que git-update-ref.

Aqui usamos git-fetchpara atualizar ramificações não atuais e git pull --ff-onlypara a ramificação atual. Isto:

  • Não requer check out branches
  • Atualiza ramificações somente se elas puderem avançar rapidamente
  • Relatará quando não puder avançar rapidamente

e aqui está:

#!/bin/bash
currentbranchref="$(git symbolic-ref HEAD 2>&-)"
git branch -r | grep -v ' -> ' | while read remotebranch
do
    # Split <remote>/<branch> into remote and branchref parts
    remote="${remotebranch%%/*}"
    branchref="refs/heads/${remotebranch#*/}"

    if [ "$branchref" == "$currentbranchref" ]
    then
        echo "Updating current branch $branchref from $remote..."
        git pull --ff-only
    else
        echo "Updating non-current ref $branchref from $remote..."
        git fetch "$remote" "$branchref:$branchref"
    fi
done

Na página de manual para git-fetch:

   <refspec>
       The format of a <refspec> parameter is an optional plus +, followed by the source ref <src>,
       followed by a colon :, followed by the destination ref <dst>.

       The remote ref that matches <src> is fetched, and if <dst> is not empty string, the local ref
       that matches it is fast-forwarded using <src>. If the optional plus + is used, the local ref is
       updated even if it does not result in a fast-forward update.

Ao especificar git fetch <remote> <ref>:<ref>(sem nenhum +), obtemos uma busca que atualiza a ref local apenas quando ela pode ser encaminhada rapidamente.

Nota : isso pressupõe que as ramificações locais e remotas tenham o mesmo nome (e que você deseja rastrear todas as ramificações); deve realmente usar informações sobre quais ramificações locais você possui e o que elas estão configuradas para rastrear.

quorniano
fonte
1
"Atualiza as ramificações somente se elas puderem avançar rapidamente" - qual o significado do avanço rápido? Se eu quero as últimas fontes em todas as minhas filiais, por que devo me preocupar com o encaminhamento rápido ou não? São coisas assim que me fazem rir do Git e dos seus Fanboi. Você não pode fazer isso com apenas um comando. Em vez disso, você precisa executar as c*netapas (em vez de 1), onde chá algum número de comandos repetidos e no número de ramificações.
jww 21/05
@jww Não ajuda "rir do Git e dos seus Fanboi" [sic] quando é o VCS que a maioria do mundo usa. Mas discordo ... acho que, no contexto desse tipo de script de "atração global", é prudente não tentar fazer alterações em ramificações não atuais se elas tiverem conflitos de mesclagem.
Ville
Isso foi útil, obrigado. Única coisa que eu não gostei foi que ele criou um ramo localmente para cada filial remota (incluindo aqueles em que eu não estou interessado), então eu mudei git branch -r | grep -v ' -> ' | while read remotebranchpara git branch -r | grep -v ' -> ' | grep -f <(git branch | cut -c 3- | awk '{print "\\S*/"$0"$"}') | while read remotebranchlimitá-lo a ramos que eu já tenho localmente. Também adicionei um git fetch --pruneno início para atualizar a lista de ramificações remotas antes de fazer qualquer coisa, o que evita alguns avisos.
Nate Cook #
11

Esse problema ainda não está resolvido, pelo menos não com facilidade / sem script: veja este post na lista de discussão git de Junio ​​C Hamano explicando a situação e fornecendo uma solução simples.

O principal raciocínio é que você não precisa disso:

Com o git que não é antigo (por exemplo, v1.5.0 ou mais recente), não há razão para ter um "dev" local que rastreie mais o controle remoto. Se você quiser apenas ver e ver, é possível verificar o ramo de rastreamento remoto diretamente em um HEAD desanexado com " git checkout origin/dev".

O que significa que os únicos casos que precisamos tornar conveniente para os usuários são lidar com essas filiais locais que "rastreiam" as remotas quando você tem alterações locais ou quando planeja ter algumas.

Se houver alterações locais em "dev" marcadas para rastrear a remoção "dev" e se você estiver em um ramo diferente de "dev", não faremos nada depois " git fetch" de atualizar o rastreamento remoto "dev" . Não vai avançar de qualquer maneira

A chamada para uma solução era uma opção ou script externo para remover as ramificações locais que seguem agora as ramificações de rastreamento remoto, em vez de mantê-las atualizadas pelo avanço rápido, como o pôster original solicitado.

Então, que tal " git branch --prune --remote=<upstream>" que itera nas filiais locais e se

(1) não é o ramo atual; e
(2) está marcado para rastrear algum ramo retirado do <upstream>; e
(3) não possui nenhum compromisso por si só;

então remover esse ramo? " git remote --prune-local-forks <upstream>" também está bem; Eu não ligo para qual comando implementa tanto esse recurso.

Nota: a partir do git 2.10, essa solução não existe. Observe que ogit remote prunesubcomando, egit fetch --prunetrata da remoção da ramificação de rastreamento remoto da ramificação que não existe mais no controle remoto, não trata da remoção da ramificação local que rastreia a ramificação de rastreamento remoto (para a qual a ramificação de rastreamento remoto é a ramificação upstream).

Jakub Narębski
fonte
Ao invés de apenas postar links, por favor postar conteúdo real, usando links como referências. Esse link agora está morto. Que pena, parecia promissor. (Sei que esta resposta foi a partir de 2009, pelo que esta é apenas uma nota para referência futura.)
michael
obrigado (e uau, resposta rápida depois de tantos anos). Agora vejo que esse segmento é um " pedido de uma solução simples", em oposição à minha leitura original ", fornece uma solução simples".
27517 Michael
@michael_n: expandido ... hmm, agora vejo que o post não era exatamente sobre a solução solicitada, mas era sobre o problema (assumindo o caso XY).
Jakub Narębski
Hmm, espreitar com a cabeça desanexada deve ser facilitado, especialmente deve mostrar informações úteis No status e permitir diminuir o avanço rápido da área de trabalho com algum feedback (como as confirmações extraídas). Então seria um substituto para filiais locais somente leitura.
Eckes
9

Há muitas respostas aceitáveis ​​aqui, mas parte do encanamento pode ser um pouco opaca para os não iniciados. Aqui está um exemplo muito mais simples que pode ser facilmente personalizado:

$ cat ~/bin/git/git-update-all
#!/bin/bash
# Update all local branches, checking out each branch in succession.
# Eventually returns to the original branch. Use "-n" for dry-run.
git_update_all() {
  local run br
  br=$(git name-rev --name-only HEAD 2>/dev/null)
  [ "$1" = "-n" ] && shift && run=echo

  for x in $( git branch | cut -c3- ) ; do
     $run git checkout $x && $run git pull --ff-only || return 2
  done

  [ ${#br} -gt 0 ] && $run git checkout "$br"
}

git_update_all "$@"

Se você adicionar ~/bin/gitao seu PATH(assumindo que o arquivo seja ~/bin/git/git-update-all), basta executar:

$ git update-all
Michael
fonte
obrigado! você me salvou uma hora brincando com bash ...
8ctopus
5

Adicionar este script para .profileno Mac OS X:

# Usage:
#   `git-pull-all` to pull all your local branches from origin
#   `git-pull-all remote` to pull all your local branches from a named remote

function git-pull-all() {
    START=$(git symbolic-ref --short -q HEAD);
    for branch in $(git branch | sed 's/^.//'); do
        git checkout $branch;
        git pull ${1:-origin} $branch || break;
    done;
    git checkout $START;
};

function git-push-all() {
    git push --all ${1:-origin};
};
Mike
fonte
1
Isso não deveria esconder todas as alterações primeiro e depois restaurá-las?
Mel
5

Aqui está uma boa resposta: Como buscar todos os ramos git

for remote in `git branch -r`; do git branch --track $remote; done
git pull --all
milkovsky
fonte
Por que você está sugerindo fazer git fetche git pull, em vez de apenas git pull?
Syntagma
Obrigado. Parece que o pull busca todos os ramos de todos os controles remotos. Alterado
milkovsky 6/15
8
Isso buscará todos os controles remotos, mas unirá apenas a ramificação atual. Se você tiver 10 controles remotos, precisará fazer o checkout manualmente de cada um e mesclar.
mpoisot
Fazer isso criará todas as filiais remotas localmente com origin/prefixo
Yassine ElBadaoui 17/17
3

Um script que escrevi para o meu GitBash . Realiza o seguinte:

  • Por padrão, puxa da origem para todas as ramificações configuradas para rastrear a origem, permite especificar um controle remoto diferente, se desejado.
  • Se o seu ramo atual estiver em um estado sujo, ele ocultará suas alterações e tentará restaurá-las no final.
  • Para cada filial local configurada para rastrear uma filial remota:
    • git checkout branch
    • git pull origin
  • Finalmente, você retornará ao seu ramo original e restaurará o estado.

** Eu uso isso, mas não testei completamente, use por sua conta e risco. Veja um exemplo desse script em um arquivo .bash_alias aqui .

    # Do a pull on all branches that are tracking a remote branches, will from origin by default.
    # If current branch is dirty, will stash changes and reply after pull.
    # Usage: pullall [remoteName]
    alias pullall=pullAll
    function pullAll (){
     # if -h then show help
     if [[ $1 == '-h' ]]
    then
      echo "Description: Pulls new changes from upstream on all branches that are tracking remotes."
      echo 
      echo "Usage: "
      echo "- Default: pullall"
      echo "- Specify upstream to pull from: pullall [upstreamName]"
      echo "- Help: pull-all -h"
    else

     # default remote to origin
     remote="origin"
     if [ $1 != "" ]
     then
       remote=$1
     fi

     # list all branches that are tracking remote
     # git branch -vv : list branches with their upstreams
     # grep origin : keep only items that have upstream of origin
     # sed "s/^.."... : remove leading *
     # sed "s/^"..... : remove leading white spaces
     # cut -d" "..... : cut on spaces, take first item
     # cut -d splits on space, -f1 grabs first item
     branches=($(git branch -vv | grep $remote | sed "s/^[ *]*//" | sed "s/^[ /t]*//" | cut -d" " -f1))

     # get starting branch name
     startingBranch=$(git rev-parse --abbrev-ref HEAD)

     # get starting stash size
     startingStashSize=$(git stash list | wc -l)

     echo "Saving starting branch state: $startingBranch"
     git stash

     # get the new stash size
     newStashSize=$(git stash list | wc -l)

     # for each branch in the array of remote tracking branches
     for branch in ${branches[*]}
     do
       echo "Switching to $branch"
       git checkout $branch

       echo "Pulling $remote"
       git pull $remote

     done

     echo "Switching back to $startingBranch"
     git checkout $startingBranch

     # compare before and after stash size to see if anything was stashed
     if [ "$startingStashSize" -lt "$newStashSize" ]
     then
       echo "Restoring branch state"
       git stash pop
     fi
    fi
    }
philosowaffle
fonte
Você pode fornecer um arquivo bat equivalente do Windows?
Jaffy
1
@ Jaffy Não sei quanto tempo tenho em minhas mãos e não sou super fluente em lotes, mas posso tentar. Vou postar meu progresso aqui , talvez outros possam intervir e ajudar?
philosowaffle
3

Se você estiver no Windows, poderá usar o PyGitUp, que é um clone do git-upPython. Você pode instalá-lo usando pip com pip install --user git-upou através do Scoop usandoscoop install git-up

[4]

Simon Hartcher
fonte
3

Apenas postando uma resposta atualizada. git-upnão é mais mantido e, se você ler a documentação, eles mencionam que a funcionalidade agora está disponível no git .

A partir do Git 2.9, o git pull --rebase --autostash faz basicamente a mesma coisa.

Portanto, se você atualizar para o Git 2.9 ou posterior, poderá usar esse alias em vez de instalar o git-up:

git config --global alias.up 'pull --rebase --autostash'

Você também pode definir isso para todos os git pullitens do Git 2.9 (obrigado @VonC, veja a resposta aqui )

git config --global pull.rebase true
git config --global rebase.autoStash true
agosto
fonte
1
Você não precisa de um alias. Um simples git pull é suficiente, com a configuração correta: stackoverflow.com/a/40067353/6309
VonC
Grande apelo fora graças @VonC Eu atualizei a minha resposta :) também pode enviar um PR a git-updocumentação porque eles não mencionar que
agosto
Isso não atualiza todas as ramificações locais de uma só vez, e é por isso que eu usei principalmente git-up.
ray
Documentação atualizada em git-up:)
ago
3

Me deparei com o mesmo problema desta pergunta ...

Pensando nisso, fiz uma pequena função de alias dentro do meu .bashrcarquivo:

gitPullAll() {
    for branch in `git branch | sed -E 's/^\*/ /' | awk '{print $1}'`; do
        git checkout $branch
        git pull -p
        printf "\n"
    done
    echo "Done"
}

Trabalhou para mim (:

Francisco Neto
fonte
2

Se refs / heads / master puder ser encaminhado rapidamente para refs / remotes / foo / master , a saída de

git merge-base refs/heads/master refs/remotes/foo/master

deve retornar o ID SHA1 ao qual refs / heads / master aponta. Com isso, é possível montar um script que atualize automaticamente todas as ramificações locais que não tiveram confirmação de desvio aplicada a elas.

Este pequeno script de shell (eu chamei de git-can-ff ) ilustra como isso pode ser feito.

#!/bin/sh

set -x

usage() {
    echo "usage: $(basename $0) <from-ref> <to-ref>" >&2
    exit 2
}

[ $# -ne 2 ] && usage

FROM_REF=$1
TO_REF=$2

FROM_HASH=$(git show-ref --hash $FROM_REF)
TO_HASH=$(git show-ref --hash $TO_REF)
BASE_HASH=$(git merge-base $FROM_REF $TO_REF)

if [ "$BASE_HASH" = "$FROM_HASH" -o \
     "$BASE_HASH" = "$FROM_REF" ]; then
    exit 0
else
    exit 1
fi
hillu
fonte
O que você implica com esse comentário?
Hillu
Eu mesmo não sou capaz de escrever o script que hillu sugere, e não tenho confiança suficiente em meu conhecimento do git para usar o git-merge-base.
Norman Ramsey
2
Receio não entender o modelo suficientemente bem para explorar o script tão gentilmente fornecido. É o suficiente para fazer uma pessoa querer mudar para mercurcial.
Norman Ramsey
Pessoalmente, achei o artigo de Tommi Virtanen "Git para cientistas da computação" bastante útil para se familiarizar com o modelo e a terminologia do git.
Hillu
2

Para concluir a resposta de Matt Connolly, esta é uma maneira mais segura de atualizar as referências da filial local que podem ser encaminhadas rapidamente, sem fazer check-out da filial. Ele não atualiza ramificações que não podem ser encaminhadas rapidamente (ou seja, que divergiram) e não atualiza a ramificação que está com check-out no momento (porque a cópia de trabalho também deve ser atualizada).

git fetch

head="$(git symbolic-ref HEAD)"
git for-each-ref --format="%(refname) %(upstream)" refs/heads | while read ref up; do
    if [ -n "$up" -a "$ref" != "$head" ]; then
        mine="$(git rev-parse "$ref")"
        theirs="$(git rev-parse "$up")"
        base="$(git merge-base "$ref" "$up")"
        if [ "$mine" != "$theirs" -a "$mine" == "$base" ]; then
            git update-ref "$ref" "$theirs"
        fi
    fi
done
Bruno De Fraine
fonte
2

Um script um pouco diferente que apenas encaminha antecipadamente ramificações cujos nomes correspondem à ramificação upstream. Ele também atualiza a ramificação atual, se for possível avançar rapidamente.

Verifique se todas as ramificações upstream de suas ramificações estão definidas corretamente executando git branch -vv. Defina o ramo upstream comgit branch -u origin/yourbanchname

Copie e cole em um arquivo e chmod 755:

#!/bin/sh

curbranch=$(git rev-parse --abbrev-ref HEAD)

for branch in $(git for-each-ref refs/heads --format="%(refname:short)"); do
        upbranch=$(git config --get branch.$branch.merge | sed 's:refs/heads/::');
        if [ "$branch" = "$upbranch" ]; then
                if [ "$branch" = "$curbranch" ]; then
                        echo Fast forwarding current branch $curbranch
                        git merge --ff-only origin/$upbranch
                else
                        echo Fast forwarding $branch with origin/$upbranch
                        git fetch . origin/$upbranch:$branch
                fi
        fi
done;
qwertzguy
fonte
2

O one-liner a seguir encaminha rapidamente todas as ramificações que possuem uma ramificação upstream, se possível, e imprime um erro caso contrário:

git branch \
  --format "%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)" |
  sh

Como funciona?

Ele usa um formato personalizado com o git branchcomando Para cada ramificação que possui uma ramificação upstream, ela imprime uma linha com o seguinte padrão:

git push . <remote-ref>:<branch>

Isso pode ser canalizado diretamente sh(assumindo que os nomes das ramificações estejam bem formados). Omita o | shpara ver o que está fazendo.

Ressalvas

O one-liner não entrará em contato com seus controles remotos. Emita um git fetchou git fetch --allantes de executá-lo.

A filial com check-out atualmente não será atualizada com uma mensagem como

! [remote rejected] origin/master -> master (branch is currently checked out)

Para isso, você pode recorrer a regular git pull --ff-only.

Alias

Adicione o seguinte ao seu .gitconfigpara git fftexecutar este comando:

[alias]
        fft = !sh -c 'git branch --format \"%(if)%(upstream:short)%(then)git push . %(upstream:short):%(refname:short)%(end)\" | sh' -

Veja também o meu .gitconfig. O alias é uma abreviação de "fast-forward tracking (branches)".

krlmlr
fonte
Esta é uma boa solução, embora eu acho que eu vou estar usando o hubsoluction propsed por @ John para ele é a melhor saída.
Didier L
Isso é rápido, simples e realmente funciona! Estou confuso com o git pushfato de ser totalmente o oposto do que você esperaria. Qual o segredo?
BrandonLWhite
@BrandonLWhite: Não entendi a pergunta. Do que você espera git push?
krlmlr
git pushtem semântica de upload - Eu tenho alguns commits localmente que desejo enviar upstream. git pulltem semântica de download - quero obter algumas confirmações remotas upstream na minha filial local. Como estamos falando sobre o download de novos commits do controle remoto para o local, git pullé a escolha óbvia. Mas não, esse truque usa git push. Como git pushresulta em mudanças remotas na minha filial local ?!
BrandonLWhite
git pushtambém pode ser usado para atualizar ramificações locais, desde que seja uma atualização de avanço rápido.
krlmlr
1

O script de @larsmans, melhorou um pouco:

#!/bin/sh

set -x
CURRENT=`git rev-parse --abbrev-ref HEAD`
git fetch --all
for branch in "$@"; do
  if ["$branch" -ne "$CURRENT"]; then
    git checkout "$branch" || exit 1
    git rebase "origin/$branch" || exit 1
  fi
done
git checkout "$CURRENT" || exit 1
git rebase "origin/$CURRENT" || exit 1

Depois que termina, deixa a cópia de trabalho com check-out do mesmo ramo que estava antes do nome do script.

A git pullversão:

#!/bin/sh

set -x
CURRENT=`git rev-parse --abbrev-ref HEAD`
git fetch --all
for branch in "$@"; do
  if ["$branch" -ne "$CURRENT"]; then
    git checkout "$branch" || exit 1
    git pull || exit 1
  fi
done
git checkout "$CURRENT" || exit 1
git pull || exit 1
Saran
fonte
1

Parece que muitos outros contribuíram com soluções semelhantes, mas pensei em compartilhar o que descobri e convidar outras pessoas a contribuir. Esta solução possui uma boa saída colorida, lida com o seu diretório de trabalho atual graciosamente e é rápida porque não faz checkout e deixa seu diretório de trabalho intacto. Além disso, é apenas um script de shell sem outras dependências além do git. (testado apenas no OSX até agora)

#!/usr/bin/env bash

gitup(){    
RED='\033[33;31m'
YELLO='\033[33;33m'
GREEN='\033[33;32m'
NC='\033[0m' # No Color

HEAD=$(git rev-parse HEAD)
CHANGED=$(git status --porcelain | wc -l)

echo "Fetching..."
git fetch --all --prune &>/dev/null
for branch in `git for-each-ref --format='%(refname:short)' refs/heads`; do

    LOCAL=$(git rev-parse --quiet --verify $branch)
    if [ "$HEAD" = "$LOCAL" ] && [ $CHANGED -gt 0 ]; then
        echo -e "${YELLO}WORKING${NC}\t\t$branch"
    elif git rev-parse --verify --quiet $branch@{u}&>/dev/null; then
        REMOTE=$(git rev-parse --quiet --verify $branch@{u})
        BASE=$(git merge-base $branch $branch@{u})

        if [ "$LOCAL" = "$REMOTE" ]; then
           echo -e "${GREEN}OK${NC}\t\t$branch" 
        elif [ "$LOCAL" = "$BASE" ]; then
            if [ "$HEAD" = "$LOCAL" ]; then
                git merge $REMOTE&>/dev/null
            else
                git branch -f $branch $REMOTE
            fi
            echo -e "${GREEN}UPDATED${NC}\t\t$branch"
        elif [ "$REMOTE" = "$BASE" ]; then
            echo -e "${RED}AHEAD${NC}\t\t$branch"
        else
            echo -e "${RED}DIVERGED${NC}\t\t$branch"
        fi
    else
        echo -e "${RED}NO REMOTE${NC}\t$branch"
    fi
done
}

https://github.com/davestimpert/gitup

Desculpe, também parece ter surgido com o mesmo nome da outra ferramenta acima.

Stimp
fonte
2
Você é quem escreveu isso? Em caso afirmativo, divulgue sua afiliação, ou seja, diga-nos como você está relacionado a ela. Por favor, leia mais sobre isso para obter mais informações. Especificamente Não conte - mostre! ; Diga-nos quais partes do seu script e como / por que ele resolve o problema.
Keale
1
Sim, eu escrevi. Incluí a fonte acima para copiar e colar rapidamente no seu .bashrc ou .zshrc.
Stimp
Esta é uma boa solução e funciona bem. Ninguém notou?
Ville
1

Isso pode ser feito usando o script abaixo ... Ele primeiro buscará todos os ramos e fará o checkout um por um e atualizará por si próprio.

#!/bin/bash
git branch -r | grep -v '\->' | while read remote; do git branch --track 
"${remote#origin/}" "$remote"; done

set -x
CURRENT=`git rev-parse --abbrev-ref HEAD`
git fetch --all
branch_name=$(git branch | awk '{print $1" "}' | grep -v '*' | xargs)
for branch in $branch_name; do
   git checkout "$branch" || exit 1
   git rebase "origin/$branch" || exit 1
   git pull origin $branch|| exit 1
done
git checkout "$CURRENT" || exit 1
git pull || exit 1
Vikash Singh
fonte
Adicione algumas explicações para pontuar sua resposta
fool-dev
1

Você não pode fazer isso com apenas um comando git, mas pode automatizá-lo com uma linha bash.

Para atualizar com segurança todos os ramos com uma linha, eis o que eu faço:

git fetch --all && for branch in $(git branch | sed '/*/{$q;h;d};$G' | tr -d '*') ; do git checkout $branch && git merge --ff-only || break ; done
  • Se não conseguir avançar rapidamente um ramo ou encontrar um erro, ele o interromperá e o deixará nesse ramo, para que você possa retomar o controle e mesclar manualmente.

  • Se todas as ramificações puderem avançar rapidamente, ela terminará com a ramificação em que você estava atualmente, deixando-o onde estava antes de atualizar.

Explicações:

Para uma melhor legibilidade, ele pode ser dividido em várias linhas:

git fetch --all && \
for branch in $(git branch | sed '/*/{$q;h;d};$G' | tr -d '*')
    do git checkout $branch && \
    git merge --ff-only || break
done
  1. git fetch --all && ... => Busca todas as referências de todos os controles remotos e continua com o próximo comando, se não houver erro.

  2. git branch | sed '/*/{$q;h;d};$G' | tr -d '*'=> Na saída de git branch, sedpegue a linha com a *e mova-a para o final (para que a ramificação atual seja atualizada por último). Em seguida, trbasta remover o *.

  3. for branch in $(...) ; do git checkout $branch && git merge --ff-only || break ; done=> Para cada nome de ramificação obtido do comando anterior, faça check-out dessa ramificação e tente mesclar com um avanço rápido. Se falhar, breaké chamado e o comando para aqui.

Claro, você pode substituir git merge --ff-onlypor git rebasese é o que você deseja.

Finalmente, você pode colocá-lo no seu bashrc como um alias:

alias git-pull-all='git fetch --all && for branch in $(git branch | sed '\''/*/{$q;h;d};$G'\'' | tr -d "*") ; do git checkout $branch && git merge --ff-only || break ; done'

Ou, se você tem medo de estragar o 'e ​​", ou simplesmente prefere manter a legibilidade sintática no seu editor, pode declarar isso como uma função:

git-pull-all()
{
    git fetch --all && for branch in $(git branch | sed '/*/{$q;h;d};$G' | tr -d '*') ; do git checkout $branch && git merge --ff-only || break ; done
}

Bônus:

Para aqueles que gostariam da explicação da sed '/*/{$q;h;d};$G'parte:

  • /*/=> Procure a linha com a *.

  • {$q => Se estiver na última linha, saia (não precisamos fazer nada porque o ramo atual já é o último na lista).

  • ;h;d} => Caso contrário, armazene a linha no buffer de espera e exclua-a na posição atual da lista.

  • ;$G => Quando atingir a última linha, acrescente o conteúdo do buffer de retenção.

Bouty
fonte
Você pode evitar toda a loucura de linhas sem fim e &&colocando-o set -eno topo do script.
mcepl 19/03
0

O "git pull --all" pode atualizar todos os meus ramos locais?

Não, eu não posso. Para o avanço rápido, acabei de escrever uma pequena ferramenta para fazer isso. https://github.com/changyuheng/git-fast-forward-all

Vantagens desta ferramenta:

  1. Suporta vários controles remotos em um repositório. ( hub syncnão suporta vários controles remotos no momento.)
  2. Suporta nomes diferentes na ramificação local e na ramificação de rastreamento remoto correspondente.
  3. Muito mais rápido que outros scripts que buscam remotamente para cada ramo.
  4. Sem regex propenso a erros de análise / edição.
changyuheng
fonte
1
Você pode evitar atingir a rede usando git fetch . refspec. O .diz para buscar a partir do repositório atual em vez do um controle remoto.
hugomg
-1

A partir do git 2.9:

git pull --rebase --autostash

Consulte https://git-scm.com/docs/git-rebase

Crie automaticamente um stash temporário antes do início da operação e aplique-o após o término da operação. Isso significa que você pode executar o rebase em uma árvore de trabalho suja. No entanto, use com cuidado: o aplicativo stash final após uma nova recuperação bem-sucedida pode resultar em conflitos não triviais.

nha
fonte
-1

De fato, com o git version 1.8.3.1, ele funciona:

[root@test test]# git br
* master
  release/0.1
  update
[root@test test]# git pull --rebase
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 9 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (9/9), done.
From http://xxx/scm/csdx/test-git
   d32ca6d..2caa393  release/0.1 -> origin/release/0.1
Current branch master is up to date.
[root@test test]# git --version
git version 1.8.3.1

Na ramificação mestre, você pode atualizar todas as outras ramificações. @Cascabel

Eu não sei qual versão quebrar / corrigi-lo, em 2.17 (que eu uso), ele pode funcionar.

daixiang0
fonte