Est-il possible de pousser une cachette git vers un référentiel distant?

206

Dans git, est-il possible de créer une cachette, de la pousser vers un référentiel distant, de récupérer la cachette sur un autre ordinateur et d'appliquer la cachette?

Ou sont mes options:

  • Créez un correctif et copiez-le sur l'autre ordinateur, ou
  • Créer une branche mineure et valider le travail incomplet dans cette branche?
Andrew Grimm
la source

Réponses:

68

Il n'est pas possible de l'obtenir via fetch ou ainsi, la refspec miroir l'est fetch = +refs/*:refs/*, et même si la cachette est, refs/stashelle n'est pas envoyée. Un explicite refs/stash:refs/stashn'a aucun effet non plus!

De toute façon, cela ne serait que déroutant, car cela ne permettrait pas de récupérer tous les masques, uniquement le dernier; la liste des cachettes est le refog de la réf refs/stashes.

u0b34a0f6ae
la source
4
Vous pouvez récupérer la dernière cachette d'une télécommande git, mais pas dans votre cachette, seulement dans une autre référence. Quelque chose comme git fetch some-remote +refs/stash:refs/remotes/some-remote/stashle git stash apply some-remote/stash. Mais vous ne pouvez pas obtenir d'anciennes cachettes car elles sont stockées dans le reflog qui n'est pas récupérable. Voir stackoverflow.com/questions/2248680/…
sj26
75

Remarque: Je viens de réécrire cette réponse avec 24 heures de plus de git-fu à mon actif :) Dans mon histoire de coquille, le shebang entier est maintenant composé de trois lignes simples. Cependant, je ne les ai pas condensés pour votre commodité.

De cette façon, j'espère que vous pourrez voir comment j'ai fait les choses, au lieu de simplement avoir à copier / coller des choses à l'aveugle.


Voici étape par étape.

Supposons que la source dans ~ / OLDREPO contient des cachettes. Créez un clone TEST ne contenant pas de stashes:

cd ~/OLDREPO
git clone . /tmp/TEST

Poussez toutes les cachettes en tant que branches temporaires:

git send-pack /tmp/TEST $(for sha in $(git rev-list -g stash); \
    do echo $sha:refs/heads/stash_$sha; done)

Boucle sur l'extrémité réceptrice pour se retransformer en cachettes:

cd /tmp/TEST/
for a in $(git rev-list --no-walk --glob='refs/heads/stash_*'); 
do 
    git checkout $a && 
    git reset HEAD^ && 
    git stash save "$(git log --format='%s' -1 HEAD@{1})"
done

Nettoyez vos succursales temporaires si vous le souhaitez

git branch -D $(git branch|cut -c3-|grep ^stash_)

Faites une liste cachée de git et vous obtiendrez quelque chose comme ceci:

stash@{0}: On (no branch): On testing: openmp import
stash@{1}: On (no branch): On testing: zfsrc
stash@{2}: On (no branch): WIP on sehe: 7006283 fixed wrong path to binary in debianized init script (reported as part of issue
stash@{3}: On (no branch): WIP on debian-collab: c5c8037 zfs_pool_alert should be installed by default
stash@{4}: On (no branch): WIP on xattrs: 3972694 removed braindead leftover -O0 flag
stash@{5}: On (no branch): WIP on testing: 3972694 removed braindead leftover -O0 flag
stash@{6}: On (no branch): WIP on testing: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{7}: On (no branch): WIP on xattrs: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{8}: On (no branch): WIP on testing: 28716d4 fixed implicit declaration of stat64
stash@{9}: On (no branch): WIP on emmanuel: bee6660 avoid unrelated changes

Sur le référentiel d'origine, le même aspect

stash@{0}: WIP on emmanuel: bee6660 avoid unrelated changes
stash@{1}: WIP on testing: 28716d4 fixed implicit declaration of stat64
stash@{2}: WIP on xattrs: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{3}: WIP on testing: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{4}: WIP on testing: 3972694 removed braindead leftover -O0 flag
stash@{5}: WIP on xattrs: 3972694 removed braindead leftover -O0 flag
stash@{6}: WIP on debian-collab: c5c8037 zfs_pool_alert should be installed by default
stash@{7}: WIP on sehe: 7006283 fixed wrong path to binary in debianized init script (reported as part of issue #57)
stash@{8}: On testing: zfsrc
stash@{9}: On testing: openmp import
sehe
la source
1
J'apprends beaucoup en peu de temps et je pense que je devrais probablement simplement utiliser plusieurs commandes dans mon approche antérieure, ce que j'essaierai de faire plus tard.
sehe
9
Cela a bien fonctionné pour moi, sauf que j'avais besoin d'un git add .avant l' git stash save ...étape, car il git stashrefuse de cacher de nouveaux fichiers à moins qu'ils n'aient été mis en scène. De plus, la canalisation du résultat de git rev-list ...through tacinverse l'ordre des stashes afin qu'ils sortent dans le même ordre.
Alan Krueger
1
@sehe Excellent script !! Deux suggestions: 1) - inverser la liste de référence finale afin que les cachettes soient dans le même ordre dans le référentiel cible que dans l'original. 2) Terminez la forboucle finale avec git branch -D stash_$a(nettoyer au fur et à mesure que les stashes sont créés) afin que si quelque chose ne va pas et que nous réessayions, nous ne retraitions pas les validations déjà stashées avec succès.
Keith Robertson
1
Merci beaucoup d'avoir pris le temps d'expliquer ce que vous avez fait au lieu de "simplement publier la solution".
Marjan Venema
1
La solution peut être encore améliorée: si vous remplacez git stash save "$(git log --format='%s' -1 HEAD@{1})"par, git update-ref --create-reflog -m "$(git show -s --format=%B $rev)" refs/stash $revvous obtenez le message de sauvegarde d'origine ( update-refc'est ce qui se passe git stash savedans les coulisses).
Sebastian Schrader
31

