Как перечислить историю изменений дерева каталогов в git

Git не отслеживает каталоги как таковые. Он отслеживает только файлы, которые живут в каком-то каталоге. (См. Как добавить пустой каталог в репозиторий Git?)

Однако, если у меня есть определенная история коммитов, я неявно также имею историю изменений в дереве каталогов.

Итак, как мне ответить на такие вопросы, как:

  • когда был создан каталог foo/bar (в Git терминология: когда был первый файл, созданный в этом каталоге). Если foo/bar уже был удалил некоторое время в истории и воссоздал позже.
  • при удалении каталога foo/bar (в терминологии Git: когда последний файл был удален из этого каталога). Как и выше, может быть более одного квалифицирующего коммита.
  • каковы подкаталоги foo/bar, существовавшие в любой момент времени в истории

Ближайший я могу найти в псевдокоде:

loop over all commits (git rev-list --all)
  start from repo root directory
  do recursively on the directory tree rebuilt so far
    call git ls-tree and grep the tree lines
    rebuild next level of directory tree
  end
end

Очевидно, это может быть написано на вашем любимом языке сценариев.

Затем у меня есть все деревья каталогов, и мне по-прежнему нужно искать их разумным способом, чтобы отвечать на вопросы типа 1 - 3. Опять же, выполнимо, но, вероятно, не через пару минут.

Вопросы:

  • Есть ли более простой способ?
  • Если нет: подходят ли скрипты уже в сети? (мой поисковик не выявил, но я тоже не нашел идеальные слова поиска)

Ответ 1

Для вопросов 1 и 2 это довольно просто:

  • когда был создан каталог foo/bar?
    git log --oneline -- foo/bar | tail -n 1

  • когда была удалена директория foo/bar?
    git log --oneline -- foo/bar | head -n 1

Однако, третья часть немного сложна, и я не могу полностью ответить на нее.

Две приведенные выше команды дают вам $FIRST_REV (созданный) и $LAST_REV (удалены).

Следующий фрагмент дает вам все коммиты, где было изменено дерево:

for rev in $(git rev-list FIRST_REV..LAST_REV)
do
  git ls-tree -r -t $rev | grep tree | cut -f 2
done

Затем у вас есть список каталогов, которые присутствовали. Но есть еще дубликаты. Передайте этот список в sort -u, и вы закончите:

#!/bin/sh
for r in $(git rev-list FIRST_REV..LAST_REV) 
do 
    git ls-tree -r -t $r | grep tree | cut -f 2
done | sort -u

Однако вы теряете информацию о коммитах, в которых были затронуты эти каталоги. Это недостаток.

И это предполагает, что foo/bar был создан только один раз и больше не присутствует.

Ответ 2

gitk foo/bar предоставляет пользовательский интерфейс для просмотра истории git, ограниченной фиксацией, которая коснулась foo/bar.