Faire ignorer les modifications de 'git log' pour certains chemins

121

Comment puis-je git logafficher uniquement les commits qui ont modifié des fichiers autres que ceux que je spécifie?

Avec git log, je peux filtrer les commits que je vois sur ceux qui touchent un ensemble donné de chemins. Ce que je veux, c'est inverser ce filtre afin que seuls les commits qui touchent des chemins autres que ceux spécifiés soient répertoriés.

Je peux obtenir ce que je veux avec

git log --format="%n/%n%H" --name-only | ~/filter-log.pl | git log --stdin --no-walk

filter-log.plest:

#!/usr/bin/perl
use strict;
use warnings;

$/ = "\n/\n";
<>;

while (<>) {
    my ($commit, @files) = split /\n/, $_;

    if (grep { $_ && $_ !~ m[^(/$|.etckeeper$|lvm/(archive|backup)/)] } @files) {
        print "$commit\n";
    }
}

sauf que je veux quelque chose d'un peu plus élégant que ça.

Notez que je ne demande pas comment faire ignorer les fichiers par git. Ces fichiers doivent être suivis et validés. C'est juste que, la plupart du temps, je ne suis pas intéressé à les voir.

Question connexe: Comment inverser `git log --grep = <pattern>` ou Comment afficher les journaux git qui ne correspondent pas à un modèle C'est la même question sauf pour les messages de validation plutôt que pour les chemins.

Forum de discussion sur ce sujet à partir de 2008: Re: Exclure des fichiers de git-diff Cela avait l'air prometteur mais le fil semble s'être tari.

Anonymoose
la source
Je ne sais pas s'il existe un moyen intégré, et votre solution perl semble assez décente. Si vous le modifiez pour accepter les chemins comme arguments de ligne de commande, vous pouvez simplement créer un alias quelque chose comme !f() { git log ... | path/to/filter-log.pl "$@" | git log --stdin --no-walk; f, ou même encapsuler cette partie de pipeline dans le script.
Cascabel
Pour contourner le problème, j'utilise findpour filtrer les répertoires dont je ne veux pas voir les commits. Si je voulais ignorer les entrées de journal des commits effectués dans le répertoire racine, SiteConfigje dirais:git log `find . -type d -mindepth 1 -maxdepth 1 ! -name *SiteConfig`
Noah Sussman
Pour Git 1.9 / 2.0 (Q1 2014), voir ma réponse ci - dessous : git log --oneline --format=%s -- . ":!sub"fonctionnera (avec la magie pathspec :(exclude)et sa forme courte:! )
VonC

Réponses:

214

Il est implémenté maintenant (git 1.9 / 2.0, Q1 2014) avec l'introduction pathspec magic :(exclude)et sa forme courte:! dans commit ef79b1f et commit 1649612 , par Nguyễn Thái Ngọc Duy ( pclouds) , la documentation peut être trouvée ici .

Vous pouvez maintenant tout consigner sauf le contenu d'un sous-dossier:

git log -- . ":(exclude)sub"
git log -- . ":!sub"

Ou vous pouvez exclure des éléments spécifiques dans ce sous-dossier

  • un fichier spécifique:

      git log -- . ":(exclude)sub/sub/file"
      git log -- . ":!sub/sub/file"
  • tout fichier donné dans sub:

      git log -- . ":(exclude)sub/*file"
      git log -- . ":!sub/*file"
      git log -- . ":(exclude,glob)sub/*/file"

Vous pouvez rendre cette exclusion insensible à la casse!

git log -- . ":(exclude,icase)SUB"

Comme l'a noté Kenny Evitt

Si vous exécutez Git dans un shell Bash, utilisez ':!sub'ou à la ":\!sub"place pour éviter les bash: ... event not founderreurs


Remarque: Git 2.13 (Q2 2017) ajoutera un synonyme ^à!

Voir commit 859b7f1 , commit 42ebeb9 (08 février 2017) par Linus Torvalds ( torvalds) .
(Fusionné par Junio ​​C Hamano - gitster- in commit 015fba3 , 27 fév 2017)

