Qui a changé

Attribue le code à la personne, au ticket, à la branche et à la pull request qui l'a produit, en utilisant uniquement l'historique git local. Signale le nom de l'auteur, le hash du commit, le numéro de ticket, le nom de la branche et la date, ainsi que les liens vers le commit et la PR. Idéal pour savoir qui a écrit, modifié, ajouté ou touché un code.

Qu'est-ce que Qui a changé ?

Cette compétence pour agents IA (prise en charge par Claude Code, Cursor, Codex et autres outils en ligne de commande) répond à la question « qui a écrit ce code, sous quel ticket et quand » sans quitter le terminal. Elle s'appuie uniquement sur l'historique git local, sans contact réseau, et fournit des liens vers le code hôte (Bitbucket, GitHub, GitLab). En remplacement d'une boucle manuelle fastidieuse, elle extrait en une commande le nom de l'auteur, le hash du commit, le numéro de ticket, le nom de la branche, la date, le numéro de PR, et des liens cliquables. Le résultat se présente sous forme d'un verdict principal (le commit possédant le plus de lignes) suivi d'un tableau complet des commits ayant touché le fichier. La compétence gère les modes fichier entier ou plage de lignes, les fonctions nommées, les attributions de confiance (ok, local-only, squash, etc.), et suit les règles d'honnêteté : aucun ticket ou auteur inventé.

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Daniyal12333/claude-code-skills/tree/HEAD/who-changed

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait Qui a changé ?

Répondez à « qui a construit ceci, sous quel ticket et quand » sans quitter le terminal. Tout ce dont vous avez besoin se trouve déjà dans l'historique git local. La seule chose qu'ajoute l'hébergeur de code, c'est le récit de la PR, et la sortie fournit un lien vers celle-ci.

Cela remplace une boucle manuelle : ouvrir le fichier, trouver la fonction, ouvrir la vue blame, copier un hash, le coller dans l'hébergeur, naviguer jusqu'à la PR, lire le ticket et l'auteur.

Ce que l'utilisateur obtient

Nom de l'auteur, hash du commit, numéro de ticket, nom de la branche, date, numéro de PR, et liens cliquables vers le commit et la PR. Un verdict principal pour la plage, puis un tableau de chaque commit qui l'a touché.

Exécuter le script

bash ~/.claude/skills/who-changed/scripts/who-changed.sh <fichier> [debut fin] [raw]

Si la compétence est installée par projet, le chemin est .claude/skills/who-changed/scripts/who-changed.sh à la place.

  • <fichier> seul : mode fichier entier. Principaux contributeurs et les 12 derniers commits.
  • <fichier> <debut> <fin> (ou un seul numéro de ligne) : mode plage de lignes. Propriété des lignes par commit.
  • Ajoutez raw : blame sans -w -C -M, pour quand l'utilisateur veut la vérité octet par octet.

Substitutions d'environnement : WHO_CHANGED_BASE définit la référence vers laquelle la recherche de PR se dirige (par défaut : origin/HEAD, puis origin/main, origin/master, main, master) ; WHO_CHANGED_TICKET_REGEX change le motif du ticket (par défaut [A-Z][A-Z0-9]+-[0-9]+).

Le script imprime un rapport séparé par des tabulations et n'écrit jamais sur le dépôt ni sur le réseau. Reformatez-le pour l'utilisateur – ne collez pas le TSV brut.

Étape 1 : résoudre la cible en une plage de lignes

