web-dev-qa-db-fra.com

Pourquoi Git dit-il que ma branche maîtresse est "déjà à jour" alors qu'elle ne l'est pas?

Problème de base

Je viens de supprimer TOUT le code d'un fichier de mon projet et de valider la modification de mon git local (exprès). J'ai fait

git pull upstream master

chercher et fusionner de l'amont (donc en théorie que le code supprimé devrait être de retour).

Git me dit que tout est à jour.

Tout n'est définitivement PAS à jour - tout ce code supprimé est toujours supprimé.

Autres informations pertinentes

Je n'ai qu'une seule branche appelée "maître".

J'ai récemment configuré "master" pour suivre en amont de la manière suivante:

Le maître de branche est configuré pour suivre le maître de branche distant depuis l’amont.

La commande git branch -vv donne:

* master 7cfcb29 [upstream/master: ahead 9] deletion test

Pourquoi pourquoi cela se produit-il? Je suis sur le point d'envoyer un e-mail à mon chef de projet pour toute modification apportée à notre code.

Mise à jour

Je pensais que c'était évident, mais de toute façon c'est mon objectif:

Obtenez le plus récent du code sur mon système.

Excusez ma colère ici, mais pourquoi une tâche aussi simple doit-elle être si difficile?

97
user1971506

Je pense que votre problème fondamental ici est que vous interprétez mal et/ou comprenez mal ce que git fait et pourquoi il le fait.

Lorsque vous clonez un autre dépôt, git fait une copie de tout ce qui est "là-bas". Il prend également "leurs" libellés de branche, tels que master, et en crée une copie dont le "nom complet" est votre git tree est (normalement) remotes/Origin/master ( mais dans votre cas, remotes/upstream/master). La plupart du temps, vous omettez également la partie remotes/ afin que vous puissiez vous référer à cette copie d'origine en tant que upstream/master.

Si vous apportez et validez des modifications sur un ou plusieurs fichiers, vous êtes le seul à en avoir. Pendant ce temps, d'autres personnes peuvent utiliser le référentiel d'origine (à partir duquel vous avez créé votre clone) pour créer d'autres clones et les modifier. Ils sont les seuls avec leurs changements, bien sûr. Finalement, quelqu'un peut avoir des modifications qu'il renvoie au propriétaire d'origine (via "Push" ou des correctifs, etc.).

La commande git pull est généralement juste un raccourci pour git fetch suivi de git merge. Ceci est important car cela signifie que vous devez comprendre ce que ces deux opérations font réellement.

La commande git fetch indique de revenir à l’endroit où vous avez cloné (ou si vous avez défini un emplacement d’extraction) et trouver "les nouveaux éléments ajoutés, modifiés ou supprimés par une autre personne". Ces modifications sont copiées et appliquées à votre copie de ce que vous avez obtenu auparavant. Ils sont pas appliqués à votre propre travail, uniquement au leur.

La commande git merge est plus compliquée et vous vous trompez. Ce que cela fait, un peu simplifié, est de comparer "ce que vous avez modifié dans votre copie" à "les modifications que vous avez extraites de quelqu'un d'autre et qui ont donc été ajoutées à votre copie du travail de quelqu'un d'autre". Si vos modifications et leurs modifications ne semblent pas être en conflit, l'opération merge les combine et vous donne un "engagement de fusion" qui lie votre développement à leur développement (bien qu'il existe un cas très courant "facile"). dans lequel vous n'avez aucun changement et vous obtenez un "avance rapide").

La situation que vous rencontrez actuellement est celle dans laquelle vous avez apporté des modifications et les avez engagées — neuf fois, en fait, d'où le "9 en avance" —et elles ont apporté des modifications non. Ainsi, fetch ne récupère rien, puis merge prend leur manque de changement et ne fait rien non plus.

Ce que vous voulez, c'est regarder, ou peut-être même "réinitialiser", "leur" version du code.

Si vous voulez simplement le regarder, vous pouvez simplement vérifier cette version:

