«Git pull --all» peut-il mettre à jour toutes mes succursales locales?


473

J'ai souvent au moins 3 agences distantes: master, staging et production. J'ai 3 succursales locales qui suivent ces succursales distantes.

La mise à jour de toutes mes succursales locales est fastidieuse:

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

J'adorerais pouvoir simplement faire un "git pull -all", mais je n'ai pas réussi à le faire fonctionner. Il semble faire un "fetch --all", puis met à jour (avance rapide ou fusionne) la branche de travail actuelle, mais pas les autres branches locales.

Je suis toujours bloqué en passant manuellement à chaque branche locale et en mettant à jour.


8
Voulez-vous la mise à jour automatisée des succursales de suivi local uniquement dans le cas d'une avance rapide? Ypu devrait, car la fusion peut avoir une confluence que vous devrez résoudre ...
Jakub Narębski

34
En supposant un temps de consultation prudent de 300 $ pour régler ce problème, ce seul problème a coûté aux entreprises 23 242 800 $ en utilisant un nombre de vues de 77 476. Considérez maintenant cette question stackoverflow.com/questions/179123/… et toutes les autres. Sensationnel.
Luke Puplett

16
@Luke Vous êtes la première personne que j'ai entendue souligner combien le temps passé à essayer de faire faire à git ce que nous voulons coûte de l'argent aux entreprises. Ces choses simples devraient être automatiques et devraient être si simples que je n'ai pas besoin d'ouvrir un navigateur pour lire les forums, OMI.
Samuel

13
@LukePuplett Il y a presque ~ 9 fois plus de questions sur git sur SO que sur Mercurial, et la majorité des premières semblent être "comment faire <opération simple> dans git?". Cela indique que git est mal conçu, mal documenté, peu intuitif ou les trois.
Ian Kemp

26
@IanKemp Je ne suis pas sûr qu'il soit sûr de faire cette affirmation sans connaître la démographie de SO. Si Mercurial n'est pas aussi couramment utilisé ici, ou si ses utilisateurs utilisent d'autres forums pour s'enquérir, je m'attends à voir le même résultat. :) Il y a environ 51 fois plus de questions sur Javascript que sur Assembly - il n'est donc pas toujours exact de juger les outils uniquement par ce type de métriques.
danShumway

Réponses:


188

Le comportement que vous décrivez pull --allest exactement comme prévu, mais pas nécessairement utile. L'option est transmise à git fetch, qui récupère ensuite toutes les références de toutes les télécommandes, au lieu de seulement celle nécessaire; pullfusionne ensuite (ou dans votre cas, rebase) la branche unique appropriée.

Si vous souhaitez consulter d'autres succursales, vous devrez les consulter. Et oui, la fusion (et le rebasage) nécessite absolument un arbre de travail, donc ils ne peuvent pas être effectués sans vérifier les autres branches. Vous pouvez encapsuler vos étapes décrites dans un script / alias si vous le souhaitez, bien que je suggère de joindre les commandes &&afin que si l'une d'entre elles échoue, elle n'essaiera pas de continuer.


2
Si vous donnez un exemple de ligne de commande, je voterais pour. J'ai ce problème sur github. J'ai créé une branche sur l'interface utilisateur. Maintenant, j'ai besoin que ma section locale montre la succursale. git pull --all; git branch ... argh ... la commande: git branch -a
mariotti

@mariotti Cela dépend de ce que vous essayez de faire, et ce n'est pas vraiment clair à partir de votre commentaire. Vous feriez mieux de poser une nouvelle question.
Cascabel

1
Ou @Jefromi .. donnez un exemple. En fait, j'étais d'accord avec vous.
mariotti

3
@mariotti Le point de cette réponse est que les commandes intégrées ne font pas réellement ce que l'OP a demandé, donc la séquence d'étapes qu'elles ont eue est nécessaire. Il est possible d'automatiser ces étapes (voir par exemple la réponse de John) mais elles doivent être effectuées. Donc, si ce que vous essayez de faire est exactement le même que l'OP, il n'y a pas vraiment d'exemple à donner, et si vous essayez de faire quelque chose de différent, alors vous devriez poser une nouvelle question - c'est ainsi que StackOverflow fonctionne! (Et votre commentaire n'est pas clair, mais ma meilleure supposition est que vous voulez quelque chose de différent de l'OP ici, alors oui, nouvelle question.)
Cascabel