Le script prend des nombres, donc un nom de fonction doit d'abord être transformé en plage. Le raccourci -L :nomFonction: de Git dépend d'un motif de fonction spécifique au langage, et pour de nombreux langages (les membres de classe TypeScript en particulier) il échoue avec no match. Résolvez vous-même la plage :

  1. Recherchez la déclaration, par exemple ^\s*(public|private|protected)?\s*(async\s+)?<nom>\s*[(<], et <nom>\s*[:=]\s*(function|\() pour les propriétés de fonctions fléchées. Pour HTML ou CSS, recherchez le sélecteur, la clé ou le bloc littéral.
  2. Lisez à partir de cette ligne et faites correspondre les accolades à partir de la première { pour trouver la ligne de fermeture.
  3. Si le nom correspond à plusieurs endroits, listez les correspondances et demandez laquelle choisir. Ne choisissez jamais silencieusement la première – les noms courants existent souvent dans plusieurs fichiers.

Les numéros de ligne proviennent de l'arbre de travail, donc ils correspondent à ce que l'utilisateur voit dans son éditeur.

Avec une plage explicite (285-300, 285, L285-L300), utilisez-la telle quelle. Avec seulement un chemin de fichier, utilisez le mode fichier entier.

Étape 2 : lire la sortie

Le bloc ## meta contient base (la référence vers laquelle la recherche de PR se dirige), base_tip_date, web et host (base des liens, dérivée de git remote), le mode de blame, et si le fichier a des modifications non commitées.

Les lignes dans ## commits sont triées par lignes possédées. Le verdict est la première ligne – le commit possédant le plus de lignes de la plage. Mentionnez le second quand la plage est vraiment partagée ; une fonction construite par quatre personnes sur trois ans ne doit pas être attribuée à une seule.

Le script construit déjà commit_url et pr_url pour les dépôts Bitbucket, GitHub et GitLab. Quand ils sont -, il n'y a pas de dépôt distant reconnaissable – dites-le et omettez les liens plutôt que d'inventer un hôte.

Présentez-le comme ceci (les noms ci-dessous sont inventés) :

### calculateTotal - src/cart/cart.service.ts:42-61

**Alice Moreno** · `a1b2c3d` · 2023-02-08 · possède 13 des 20 lignes
Ticket SHOP-88 · branche `SHOP-88-cart-totals` · PR #41

| Lignes | Auteur | Commit | Date | Ticket | Branche | PR |
|---|---|---|---|---|---|---|
| 13 | Alice Moreno | a1b2c3d | 2023-02-08 | SHOP-88 | SHOP-88-cart-totals | #41 |
| 6 | Ben Carter | 9f8e7d6 | 2026-03-11 | SHOP-310 | SHOP-310-rounding-fix | non fusionné |
| 1 | Chen Wei | 4c5d6e7 | 2024-06-20 | — | release-2024-06 | #112 |

Puis les liens, puis une ligne d'avertissement chaque fois qu'un indicateur de confiance est déclenché.

Étiquettes de confiance et quoi dire à propos de chacune

ÉtiquetteSignificationDites à l'utilisateur
okLa branche de ticket fusionnée via une PRRien à dire
local-onlyLe commit n'est pas encore sur base« Pas encore fusionné à la branche de base » – il n'y a pas de PR
no-pr-mergeSur la ligne principale, mais aucune fusion de PR ne le porteCommit directement sur la ligne principale, ou l'objet de la fusion a enfreint la convention
squashNuméro de PR lu depuis un suffixe (#123)Fusion écrasée, donc l'historique ligne par ligne à l'intérieur de cette PR est aplati
low-no-ticketAucun ticket dans le message ou la brancheIndiquez qu'aucun ticket n'a été enregistré. N'en devinez pas un
low-integration-branchFusionné via une branche sans ticket dans son nomLa PR est l'endroit où la branche d'intégration a atterri, pas là où le code a été écrit. Confiance faible
uncommittedPas encore commitéModifications locales non commitées ; exclues du verdict

Le code plus ancien arrive souvent via des branches d'intégration de longue durée (develop, release-*, ui-dev). Pour celles-ci, la réponse honnête est « fusionnée sur la ligne principale via PR #112, mais cette PR est une branche d'intégration – ce n'est pas la PR dans laquelle ce code a été écrit. »

Pourquoi -w -C -M est la valeur par défaut

-w ignore les espaces, -M suit les lignes déplacées dans le fichier, -C suit les lignes copiées depuis un autre fichier. Ce n'est pas cosmétique. Prenons un bloc écrit en 2023 et seulement ré-indenté en 2026 (chiffres illustratifs) :

  • intelligent (-w -C -M) : 9 des 9 lignes vont à l'auteur original, 2023 – correct
  • brut : 8 des 9 lignes vont à la personne qui a ré-indenté, 2026 – lui crédite du code ancien

Ainsi, l'auteur nommé est la dernière personne à avoir changé le comportement, pas la dernière à avoir touché les octets. Dites-le quand cela compte, et proposez raw comme suivi.

Règles d'honnêteté

  • N'inventez jamais un ID de ticket. Aucun ticket dans le message du commit ou la branche : signalez — et dites qu'aucun ticket n'a été enregistré.
  • N'inventez jamais un auteur. Si le blame n'est pas concluant, indiquez quels commits sont candidats.
  • Distinguez « fusionné via » de « écrit dans » chaque fois qu'un indicateur de faible confiance se déclenche.
  • Si base_tip_date semble obsolète, notez que la référence de base n'a pas été récupérée récemment et que les fusions récentes peuvent être manquées. Suggérez git fetch origin – ne l'exécutez pas sans demande.

Limites à mentionner quand approprié

  • Le « pourquoi » n'est pas dans git. La description de la PR, les relecteurs et le titre du ticket nécessitent l'API et les identifiants de l'hébergeur ou du suivi. Le lien vers la PR est le point de relais.
  • Les fusions écrasées (squash) aplatissent l'historique. Si une PR a été écrasée, un seul commit possède toute la fonctionnalité et l'attribution ligne par ligne à l'intérieur est perdue.
  • --ancestry-path choisit la première fusion qui a porté le commit jusqu'à la ligne principale. Avec des branches de fonctionnalités imbriquées, cela peut être une fusion intermédiaire, pas la PR du ticket lui-même.
  • Les contributeurs de fichier entier ne suivent pas les renommages. git shortlog n'a pas de --follow ; la liste des commits suit les renommages, mais le décompte des contributeurs non.
  • -C surprend parfois en attribuant du code copié-collé au fichier d'origine.
  • La détection de PR reconnaît trois styles de message de fusion : Bitbucket (Merged in <branche> (pull request #N)), GitHub (Merge pull request #N from ...), et squash (... (#N)). Les autres styles échouent dans no-pr-merge.

Solution de secours si le script n'est pas disponible

git blame -w -C -M -L <debut>,<fin> --date=short -- <fichier>
git log -1 --date=short --pretty=format:"%H%n%an%n%ad%n%s%n%b" <sha>
git merge-base --is-ancestor <sha> origin/main     # pas un ancêtre -> non fusionné
git log --merges --ancestry-path --reverse --pretty=format:"%h|%s" <sha>..origin/main | grep -m1 "pull request"
git branch --contains <sha>                        # cas non fusionné

Analysez l'objet de la fusion pour la branche et le numéro de PR. Deux pièges : git shortlog a besoin d'un HEAD explicite sinon il lit silencieusement l'entrée standard et ne retourne rien, et les noms de branche peuvent être préfixés (feature/SHOP-88-...), donc n'ancrez pas la regex du ticket au début de la chaîne.

Skills associés