git checkout upstream/master

Cela indique à git que vous souhaitez déplacer le répertoire actuel vers la branche dont le nom complet est en fait remotes/upstream/master. Vous verrez leur code à la dernière fois que vous avez exécuté git fetch et obtenu leur dernier code.

Si vous voulez abandonner tous vos propres changements, vous devez changer l'idée de git de quelle révision votre étiquette, master, doit nommer. Actuellement, il nomme votre dernier commit. Si vous revenez sur cette branche:

git checkout master

alors la commande git reset vous permettra de "déplacer l'étiquette", pour ainsi dire. Le seul problème qui reste (en supposant que vous soyez vraiment prêt à abandonner tout ce que vous avez fait) est de savoir où l’étiquette doit pointer.

git log vous permettra de trouver les noms numériques, tels que 7cfcb29, qui sont des noms permanents (qui ne changent jamais), et il existe un nombre ridicule d'autres moyens de les nommer, mais dans ce cas veux le nom upstream/master.

Pour déplacer l'étiquette, effacez vos propres modifications (toutes celles que vous avez engagées sont en réalité récupérables pendant un bon bout de temps mais c'est beaucoup plus difficile après cela alors soyez très sûr):

git reset --hard upstream/master

Le --hard indique à git d'effacer ce que vous avez fait, de déplacer le libellé de la branche actuelle, puis d'extraire le commit donné.

Ce n'est pas super commun de vraiment vouloir git reset --hard et d'effacer un tas de travail. Une méthode plus sûre (rendant beaucoup plus facile de récupérer ce travail si vous décidez que cela en vaut la peine après tout) est de renommer votre branche existante:

git branch -m master bunchofhacks

puis créez une nouvelle branche locale nommée master qui "suit" (je n'aime pas vraiment ce terme car je pense que cela confond les gens, mais c'est le terme génial :-)) le maître de Origin (ou en amont):

git branch -t master upstream/master

que vous pouvez ensuite obtenir avec:

git checkout master

