web-dev-qa-db-fra.com

cacher automatiquement les changements de sauvegarde/pop sur git rebase?

mon flux de travail git utilise beaucoup de rebase. Je récupère toujours les modifications en amont (le référentiel principal que j'ai créé), puis je les fusionne vers mes branches, puis je me rebase pour supprimer les commits de fusion inutiles (pour moi: D) et les scissions d'arborescence.

une chose sur ce flux de travail qui m'énerve est:

$ git rebase upstream/master
Cannot rebase: You have unstaged changes.
Please commit or stash them.

$ git stash
Saved working directory and index state WIP on cc: abc1234 Merge remote-tracking branch 'upstream/master' into local_branch
HEAD is now at abc1234 Merge remote-tracking branch 'upstream/master' into local_branch

$ git rebase upstream/master
First, rewinding head to replay your work on top of it...
Applying: awesome code change

$ git stash pop

nous avons donc 4 commandes, 1 = rebase en échec, 2 = stash, 3 = rebase, 4 = stash pop. tout sauf 3 est juste un travail stupide.

La question est donc la suivante: quel est le moyen le plus recommandé de l’automatiser? un alias pour exécuter git stash/rebase/pop à chaque fois? un config git qui oblige rebase à cacher ou à le traiter comme un autre s’engage à présenter une nouvelle demande par la suite? autre chose?

29
gcb

Edit: Depuis la version 1.8.4 de Git, mais avec un bogue important corrigé dans la version 2.0.1 de Git, git rebase a maintenant --autostash. Vous pouvez configurer git rebase pour utiliser --autostash par défaut également, avec git config --global rebase.autoStash true. Veuillez noter la phrase suivante de la documentation :

Cependant, utilisez-le avec précaution: la réserve finale application après un rebase réussi pourrait entraîner non négligeable conflits.

(Je préfère toujours faire des commits.)

TL; DR réponse: faites juste un commit (puis annulez-le plus tard)

Cela vous aidera peut-être à comprendre que git stash est en réalité simplement git commit (sous une forme plus complexe, qui valide d'abord l'index, puis l'arbre de travail. Lorsque vous appliquez un stash, vous pouvez maintenir la séparation entre index et arbre de travail, ou combinez-les en un simple changement d’arbre de travail).

Ce qui rend une réserve spéciale, c’est que les commits qu’il effectue - les deux ou, avec -u ou -a, voire trois commits - sont effectués sous une forme inhabituelle (en tant que commit de fusion qui n’est pas vraiment une fusion) et ne sont placés sur aucune branche (au lieu de cela). , la référence spéciale refs/stash est utilisée pour les conserver et les trouver).

Comme ils ne font pas partie d'une branche, rebase ne les touche pas et, dans votre flux de travail, c'est le git stash pop qui introduit les modifications de l'arbre de travail dans votre nouvel arbre de travail. Cependant, si vous faites votre propre commit (normal), sur une branche, et que vous le rebasez et l'incluez, ce commit normal sera rebasé avec les autres. Nous arriverons à un dernier problème dans un instant; pour l'instant, rédigeons cela comme une série de commits qui sont (ou ne sont pas) rebasés:

... do some work ...
... make some commits ...
... more work ...
... do something that causes upstream/master to update, such as git fetch upstream
$ git stash

À ce stade, voici ce que vous avez:

... - o - * - A - B - C     <-- HEAD=master
           \          |\
            \         i-w   <-- stash
             \
              @-@-@         <-- upstream/master

Ici, A, B et C sont vos commits (je suppose que vous en avez fait 3), tous sur une branche master. Le i-w suspendu commit C est votre cachette, qui n'est pas sur la branche, mais est toujours un commit à deux-commit "git stash bag" et est en fait attaché à votre dernier commit (C). Les __ commits @ (il peut y en avoir un seul) sont les nouveaux commits en amont.

(Si vous avez validé no, votre sacoche se bloque, commit *, et votre branche actuelle pointe sur commit *, de sorte que git rebase n'a pas d'autre travail à faire que de déplacer le pointeur de votre branche actuelle. Tout fonctionne. les mêmes, dans ce cas, mais je suppose qu'il y a des commits.)

Maintenant, vous exécutez git rebase upstream/master. Cela copie vos commits dans de nouveaux commits, avec de nouveaux ID et de nouveaux ID parents, afin qu'ils soient placés au sommet du dernier @. Le sac de rangement ne bouge pas, le résultat est le suivant:

... - o - * - A - B - C     [abandoned, except for the stash]
           \          |\
            \         i-w   <-- stash
             \
              @-@-@         <-- upstream/master
                   \
                    A'-B'-C'   <-- HEAD=master

Vous utilisez maintenant git stash pop, qui restaure les éléments i/w en tant que modifications de l’arbre de travail, en effaçant le libellé stash (plus précisément, en le décomposant de sorte que stash@{1}, s’il existe, est maintenant stash, etc.). Cela libère les dernières références à la chaîne A - B - C originale, ce qui signifie que nous n’avons pas non plus besoin du bit i-w, ce qui nous permet de le redessiner de la manière la plus simple:

... - @            <-- upstream/master
       \
        A'-B'-C'   <-- HEAD=master plus work tree changes

Maintenant, voyons ce qui se passe si, au lieu de git stash save, vous faites simplement un git commit -a (ou git add et git commit sans -a) pour créer un commit D réel. Vous commencez avec:

... - o-*-A-B-C-D   <-- HEAD=master
         \
          @-@-@     <-- upstream/master

Maintenant vous git rebase upstream/master, qui copie A à D pour les placer à la fin du dernier @, et vous avez ceci:

... - o-*-@-@-@     <-- upstream/master
               \
                A'-B'-C'-D'   <-- HEAD=master

Le seul problème est que vous avez cette validation supplémentaire non désirée D (enfin, D' maintenant), au lieu de modifications de l’arbre de travail non validées. Mais ceci est trivialement annulé avec git reset pour revenir en arrière sur un commit. Nous pouvons utiliser une réinitialisation --mixed - la valeur par défaut - pour redéfinir l'index (zone de stockage intermédiaire), de manière à "annuler l'ajout" de tous les fichiers ou, si vous voulez, conserver git add-, une réinitialisation --soft. (Ni n'affecte le graphe de validation résultant, seul l'état de l'index est différent.)

git reset --mixed HEAD^   # or leave out `--mixed` since it's the default

Voici à quoi ça ressemble:

... - o-*-@-@-@     <-- upstream/master
               \
                A'-B'-C'      <-- HEAD=master
                        \
                         D'   [abandoned]

Vous pensez peut-être que c'est inefficace, mais lorsque vous utilisez git stash, vous effectuez au moins deux commits, que vous abandonnerez ensuite lorsque vous les git stash pop. La vraie différence est qu'en effectuant des validations temporaires sans publication, vous les redéfinissez automatiquement.

N'ayez pas peur des commits temporaires

Il y a une règle générale avec git: faites beaucoup d'engagements temporaires, pour sauvegarder votre travail au fur et à mesure. Vous pouvez toujours les rebaser plus tard. C'est à la place de ceci:

... - * - A - B - C   <-- mybranch

A, B et C sont des commits parfaits et finaux au-dessus de commit * (de quelqu'un d'autre ou de quelque chose déjà publié), faites ceci:

... - * - a1 - a2 - b1 - a3 - b2 - a4 - b3 - c1 - b4 - c2 - c3

a1 est une attaque initiale à A, a2 corrige un bogue dans a1, b1 est une tentative initiale de rendre b travail, a3 provient du fait que b1 exige que A soit différent après tout, b2 corrige un bogue dans b1, a4 bug dans le changement de a3 en a2, et b3 est ce que b1 aurait dû faire; alors c1 est une tentative initiale de C, b4 est un autre correctif de b1, c2 est un raffinement, etc.Disons qu'après c3, vous pensez que c'est presque prêt. Maintenant, vous exécutez git rebase -i Origin/master ou quoi que ce soit, mélangez les lignes pick pour obtenir a1 à a4 dans ordre, b1 à b4 dans ordre, et c1 à c3 dans ordre, et laissez le rebase s'exécuter. Ensuite, vous corrigez les conflits et assurez-vous que tout est correct, puis vous exécutez un autre git rebase -i pour réduire les quatre versions a en A, etc.

Une fois que vous avez terminé, on dirait que vous avez créé une A parfaite la première fois (ou peut-être avec a4 ou un autre selon les commits que vous gardez et ceux que vous supprimez et si vous redéfinissez à tout moment timbres dans les choses). D'autres personnes peuvent ne pas vouloir ou ne pas avoir besoin de voir votre travail intermédiaire — bien que vous puissiez le conserver, pas la combinaison des commits, si cela est utile. En attendant, vous n’avez jamais besoin de trucs non engagés que vous devez rebaser, parce que vous avez juste des commits de trucs partiels.

Il est utile de donner à ces commits des noms, dans le texte de validation d’une ligne, qui guideront votre travail de rebase ultérieur:.

git commit -m 'temp commit: work to enable frabulator, incomplete'

and so on.

35
torek

Une réponse simple:git rebase -i --autosquash --autostash <tree-ish>

-i = interactively rebase

https://devdocs.io/git/git-rebase


Cette volonté... 

  • Cachez automatiquement vos modifications
  • Réabonnement interactif de <tree-ish>
    • Positionnement automatique de vos courges et correctifs
  • Auto pop stash dans le répertoire de travail après rebase

tree-ish peut être un commit hash ou un nom de branche ou un étiquette ou tout identifiant .

15
abhisekp

Vous pouvez utiliser un outil externe appelé git-up , qui fait exactement ce que vous dites pour toutes les branches. Cela vous aidera également à garder un graphique d’historique vierge.

Je l'utilise depuis quelques années et cela fonctionne plutôt bien, surtout si vous n'êtes pas un expert. Si vous ajoutez une fonctionnalité de rebasement automatique, vous devez savoir comment récupérer correctement une rebase défaillante (continuer, abandonner, ...)

Installer

Ouvrez un shell et lancez:

Sudo gem install git-up

Configuration

Ouvrez votre fichier de configuration global (~/.gitconfig) et ajoutez les éléments suivants:

[git-up "fetch"]
    all = true    # up all branches at once, default false
    Prune = true  # Prune deleted remote branches, default false
[git-up "rebase"]
    # recreate merges while rebasing, default: unset
    arguments = --preserve-merges
    # uncomment the following to show changed commit on rebase
    # log-hook = "git --no-pager log --oneline --color --decorate $1..$2"

Voir le documentation officielle pour plus d'options.

Invocation

Si tout est bien configuré, lancez simplement:

git up

Cela équivaut (à peu près) à exécuter les tâches suivantes:

git stash
git fetch --all
[foreach branch]
    git rebase --preserve-merges <branch> <remote>/<branch>
    git merge --ff-only <branch>
[end foreach]
git checkout <prev_branch>
git stash pop
2
giosh94mhz

L'ensemble du flux de travail en une seule commande, y compris la récupération:

git pull --rebase --autostash [...]
2
Marc

Réponse de tjsingleton blogg

faire un alias de commande de:

git stash && git pull --rebase && git stash pop

mettre à jour

Si vous utilisez idea, en poussant avec un répertoire de travail malpropre, une invite de dialogue, choisissez rebase/fusion, sera masquée, rebasée/fusionnée et affichée automatiquement.

1
Sisyphus