Je suis un peu en retard à la fête, mais je pense avoir trouvé quelque chose qui fonctionne pour moi à ce sujet et cela pourrait vous plaire aussi si vos circonstances sont identiques ou similaires.

Je travaille sur une fonctionnalité dans sa propre branche. La branche n'est pas fusionnée en master et poussée jusqu'à sa fin ou j'ai fait des commits que je me sens à l'aise de montrer au public. Donc, ce que je fais quand je veux transférer des modifications non échelonnées vers un autre ordinateur, c'est:

  • Effectuez un commit, avec un message de commit comme " [non-commit] FOR TRANSFER ONLY", présentant le contenu que vous souhaitez transférer.
  • Connectez-vous à l'autre ordinateur.
  • Alors fais:

    git pull ssh+git://<username>@<domain>/path/to/project/ rb:lb

    L'URL peut différer pour vous si vous accédez à votre référentiel d'une manière différente. Cela va extraire les modifications de cette URL de la branche distante "rb" dans la branche locale "lb". Notez que j'ai un serveur ssh exécuté sur mon propre ordinateur et que je peux accéder au référentiel de cette façon.

  • git reset HEAD^(implique --mixed)

    Cela réinitialise le HEAD pour pointer vers l'état avant le commit "[non-commit]".

De git-reset (1): " --mixed: réinitialise l'index mais pas l'arborescence de travail (c'est-à-dire que les fichiers modifiés sont conservés mais pas marqués pour validation) [...]"

Ainsi, vous aurez vos modifications aux fichiers à la fin, mais aucune validation n'est effectuée pour le master et pas besoin de stash.

Cela vous obligera cependant à le faire git reset --hard HEAD^dans le référentiel dans lequel vous avez fait le "[non-commit]", car ce commit est une ordure.

Victor Zamanian
la source
C'est beaucoup plus sale que de simplement créer une nouvelle branche de fonctionnalité puis de la supprimer par la suite ....
Taegost
@Taegost dépend de votre environnement, je suppose. Peut-être des trucs CI / CD empêchant simplement de pousser les branches en amont de bon gré mal gré. Mais oui, selon ce que vous préférez, vous voudrez peut-être simplement créer une branche pour accomplir la même chose.
Victor Zamanian
22

Il est un peu tard, mais cette réponse pourrait aider quelqu'un. Je voulais savoir cela parce que je voulais être en mesure de pousser une fonctionnalité / bug / quoi que ce soit en cours et de travailler à partir du même point sur un autre ordinateur.

Ce qui fonctionne pour moi, c'est de valider mon code en cours (dans une branche sur laquelle je travaille seul). Quand j'arrive à mon autre ordinateur, faites un pull, puis annulez le commit avec:

git reset --soft HEAD^

Continuez à travailler tel que vous étiez, avec tous vos changements en cours, non validés et non planifiés.

J'espère que ça aide.

