web-dev-qa-db-fra.com

Git sous-arbre - sous-arbre à jour mais ne peut pas pousser

J'utilise le sous-arbre Git avec quelques projets sur lesquels je travaille, afin de partager du code de base entre eux. Le code de base est mis à jour souvent, et les mises à niveau peuvent être effectuées dans n'importe quel projet, et tous éventuellement mis à jour.

J'ai rencontré un problème où git rapporte que mon sous-arbre est à jour, mais que push est rejeté. Par exemple:

#! git subtree pull --prefix=public/shared project-shared master
From github.com:****
* branch            master     -> FETCH_HEAD
Already up-to-date.

Si je pousse, je devrais recevoir un message disant qu'il n'y a rien à pousser ... bon? DROITE? :(

#! git subtree Push --prefix=public/shared project-shared master
git Push using:  project-shared master
To [email protected]:***
! [rejected]        72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to Push some refs to '[email protected]:***'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git Push --help' for details.

Quelle pourrait être la raison de cela? Pourquoi est-ce que pousser échoue?

75
mateusz

J'ai trouvé la réponse sur ce blog commentaire https://coderwall.com/p/ssxp5q

Si vous rencontrez le problème "Les mises à jour ont été rejetées car le bout de votre branche actuelle est Derrière. Fusionner les modifications distantes (par exemple," git pull ")" problème lorsque vous appuyez (pour Quelle que soit la raison, notamment déconner avec l’historique de git), vous devrez alors imbriquer git commandes afin que vous puissiez forcer un Push to heroku. Par exemple, étant donné l'exemple ci-dessus:

git Push heroku `git subtree split --prefix pythonapp master`:master --force
82
Eric Woodruff

Sous Windows, la commande imbriquée ne fonctionne pas:

git Push heroku `git subtree split --prefix pythonapp master`:master --force

Vous pouvez simplement lancer le bit imbriqué en premier:

git subtree split --prefix pythonapp master

Cela (après beaucoup de nombres) retournera un jeton, par exemple.

157a66d050d7a6188f243243264c765f18bc85fb956

Utilisez ceci dans la commande contenant, par exemple:

git Push heroku 157a66d050d7a6188f243243264c765f18bc85fb956:master --force
30
Chris Jordan

Utilisez le drapeau --onto:

# DOESN'T WORK: git subtree Push --prefix=public/shared project-shared master --onto=project-shared/master

[EDIT: Malheureusement, subtree Push ne transfère pas --onto à la variable split sous-jacente, l'opération doit donc être effectuée en deux commandes! Cela fait, je vois que mes commandes sont identiques à celles de l’une des autres réponses, mais l’explication est différente, je vais donc la laisser ici de toute façon.]

git Push project-shared $(git subtree split --prefix=public/shared --onto=project-shared/master):master

Ou si vous n'utilisez pas bash:

git subtree split --prefix=public/shared --onto=project-shared/master
# This will print an ID, say 0123456789abcdef0123456789abcdef,
# which you then use as follows:
git Push project-shared 01234567:master

J'ai passé des heures à fouiller dans la source de git-subtree pour comprendre celle-ci, alors j'espère que vous l'apprécierez;)

subtree Push commence par exécuter subtree split, qui réécrit votre historique de validation dans un format prêt à être envoyé. En procédant ainsi, il supprime public/shared/ de tout chemin qui le contient et supprime toutes les informations relatives aux fichiers qui ne l’ont pas. Cela signifie que même si vous extrayez non écrasé, toutes les validations de sous-référentiels en amont sont ignorées car elles nomment les fichiers par leur chemin nu. (Les validations qui ne touchent aucun fichier sous public/shared/, ou les validations de fusion identiques à un parent, sont également réduites. [EDIT: De plus, j'ai depuis trouvé une détection de squash, alors je pense que c'est seulement si vous avez tiré non écrasé, et que la fusion simpliste décrite dans une autre réponse parvient à choisir le chemin non écrasé et à l'annuler.]) Le résultat est que le contenu qu'elle tente de pousser finit par contenir tout travail Une personne engagée dans le référentiel hôte actuel à partir duquel vous insérez, mais pas les personnes engagées directement dans le sous-référentiel ou via un autre référentiel hôte.

Cependant, si vous utilisez --onto, alors toutes les commits en amont sont enregistrés comme acceptant d’utiliser verbatim. Ainsi, lorsque le processus de réécriture apparaît comme l’un des parents d’une fusion qu’il souhaite réécrire, il les conserve au lieu d’essayer de réécrire. de la manière habituelle.

26
entheh

Pour une application de type "pages GitHub", dans laquelle vous déployez une sous-arborescence "dist" dans une branche gh-pages, la solution pourrait ressembler à ceci:

git Push Origin `git subtree split --prefix dist master`:gh-pages --force

Je mentionne cela car il semble légèrement différent des exemples de heroku donnés ci-dessus. Vous pouvez voir que mon dossier "dist" existe sur la branche maître de mon référentiel, puis j'appuie dessus en tant que sous-arbre de la branche gh-pages qui se trouve également sur Origin.

9
Colin D

J'ai déjà rencontré ce problème auparavant, et voici comment je l'ai résolu.

Ce que j’ai découvert, c’est que j’avais une branche qui n’était pas rattachée à la branche principale locale. Cette branche existe et sa pendaison dans le vide. Dans votre cas, c'est probablement appelé project-shared. En supposant que ce soit le cas et que vous fassiez un git branch, vous pouvez voir une branche project-shared locale, puis vous pouvez «ajouter» nouveaux commits dans votre branche project-shared existante en effectuant:

git subtree split --prefix=public/shared --onto public-shared --branch public-shared

La façon dont j'ai compris que git subtree commencera à créer une nouvelle branche à partir de --onto, dans ce cas, c'est la branche locale public-shared. Ensuite, la branche signifie créer une branche, qui remplace simplement l'ancienne branche public-shared.

Cela conservera tous les SHA précédents de la branche public-shared. Enfin, vous pouvez faire un

git Push project-shared project-shared:master

En supposant que vous ayez aussi un project-shared à distance; cela va pousser la branche locale suspendue dans le videproject-shared à la branche master du project-shared distant.

3
snw

Ceci est dû à la limitation de l'algorithme d'origine. Lors du traitement des commits de fusion, l'algorithme d'origine utilise un critère simplifié pour couper les parents non apparentés. En particulier, il vérifie, s'il existe un parent, qui a le même arbre. Si un tel parent est trouvé, la validation de fusion sera réduite et utilisée à la place, en supposant que les modifications apportées aux autres parents soient indépendantes de la sous-arborescence. Dans certains cas, cela entraînerait la suppression de parties de l’historique, qui modifieraient réellement le sous-arbre. En particulier, il supprimerait des séquences de commits, qui toucheraient un sous-arbre, mais produiraient la même valeur de sous-arbre.

Voyons un exemple (que vous pouvez facilement reproduire) pour mieux comprendre comment cela fonctionne. Considérez l’historique suivant (le format de ligne est le suivant: commit [arbre] sujet):

% git log --graph --decorate --pretty=oneline --pretty="%h [%t] %s"
*   E [z] Merge branch 'master' into side-branch
|\
| * D [z] add dir/file2.txt
* | C [y] Revert "change dir/file1.txt"
* | B [x] change dir/file1.txt
|/
*   A [w] add dir/file1.txt

Dans cet exemple, nous divisons sur dir. Les validations D et E ont le même arbre z, car nous avons commit C, qui a annulé le commit B, donc la séquence B-C ne fait rien pour dir même si elle a été modifiée. 

Faisons maintenant le fractionnement. D'abord nous nous sommes séparés sur commit C.

% git log `git subtree split -P dir C` ...
* C' [y'] Revert "change dir/file1.txt"
* B' [x'] change dir/file1.txt
* A' [w'] add dir/file1.txt

Ensuite, nous nous sommes séparés sur commit E.

% git log `git subtree split -P dir E` ...
* D' [z'] add dir/file2.txt
* A' [w'] add dir/file1.txt

