Wenn Sie 'Git Log' festlegen, werden Änderungen für bestimmte Pfade ignoriert

121

Wie kann ich git lognur Commits anzeigen, bei denen andere als die von mir angegebenen Dateien geändert wurden?

Mit git logkann ich die Commits filtern, die ich für diejenigen sehe, die einen bestimmten Satz von Pfaden berühren. Ich möchte diesen Filter invertieren, damit nur festgeschrieben wird, dass andere als die angegebenen Berührungspfade aufgelistet werden.

Ich kann bekommen, was ich will

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

wo filter-log.plist:

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

außer ich möchte etwas etwas eleganteres als das.

Beachten Sie, dass ich nicht frage, wie Git die Dateien ignorieren soll. Diese Dateien sollten verfolgt und festgeschrieben werden. Es ist nur so, dass ich die meiste Zeit nicht daran interessiert bin, sie zu sehen.

Verwandte Frage: Wie man `git log --grep = <pattern>` invertiert oder wie man git logs anzeigt, die nicht mit einem Muster übereinstimmen Es ist dieselbe Frage, außer für Commit-Nachrichten und nicht für Pfade.

Forumsdiskussion zu diesem Thema aus dem Jahr 2008: Betreff : Ausschließen von Dateien aus git-diff Das sah vielversprechend aus, aber der Thread scheint ausgetrocknet zu sein.

Anonymoose
quelle
Ich bin mir nicht sicher, ob es einen eingebauten Weg gibt, und Ihre Perl-Lösung sieht ziemlich anständig aus. Wenn Sie es so ändern, dass die Pfade als Befehlszeilenargumente akzeptiert werden, können Sie einfach einen Alias ​​erstellen !f() { git log ... | path/to/filter-log.pl "$@" | git log --stdin --no-walk; foder sogar diesen Pipeline-Teil in das Skript einbinden.
Cascabel
Um dieses Problem zu umgehen, filtere ich findVerzeichnisse heraus, deren Commits ich nicht sehen möchte. Wenn ich Protokolleinträge von Commits, die in das Stammverzeichnis eingegeben wurden, ignorieren wollte, SiteConfigwürde ich sagen:git log `find . -type d -mindepth 1 -maxdepth 1 ! -name *SiteConfig`
Noah Sussman
Für Git 1.9 / 2.0 (Q1 2014) siehe meine Antwort unten : git log --oneline --format=%s -- . ":!sub"wird funktionieren (mit der Pathspec-Magie :(exclude)und ihrer Kurzform:! )
VonC

Antworten:

214

Es ist jetzt implementiert (git 1.9 / 2.0, Q1 2014) mit der Einführung pathspec magic :(exclude)und seiner Kurzform:! in commit ef79b1f und commit 1649612 von Nguyễn Thái Ngọc Duy ( pclouds) . Die Dokumentation finden Sie hier .

Sie können jetzt alles außer dem Inhalt eines Unterordners protokollieren:

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

Oder Sie können bestimmte Elemente in diesem Unterordner ausschließen

  • eine bestimmte Datei:

      git log -- . ":(exclude)sub/sub/file"
      git log -- . ":!sub/sub/file"
  • jede gegebene Datei innerhalb sub:

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

Sie können diesen Ausschlussfall unempfindlich machen!

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

Wie Kenny Evitt bemerkte

Wenn Sie Git in einer Bash-Shell ausführen, verwenden Sie stattdessen ':!sub'oder ":\!sub", um Fehler zu vermeidenbash: ... event not found


Hinweis: Git 2,13 (Q2 2017) ein Synonym hinzufügen wird ^zu!

Siehe Commit 859b7f1 , Commit 42ebeb9 (08. Februar 2017) von Linus Torvalds ( torvalds) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 015fba3 , 27. Februar 2017)

pathspec magic: füge ' ^' als Alias ​​für ' !' hinzu