Ce que les trois dernières commandes font (il y a des raccourcis pour n'en faire que deux) est de changer le nom collé sur l'étiquette existante, puis de créer une nouvelle étiquette, puis d'y basculer:

avant de faire quoi que ce soit:

C0 -    "remotes/upstream/master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "master"

après git branch -m:

C0 -    "remotes/upstream/master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "bunchofhacks"

après git branch -t master upstream/master:

C0 -    "remotes/upstream/master", "master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "bunchofhacks"

Ici C0 est le dernier commit (un arbre source complet) que vous aviez lors de la première utilisation de votre git clone. C1 à C9 sont vos commits.

Notez que si vous deviez git checkout bunchofhacks puis git reset --hard HEAD^^, la dernière image serait modifiée en:

C0 -    "remotes/upstream/master", "master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 -    "bunchofhacks"
                                                      \
                                                       \- C8 --- C9

La raison en est que HEAD^^ nomme la révision deux à partir de l’en-tête de la branche actuelle (qui, juste avant la réinitialisation, serait bunchofhacks), et reset --hard déplace alors l’étiquette. Les commits C8 et C9 sont maintenant quasiment invisibles (vous pouvez utiliser des fonctions telles que le refog et git fsck pour les trouver, mais ce n’est plus trivial). Vos étiquettes sont à vous de déplacer comme bon vous semble. La commande fetch s’occupe de celles qui commencent par remotes/. Il est classique de faire correspondre "votre" à "leur" (ainsi, s'ils ont un remotes/Origin/mauve, vous nommez le vôtre mauve aussi), mais vous pouvez taper "leur" à chaque fois que vous souhaitez nommer/voir. engage tu as "d'eux". (N'oubliez pas que "un commit" est un arbre source complet. Vous pouvez choisir un fichier spécifique dans un commit, avec git show par exemple, si et quand vous le souhaitez.)

183
torek

J'ai eu le même problème que toi.

J'ai fait git statusgit fetchgit pull, mais ma branche était toujours en retard sur Origin. J'avais des dossiers et des fichiers poussés sur la télécommande et j'ai vu les fichiers sur le Web, mais ils étaient manquants chez moi.

Enfin, ces commandes ont mis à jour tous les fichiers et dossiers de mon local:

git fetch --all
git reset --hard Origin/master 

ou si vous voulez une branche

git checkout your_branch_name_here
git reset --hard Origin/your_branch_name_here
5
jturi

Comme le disent les autres affiches, transférez les modifications d’amont dans votre référentiel. Si vous souhaitez remplacer ce qui se trouve dans votre référentiel par ce qui se trouve en amont, vous avez plusieurs options. Au pied levé, j'irais avec

git checkout HEAD^1  # Get off your repo's master.. doesn't matter where you go, so just go back one commit
git branch -d master  # Delete your repo's master branch
git checkout -t upstream/master  # Check out upstream's master into a local tracking branch of the same name
3
Paul

Toutes les modifications que vous avez effectuées, telles que la suppression de tous vos fichiers de projet, seront toujours en place après une extraction. Tout ce que vous pouvez faire, c'est fusionner les dernières modifications apportées ailleurs dans votre propre branche. Si votre branche a tout supprimé, vous obtiendrez au mieux des conflits de fusion lorsque des modifications en amont affectent les fichiers que vous avez supprimés. Donc, bref, oui tout est à jour.

Si vous décrivez le résultat que vous souhaitez obtenir au lieu de "tous les fichiers supprimés", quelqu'un peut peut-être suggérer un plan d'action approprié.

Mise à jour:

OBTENIR LE PLUS RÉCENT DU CODE SUR MON SYSTÈME

Ce que vous ne semblez pas comprendre, c'est que vous avez déjà le code le plus récent, qui est le vôtre. Si vous voulez vraiment voir le travail le plus récent de quelqu'un d'autre qui se trouve sur la branche principale, procédez comme suit:

git fetch upstream
git checkout upstream/master

Notez que cela ne vous laissera pas en mesure de (re) commencer immédiatement votre propre travail. Si vous avez besoin de savoir comment annuler une action que vous avez effectuée ou annuler les modifications que vous ou une autre personne avez apportées, veuillez fournir des détails. Pensez également à savoir à quoi sert le contrôle de version, car vous semblez mal comprendre son objectif fondamental.

2
Ryan Stewart

La meilleure réponse est bien meilleure en termes d’étendue et de profondeur des informations données, mais il semble que si vous vouliez résoudre votre problème presque immédiatement, et que vous ne vous dérangiez pas sur certains principes de base du contrôle de version, vous pourriez ...

  1. Passer au maître

    $ git checkout upstream master
    
  2. Supprimez votre branche indésirable. (Remarque: il doit s'agir de l'indicateur -D au lieu de l'indicateur normal -d car votre branche comporte de nombreux validations avant le maître.)

    $ git branch -d <branch_name>
    
  3. Créer une nouvelle branche

    $ git checkout -b <new_branch_name>
    
1
BrotherDonkey

S'il vous plaît, corrigez-moi si je me trompe, mais je pense que c'est parce que votre branche est en avance de 9 commits sur la branche principale en amont. Lorsque vous effectuez un tirage à partir de la télécommande, vous avez déjà ces modifications, c'est simplement que vous les avez devancées de 9 commits. Il n’aurait pas de sens pour ces anciennes modifications, que vous avez déjà dans votre historique et qui sont en avance de 9 commises, de remplacer vos nouvelles modifications.

Je crois que si les commits de la télécommande étaient plus récents alors ils seraient fusionnés (par défaut; sauf si vous spécifiez --rebase) dans votre branche actuelle.

Comme Ryan l'a dit, je vous suggère de préciser exactement votre objectif afin que quelqu'un puisse vous aider plus directement.

1
Jorge Israel Peña