pathspec magic: ajoutez « ^» comme alias pour « !»

Le choix de ' !' pour un pathspec négatif finit non seulement par ne pas correspondre à ce que nous faisons pour les révisions, mais c'est aussi un caractère horrible pour l'expansion du shell car il doit être cité.

Ajoutez donc « ^» comme alias alternatif pour une entrée de spécification de chemin d'exclusion.


Notez qu'avant Git 2.28 (T3 2020), l'utilisation de pathspec négative, lors de la collecte des chemins, y compris ceux non suivis dans l'arbre de travail, était interrompue.

Voir commit f1f061e (05 juin 2020) par Elijah Newren ( newren) .
(Fusionné par Junio ​​C Hamano - gitster- dans commit 64efa11 , 18 juin 2020)

dir: correction du traitement des pathspecs annulés

Signalé par: John Millikin
Signature par: Elijah Newren

do_match_pathspec()a commencé la vie au fur match_pathspec_depth_1()et à mesure que l'exactitude était censée être appelée match_pathspec_depth(). match_pathspec_depth()a été renommé plus tard en match_pathspec(), donc l'invariant auquel nous nous attendons aujourd'hui est qu'il do_match_pathspec()n'y a aucun appelant direct en dehors de match_pathspec().

Malheureusement, cette intention a été perdue avec le renommage des deux fonctions, et des appels supplémentaires à do_match_pathspec()ont été ajoutés dans les commits 75a6315f74 (" ls-files: add pathspec matching for submodules", 2016-10-07, Git v2.11.0-rc0 - merge listée dans batch # 11 ) et 89a1f4aaf7 (" dir: si notre spécification de chemin peut correspondre à des fichiers sous un répertoire, recurse dedans", 17/09/2019, Git v2.24.0-rc0).

Bien sûr, do_match_pathspec()avait un avantage important par rapport à match_pathspec()- match_pathspec()coder en dur les indicateurs à l'une des deux valeurs, et ces nouveaux appelants devaient passer une autre valeur pour les indicateurs.

De plus, bien que l'appel do_match_pathspec()direct soit incorrect, il n'y avait probablement aucune différence dans la sortie de fin observable, car le bogue signifiait simplement que cela fill_diretory()se répéterait dans des répertoires inutiles.

Étant donné que les vérifications ultérieures de la correspondance de ce chemin sur les chemins individuels sous le répertoire entraîneraient le filtrage de ces chemins supplémentaires, la seule différence par rapport à l'utilisation de la mauvaise fonction était un calcul inutile.

Le deuxième de ces mauvais appels à do_match_pathspec()était impliqué - soit via un mouvement direct ou via la copie + édition - dans un certain nombre de refactors ultérieurs.

Voir commits 777b420347 (" dir: synchronize treat_leading_path()and read_directory_recursive()", 2019-12-19, Git v2.25.0-rc0 - merge ), 8d92fb2927 (" dir: replace l'algorithme exponentiel par un linéaire", 2020-04-01, Git v2.27.0 -rc0 - fusion répertoriée dans le lot n ° 5 ) et 95c11ecc73 ("Correction de l' fill_directory()API sujette aux erreurs ; ne renvoyer que les correspondances", 2020-04-01, Git v2.27.0-rc0 - fusion répertoriée dans le lot n ° 5 ) .

Le dernier de ceux-ci a introduit l'utilisation de do_match_pathspec()sur un fichier individuel, et a donc entraîné le retour de chemins individuels qui ne devraient pas l'être.

Le problème avec l' appel au do_match_pathspec()lieu de match_pathspec()est que tous les modèles niées comme « `: unwanted_path`` seront ignorés .

Ajoutez une nouvelle match_pathspec_with_flags()fonction pour répondre aux besoins de spécification d'indicateurs spéciaux tout en vérifiant correctement les modèles annulés, ajoutez un gros commentaire ci-dessus do_match_pathspec()pour empêcher les autres de l'utiliser à mauvais do_match_pathspec()escient et corrigez les appelants actuels de pour utiliser à la place soit match_pathspec()ou match_pathspec_with_flags().

Une dernière remarque est que cela DO_MATCH_LEADING_PATHSPECnécessite une attention particulière lors de l'utilisation DO_MATCH_EXCLUDE.

Le point de DO_MATCH_LEADING_PATHSPECest que si nous avons un pathspec comme

*/Makefile

et nous vérifions un chemin de répertoire comme

src/module/component

que nous voulons le considérer comme une correspondance afin que nous rentrions dans le répertoire car il _might_ a un fichier nommé Makefilequelque part ci-dessous.

Cependant, lorsque nous utilisons un modèle d'exclusion, c'est-à-dire que nous avons une spécification de chemin comme

:(exclude)*/Makefile

nous ne voulons PAS dire qu'un chemin de répertoire comme

src/module/component

est une correspondance (négative).

Bien qu'il puisse y avoir un fichier nommé 'Makefile' quelque part sous ce répertoire, il peut également y avoir d'autres fichiers et nous ne pouvons pas exclure de manière préventive tous les fichiers de ce répertoire; nous devons récurer puis vérifier les fichiers individuels.

Ajustez la DO_MATCH_LEADING_PATHSPEClogique pour ne s'activer que pour les pathspecs positifs.

VonC
la source
7
Pouvez-vous faire plusieurs fichiers?
Justin Thomas
12
@JustinThomas Je crois (pas encore testé) que vous pouvez répéter ce modèle d'exclusion de chemin plusieurs fois ":(exclude)pathPattern1" ":(exclude)pathPattern2", ignorant ainsi plusieurs dossiers / fichiers.
VonC
7
Si vous exécutez Git dans un shell Bash, utilisez ':!sub'plutôt pour éviter les bash: ... event not founderreurs . ":\!sub"ne fonctionne pas.
Kenny Evitt le
1
@KennyEvitt Merci pour votre modification et commentaire. J'ai inclus ce dernier dans la réponse pour plus de visibilité.
VonC
2
Pour ceux qui se demandent où se trouve la documentation officielle sur cette fonctionnalité, voir git help glossary(que j'ai trouvé dans git help -g[ce que j'ai trouvé suggéré dans git help]).
ravron
4