Oui, quelque chose de différent. Mais votre réponse était tout simplement parfaite pour le contexte. Et je n'aurai peut-être pas besoin de demander plus à cause de votre réponse. Juste: La réponse acceptée utilise git-up, qui est simplement une interface pour la ligne de commande git (je suppose). J'espérais que vous pourriez le rendre explicite en quelques lignes de commandes git. La réponse actuelle n'est PAS git.
mariotti

206

J'utilise la syncsous - commande de hub pour automatiser cela. J'ai alias git=hubdans mon .bash_profile, donc la commande que je tape est:

git sync

Cela met à jour toutes les branches locales qui ont une branche amont correspondante. Depuis la page de manuel:

  • Si la branche locale est obsolète, avancez-la rapidement;
  • Si la branche locale contient du travail non poussé, prévenez-le;
  • Si la branche semble fusionnée et que sa branche en amont a été supprimée, supprimez-la.

Il gère également le stockage / le retrait des modifications non validées sur la branche actuelle.

J'avais l'habitude d'utiliser un outil similaire appelé git-up , mais il n'est plus maintenu et git syncfait presque exactement la même chose.


15
Et Windows?
Violet Giraffe

6
@ TrentonD.Adams Les dates de validation et les dates d'auteur sont des concepts différents. Un rebase changera la date de validation mais pas la date de l'auteur (sauf dans les conflits, où la date de l'auteur change également). La date de l'auteur reflète la date de création de l'arborescence du commit et ne doit pas changer lors d'un rebase non conflictuel. La date de validation change car le rebase crée toujours un nouveau commit. Les dates de validation seront donc toujours dans le bon ordre.
Dev

16
Pour désactiver le comportement de rebasage automatique de git-up, exécutez git config --global git-up.rebase.auto false.
Dan Loewenherz

18
@MaxYankov Il faut généralement éviter de rebaser l'historique partagé, il n'y a rien de mal à rebaser les commits locaux pendant un pull.
Dev

23
Rebaser les commits locaux, c'est réécrire l'histoire et la rendre plus simple qu'elle ne l'est réellement. Avec rebase, vous pouvez vous retrouver avec du code qui a fusionné automatiquement mais ne compile pas, ou pire, compile mais ne fonctionne pas. La fusion reconnaît la façon dont vous avez travaillé: vous avez implémenté les modifications et les avez testées avant d'incorporer les modifications d'autres personnes, et la fusion de validation est un point très utile: c'est l'endroit où vous vous assurez que les différents chagesets fonctionnent bien ensemble. Le remodelage donne l'impression que ce processus ne se produit jamais, ce qui n'est tout simplement pas vrai et est une pratique très dangereuse.
Max Yankov

39

Je sais que cette question a presque 3 ans, mais je me suis posé la même question et je n'ai trouvé aucune solution toute faite. J'ai donc créé moi-même un script shell de commande git personnalisé.

Ça y est, le git-ffwd-updatescript fait ce qui suit ...

  1. il émet un git remote updatepour récupérer les derniers tours
  2. utilise ensuite git remote showpour obtenir une liste des branches locales qui suivent une branche distante (par exemple les branches qui peuvent être utilisées avec git pull)
  3. puis il vérifie git rev-list --count <REMOTE_BRANCH>..<LOCAL_BRANCH>le nombre de validations de la branche locale derrière la télécommande (et vice versa)
  4. si la succursale locale a 1 ou plusieurs validations, elle ne peut PAS être accélérée et doit être fusionnée ou rebasée manuellement
  5. si la branche locale a 0 commits en avant et 1 ou plusieurs commits en arrière, elle peut être avancée rapidement par git branch -f <LOCAL_BRANCH> -t <REMOTE_BRANCH>

le script peut être appelé comme:

$ 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

Le script complet, doit être enregistré en tant que git-ffwd-updateet doit être sur le 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 $@

1
Merci pour ce script. Est-il possible que quelqu'un puisse convertir ce script en lot Windows?
Saariko

@Saariko pourquoi ne voulez-vous pas utiliser git sur un shell Windows normal? Si vous utilisez quelque chose comme cygwin, ce script devrait fonctionner correctement ... (même si je ne l'ai pas testé)
muhqu

@RyanWilcox merci, je l'utilise comme tous les jours (de travail) ... ;-) vous voudrez peut-être jeter un œil à mes fichiers dot pour plus de scripts et d'alias liés à git: github.com/muhqu/dotfiles
muhqu

@muhqu J'essaie d'utiliser votre script et je ne sais pas pourquoi cela a fonctionné la première fois mais il ne fonctionne pas "comme prévu" en ce moment. Par exemple, jetez un œil à cela . Pourquoi master reste-t-il 78 commits derrière après avoir exécuté votre script?
BPL

1
@muhqu Sur les versions plus récentes de git, -t et -l ne sont pas censés être utilisés ensemble dans un même git branchappel. J'ai supprimé le -l pour changer l'appel git branch -f $LB -t $ARB >/dev/null;et maintenant le script fonctionne comme il se doit.
Radek Liska

24

Ce n'est pas si difficile à automatiser:

#!/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

4
Il est probablement préférable de ne pas utiliser d'alias dans les scripts. Cela ne récupère également rien, redéfinit simplement le contenu déjà récupéré. Vous devez changer git rebase origin/$branchpour git pull, afin qu'il récupère à partir de la branche de suivi appropriée (vraisemblablement à l'origine) et soit fusionner ou rebaser comme déterminé par la configuration.
Cascabel

@Jefromi: J'avais oublié le fetch. Avoir édité; fonctionnalités supplémentaires / corrections tout ce qui dépend de l'OP.
Fred Foo

8
Je pense toujours que vous voudrez peut-être utiliser pull(ou vérifier branch.<branch>.rebase), afin de ne pas rebaser accidentellement une branche qui est configurée pour tirer normalement (fusionner).
Cascabel

1
Pensez à utiliser set -eau lieu de || exit 1pour faire quitter l'interpréteur à la première erreur.
crishoj

18

Ce n'est toujours pas automatique, car je souhaite qu'il y ait une option pour - et il devrait y avoir une vérification pour s'assurer que cela ne peut se produire que pour les mises à jour à avance rapide (c'est pourquoi faire un pull manuellement est beaucoup plus sûr !!), mais mises à part, vous pouvez:

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

pour mettre à jour la position de votre succursale locale sans avoir à la vérifier.

Remarque: vous perdrez votre position de branche actuelle et la déplacerez à l'endroit où se trouve la branche d'origine, ce qui signifie que si vous devez fusionner, vous perdrez des données!


1
C'est exactement la solution que je cherchais. Je n'ai généralement pas de modifications non poussées sur plusieurs branches et je souhaite simplement mettre à jour mes différentes branches locales pour qu'elles correspondent à la télécommande. Cette solution est beaucoup plus agréable que ma méthode habituelle de suppression / re-checkout!
Dave Knight

1
combiné en une seule commande:git fetch origin other-branch:other-branch
fabb

12

Il y a beaucoup de réponses ici mais aucune qui permet git-fetchde mettre à jour la référence locale directement, ce qui est beaucoup plus simple que de vérifier les branches et plus sûr que git-update-ref.

Ici, nous utilisons git-fetchpour mettre à jour les branches non actuelles et git pull --ff-onlypour la branche actuelle. Il:

  • Ne nécessite pas de vérifier les succursales
  • Met à jour les branches uniquement si elles peuvent être avancées rapidement
  • Signaler quand il ne peut pas avancer rapidement

et le voici:

#!/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

Depuis la page de manuel de 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.

En spécifiant git fetch <remote> <ref>:<ref>(sans aucun +), nous obtenons une extraction qui met à jour la référence locale uniquement lorsqu'elle peut être expédiée rapidement.

Remarque : cela suppose que les branches locales et distantes sont nommées de la même manière (et que vous souhaitez suivre toutes les branches), cela devrait vraiment utiliser des informations sur les branches locales que vous avez et sur ce qu'elles sont configurées pour suivre.


1
"Met à jour les succursales uniquement si elles peuvent être avancées " - quelle est l'importance de l'avance rapide? Si je veux les dernières sources dans toutes mes succursales, pourquoi devrais-je me soucier de la transmission rapide ou non? Ses choses comme ça qui me font rire de Git et de ses Fanboi. Vous ne pouvez pas le faire en une seule commande. Au lieu de cela, vous devez effectuer des c*nétapes (au lieu de 1), où se ctrouve un certain nombre de commandes répétées et nle nombre de branches.
JWW

@jww Cela n'aide pas de "rire de Git et de ses Fanboi" [sic] quand c'est le VCS que la plupart du monde utilise. Mais je m'éloigne ... Je pense que dans le contexte de ce type de script "global pull", il est prudent de ne pas tenter d'apporter des modifications aux branches non actuelles si elles ont des conflits de fusion.
Ville

Cela a été utile, merci. La seule chose que je n'ai pas aimé qu'il a créé une branche localement pour chaque branche à distance (y compris ceux dans lesquels je ne suis pas intéressé), donc je l' ai changé git branch -r | grep -v ' -> ' | while read remotebranchpour git branch -r | grep -v ' -> ' | grep -f <(git branch | cut -c 3- | awk '{print "\\S*/"$0"$"}') | while read remotebranchla limiter aux branches , je l' ai déjà sur place. J'ai également ajouté un git fetch --pruneau début pour mettre à jour la liste des branches distantes avant de faire quoi que ce soit, ce qui évite certains avertissements.
Nate Cook

11

Ce problème n'est pas résolu (encore), du moins pas facilement / sans script: voir ce post sur la liste de diffusion git par Junio ​​C Hamano expliquant la situation et appelant à une solution simple.

Le raisonnement principal est que vous ne devriez pas en avoir besoin:

Avec git qui n'est pas ancien (c.-à-d. V1.5.0 ou plus récent), il n'y a aucune raison d'avoir des "dev" locaux qui suivent uniquement la télécommande. Si vous voulez seulement aller voir et voir, vous pouvez consulter la branche de suivi à distance directement sur une tête détachée avec " git checkout origin/dev".

Ce qui signifie que les seuls cas dont nous avons besoin pour le rendre pratique pour les utilisateurs sont de gérer ces branches locales qui "suivent" les branches distantes lorsque vous avez des modifications locales ou lorsque vous prévoyez d'en avoir.

Si vous avez des changements locaux sur "dev" qui sont marqués pour suivre la suppression de "dev", et si vous êtes sur une branche différente de "dev", alors nous ne devons rien faire après " git fetch" met à jour le suivi à distance "dev" . Il ne progressera pas de toute façon

L'appel à une solution était pour une option ou un script externe pour élaguer les branches locales qui suivent maintenant les branches de suivi à distance, plutôt que de les garder à jour par avance rapide, comme l'affiche originale demandée.

Alors que diriez-vous " git branch --prune --remote=<upstream>" qui itère sur les branches locales, et si

(1) ce n'est pas la branche actuelle; et
(2) il est marqué pour suivre une branche prise de <l'amont>; et
(3) il n'a aucun engagement à lui seul;

puis supprimez cette branche? " git remote --prune-local-forks <upstream>" est également très bien; Je me fiche de la commande qui implémente autant cette fonctionnalité.

Remarque: à partir de git 2.10, aucune solution de ce type n'existe. Notez que lagit remote prunesous-commande, etgit fetch --pruneconcerne la suppression de la branche de suivi à distance pour la branche qui n'existe plus sur la télécommande, pas la suppression de la branche locale qui suit la branche de suivi à distance (pour laquelle la branche de suivi à distance est la branche en amont).


Plutôt que de publier uniquement des liens, veuillez publier du contenu réel, en utilisant des liens comme références. Ce lien est maintenant mort. Dommage, cela semblait prometteur. (Je me rends compte que cette réponse était de 2009, donc ce n'est qu'une note pour référence future.)
Michael

merci (et wow, réponse rapide après tant d'années). Je vois maintenant que ce fil est un " appel à une solution simple" par opposition à ma mauvaise lecture d'origine, " fournit une solution simple".
Michael

@michael_n: élargi ... hmm, maintenant je vois que le message n'était pas exactement sur la solution demandée, mais c'était sur le problème (en supposant un cas de problème XY).
Jakub Narębski

Hmm, jeter un œil avec la tête détachée devrait être rendu plus facile, en particulier, il devrait montrer des informations utiles sur l'état et permettre d'avancer rapidement l'espace de travail avec quelques commentaires (comme les commits tirés). Ce serait alors un remplacement pour les succursales locales en lecture seule.
eckes

9

Il y a beaucoup de réponses acceptables ici, mais une partie de la plomberie peut être un peu opaque pour les non-initiés. Voici un exemple beaucoup plus simple qui peut facilement être personnalisé:

$ 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 "$@"

Si vous ajoutez ~/bin/gità votre PATH(en supposant que le fichier l'est ~/bin/git/git-update-all), vous pouvez simplement exécuter:

$ git update-all

Merci! tu m'as sauvé une heure de jeu avec bash ...
8ctopus

5

Ajoutez ce script à .profilesur 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};
};

1
Cela ne devrait-il pas d'abord cacher toutes les modifications, puis les restaurer?
Mel

5

Voici une bonne réponse: Comment récupérer toutes les branches git

for remote in `git branch -r`; do git branch --track $remote; done
git pull --all

Pourquoi proposez-vous de faire git fetchet git pull, au lieu de juste git pull?
syntagma

Merci. Il semble que la traction récupère toutes les branches de toutes les télécommandes. Changé
milkovsky

8
Cela récupérera toutes les télécommandes, mais ne fusionnera que la branche actuelle. Si vous avez 10 télécommandes, vous devrez les commander manuellement et les fusionner.
mpoisot

Cela créera toutes les succursales distantes localement avec le origin/préfixe
Yassine ElBadaoui

3

Un script que j'ai écrit pour mon GitBash . Accomplit ce qui suit:

  • Par défaut, l'extraction de l'origine pour toutes les branches configurées pour suivre l'origine vous permet de spécifier une télécommande différente si vous le souhaitez.
  • Si votre branche actuelle est dans un état sale, elle cache vos modifications et tentera de restaurer ces modifications à la fin.
  • Pour chaque branche locale configurée pour suivre une branche distante:
    • git checkout branch
    • git pull origin
  • Enfin, vous ramènera à votre branche d'origine et restaurerez l'état.

** J'utilise ceci mais je n'ai pas testé à fond, utilisez à vos risques et périls. Voir un exemple de ce script dans un fichier .bash_alias ici .

    # 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
    }

Pouvez-vous fournir un fichier Windows bat équivalent?
Jaffy

1
@Jaffy Je ne sais pas combien de temps j'ai sur mes mains et je ne suis pas très fluide en lot, mais je peux essayer. Je posterai mes progrès ici , peut-être que d'autres peuvent intervenir et aider?
philosowaffle

3

Si vous êtes sous Windows, vous pouvez utiliser PyGitUp qui est un clone de git-upPython. Vous pouvez l'installer en utilisant pip avec pip install --user git-upou via Scoop en utilisantscoop install git-up

[4]


3

Il suffit de poster une réponse mise à jour. git-upn'est plus maintenu et si vous lisez la documentation, ils mentionnent que la fonctionnalité est maintenant disponible dans git .

Depuis Git 2.9, git pull --rebase --autostash fait essentiellement la même chose.

Par conséquent, si vous effectuez une mise à jour vers Git 2.9 ou version ultérieure, vous pouvez utiliser cet alias au lieu d'installer git-up:

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

Vous pouvez également définir cela pour tous à git pullpartir de Git 2.9 (merci @VonC, veuillez voir sa réponse ici )

git config --global pull.rebase true
git config --global rebase.autoStash true

1
Vous n'avez pas besoin d'un alias. Un simple git pull suffit, avec la bonne configuration: stackoverflow.com/a/40067353/6309
VonC

Grand appel à merci @ VonC Je mis à jour ma réponse :) pourrait également soumettre une PR à la git-updocumentation parce qu'ils ne mentionnent pas que
août

