Fusionner, mettre à jour et retirer les branches Git sans utiliser les checkouts

je travaille sur un projet qui a 2 branches, A et B. je travaille généralement sur la branche A, et fusionne des trucs de la branche B. Pour la fusion, je ferais typiquement:

git merge origin/branchB

cependant, je voudrais aussi garder une copie locale de la branche B, car je peux parfois vérifier la branche sans d'abord fusionner avec ma branche A. Pour cela, je ferais:

git checkout branchB
git pull
git checkout branchA

est-il un moyen de faire le ci-dessus dans une commande, et sans avoir à changer de branche d'avant en arrière? Devrais-je utiliser git update-ref pour ça? Comment?

467
demandé sur charles 2010-07-10 00:32:52

10 réponses

La Réponse Courte

tant que vous faites une fast-forward fusionner, alors vous pouvez simplement utiliser

git fetch <remote> <sourceBranch>:<destinationBranch>

exemples:

# Merge local branch foo into local branch master,
# without having to checkout master first.
# Here `.` means to use the local repository as the "remote":
git fetch . foo:master

# Merge remote branch origin/foo into local branch foo,
# without having to checkout foo first:
git fetch origin foo:foo

tandis que la réponse D'Amber fonctionnera également dans les cas d'avance rapide, en utilisant git fetch de cette façon est plutôt un peu plus sûr que de simplement forcer la référence de branche, puisque git fetch empêchera automatiquement accidentelle non pour avancer rapidement aussi longtemps que vous n'utilisez pas + dans le refspec.

La Longue Réponse

vous ne pouvez pas fusionner une branche B dans la branche A sans vérifier une première si cela résulterait en une fusion non-fast-forward. Cela est dû au fait qu'une copie de travail est nécessaire pour résoudre tout conflit potentiel.

cependant, dans le cas de fusions fast-forward , c'est possible , parce que de telles fusions peuvent ne jamais déboucher sur des conflits, par définition. Pour ce faire, sans vérifier d'abord une branche, vous pouvez utiliser git fetch avec un refspec.

voici un exemple de mise à jour master (Non-fast-forward changes) si vous avez une autre branche feature coché:

git fetch upstream master:master

ce cas d'utilisation est si courant, que vous voudrez probablement créer un alias pour lui dans votre fichier de configuration git, comme celui-ci:

[alias]
    sync = !sh -c 'git checkout --quiet HEAD; git fetch upstream master:master; git checkout --quiet -'

ce que cet alias fait est le suivant:

  1. git checkout HEAD : cela met votre copie de travail dans un État de tête détaché. Ceci est utile si vous voulez mettre à jour master alors que vous arrive d'être contrôlé. Je pense que c'était nécessaire parce que sinon la référence de la branche pour master ne bougera pas, mais je ne me souviens pas si c'est vraiment le haut de ma tête.

  2. git fetch upstream master:master : ce qui fait suivre votre local master au même endroit que upstream/master .

  3. git checkout - "vérifie votre succursale précédemment cochée (c'est ce que le - fait dans ce cas).

La syntaxe de git fetch pour les (non-)avance rapide fusionne

si vous voulez que la commande fetch échoue si la mise à jour est non-fast-forward, alors vous utilisez simplement un refspec du formulaire

git fetch <remote> <remoteBranch>:<localBranch>

si vous voulez autoriser des mises à jour non-fast-forward, alors vous ajoutez un + au recto du refspec:

git fetch <remote> +<remoteBranch>:<localBranch>

notez que vous pouvez passer votre repo local comme paramètre "remote" en utilisant . :

git fetch . <sourceBranch>:<destinationBranch>

La Documentation

