Comment afficher VRAIMENT les journaux des fichiers renommés avec git?


132

Je suis relativement nouveau dans git, j'ai utilisé Subversion avant.

J'ai remarqué que la plupart des interfaces graphiques git et des plugins IDE ne semblent pas pouvoir afficher l'historique d'un fichier si le fichier a été renommé. Quand j'utilise

git log --follow

sur la ligne de commande, je peux voir le journal entier à travers les renommés.

Selon Linus Torvalds, le commutateur --follow est un plaisir pour «SVN noob», les utilisateurs sérieux de git ne l'utilisent pas:

--follow est un hack total, destiné à satisfaire les anciens utilisateurs de SVN qui n'ont jamais rien su de choses comme la parentalité ou de jolis graphes de révision de toute façon.

Ce n'est pas totalement fondamental, mais l'implémentation actuelle de "--follow" est vraiment une chose de prétraitement rapide boulonnée sur la logique de marche des révisions, plutôt que d'être vraiment intégrale.

Il a littéralement été conçu comme un plaisir "SVN noob", pas comme une "vraie fonctionnalité git". L'idée était que vous vous échappiez de l'état d'esprit (brisé) de penser que les renommés comptent dans l'ensemble.

Ma question : Comment les utilisateurs hardcore de git parmi vous obtiennent-ils l'historique d'un fichier lorsqu'il a été renommé? Quelle est la «vraie» façon de faire cela?


17
@ David Hall: git mv oldfile newfilene cause pas le changement de nom à enregistrer du tout - il est tout de même que la suppression d' un fichier et d' ajouter un autre. git ne fonctionne que sur les renommés et les copies de l'état de l'arborescence à chaque commit après coup.
Mark Longair

20
@David Hall: Si vous renommez le fichier avec un autre outil en dehors de git (par exemple /bin/mv oldfile newfile), mais que vous le faites ensuite git add newfile; git rm oldfile, le résultat est indiscernable de celui de git mv oldfile newfile.
Mark Longair

1
Cette idéologie s'effondre si jamais vous déplacez un fichier vers un nouveau référentiel, auquel cas l'incapacité de déplacer son historique entier peut être un problème majeur. Bien que, bien sûr, il y ait des limites à la quantité d'historique véritable que peut réellement contenir un fichier dans un projet complexe.
Roman Starkov

1
Note: git log --follows'améliore un peu avec git 2.9 (juin 2016): voir ma réponse ci
VonC

1
À partir de la version 2.15, vous voudrez peut-être essayer --color-movedquand vous diff.
Michael

Réponses:


71

Je pense que la motivation générale derrière le point de Linus est que - et prenez cela avec une pincée de sel - les utilisateurs de git hardcore ne se soucient jamais de l'histoire d'un "fichier". Vous placez le contenu dans un référentiel git car le contenu dans son ensemble a une histoire significative.

Un changement de nom de fichier est un petit cas particulier de "contenu" se déplaçant entre les chemins. Vous pourriez avoir une fonction qui se déplace entre les fichiers qu'un utilisateur git pourrait traquer avec "pioche" fonctionnellement (par exemple log -S).

D'autres changements de "chemin" incluent la combinaison et le fractionnement de fichiers; git ne se soucie pas vraiment du fichier que vous considérez renommé et de celui que vous considérez comme copié (ou renommé et supprimé), il suit simplement le contenu complet de votre arbre.

git encourage la réflexion «arbre entier» là où autant de systèmes de contrôle de version sont très centrés sur les fichiers. C'est pourquoi git fait plus souvent référence aux "chemins" qu'aux "noms de fichiers".


Salut Charles, Merci pour votre réponse. Il semble que j'utilise git de la même manière que SVN. Bien que je comprenne que git est très différent des autres systèmes de contrôle de version, de nombreux concepts de git me semblent encore étranges ... Je devrais probablement terminer ce livre de git que j'ai acheté récemment.
Mike

24
Le point de Linus était qu'une "bonne" interface graphique serait capable de suivre des morceaux de code à travers les fichiers, et il espérait que nous aurions de tels outils maintenant. Malheureusement, nous n'avons toujours pas ce luxe et --follow est toujours utile.
Michael Parker