Cela ne met pas à jour toutes les branches locales à la fois, c'est pourquoi j'ai principalement utilisé git-up.
ray

Documentation mise à jour le git-up:)
août

3

Je suis tombé sur le même numéro de cette question ...

Je me demandais à ce sujet, j'ai fait une petite fonction d'alias dans mon .bashrcfichier:

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

A travaillé pour moi (:


2

Si les références / têtes / maître peuvent être transférées rapidement vers les références / télécommandes / foo / master , la sortie de

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

devrait retourner l'id SHA1 vers lequel se réfèrent / se dirigent / se dirigent les points. Avec cela, vous pouvez créer un script qui met automatiquement à jour toutes les branches locales auxquelles aucun commit de déviation ne leur a été appliqué.

Ce petit script shell (je l'ai appelé git-can-ff ) illustre comment cela peut être fait.

#!/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

Qu'impliquez-vous par ce commentaire?
hillu

Je ne suis pas moi-même capable d'écrire le script suggéré par hillu, et je n'ai pas assez confiance en mes connaissances git pour utiliser git-merge-base.
Norman Ramsey

2
Je crains de ne pas bien comprendre le modèle pour exploiter le script si gentiment fourni. Il suffit de donner envie à une personne de passer au mercuriel.
Norman Ramsey

J'ai personnellement trouvé l'article de Tommi Virtanen "Git pour les informaticiens" très utile pour se familiariser avec le modèle et la terminologie de git.
hillu

2

Pour compléter la réponse de Matt Connolly, il s'agit d'un moyen plus sûr de mettre à jour les références de succursales locales qui peuvent être transmises rapidement, sans vérifier la succursale. Il ne met pas à jour les branches qui ne peuvent pas être avancées rapidement (c'est-à-dire qui ont divergé), et il ne met pas à jour la branche actuellement extraite (car alors la copie de travail doit également être mise à jour).

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

2

Un script légèrement différent qui avance uniquement les branches dont le nom correspond à leur branche en amont. Il met également à jour la branche actuelle si une avance rapide est possible.

Assurez-vous que toutes les branches en amont de vos branches sont correctement définies en exécutant git branch -vv. Définissez la branche amont avecgit branch -u origin/yourbanchname

Copiez-collez dans un fichier et 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;

2

Le one-liner suivant avance rapidement toutes les branches qui ont une branche en amont si possible, et affiche une erreur sinon:

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

Comment ça marche?

Il utilise un format personnalisé avec la git branchcommande. Pour chaque branche qui a une branche en amont, elle imprime une ligne avec le modèle suivant:

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

Cela peut être directement canalisé sh(en supposant que les noms de branche sont bien formés). Omettez le | shpour voir ce qu'il fait.

Avertissements

Le one-liner ne contactera pas vos télécommandes. Émettez un git fetchou git fetch --allavant de l'exécuter.

La branche actuellement extraite ne sera pas mise à jour avec un message comme

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

Pour cela, vous pouvez recourir à la régularité git pull --ff-only.

Alias

Ajoutez ce qui suit à votre .gitconfigafin d' git fftexécuter cette commande:

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

Voir aussi mon .gitconfig. L'alias est un raccourci pour "suivi rapide (branches)".


C'est une bonne solution, bien que je pense que j'utiliserai la hubsolution de propulsion proposée par @John pour sa meilleure sortie.
Didier L

C'est rapide, simple et fonctionne réellement! Je suis déconcerté par le git pushcar il est totalement opposé à ce que vous attendez. Quel est le secret?
BrandonLWhite

@BrandonLWhite: Je ne comprends pas la question. Qu'attendez-vous de git push?
krlmlr

git pusha la sémantique de téléchargement - J'ai des commits localement que je veux envoyer en amont. git pulla la sémantique de téléchargement - je veux obtenir des validations à distance en amont dans ma branche locale. Puisque nous parlons de télécharger de nouveaux commits de la télécommande vers la locale, git pullc'est le choix évident. Mais non, cette astuce utilise git push. Comment cela se git pushtraduit-il par l'extraction de modifications à distance dans ma succursale locale?!
BrandonLWhite

git pushpeut également être utilisé pour mettre à jour les succursales locales, à condition qu'il s'agisse d'une mise à jour à avance rapide.
krlmlr

1

Le script de @larsmans, un peu amélioré:

#!/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

Cela, une fois terminé, laisse la copie de travail extraite de la même branche qu'avant l'appel du script.

La git pullversion:

#!/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

1

Il semble que beaucoup d'autres ont apporté des solutions similaires, mais j'ai pensé partager ce que j'avais trouvé et inviter d'autres personnes à contribuer. Cette solution a une belle sortie colorée, gère gracieusement votre répertoire de travail actuel, et est rapide car elle ne fait aucune extraction et laisse votre répertoire de travail intact. De plus, c'est juste un script shell sans dépendances autres que git. (testé jusqu'à présent sur OSX uniquement)

#!/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

Désolé, je semble également avoir trouvé le même nom que l'autre outil ci-dessus.


2
Êtes-vous celui qui a écrit cela? Dans l'affirmative, veuillez divulguer votre affiliation, c'est-à-dire nous dire comment vous y êtes lié. Veuillez en lire plus à ce sujet pour plus d'informations. Plus précisément, ne dites pas - montrez! ; Dites-nous quelles parties de votre script et comment / pourquoi il résout le problème.
Keale

1
Oui je l'ai écrit. J'ai inclus la source ci-dessus pour un copier-coller rapide dans votre .bashrc ou .zshrc.
Stimp

Ceci est une bonne solution et fonctionne bien. Personne n'avait pris connaissance?
Ville

1

Cela peut être fait en utilisant le script ci-dessous ... Il va d'abord récupérer toutes les branches et passer une à une et mettre à jour par lui-même.

#!/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

Ajoutez quelques explications pour noter votre réponse
fool-dev

1

Vous ne pouvez pas le faire avec une seule commande git mais vous pouvez l'automatiser avec une seule ligne bash.

Pour mettre à jour en toute sécurité toutes les branches avec une seule ligne, voici ce que je fais:

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
  • S'il ne peut pas avancer rapidement une branche ou rencontrer une erreur, il s'arrêtera et vous laissera dans cette branche afin que vous puissiez reprendre le contrôle et fusionner manuellement.

  • Si toutes les branches peuvent être transférées rapidement, cela se terminera avec la branche dans laquelle vous vous trouviez actuellement, vous laissant où vous étiez avant la mise à jour.

Explications:

Pour une meilleure lisibilité, il peut être réparti sur plusieurs lignes:

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 && ... => Récupère toutes les références de toutes les télécommandes et continue avec la commande suivante s'il n'y a pas eu d'erreur.

  2. git branch | sed '/*/{$q;h;d};$G' | tr -d '*'=> A partir de la sortie de git branch, sedprenez la ligne avec a *et déplacez-la à la fin (pour que la branche courante soit mise à jour en dernier). trRetirez ensuite simplement le *.

  3. for branch in $(...) ; do git checkout $branch && git merge --ff-only || break ; done=> Pour chaque nom de branche obtenu à partir de la commande précédente, extrayez cette branche et essayez de fusionner avec une avance rapide. S'il échoue, il breakest appelé et la commande s'arrête ici.

Bien sûr, vous pouvez remplacer git merge --ff-onlyavec git rebasesi elle est ce que vous voulez.

Enfin, vous pouvez le mettre dans votre bashrc comme 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 si vous avez peur de vous tromper avec les 'et ", ou si vous préférez simplement garder la lisibilité syntaxique dans votre éditeur, vous pouvez le déclarer comme une fonction:

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
}

Prime:

Pour ceux qui voudraient l'explication de la sed '/*/{$q;h;d};$G'partie:

  • /*/=> Recherchez la ligne avec un *.

  • {$q => S'il est dans la dernière ligne, quittez (nous n'avons rien à faire car la branche courante est déjà la dernière de la liste).

  • ;h;d} => Sinon, stockez la ligne dans le tampon de maintien et supprimez-la à la position actuelle de la liste.

  • ;$G => Quand il atteint la dernière ligne, ajoutez le contenu du tampon de maintien.