de la documentation git fetch qui explique cette syntaxe (l'emphase est mienne):

<refspec>

le format d'un paramètre <refspec> est optionnel plus + , suivi de la source ref <src> , suivi de deux points : , suivi de la destination ref <dst> .

la référence à distance qui correspond à <src> est récupérée, et si <dst> n'est pas une chaîne vide, le ref local qui correspond est transmis rapidement en utilisant <src> . Si l'option plus + est utilisée, la réf locale est mise à jour même si elle ne donne pas lieu à une mise à jour accélérée.

Voir Aussi

  1. Git checkout et de fusion, sans toucher à l'arbre de travail

  2. Fusion sans changer le répertoire de travail

707
répondu Community 2017-07-17 01:13:59

Non, il n'y en a pas. Une vérification de la branche cible est nécessaire pour vous permettre de résoudre les conflits, entre autres choses (si Git est incapable de les fusionner automatiquement).

cependant, si la fusion est une fusion qui serait rapide, vous n'avez pas besoin de vérifier la branche cible, parce que vous n'avez pas réellement besoin de fusionner quoi que ce soit - tout ce que vous avez à faire est de mettre à jour la branche pour pointer vers la nouvelle réf.head. Vous pouvez le faire avec git branch -f :

git branch -f branch-b branch-a

mettra à jour branch-b pour pointer vers la tête de branch-a .

l'option -f signifie --force , ce qui signifie que vous devez faire attention en l'utilisant. Ne l'utilisez que si vous êtes sûr que la fusion sera accélérée.

74
répondu Amber 2014-11-07 11:03:55

comme Amber l'a dit, les fusions rapides sont le seul cas dans lequel vous pourriez faire ceci. Toute autre Fusion nécessite de passer par l'ensemble de la fusion à trois-application de correctifs, résolution de conflits-et cela signifie qu'il doit y avoir des fichiers autour.

il se trouve que j'ai un script que j'utilise pour exactement ceci: faire des fusions rapides sans toucher à l'arbre de travail (sauf si vous fusionnez avec HEAD). C'est un peu long, parce que c'est au moins un peu robust - il vérifie pour s'assurer que la fusion serait un fast-forward, puis l'exécute sans vérifier la branche, mais en produisant les mêmes résultats que si vous aviez - vous voyez le diff --stat résumé des changements, et l'entrée dans le refrog est exactement comme une fusion fast forward, au lieu du "reset" celui que vous obtenez si vous utilisez branch -f . Si vous l'appelez git-merge-ff et que vous le déposez dans votre répertoire bin, vous pouvez l'appeler comme une commande git: git merge-ff .

#!/bin/bash

_usage() {
    echo "Usage: git merge-ff <branch> <committish-to-merge>" 1>&2
    exit 1
}

_merge_ff() {
    branch=""
    commit=""

    branch_orig_hash="$(git show-ref -s --verify refs/heads/$branch 2> /dev/null)"
    if [ $? -ne 0 ]; then
        echo "Error: unknown branch $branch" 1>&2
        _usage
    fi

    commit_orig_hash="$(git rev-parse --verify $commit 2> /dev/null)"
    if [ $? -ne 0 ]; then
        echo "Error: unknown revision $commit" 1>&2
        _usage
    fi

    if [ "$(git symbolic-ref HEAD)" = "refs/heads/$branch" ]; then
        git merge $quiet --ff-only "$commit"
    else
        if [ "$(git merge-base $branch_orig_hash $commit_orig_hash)" != "$branch_orig_hash" ]; then
            echo "Error: merging $commit into $branch would not be a fast-forward" 1>&2
            exit 1
        fi
        echo "Updating ${branch_orig_hash:0:7}..${commit_orig_hash:0:7}"
        if git update-ref -m "merge $commit: Fast forward" "refs/heads/$branch" "$commit_orig_hash" "$branch_orig_hash"; then
            if [ -z $quiet ]; then
                echo "Fast forward"
                git diff --stat "$branch@{1}" "$branch"
            fi
        else
            echo "Error: fast forward using update-ref failed" 1>&2
        fi
    fi
}

while getopts "q" opt; do
    case $opt in
        q ) quiet="-q";;
        * ) ;;
    esac
done
shift $((OPTIND-1))

case $# in
    2 ) _merge_ff "" "";;
    * ) _usage
esac

Si quelqu'un voit des problèmes avec ce script, veuillez commenter! C'était un travail d'écriture et d'oubli, mais je serais heureux de l'améliorer.

28
répondu Cascabel 2014-05-29 20:08:32

Vous ne pouvez le faire que si la fusion est un fast-forward. Si ce n'est pas le cas, git doit vérifier les fichiers pour pouvoir les fusionner!

À faire pour une avance rapide seulement :

git fetch <branch that would be pulled for branchB>
git update-ref -m "merge <commit>: Fast forward" refs/heads/<branch> <commit>

<commit> est le commit récupéré, celui vers lequel vous voulez aller de l'avant. C'est essentiellement comme utiliser git branch -f pour déplacer la branche, sauf qu'il l'enregistre aussi dans le reflog comme si vous aviez réellement fait la fusion.

