Question Fusionner, mettre à jour et tirer des branches Git sans utiliser les extractions


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

git merge origin/branchB

Cependant, j'aimerais aussi conserver une copie locale de la succursale B, car il m'arrive de vérifier la succursale sans fusionner avec ma succursale A. Pour ce faire, je ferais:

git checkout branchB
git pull
git checkout branchA

Y at-il un moyen de faire ce qui précède dans une commande, et sans avoir à changer de branche avant et en arrière? Devrais-je utiliser git update-ref pour ça? Comment?


437
2017-07-09 20:32


origine


Réponses:


La réponse courte

Tant que vous faites un avance rapide 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 travaillera é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 forcer la référence de la branche, puisque git fetch empêchera automatiquement toute avance accidentelle non-rapide tant que vous n'utiliserez pas + dans le refspec.

La longue réponse

Vous ne pouvez pas fusionner une branche B dans la branche A sans extraire d'abord A si cela aboutit à une fusion sans avance rapide. C'est parce qu'une copie de travail est nécessaire pour résoudre les conflits potentiels.

cependant, dans le cas de fusions rapides, cela est possible, parce que de telles fusions ne peuvent jamais donner lieu à des conflits, par définition. Pour ce faire, sans d'abord vérifier une branche, vous pouvez utiliser git fetch avec un refspec.

Voici un exemple de mise à jour master (interdisant les changements non-rapides) si vous avez une autre branche feature vérifié:

git fetch upstream master:master

Ce cas d'utilisation est si courant que vous voudrez probablement créer un alias 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 fait cet alias est le suivant:

  1. git checkout HEAD: cela met votre copie de travail dans un état détaché. Ceci est utile si vous souhaitez mettre à jour master alors que vous l'avez fait vérifier. Je pense qu'il était nécessaire de faire avec car sinon la référence de la branche pour master ne bougera pas, mais je ne me souviens pas si c'est vraiment hors de ma tête.

  2. git fetch upstream master:master: cela accélère votre local master au même endroit que upstream/master.

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

La syntaxe de git fetch pour les fusions rapides (non)

Si vous voulez le fetch commande d'échouer si la mise à jour est non-fast-forward, alors vous utilisez simplement une refspec du formulaire

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

Si vous souhaitez autoriser les mises à jour sans avance rapide, ajoutez un + à l'avant de la refspec:

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

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

git fetch . <sourceBranch>:<destinationBranch>

La documentation

Du git fetch la documentation qui explique cette syntaxe (Je souligne):

<refspec>

Le format d'un <refspec> le paramètre est un plus optionnel +, suivi de la référence de la source <src>, suivi de deux points :, suivi de l'adresse de destination <dst>.

La télécommande qui correspond <src>est récupéré, et si <dst> n'est pas une chaîne vide, l'identificateur local qui correspond est avancé en utilisant <src>. Si le plus optionnel + est utilisé, le ref local est mis à jour même s'il ne résulte pas une mise à jour rapide.

Voir également

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

  2. Fusion sans modification du répertoire de travail


662
2017-07-18 12:06



Non, il n'y en a pas. Une extraction 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 rapide, vous n'avez pas besoin de vérifier la branche cible, car vous n'avez pas besoin de fusionner quoi que ce soit - il vous suffit de mettre à jour la branche pour pointer vers la nouvelle tête ref. Vous pouvez le faire avec git branch -f:

git branch -f branch-b branch-a

Mettra à jour branch-b pointer le chef de branch-a.

le -f option signifie --force, ce qui signifie que vous devez faire attention quand vous l'utilisez. Ne l'utilisez pas à moins d'être sûr que la fusion sera rapide.


73
2017-11-11 17:10



Comme l'a dit Amber, les fusions rapides sont le seul cas dans lequel vous pourriez éventuellement le faire. Toute autre fusion doit passer par toute la fusion à trois voies, en appliquant des correctifs, en résolvant des conflits, ce qui signifie qu'il doit y avoir des fichiers.

J'utilise un script pour cela: faire des fusions rapides sans toucher à l'arborescence (à moins que vous ne fusionniez avec HEAD). C'est un peu long, car il est au moins un peu robuste - il vérifie que la fusion est un rapide, 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 reflog est exactement comme une fusion avant rapide, au lieu de la "réinitialisation" que vous obtenez si vous utilisez branch -f. Si vous le nommez git-merge-ff et déposez-le 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="$1"
    commit="$2"

    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 "$1" "$2";;
    * ) _usage
esac

P.S. Si quelqu'un voit des problèmes avec ce script, s'il vous plaît commenter! C'était un travail d'écriture et d'oubli, mais je serais heureux de l'améliorer.


28
2017-11-11 17:40



Vous ne pouvez le faire que si la fusion est une avance rapide. Si ce n'est pas le cas, alors git doit faire vérifier les fichiers pour pouvoir les fusionner!

Pour le 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 que vous voulez faire avancer rapidement. C'est fondamentalement comme utiliser git branch -f pour déplacer la branche, sauf qu'elle l'enregistre également dans le reflet 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 faites pas ceci pour quelque chose qui n'est pas un fast-forward, ou vous allez simplement réinitialiser votre branche à l'autre commit. (Pour vérifier, voir si git merge-base <branch> <commit> donne le SHA1 de la branche.)


19
2017-07-10 00:59



Une autre manière, certes, assez brute est de simplement recréer la branche:

git fetch remote
git branch -f localbranch remote/remotebranch

Cela rejette la branche périmée locale et en recrée une avec le même nom, donc utilisez-la avec soin ...


10
2018-06-08 10:08



Dans votre cas, vous pouvez utiliser

git fetch origin branchB:branchB

qui fait ce que vous voulez (en supposant que la fusion soit rapide). Si la branche ne peut pas être mise à jour car elle nécessite une fusion sans avance rapide, elle échoue en toute sécurité avec un message.

Cette forme de fetch a aussi des options plus utiles:

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

Notez que <remote>  peut être un dépôt local, et <sourceBranch> peut être une branche de suivi. Vous pouvez donc mettre à jour une branche locale, même si elle n'est pas extraite, sans accéder au réseau.

Actuellement, mon accès au serveur en amont est via un VPN lent, donc je me connecte périodiquement, git fetch pour mettre à jour toutes les télécommandes, puis déconnectez-vous. Ensuite, si, disons, le maître distant a changé, je peux le faire

git fetch . remotes/origin/master:master

pour mettre mon maître local à jour en toute sécurité, même si j'ai actuellement une autre branche à vérifier. Aucun accès réseau requis.


7
2017-11-17 00:07



Vous pouvez cloner le repo et faire la fusion dans le nouveau repo. Sur le même système de fichiers, cela va relier plutôt que de copier la plupart des données. Terminer en tirant les résultats dans le repo original.


6
2017-11-11 18:00



Entrer git-forward-merge:

Sans avoir besoin de vérifier la destination, git-forward-merge <source> <destination> fusionne la source dans la branche 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
2018-05-29 17:15



Pour de nombreux cas (comme 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 reflog ressemble à un dépassement et empêchera qu'il soit plus rapide. Pour faciliter la récupération, ajoutez ce qui suit dans votre configuration git

[core]
    logallrefupdates=true

Puis tapez

git reflog show mybranch

pour voir l'histoire récente de votre succursale


3
2017-12-20 00:22