Vous pouvez éviter toute la folie des lignes sans fin et &&en plaçant set -een haut du script.
mcepl

0

«Git pull --all» peut-il mettre à jour toutes mes succursales locales?

Non ça ne peut pas. Pour une avance rapide, je viens d'écrire un petit outil pour le faire. https://github.com/changyuheng/git-fast-forward-all

Avantages de cet outil:

  1. Prend en charge plusieurs télécommandes dans un référentiel. ( hub syncne prend pas en charge plusieurs télécommandes pour le moment.)
  2. Prend en charge différents noms sur la branche locale et la branche de suivi à distance correspondante.
  3. Beaucoup plus rapide que les autres scripts qui récupèrent à distance pour chaque branche.
  4. Pas d'analyse / d'édition d'expressions rationnelles sujettes aux erreurs.

1
Vous pouvez éviter de toucher le réseau en utilisant git fetch . refspec. Le .dit d'aller chercher à partir du référentiel actuel plutôt que du référentiel distant.
hugomg

-1

Depuis git 2.9:

git pull --rebase --autostash

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

Créez automatiquement une réserve temporaire avant le début de l'opération et appliquez-la après la fin de l'opération. Cela signifie que vous pouvez exécuter rebase sur un arbre de travail sale. Cependant, utilisez-le avec précaution: l'application de stockage finale après un rebasage réussi peut entraîner des conflits non triviaux.


-1

En fait, avec git version 1.8.3.1, cela fonctionne:

[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

Dans la branche principale, vous pouvez mettre à jour toutes les autres branches. @Cascabel

Je ne sais pas quelle version le casser / réparer, en 2.17 (que j'utilise), cela peut fonctionner.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.