Oui, nous avons perdu deux commits. Cela entraîne une erreur lors de la tentative d'envoi de la deuxième division, car elle ne possède pas ces deux commits, qui sont déjà entrés dans l'origine.

Habituellement, vous pouvez tolérer cette erreur en utilisant Push --force, car les validations abandonnées ne contiennent généralement pas d'informations critiques. À long terme, le bogue doit être corrigé afin que l'historique de scission contienne tous les commits, qui touchent dir, comme prévu. Je m'attendrais à ce que le correctif inclue une analyse plus approfondie des commits des parents pour les dépendances cachées.

Pour référence, voici la partie du code d'origine, responsable du comportement.

copy_or_skip()
  ...
  for parent in $newparents; do
      ptree=$(toptree_for_commit $parent) || exit $?
      [ -z "$ptree" ] && continue
      if [ "$ptree" = "$tree" ]; then
          # an identical parent could be used in place of this rev.
          identical="$parent"
      else
          nonidentical="$parent"
      fi
  ...
  if [ -n "$identical" ]; then
      echo $identical
  else
      copy_commit $rev $tree "$p" || exit $?
  fi
2
klimkin

Vous pouvez forcer les modifications locales Push vers le dépôt de sous-arbre distant

git Push subtree_remote_address.git `git subtree split --prefix=subtree_folder`:refs/heads/branch --force
0
FunkyKat

Une autre solution [plus simple] consiste à faire avancer la tête de la télécommande en faisant un autre commit si vous le pouvez. Après avoir tiré cette tête avancée dans la sous-arborescence locale, vous pourrez alors appuyer à nouveau dessus.

0
9swampy

Eu ce problème, aussi. Voici ce que j'ai fait, en me basant sur les principales réponses:

Donné:

#! git subtree Push --prefix=public/shared project-shared master
git Push using:  project-shared master
To [email protected]:***
! [rejected]        72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to Push some refs to '[email protected]:***'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git Push --help' for details.

Entrez ceci:

git Push <Origin> 72a6157733c4e0bf22f72b443e4ad3be0bc555ce:<branch> --force

Notez que le jeton généré par "git subtree Push" est utilisé dans "git Push".

0
Amos

C'est donc ce que j'ai écrit sur la base de ce que @entheh a dit.

for /f "delims=" %%b in ('git subtree split --prefix [name-of-your-directory-on-the-file-system-you-setup] -b [name-of-your-subtree-branch]') do @set token=%%b
git Push [alias-for-your-subtree] %token%:[name-of-your-subtree-branch] --force

pause
0
Demodave

Un PowerShell rapide pour les utilisateurs Windows basé sur la solution de Chris Jordan

$id = git subtree split --prefix pythonapp master
Write-Host "Id is: $id"
Invoke-Expression "git Push heroku $id`:master --force"
0
Rtype

La réponse d'Eric Woodruff ne m'a pas aidé, mais ce qui suit l'a fait:

J'ai l'habitude 'git subtree pull' avec l'option '--squash'. Il semble que cela rendait les choses plus difficiles à réconcilier, donc je devais effectuer une extraction de sous-arbre sans écraser cette fois, résoudre certains conflits, puis pousser.

Je dois ajouter que le coup de main écrasé n'a révélé aucun conflit, m'a dit que tout allait bien.

0
Zsolt Szatmari