s'il vous Plaît, s'il vous plaît, s'il vous plaît à ne pas faire pour quelque chose qui n'est pas un fast-forward, ou vous aurez juste être la réinitialisation de votre branche à l'autre de la validation. (Pour vérifier ,voir si git merge-base <branch> <commit> donne SHA1 de la succursale.)

19
répondu Cascabel 2014-07-04 07:15:11

une autre façon, assez brutale, c'est de recréer la branche:

git fetch remote
git branch -f localbranch remote/remotebranch

cela jette la branche périmée locale et re-crée un avec le même nom, donc utiliser avec soin ...

10
répondu kkoehne 2011-06-08 10:08:14

Dans votre cas, vous pouvez utiliser

git fetch origin branchB:branchB

qui fait ce que vous voulez (en supposant que la fusion est rapide). Si la branche ne peut pas être mise à jour parce qu'elle nécessite une fusion non-fast-forward, alors cela échoue en toute sécurité avec un message.

cette forme de fetch a quelques options plus utiles aussi:

git fetch <remote> <sourceBranch>:<destinationBranch>

notez que <remote> peut être un dépôt local , et <sourceBranch> peut être un suivi direction. Ainsi, vous pouvez mettre à jour une branche locale, même si elle n'est pas cochée, sans accéder au réseau .

actuellement, mon accès au serveur amont est via un VPN lent, donc je me connecte périodiquement, git fetch pour mettre à jour tous les télécommandes, puis déconnecter. Alors si, par exemple, le maître distant a changé, je peux faire

git fetch . remotes/origin/master:master

pour mettre à jour en toute sécurité mon capitaine local, même si je fais actuellement vérifier une autre branche. Aucun réseau d'accès requis.

8
répondu Bennett McElwee 2016-05-24 22:39:47

Vous pouvez cloner le repo et faire la fusion dans le nouveau repo. Sur le même système de fichiers, ce sera un lien dur plutôt qu'une copie de la plupart des données. Terminer en tirant les résultats dans le premier repo.

6
répondu wnoise 2010-11-11 18:00:02

Entrée git-de l'avant-fusion :

sans avoir à vérifier la destination, git-forward-merge <source> <destination> fusionne la source dans la direction de destination.

https://github.com/schuyler1d/git-forward-merge

ne fonctionne que pour les fusions automatiques, s'il y a des conflits, vous devez utiliser la fusion régulière.

3
répondu lkraider 2014-05-29 17:15:10

pour de nombreux cas (tels que la fusion), vous pouvez simplement utiliser la branche distante sans avoir à mettre à jour la branche de suivi locale. L'ajout d'un message dans le nouveau carnet semble exagéré et l'empêchera d'être plus rapide. Pour le rendre plus facile à récupérer, ajoutez ce qui suit dans votre git config

[core]
    logallrefupdates=true

puis tapez

git reflog show mybranch

pour voir l'histoire récente de votre branche

3
répondu Casebash 2014-07-25 22:19:35

j'ai écrit une fonction shell pour un cas d'utilisation similaire que je rencontre quotidiennement sur des projets. Il s'agit essentiellement d'un raccourci pour tenir les branches locales à jour avec une branche commune comme develop avant d'ouvrir un PR, etc.

poster ceci même si vous ne voulez pas utiliser checkout , dans le cas où d'autres ne se soucient pas de cette contrainte.

glmh ("git pull et fusionner ici") automatiquement checkout branchB , pull le dernier , re - checkout branchA , et merge branchB .

ne répond pas à la nécessité de conserver une copie locale de branchA, mais pourrait facilement être modifié pour le faire en ajoutant une étape avant de vérifier branchB. Quelque chose comme...

git branch ${branchA}-no-branchB ${branchA}

pour les fusions rapides simples, ceci saute à l'invite de message de propagation.

pour les fusions Non fast-forward, cela place votre branche dans le conflit état de résolution (vous devrez probablement intervenir).

à setup, ajouter à .bashrc ou .zshrc , etc:

glmh() {
    branchB=
    [ $# -eq 0 ] && { branchB="develop" }
    branchA="$(git branch | grep '*' | sed 's/* //g')"
    git checkout ${branchB} && git pull
    git checkout ${branchA} && git merge ${branchB} 
}

Utilisation:

# No argument given, will assume "develop"
> glmh

# Pass an argument to pull and merge a specific branch
> glmh your-other-branch

Note: il S'agit de pas de suffisamment robuste pour transférer args au-delà du nom de la branche à git merge

2
répondu rkd 2016-02-13 00:03:02