Створення 'git log' ігнорує зміни для певних шляхів


121

Як я можу змусити git logпоказувати лише коміти, які змінили файли, окрім тих, які я вказав?

Завдяки цьому git logя можу фільтрувати комітети, які я бачу, до тих, які торкаються заданого набору шляхів. Я хочу, щоб перевернути цей фільтр, щоб у списку відображалися лише ті дотики, які не вказані.

Я можу отримати те, що хочу

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

де 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";
    }
}

окрім того, що я хочу чогось дещо більш елегантного.

Зауважте, що я не запитую, як змусити git ігнорувати файли. Ці файли слід відстежувати та виконувати. Просто це, більшість часу, мені не цікаво бачити їх.

Пов'язане запитання: Як інвертувати `git log --grep = <pattern>` або Як показати журнали git, які не відповідають шаблону Це те саме питання, за винятком повідомлень про фіксацію, а не для шляхів.

Дискусія на форумі з цього приводу з 2008 року: Re: Виключення файлів із git-diff Це виглядало багатообіцяюче, але нитка, схоже, пересохла.


Я не впевнений, чи є вбудований спосіб, і ваше рішення Perl виглядає досить пристойно. Якщо ви модифікуєте його для прийняття контурів як аргументів командного рядка, ви можете просто створити псевдонім на кшталт !f() { git log ... | path/to/filter-log.pl "$@" | git log --stdin --no-walk; fабо навіть перегорнути цю частину конвеєра в сценарій.
Каскабель

Як вирішення, я використовую findдля фільтрації каталогів, чиї комісії я не хочу бачити. Якби я хотів ігнорувати записи журналу з комітетів, внесених до каталогу кореневого рівня, SiteConfigя б сказав:git log `find . -type d -mindepth 1 -maxdepth 1 ! -name *SiteConfig`
Ной Суссман

Для Git 1.9 / 2.0 (1 квартал 2014 року) дивіться мою відповідь нижче : git log --oneline --format=%s -- . ":!sub"буде працювати (з магією:(exclude):!
pathspec

Відповіді:


214

Він реалізований зараз (git 1.9 / 2.0, Q1 2014) із магією:(exclude):! вступу pathspec магією та її короткою формою у фіксації ef79b1f та відданні 1649612 , автор Nguyễn Thái Ngọc Duy ( pclouds) , тут можна знайти документацію .

Тепер ви можете записати все, крім вмісту підпапки:

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

Або ви можете виключити конкретні елементи з цієї підпапки

  • конкретний файл:

      git log -- . ":(exclude)sub/sub/file"
      git log -- . ":!sub/sub/file"
  • будь-який файл у межах sub:

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

Ви можете зробити цей випадок відчуження нечутливим!

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

Як зазначив Кенні Евітт

Якщо ви працюєте з Git в оболонці Bash, використовуйте ':!sub'або ":\!sub"замість цього уникайте bash: ... event not foundпомилок


Примітка: Git 2.13 (Q2 2017) додасть синонім ^до!

Див. Комісію 859b7f1 , фіксувати 42ebeb9 (08 лютого 2017 р.) Від Лінуса Торвальда ( torvalds) .
(Об'єднав Хуніо С Хамано - gitster- у комітеті 015fba3 , 27 лютого 2017 р.)

pathspec magic: додайте " ^" як псевдонім для " !"

Вибір " !" для негативного проспекту шляху не тільки не відповідає тому, що ми робимо для редагувань, це також жахливий характер для розширення оболонки, оскільки воно потребує цитування.

Тож додайте "^ " як альтернативний псевдонім для виключення запису pathspec.


Зауважте, що до Git 2.28 (Q3 2020) використання негативного шляху проспекту під час збору шляхів, включаючи необроблені в робочому дереві, було порушено.

Див. Комісію f1f061e (05 червня 2020 р.) Від Іллі Ньюрена ( newren) .
(Об'єднав Хуніо С Хамано - gitster- у комітеті 64efa11 , 18 червня 2020 р.)

dir: виправити обробку заперечуваних перспектив

Повідомляв: Джон Міллікін
Підписався: Ілля Ньюрен

do_match_pathspec()почалося життя так, як match_pathspec_depth_1()і для правильності потрібно було лише покликатись match_pathspec_depth(). match_pathspec_depth()Пізніше було перейменовано на match_pathspec(), тому інваріант, якого ми очікуємо сьогодні, - це те, що do_match_pathspec()він не має прямих абонентів за межами match_pathspec().

На жаль, цей намір було втрачено із перейменовуванням двох функцій, і додаткові виклики do_match_pathspec()були додані до комітетів 75a6315f74 (" ls-files: додавання узгодження шляху для підмодулів", 2016-10-07, Git v2.11.0-rc0 - злиття, зазначене в batch # 11 ) та 89a1f4aaf7 (" dir: якщо наш pathspec може збігатися з файлами під dir, повторити його", 2019-09-17, Git v2.24.0-rc0).

Звичайно, do_match_pathspec()був важливий прогрес над match_pathspec()- match_pathspec()прапори жорсткого коду до одного з двох значень, і цим новим абонентам потрібно було передавати якесь інше значення для прапорів.

Крім того, хоча виклик do_match_pathspec()безпосередньо був невірним, ймовірно, не було різниці у видимому кінцевому виході, оскільки помилка просто означала, що fill_diretory()повториться в непотрібні каталоги.

Оскільки наступні перевірки відповідності цього шляху в окремих шляхах під каталогом призвели до того, що ці додаткові шляхи будуть відфільтровані, єдиною відмінністю від використання неправильної функції були непотрібні обчислення.

Другий із тих поганих дзвінків до do_match_pathspec()участі - або прямим рухом, або копіюванням + редагуванням - у низку пізніших рефакторів.

Див. Коміти 777b420347 (" dir: синхронізувати treat_leading_path()та read_directory_recursive()", 2019-12-19, Git v2.25.0-rc0 - об'єднати ), 8d92fb2927 (" dir: замінити експоненціальний алгоритм на лінійний", 2020-04-01, Git v2.27.0 -rc0 - злиття, вказане в партії №5 ), і 95c11ecc73 ("Виправити fill_directory()API, схильний до помилок ; зробити це лише поверненнями", 2020-04-01, Git v2.27.0-rc0 - злиття, вказане в партії №5 ) .

Останній із них запровадив використання do_match_pathspec()окремого файлу, і таким чином було повернуто окремі шляхи, яких не повинно бути.

Проблема з викликом do_match_pathspec()замість того match_pathspec(), що будь-які заперечувані шаблони, такі як ``:! Нежеланий_путь``, будуть ігноровані .

Додайте нову match_pathspec_with_flags()функцію для задоволення потреб у введенні спеціальних прапорів, при цьому все ще правильно перевіряючи заперечні візерунки, додайте великий коментар вище, do_match_pathspec()щоб запобігти іншим неправомірному використанню, а також виправте поточних абонентів, do_match_pathspec()щоб замість цього використовувати match_pathspec()або match_pathspec_with_flags().

Одне заключне зауваження - це те, що DO_MATCH_LEADING_PATHSPECпотребує особливої ​​уваги при роботі DO_MATCH_EXCLUDE.

Сенс у DO_MATCH_LEADING_PATHSPECтому, що якщо ми маємо подібну перспективу

*/Makefile

і ми перевіряємо шлях до каталогу

src/module/component

що ми хочемо вважати його збігом, щоб ми рекурсували в каталог, оскільки в ньому _mightє файл, названий Makefileдесь нижче.

Однак, коли ми використовуємо схему виключення, тобто ми маємо схожу перспективу

:(exclude)*/Makefile

ми НЕ хочемо сказати, що такий шлях до каталогу

src/module/component

- це (негативна) відповідність.

Хоча десь під цим каталогом може бути файл з назвою 'Makefile', також можуть бути інші файли, і ми не можемо попередньо виключити всі файли в цьому каталозі; нам потрібно повторити і перевірити окремі файли.

Відрегулюйте DO_MATCH_LEADING_PATHSPECлогіку, щоб активуватися лише для позитивних перспектив.


7
Чи можете ви зробити кілька файлів?
Джастін Томас

12
@JustinThomas Я вважаю (ще не перевірено), що ви можете повторити цю схему виключення шляху кілька разів ":(exclude)pathPattern1" ":(exclude)pathPattern2", отже, ігноруючи кілька папок / файлів.
VonC

7
Якщо ви використовуєте Git в оболонці Bash, використовуйте ':!sub'натомість, щоб уникнути bash: ... event not foundпомилок . ":\!sub"не працює.
Кенні Евітт

1
@KennyEvitt Дякуємо за вашу редакцію та коментар. Останній я включив у відповідь для більшої наочності.
VonC

2
Для тих, хто цікавиться, де знаходиться офіційна документація щодо цієї функціональності, див. git help glossary(Що я знайшов у списку git help -g[що я знайшов, запропонований в git help]).
ravron

4

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

Якщо ви використовуєте Bash, ви повинні мати можливість використовувати розширену функцію глобалізації, щоб отримати лише потрібні файли:

$ 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

Ви можете комбінувати це з globstarрекурсивними діями.


7
Це не відображає комітетів, що впливають на файли, які більше не існують. Дуже близько і приємно зламати все-таки.
Anonymousmoose

-2

Ви можете тимчасово проігнорувати зміни у файлі за допомогою:

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

Вперед, всі зміни цих файлів буде проігноровано git status, git commit -aі т.д.

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

і виконувати як звичайно.


9
Здається, це вирішує дещо іншу ситуацію. git update-index --skip-worktreeне викликає git logфільтрування комітетів, які вже були зроблені.
Anonymousmoose
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.