11
Est-ce que git vous donne une autre solution que --followpour ça?
Griwes

13
Je dirais que la pensée «arbre entier» est améliorée en --followétant la valeur par défaut. Ce que je veux dire, c'est que lorsque je veux voir l'historique du code dans un fichier, je ne me soucie pas vraiment de savoir si le fichier a été renommé ou non, je veux juste voir l'historique du code, quels que soient les renommés. Donc, à mon avis, il est logique --followd'être la valeur par défaut parce que je ne me soucie pas des fichiers individuels; --followm'aide à ignorer les noms de fichiers individuels, qui sont généralement assez insignifiants.
Édité le

2
Donc ... si je décide de me soucier du "contenu" au lieu du fichier, comment puis-je imprimer les commits pertinents pour le contenu qui se trouve actuellement dans ce fichier? Je serais ravi si git le suivait à travers différents fichiers pour moi et rapportait un journal de tous les changements - je ne vois tout simplement pas comment l'obtenir.
Ed Avis

36

J'ai exactement le même problème que vous rencontrez. Même si je ne peux pas vous donner de réponse, je pense que vous pouvez lire cet e-mail que Linus a écrit en 2005, il est très pertinent et pourrait vous donner un indice sur la façon de gérer le problème:

… Je prétends que tout SCM qui essaie de suivre les renommés est fondamentalement cassé à moins qu'il ne le fasse pour des raisons internes (c'est-à-dire pour permettre des deltas efficaces), exactement parce que les renommés n'ont pas d'importance. Ils ne vous aident pas et ce ne sont pas ce qui vous intéressait de toute façon .

Ce qui compte, c'est de trouver «d'où cela vient», et l'architecture git le fait très bien en effet - bien mieux que toute autre chose. …

Je l'ai trouvé référencé par cet article de blog, ce qui pourrait également vous être utile pour trouver une solution viable:

Dans le message, Linus a expliqué comment un système de suivi de contenu idéal peut vous permettre de trouver comment un bloc de code a pris la forme actuelle. Vous commenceriez à partir du bloc de code actuel dans un fichier, reviendriez dans l'historique pour trouver le commit qui a changé le fichier. Ensuite, vous inspectez le changement du commit pour voir si le bloc de code qui vous intéresse est modifié par celui-ci, car un commit qui modifie le fichier peut ne pas toucher le bloc de code qui vous intéresse, mais seulement quelques autres parties du fichier.

Lorsque vous constatez qu'avant la validation, le bloc de code n'existait pas dans le fichier, vous inspectez la validation plus en profondeur. Vous constaterez peut-être que c'est l'une des nombreuses situations possibles, notamment:

  1. Le commit a vraiment introduit le bloc de code. L'auteur du commit était l'inventeur de cette fonctionnalité intéressante pour laquelle vous recherchiez son origine (ou le coupable qui a introduit le bogue); ou
  2. Le bloc de code n'existait pas dans le fichier, mais cinq copies identiques de celui-ci existaient dans différents fichiers, qui ont tous disparu après la validation. L'auteur du commit a refactoré le code dupliqué en introduisant une seule fonction d'assistance; ou
  3. (cas particulier) Avant le commit, le fichier qui contient actuellement le bloc de code qui vous intéresse n'existait pas, mais un autre fichier avec un contenu presque identique existait, et le bloc du code qui vous intéresse, ainsi que tous les autres contenus du fichier existaient à l'époque, existaient dans cet autre fichier. Il est parti après la validation. L'auteur du commit a renommé le fichier en lui apportant une modification mineure.

Dans git, l'outil de suivi de contenu ultime de Linus n'existe pas encore de manière entièrement automatisée. Mais la plupart des ingrédients importants sont déjà disponibles.

Veuillez nous tenir au courant de vos progrès à ce sujet.


Merci d'avoir publié ces articles. Ce n'est que lorsque je les ai lus que j'ai pleinement saisi l'idée de l'histoire du contenu!. J'y ai pensé de la mauvaise façon!
DavidG