tl; dr: shopt -s extglob && git log !(unwanted/glob|another/unwanted/glob)

Si vous utilisez Bash, vous devriez pouvoir utiliser la fonction de globalisation étendue pour obtenir uniquement les fichiers dont vous avez besoin:

$ cd -- "$(mktemp --directory)" 
$ git init
Initialized empty Git repository in /tmp/tmp.cJm8k38G9y/.git/
$ mkdir aye bee
$ echo foo > aye/foo
$ git add aye/foo
$ git commit -m "First commit"
[master (root-commit) 46a028c] First commit
 0 files changed
 create mode 100644 aye/foo
$ echo foo > bee/foo
$ git add bee/foo
$ git commit -m "Second commit"
[master 30b3af2] Second commit
 1 file changed, 1 insertion(+)
 create mode 100644 bee/foo
$ shopt -s extglob
$ git log !(bee)
commit ec660acdb38ee288a9e771a2685fe3389bed01dd
Author: My Name <[email protected]>
Date:   Wed Jun 5 10:58:45 2013 +0200

    First commit

Vous pouvez combiner cela avec globstarpour une action récursive.

l0b0
la source
7
Cela ne montre pas les validations affectant les fichiers qui n'existent plus. Très proche et agréable hack tout de même.
Anonymoose
-2

Vous pouvez ignorer temporairement les modifications d'un fichier avec:

git update-index --skip-worktree path/to/file

À l' avenir, toutes les modifications apportées à ces fichiers seront ignorés par git status, git commit -aetc. Lorsque vous êtes prêt à commettre ces fichiers, juste inverser:

git update-index --no-skip-worktree path/to/file

et engagez-vous normalement.

rubysolo
la source
9
Cela semble répondre à une situation légèrement différente. git update-index --skip-worktreen'entraîne pas le git logfiltrage des commits déjà effectués.
Anonymoose