Fare "git log" ignora le modifiche per determinati percorsi


121

Come posso git logmostrare solo commit che hanno modificato file diversi da quelli specificati?

Con git log, posso filtrare i commit che vedo su quelli che toccano un dato insieme di percorsi. Quello che voglio è invertire quel filtro in modo che vengano elencati solo i commit che toccano percorsi diversi da quelli specificati.

Posso ottenere quello che voglio con

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

dov'è filter-log.pl:

#!/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";
    }
}

tranne che voglio qualcosa di un po 'più elegante di quello.

Si noti che sto non chiedendo come fare git ignorare i file. Questi file dovrebbero essere tracciati e salvati . È solo che, il più delle volte, non mi interessa vederli.

Domanda correlata: Come invertire `git log --grep = <pattern>` o Come mostrare i log git che non corrispondono a uno schema È la stessa domanda tranne che per i messaggi di commit piuttosto che per i percorsi.

Discussione del forum su questo argomento dal 2008: Re: Esclusione di file da git-diff Sembrava promettente ma il thread sembra essersi prosciugato.


Non sono sicuro che esista un modo integrato e la tua soluzione perl sembra abbastanza decente. Se lo modifichi per accettare i percorsi come argomenti della riga di comando, potresti semplicemente creare un alias simile !f() { git log ... | path/to/filter-log.pl "$@" | git log --stdin --no-walk; f, o anche racchiudere quella parte della pipeline nello script.
Cascabel

Come soluzione alternativa, utilizzo findper filtrare le directory di cui non voglio vedere i commit. Se volessi ignorare le voci di log dai commit effettuati nella directory a livello di root, SiteConfigdirei:git log `find . -type d -mindepth 1 -maxdepth 1 ! -name *SiteConfig`
Noah Sussman

Per Git 1.9 / 2.0 (Q1 2014), vedere la mia risposta di seguito : git log --oneline --format=%s -- . ":!sub"funzionerà (con la magia pathspec :(exclude)e la sua forma abbreviata:! )
VonC

Risposte:


214

È ora implementato (git 1.9 / 2.0, Q1 2014) con l'introduzione pathspec magic :(exclude)e la sua forma abbreviata:! in commit ef79b1f e commit 1649612 , di Nguyễn Thái Ngọc Duy ( pclouds) , la documentazione può essere trovata qui .

Ora puoi registrare tutto tranne il contenuto di una sottocartella:

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

Oppure puoi escludere elementi specifici all'interno di quella sottocartella

  • un file specifico:

      git log -- . ":(exclude)sub/sub/file"
      git log -- . ":!sub/sub/file"
  • qualsiasi file all'interno di sub:

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

Puoi rendere questa esclusione senza distinzione tra maiuscole e minuscole!

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

Come ha notato Kenny Evitt

Se stai eseguendo Git in una shell Bash, usa ':!sub'o ":\!sub"invece per evitare bash: ... event not founderrori


Nota: Git 2.13 (Q2 2017) aggiungerà un sinonimo ^a!

Vedere commit 859b7f1 , commit 42ebeb9 (08 febbraio 2017) di Linus Torvalds ( torvalds) .
(Fuso da Junio ​​C Hamano - gitster- in commit 015fba3 , 27 febbraio 2017)

pathspec magic: aggiungi " ^" come alias per " !"

La scelta di " !" per un pathspec negativo finisce per non solo non corrispondere a ciò che facciamo per le revisioni, ma è anche un carattere orribile per l'espansione della shell poiché necessita di citazioni.

Quindi aggiungi " ^" come alias alternativo per una voce pathspec esclusa.


Si noti che, prima di Git 2.28 (Q3 2020), l'uso di pathspec negativo, durante la raccolta di percorsi inclusi quelli non tracciati nell'albero di lavoro, era interrotto.

Vedi commit f1f061e (05 giugno 2020) di Elijah Newren ( newren) .
(Fusa da Junio ​​C Hamano - gitster- in commit 64efa11 , 18 giugno 2020)

dir: risolto il trattamento dei pathspec negati

Segnalato da: John Millikin
Firmato da: Elijah Newren

do_match_pathspec()ha iniziato la vita come match_pathspec_depth_1()e per la correttezza doveva essere chiamato solo da match_pathspec_depth(). match_pathspec_depth()è stato successivamente rinominato in match_pathspec(), quindi l'invariante che ci aspettiamo oggi è che do_match_pathspec()non ha chiamanti diretti al di fuori di match_pathspec().

Purtroppo, questa intenzione è stato perso con la rinomina delle due funzioni, e ulteriori chiamate a do_match_pathspec()sono stati aggiunti nel commit 75a6315f74 ( " ls-files: aggiungere corrispondenza pathspec per sottomoduli", 2016/10/07, Git V2.11.0-RC0 - merge elencato in batch # 11 ) e 89a1f4aaf7 (" dir: se il nostro pathspec potrebbe corrispondere ai file in una directory, ricorsivi in ​​essa", 2019-09-17, Git v2.24.0-rc0).

Ovviamente, do_match_pathspec()aveva un vantaggio importante match_pathspec(): match_pathspec()codificava i flag su uno dei due valori e questi nuovi chiamanti dovevano passare qualche altro valore per i flag.

Inoltre, sebbene la chiamata do_match_pathspec()diretta non fosse corretta, probabilmente non c'era alcuna differenza nell'output finale osservabile, perché il bug significava semplicemente che si fill_diretory()sarebbe ripresentato in directory non necessarie.

Poiché i successivi controlli di corrispondenza "fa questo percorso" sui singoli percorsi nella directory causerebbero il filtraggio di quei percorsi aggiuntivi, l'unica differenza dall'utilizzo della funzione sbagliata era il calcolo non necessario.

La seconda di quelle chiamate sbagliate a è do_match_pathspec()stata coinvolta - tramite movimento diretto o tramite copia + modifica - in una serie di refactors successivi.

Vedi commit 777b420347 (" dir: synchronize treat_leading_path()and read_directory_recursive()", 2019-12-19, Git v2.25.0-rc0 - merge ), 8d92fb2927 (" dir: sostituisci algoritmo esponenziale con uno lineare", 2020-04-01, Git v2.27.0 -rc0 - unione elencata nel batch # 5 ) e 95c11ecc73 ("Correzione fill_directory()dell'API soggetta a errori ; rendila solo corrispondenze", 2020-04-01, Git v2.27.0-rc0 - unione elencata nel batch # 5 ) .

L'ultimo di questi ha introdotto l'uso di do_match_pathspec()su un singolo file e quindi ha restituito percorsi individuali che non dovrebbero essere.

Il problema con la chiamata do_match_pathspec()invece di match_pathspec()è che qualsiasi modello negato come '`:! Unanted_path' 'verrà ignorato .

Aggiungi una nuova match_pathspec_with_flags()funzione per soddisfare le esigenze di specificare flag speciali mentre stai ancora controllando correttamente i modelli negati, aggiungi un grande commento sopra do_match_pathspec()per impedire ad altri di do_match_pathspec()usarlo in modo improprio e correggi i chiamanti correnti per utilizzare invece match_pathspec()o match_pathspec_with_flags().

Un'ultima nota è che DO_MATCH_LEADING_PATHSPECrichiede una considerazione speciale quando si lavora con DO_MATCH_EXCLUDE.

Il punto DO_MATCH_LEADING_PATHSPECè che se abbiamo un pathspec come

*/Makefile

e stiamo controllando un percorso di directory come

src/module/component

che vogliamo considerarlo una corrispondenza in modo da ricorrere alla directory perché _might_ ha un file denominato Makefileda qualche parte sotto.

Tuttavia, quando stiamo usando un pattern di esclusione, cioè abbiamo un pathspec simile

:(exclude)*/Makefile

NON vogliamo dire che un percorso di directory come

src/module/component

è una corrispondenza (negativa).

Anche se potrebbe esserci un file chiamato 'Makefile' da qualche parte sotto quella directory, potrebbero esserci anche altri file e non possiamo escludere preventivamente tutti i file in quella directory; dobbiamo ricorrere e quindi controllare i singoli file.

Regola la DO_MATCH_LEADING_PATHSPEClogica per essere attivato solo per pathspec positivi.


7
Puoi fare più file?
Justin Thomas

12
@JustinThomas Credo (non ancora testato) che sia possibile ripetere più volte il pattern di esclusione del percorso ":(exclude)pathPattern1" ":(exclude)pathPattern2", ignorando quindi più cartelle / file.
VonC

7
Se stai eseguendo Git in una shell Bash, usa ':!sub'invece per evitare bash: ... event not founderrori . ":\!sub"non funziona.
Kenny Evitt

1
@KennyEvitt Grazie per la tua modifica e commento. Ho incluso quest'ultimo nella risposta per una maggiore visibilità.
VonC

2
Per coloro che si chiedono dove sia la documentazione ufficiale su questa funzionalità, vedere git help glossary(che ho trovato elencato in git help -g[che ho trovato suggerito in git help]).
ravron

4

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

Se stai usando Bash dovresti essere in grado di utilizzare la funzione di globbing esteso per ottenere solo i file di cui hai bisogno:

$ 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 <jdoe@example.org>
Date:   Wed Jun 5 10:58:45 2013 +0200

    First commit

Puoi combinarlo con l' globstarazione ricorsiva.


7
Questo non mostra i commit che interessano file che non esistono più. Trucco molto vicino e carino lo stesso.
Anonymoose

-2

Puoi ignorare temporaneamente le modifiche in un file con:

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

Andando avanti, tutte le modifiche a tali file verranno ignorati da git status, git commit -a, ecc Quando si è pronti a commettere quei file, basta invertire tale tendenza:

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

e impegnati normalmente.


9
Questo sembra affrontare una situazione leggermente diversa. git update-index --skip-worktreenon fa git logfiltrare i commit che sono già stati fatti.
Anonymoose
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.