Cet e-mail de Linus est génial, merci d'avoir publié ceci.
mik01aj

Fait amusant, Git v2.15 ajoute--color-moved une évolution vers ce «système de suivi idéal». Je jouais avec pour le voir suivre les lignes déplacées dans un fichier, mais je me suis rendu compte accidentellement qu'il suivait les lignes déplacées dans tout le diff.
Michael

2
Linus explique une situation complexe. Mais ici, nous avons une situation simple: un fichier vient d'être renommé (ou déplacé vers un autre répertoire). Il devrait donc y avoir une solution simple. Je pense que le problème vient du fait que contrairement à Subversion, l'utilisateur ne peut pas indiquer à Git au moment de la validation d'où vient un fichier, et cela --followpeut être faux (par exemple, si 2 fichiers ont le même contenu, ou s'il y a une modification en plus du déplacement de fichier).
vinc17

13

J'ai remarqué que la plupart des interfaces graphiques git et des plugins IDE ne semblent pas pouvoir afficher l'historique d'un fichier si le fichier a été renommé

Vous serez heureux de savoir que certains outils d'interface utilisateur Git populaires le prennent désormais en charge. Il existe des dizaines d'outils d'interface utilisateur Git disponibles, je ne les énumérerai donc pas tous, mais par exemple:

  • SourceTree, lors de la visualisation d'un journal de fichiers, a une case à cocher "Suivre les fichiers renommés" en bas à gauche
  • TortoiseGit a une case à cocher «suivre les renommés» dans la fenêtre du journal en bas à gauche.

Plus d'informations sur les outils d'interface utilisateur Git:


La source fonctionne très bien lorsque vous renommez une fois, lorsque vous renommez deux fois, les détails de modification ne sont pas disponibles pour les validations avant de renommer. J'ai signalé le bogue ici: jira.atlassian.com/browse/SRCTREE-5715
Intel

gitk fonctionne très bien même lorsqu'un fichier est renommé deux fois dans l'historique. La commande ressemble à ceci "gitk --follow path / to / file"
Intel

6

Remarque: git 2.9 (juin 2016) améliorera un peu la nature "boguée" de git log --follow:

Voir commit ca4e3ca (30 mars 2016) par SZEDER Gábor ( szeder) .
(Fusionné par Junio ​​C Hamano - gitster- in commit 26effb8 , 13 avril 2016)

diffcore: correction de l'ordre d'itération de fichiers identiques lors de la détection de renommage

Si les deux chemins « dir/A/file» et « dir/B/file» ont un contenu identique et que le répertoire parent est renommé, par exemple « git mv dir other-dir», alors diffcoresignale les noms exacts suivants:

renamed:    dir/B/file -> other-dir/A/file
renamed:    dir/A/file -> other-dir/B/file

(notez l'inversion ici:, B/file -> A/fileet A/file -> B/file)

Bien que techniquement pas faux, cela est déroutant non seulement pour l'utilisateur, mais aussi pour les commandes git qui prennent des décisions basées sur les informations de renommage, par exemple git log --follow other-dir/A/file«suit dir/B/file» après le changement de nom.

Ce comportement est un effet secondaire de commit v2.0.0-rc4 ~ 8 ^ 2 ~ 14 ( diffcore-rename.c: simplifier la recherche de noms exacts, 14/11/2013): la table de hachage stockant les sources renvoie les entrées du même compartiment, c'est-à-dire les sources correspondant à la destination actuelle , dans l'ordre LIFO.
Ainsi, l'itération examine d'abord ' other-dir/A/file' et ' dir/B/file' et, après avoir trouvé un contenu et un nom de base identiques, signale un changement de nom exact.


2

Sous Linux, j'ai vérifié que SmartGit et GitEye sont capables de suivre les renommés en suivant l'historique d'un fichier particulier. Cependant, contrairement à gitk et GitEye, SmartGit affiche une vue de fichier et une vue de référentiel séparées (qui contient la structure du répertoire mais pas la liste des fichiers contenus dans)

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.