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?
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:
-
git checkout HEAD
: cela met votre copie de travail dans un État de tête détaché. Ceci est utile si vous voulez mettre à jourmaster
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 pourmaster
ne bougera pas, mais je ne me souviens pas si c'est vraiment le haut de ma tête. -
git fetch upstream master:master
: ce qui fait suivre votre localmaster
au même endroit queupstream/master
. -
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
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.
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.
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>
où <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.)
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 ...
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.
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.
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.
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
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