Die Wahl von ' !' für eine negative Pfadspezifikation entspricht nicht nur nicht dem, was wir für Revisionen tun, sondern ist auch ein schrecklicher Charakter für die Shell-Erweiterung, da sie zitiert werden muss.

^Fügen Sie daher ' ' als alternativen Alias ​​für einen ausschließenden Pfadspezifikationseintrag hinzu.


Beachten Sie, dass vor Git 2.28 (Q3 2020) die Verwendung einer negativen Pfadspezifikation beim Sammeln von Pfaden, einschließlich nicht verfolgter Pfade im Arbeitsbaum, unterbrochen wurde.

Siehe Commit f1f061e (05. Juni 2020) von Elijah Newren ( newren) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 64efa11 , 18. Juni 2020)

dir: Fix Behandlung von negierten Pathspecs

Berichtet von: John Millikin
Unterzeichnet von: Elijah Newren

do_match_pathspec()begann das Leben als match_pathspec_depth_1()und für die Richtigkeit sollte nur von gerufen werden match_pathspec_depth(). match_pathspec_depth()wurde später in umbenannt match_pathspec(), daher ist die Invariante, die wir heute erwarten, dass do_match_pathspec()es keine direkten Anrufer außerhalb von gibt match_pathspec().

Leider ging diese Absicht mit der Umbenennung der beiden Funktionen verloren, und zusätzliche Aufrufe do_match_pathspec()wurden in den Commits 75a6315f74 hinzugefügt (" ls-files: Pfadspezifikationsübereinstimmung für Submodule hinzufügen", 2016-10-07, Git v2.11.0-rc0 - Zusammenführung in aufgeführt Batch # 11 ) und 89a1f4aaf7 (" dir: Wenn unsere Pfadspezifikation möglicherweise mit Dateien unter einem Verzeichnis übereinstimmt, greifen Sie darauf zurück", 2019-09-17, Git v2.24.0-rc0).

do_match_pathspec()Hatte natürlich einen wichtigen Vorteil gegenüber match_pathspec()- match_pathspec()würde Flags auf einen von zwei Werten fest codieren , und diese neuen Anrufer mussten einen anderen Wert für Flags übergeben.

Auch wenn der do_match_pathspec()direkte Aufruf falsch war, gab es wahrscheinlich keinen Unterschied in der beobachtbaren fill_diretory()Endausgabe , da der Fehler nur bedeutete, dass er in nicht benötigte Verzeichnisse zurückkehren würde.

Da nachfolgende Überprüfungen der Pfadübereinstimmung einzelner Pfade unter dem Verzeichnis dazu führen würden, dass diese zusätzlichen Pfade herausgefiltert werden, bestand der einzige Unterschied zur Verwendung der falschen Funktion in einer unnötigen Berechnung.

Der zweite dieser schlechten Anrufe do_match_pathspec()betraf - entweder durch direkte Bewegung oder durch Kopieren + Bearbeiten - eine Reihe späterer Refaktoren.

Siehe Commits 777b420347 (" dir: synchronize treat_leading_path()and read_directory_recursive()", 2019-12-19, Git v2.25.0-rc0 - merge ), 8d92fb2927 (" dir: Exponentialalgorithmus durch einen linearen ersetzen", 2020-04-01, Git v2.27.0 -rc0 - Zusammenführung in Stapel 5 aufgelistet ) und 95c11ecc73 ("Fehleranfällige fill_directory()API korrigieren ; nur Übereinstimmungen zurückgeben", 2020-04-01, Git v2.27.0-rc0 - Zusammenführung in Stapel 5 aufgeführt ) .

Die letzte davon führte die Verwendung von do_match_pathspec()für eine einzelne Datei ein und führte somit dazu, dass einzelne Pfade zurückgegeben wurden, die nicht sein sollten.

Das Problem beim Aufrufen do_match_pathspec()statt match_pathspec()ist, dass alle negierten Muster wie '`:! Unerwünschter_Pfad`` ignoriert werden .

Fügen Sie eine neue match_pathspec_with_flags()Funktion hinzu, um die Anforderungen der Angabe spezieller Flags zu erfüllen, während negierte Muster weiterhin korrekt überprüft werden. Fügen Sie oben einen großen Kommentar hinzu do_match_pathspec(), um zu verhindern, dass andere sie missbrauchen, und korrigieren Sie aktuelle Anrufer, do_match_pathspec()um stattdessen entweder match_pathspec()oder zu verwenden match_pathspec_with_flags().

Ein letzter Hinweis ist, dass DO_MATCH_LEADING_PATHSPECbei der Arbeit mit besonders berücksichtigt werden muss DO_MATCH_EXCLUDE.

Der Punkt DO_MATCH_LEADING_PATHSPECist, wenn wir eine Pfadspezifikation wie haben

*/Makefile

und wir überprüfen einen Verzeichnispfad wie

src/module/component

dass wir es als Übereinstimmung betrachten wollen, damit wir in das Verzeichnis zurückkehren, weil es _might_ eine Datei hat, die Makefileirgendwo unten benannt ist.

Wenn wir jedoch ein Ausschlussmuster verwenden, dh wir haben eine Pfadspezifikation wie

:(exclude)*/Makefile

Wir wollen NICHT sagen, dass ein Verzeichnispfad wie

src/module/component

ist eine (negative) Übereinstimmung.

Während sich unter diesem Verzeichnis möglicherweise eine Datei mit dem Namen "Makefile" befindet, können sich auch andere Dateien befinden, und wir können nicht alle Dateien in diesem Verzeichnis vorbeugend ausschließen. Wir müssen einzelne Dateien rekursieren und dann überprüfen.

Passen Sie die DO_MATCH_LEADING_PATHSPECLogik so an, dass sie nur für positive Pfadspezifikationen aktiviert wird.

VonC
quelle
7
Können Sie mehrere Dateien erstellen?
Justin Thomas
12
@JustinThomas Ich glaube (noch nicht getestet), dass Sie dieses Pfadausschlussmuster mehrmals wiederholen können, wodurch ":(exclude)pathPattern1" ":(exclude)pathPattern2"mehrere Ordner / Dateien ignoriert werden.
VonC
7
Wenn Sie Git in einer Bash-Shell ausführen, verwenden Sie ':!sub'stattdessen, um bash: ... event not foundFehler zu vermeiden . ":\!sub"funktioniert nicht
Kenny Evitt
1
@KennyEvitt Vielen Dank für Ihre Bearbeitung und Kommentar. Letzteres habe ich für mehr Sichtbarkeit in die Antwort aufgenommen.
VonC
2
Für diejenigen, die sich fragen, wo sich die offizielle Dokumentation zu dieser Funktionalität befindet, siehe git help glossary(die ich in git help -g[die ich in vorgeschlagen gefunden habe git help] aufgelistet gefunden habe ).
Ravron
4

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

Wenn Sie Bash verwenden , sollten Sie die erweiterte Globbing- Funktion verwenden können, um nur die Dateien abzurufen , die Sie benötigen:

$ 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

Sie können dies mit globstarfür rekursive Aktionen kombinieren .

l0b0
quelle
7
Dies zeigt keine Commits an, die sich auf nicht mehr vorhandene Dateien auswirken. Sehr nah und trotzdem netter Hack.
Anonymoose
-2

Sie können die Änderungen in einer Datei vorübergehend ignorieren mit:

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

In Zukunft werden alle Änderungen an diesen Dateien durchgeführt werden , ignoriert werden git status, git commit -ausw. Wenn Sie bereit sind , diese Dateien zu begehen, ist es gerade umgekehrt:

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

und wie gewohnt festschreiben.

rubysolo
quelle
9
Dies scheint eine etwas andere Situation anzugehen. git update-index --skip-worktreebewirkt nicht git log, dass bereits vorgenommene Commits gefiltert werden.
Anonymoose