Sir Robert
la source
Lorsque j'essaie de le faire, l'Origin maintient toujours le Commit, qui n'était pas engagé. Proche mais pas de cigare pour moi.
rezwits
@rezwits Oui, la télécommande la garde, mais il est assez facile de supprimer simplement la branche temporaire d'origine.
Sir Robert
c'est d'ailleurs ce que je fais!
rezwits
19

Il semble y avoir une astuce très soignée pour résoudre ce problème. vous pouvez utiliser git diff > file.diff(et valider le fichier), puis restaurer les modifications à l'aidegit apply file.diff (de n'importe où) pour obtenir le même résultat.

Cela a également été expliqué ici .

Daniel Dubovski
la source
5
si vous avez des fichiers non suivis: 1. git add. 2. git diff HEAD> file.diff
trickpatty
La messagerie du diff pour vous ne permet aucun engagement / empreinte sur le dépôt! (par exemple: Note-to-self via l'application de bureau Signal), ou e-mail.
John Mee
9

J'irais avec la deuxième approche bien que je ne sache pas pourquoi vous ne pouvez pas le valider dans la branche master / sélectionnée. Il est également possible de faire de la cueillette.

Eimantas
la source
27
Il n'y a aucune raison technique de ne pas s'engager sur master / en vedette, juste que je veux dire "Ce n'est pas un vrai commit, c'est juste pour sauvegarder mon travail afin de pouvoir le récupérer sur une autre machine".
Andrew Grimm
4

AFAIK l'idée de la cachette est de cacher quelque chose de moins important sous le tapis local . Personne ne devrait connaître votre merde préférée ;-) Le seul "mais" est: Mais si je développe sur quelques postes de travail? Alors scpc'est bien mieux.

argent_smith
la source
9
Quelque chose d'aussi drôle devrait être un commentaire. ;-)
Andrew Grimm
2
Total git-ssh-newbie ici mais pouvez-vous utiliser scp avec github alors?
Koen
Non, le frontend git-ssh de github est programmé pour que vous n'ayez jamais de shell / console ssh. Il ne peut exécuter que le processus git côté serveur.
argent_smith
1
Donc, scp n'est pas vraiment une option pour ce scénario si votre branche principale est sur github? Avez-vous d'autres suggestions pour transférer une cachette dans ce cas?
Koen
1
J'ai essayé de souligner que le transfert de données n'est pas possible du tout, AFAIK.
argent_smith
2

La réponse actuellement acceptée est techniquement correcte, vous ne pouvez pas dire directement à Git de pousser toutes vos cachettes vers une télécommande, puis de tout ranger dans vos cachettes locales sur un autre ordinateur.

Et bien que la réponse actuellement notée devrait fonctionner, je n'aimais pas qu'elle crée un tas de branches temporaires et qu'elle nécessite de vérifier manuellement le commit de la sauvegarde et de l'enregistrer en tant que cache, ce qui peut entraîner des problèmes comme ce commentaire mentionné , et conduit à un doublon On (no branch): On testing:. Il doit certainement exister un meilleur moyen!

Donc, bien que vous ne puissiez pas pousser directement les stashes, un stash est juste un commit (en fait deux commits), et selon la git pushpage de manuel, vous pouvez pousser les commits:

C'est <src>souvent le nom de la branche que vous souhaitez pousser, mais il peut s'agir de n'importe quelle "expression SHA-1" arbitraire ...

J'ai choisi de pousser les cachettes pour refs/stashes/*ne pas encombrer ma télécommande avec des branches supplémentaires. Je peux donc le faire avec:

git push origin stash@{0}:refs/stashes/$(git rev-parse --short stash@{0})

(La rev-parsecommande obtient le hachage court de la cachette, qui sera unique pour le dépôt.)

Ensuite, je dois récupérer la cachette de l'autre ordinateur. Git ne récupère que les branches par défaut, j'ai donc besoin de récupérer les stashes spécifiquement:

git fetch origin refs/stashes/*:refs/stashes/*

Maintenant, reconvertissez la validation de la sauvegarde en une sauvegarde réelle. Comme mentionné, bien que je puisse simplement vérifier la validation, la réinitialisation et la sauvegarde de la cachette comme d'habitude, je n'aime pas que cela nécessite des étapes supplémentaires, ou qu'il ne puisse pas maintenir l'état d'index pour la cachette. Je cherchais en ligne un moyen de le faire automatiquement, mais ma recherche-fu m'a échoué. Enfin, j'ai cherché dans la page de manuel git stash, où j'ai trouvé ceci:

create
Crée une cachette (qui est un objet de validation normal) et renvoie son nom d'objet, sans la stocker n'importe où dans l'espace de noms ref. Ceci est destiné à être utile pour les scripts. Ce n'est probablement pas la commande que vous souhaitez utiliser; voir "enregistrer" ci-dessus.

store
Stocke un stash donné créé via git stash create (qui est un commit de fusion pendant) dans la référence de stash, mettant à jour le reflog de stash. Ceci est destiné à être utile pour les scripts. Ce n'est probablement pas la commande que vous souhaitez utiliser; voir "enregistrer" ci-dessus.

Puisque j'ai déjà le commit, storeça sonne comme ce que je veux. Je peux donc faire:

git stash store --message "$(git show --no-patch --format=format:%s <SHA>)" <SHA>

Remplacer <SHA>par la cachette qui venait d'être récupérée.

(La git showcommande obtient le message de validation du commit de sauvegarde, à utiliser comme message pour le journal de sauvegarde.)

La cachette apparaît maintenant comme normale dans mon référentiel local:

$ git stash list
stash@{0}: On master: temp
...

Pour nettoyer la télécommande, les cachettes peuvent être supprimées de la télécommande comme suit:

git push origin :refs/stashes/<SHA>

Cette méthode a également l'avantage d'être idempotente: si vous exécutez à pushnouveau la commande, elle fera rapport Everything up-to-date. La fetchcommande peut également être exécutée en toute sécurité à plusieurs reprises. Bien que le stash storesaute le stockage de la cachette s'il est identique à la cachette la plus récente, il n'empêche pas les doublons d'anciennes cachettes. Cela peut être contourné cependant, comme je le fais dans mon git-rstashscript, voir ci-dessous.


Pour terminer, vous pouvez également facilement pousser tous les masques (avec ):

for i in $(seq 0 $(expr $(git rev-list --walk-reflogs --count stash) - 1))
do
  git push origin stash@{$i}:refs/stashes/$(git rev-parse --short stash@{$i})
done

ou importez tous les stashes récupérés:

for stash in $(ls .git/refs/stashes)
do
  git stash store --message "$(git show --no-patch --format=format:%s $stash)" $stash
done

J'ai créé un script qui peut être appelé comme une sous-commande (par exemple git rstash push 0) donc je n'ai pas à me souvenir de tout cela. git-rstashpeut être trouvé ici.

Scott Weldon
la source
1

Ce qui suit ne fonctionne pas avec la stash, mais avec les modifications non validées dans le répertoire de travail. Il crée une branche, valide automatiquement toutes les modifications en cours et envoie à la télécommande:

commit_and_push_ ( ) {
    # This will:
    #  1. checkout a new branch stash-XXX
    #  2. commit the current changes in that branch
    #  3. push the branch to the remote
    local locbr=${1:-autostash-XXX}
    git checkout -b $locbr
    git add .
    git commit -a -m "Automatically created commit"
    git push origin $locbr
    echo "Autocommitted changes in branch $locbr ..."
}

Utilisez comme:

commit_and_push_ my-temp-branch
commit_and_push_
blueFast
la source
0

Je voudrais simplement créer une nouvelle branche cachée et la supprimer chaque fois que cette branche n'est pas requise.

git add . // Add work-in-progress job
git checkout -b stash-branch // Create and checkout to stash-branch
git commit -m 'WIP: job description' // Commit message
git push origin stash-branch // Push to remote
git pull origin stash-branch // Pull the stash-branch
git checkout master // Checkout to working branch
git rebase stash-branch // Rebase the stash-branch
git reset --soft // Equivalent to stash!!
git branch -d stash-branch // Delete when not needed from local
git push -d origin stash-branch // Delete when not needed from remote
Bhojendra Rauniyar
la source
-9

Utilisez simplement Dropbox comme ce type. De cette façon, vous n'avez pas à vous soucier de pousser les stashes car tout votre code serait sauvegardé.

http://blog.sapegin.me/all/github-vs-dropbox

NYC Tech Engineer
la source
2
Avant que quiconque ait une réaction instinctive, je ne dis pas d'utiliser Dropbox au lieu de Github, mais de stocker du code qui n'est pas prêt pour une validation dans Dropbox qui serait toujours sous contrôle de version localement.
NYC Tech Engineer
4
cela prendra trop de temps pour copier tout le projet sur le cloud distant.
Stav Alfi