История проекта: git log, диапазоны и поиск

Рейтинг: 55.2% · 12 голосов
Самый подробный курс по Git - от первого коммита до объектной базы и packfiles. Терминал и первый репозиторий, ветки и слияние, конфликты, удалённая работа и форджи (GitHub, GitLab), rebase и переписывание истории, спасение кода через reflog и fsck, stash и worktree, bisect и blame, подмодули и монорепо, хуки и подпись коммитов, внутреннее устройство и масштаб. Для новичков и профи. Актуально на 2026 (Git 2.54, дорожная карта 3.0).
Ответить
Аватара пользователя
Pavel_Git
Сообщения: 52
Зарегистрирован: 11 май 2026, 05:31

История проекта: git log, диапазоны и поиск

Сообщение Pavel_Git »

Оглавление курса (52)
  1. Что такое контроль версий и почему победил Git
  2. Установка Git на Windows, macOS и Linux и первая настройка
  3. Терминал и как спрашивать у Git: выживание в командной строке
  4. Ментальная модель Git: три состояния, коммит-снимок и граф
  5. Первый репозиторий: init, add, commit, status
  6. Просмотр изменений: status, diff и индексация по частям
  7. История проекта: git log, диапазоны и поиск (вы здесь)
  8. .gitignore: что не должно попасть в репозиторий
  9. .gitattributes: окончания строк, бинарники и атрибуты
  10. Ветки как указатели: branch, switch и detached HEAD
  11. Слияние веток: fast-forward против трёхстороннего merge
  12. Конфликты слияния: анатомия и уверенное разрешение
  13. Практикум: твой первый день с Git от нуля до push
  14. Удалённые репозитории: remote, fetch, базовый pull и refspec
  15. git push: безопасная отправка и защита от перезаписи
  16. Клонирование: shallow, partial clone, bundle и backfill
  17. GitHub, GitLab и форки: pull request, rulesets и защита веток
  18. Рабочие процессы команды: GitFlow, GitHub Flow, trunk-based
  19. Merge queue и merge trains: безопасное вливание в trunk
  20. Культура code review и Conventional Commits
  21. git reset: три дерева и отмена индексации и коммитов
  22. git revert: безопасная отмена в общей истории
  23. Правка последнего коммита: git commit --amend
  24. git rebase: пересадка истории и золотое правило
  25. Интерактивный rebase: переписать серию коммитов
  26. git cherry-pick: перенос отдельных коммитов
  27. Stacked diffs: цепочка зависимых PR
  28. Массовое переписывание истории: git filter-repo и git replace
  29. git reflog: журнал, который помнит всё
  30. Восстановление утерянных коммитов, веток и файлов
  31. Откат опасных операций: reset --hard, сломанный rebase, плохой merge
  32. git stash: тайник для незавершённой работы
  33. git worktree: несколько рабочих деревьев одного репозитория
  34. git bisect: бинарный поиск регрессии
  35. git blame, mailmap и археология кода
  36. Теги, релизы, archive и bundle
  37. Подмодули: внешние репозитории внутри проекта
  38. Subtree, монорепозитории и sparse-checkout
  39. Git LFS и большие файлы
  40. Вспомогательные команды: clean, mv, rm, restore
  41. Псевдонимы и кастомизация .gitconfig
  42. Хуки Git: автоматизация на клиенте и сервере
  43. Подпись коммитов и тегов: SSH, GPG и Sigstore
  44. Внутреннее устройство I: объектная база Git
  45. Внутреннее устройство II: ссылки, HEAD, notes и reftable
  46. Внутреннее устройство III: индекс, packfiles, gc и обслуживание
  47. От SHA-1 к SHA-256: переход на новый хеш
  48. Огромные репозитории: partial clone, FSMonitor, Scalar
  49. Git в CI/CD: shallow, кеш, merge_group и безопасность
  50. Куда движется Git: дорожная карта 3.0 и экосистема
  51. Профессиональный setup: собираем рабочее окружение Git
  52. 30+ типичных ошибок Git и как из них выбираться
Представь: тебя ставят дежурным по продакшену, и в три ночи падает оплата. Тебе не нужен весь проект - тебе нужен один вопрос: "когда и кто тронул функцию calculatePrice, и в каком коммите она перестала работать". Без умения читать историю ты будешь листать diff'ы глазами час. С умением - найдёшь виновный коммит за тридцать секунд одной командой. Этот урок про то, как git превращает свалку коммитов в инструмент расследования. Мы разберём, как посмотреть историю git по-человечески, как фильтровать её по автору, дате и тексту, и - самое мощное и недооценённое - как искать по содержимому изменений через pickaxe и понимать диапазоны коммитов. Ключевые фразы тут не для красоты: git log, git история коммитов, git log graph, git grep, git shortlog - это твой ежедневный инструментарий.

git log: что это на самом деле и как читать вывод

git log - это не "показать список коммитов". Это обход графа коммитов от заданной точки (по умолчанию от HEAD) вглубь по родительским связям. Напомню легенду из прошлых уроков: узел - это коммит, стрелка идёт от коммита к его родителю, ярлык - это ветка, HEAD или тег. git log стартует с HEAD и идёт по стрелкам назад, печатая каждый встреченный узел.

Запусти голый и получишь что-то вроде:

Код: Выделить всё

commit 9f3a1c7e2b4d8a01f6c3e9b7a25d4f08c1e6b3a9 (HEAD -> main, origin/main)
Author: Marina Volkova <marina@example.com>
Date:   Wed Jun 10 14:22:03 2026 +0300

    fix: округление цены при скидке больше 50 процентов

commit 2c8b4f19a7e03d6155b9c0e4f72a18d3e6b09c5a
Author: Oleg Petrov <oleg@example.com>
Date:   Tue Jun 9 18:05:41 2026 +0300

    refactor: вынес расчёт скидки в отдельный метод
Разберём построчно. Первая строка - полный SHA коммита плюс в скобках decoration: что на этот коммит указывает. Тут

Код: Выделить всё

HEAD -> main
значит "HEAD сейчас на ветке main, и main указывает на этот коммит", а

Код: Выделить всё

origin/main
- что удалённая ветка тоже здесь. Эти ярлыки - не часть коммита, это просто указатели из .git/refs, которые git дорисовывает поверх. Author - кто написал изменения, Date - когда. Запомни: git хранит две даты и два человека на коммит - author (написал код) и committer (применил коммит). При rebase author остаётся, а committer и его дата меняются. По умолчанию log показывает author.

Голый log многословен. На практике почти всегда хочется компактно. Вот рабочий набор:

Код: Выделить всё

git log --oneline
git log --oneline --graph --all --decorate
git log --stat
git log -p
--oneline сжимает каждый коммит в строку "короткий SHA + первая строка сообщения". Это и есть тот самый git log oneline, который ты будешь набирать сто раз в день:

Код: Выделить всё

9f3a1c7 fix: округление цены при скидке больше 50 процентов
2c8b4f1 refactor: вынес расчёт скидки в отдельный метод
a01d9e3 feat: добавлен расчёт цены с учётом валюты
--stat добавляет к каждому коммиту сводку "какие файлы и насколько изменились" (плюсы и минусы по строкам), а -p (patch) показывает полный diff каждого коммита. -p - это когда ты хочешь не "что менялось", а "покажи мне ровно эти строки".

Изображение

git log graph: рисуем дерево веток в терминале

Линейный список врёт о форме истории - он не показывает ветвления и слияния. Чтобы увидеть настоящую топологию, нужен git log graph. Магическая тройка:

Код: Выделить всё

git log --oneline --graph --all --decorate
  • --graph рисует слева ASCII-граф связей коммит-родитель.
  • --all стартует обход не только от HEAD, а от всех ссылок (всех веток и тегов). Без --all ты увидишь только то, что достижимо из текущей ветки.
  • --decorate печатает ярлыки веток/тегов у коммитов (в современном git он включён по умолчанию через log.decorate=auto, но писать явно не вредно).
Вывод выглядит так:

Код: Выделить всё

*   7d4e9a1 (HEAD -> main) Merge branch feature-currency
|\
| * a01d9e3 (feature-currency) feat: расчёт цены с учётом валюты
| * 88c2f0b feat: таблица курсов валют
* | 9f3a1c7 fix: округление цены при скидке
|/
* 2c8b4f1 refactor: вынес расчёт скидки
Читается так: звёздочка - это коммит на текущей "дорожке", вертикальные палки - продолжение веток во времени (вниз = в прошлое). Коммит 7d4e9a1 - это merge: у него две стрелки вверх (две родительские линии расходятся через ). Левая линия - main с фиксом округления, правая - feature-currency с двумя коммитами. Внизу - точка, где обе ветки расходились от общего предка 2c8b4f1. Вот так глазами видно "разработка шла в двух ветках и слилась". Это и есть посмотреть историю git как граф, а не как плоский журнал.

--pretty=format: лепим свой формат вывода

Когда нужно вытащить из истории конкретные поля (для скрипта, для отчёта, для красивого алиаса), используют --pretty=format: с плейсхолдерами. Каждый плейсхолдер - это поле коммита:

Код: Выделить всё

git log --pretty=format:"%h %ad | %an: %s" --date=short
Расшифровка плейсхолдеров: %h - короткий хеш, %H - полный, %an - имя автора, %ae - его почта, %ad - дата автора (формат задаётся через --date=), %s - тема (первая строка сообщения), %d - decoration (ярлыки), %ci - дата коммиттера в ISO. Результат:

Код: Выделить всё

9f3a1c7 2026-06-10 | Marina Volkova: fix: округление цены при скидке
2c8b4f1 2026-06-09 | Oleg Petrov: refactor: вынес расчёт скидки
Отдельно стоят цветовые плейсхолдеры %C(...) - они раскрашивают вывод. Именно из них собирают тот самый "красивый lg", про который дальше. Полную таблицу плейсхолдеров держи под рукой в

Код: Выделить всё

git help log
, секция PRETTY FORMATS - их там несколько десятков.

Фильтры: ищем нужное в git история коммитов

История большого проекта - тысячи коммитов. Листать руками бессмысленно, надо фильтровать. Базовые фильтры комбинируются (логическое И):

Код: Выделить всё

git log --author="Marina"
git log --since="2 weeks ago" --until="2026-06-13"
git log --grep="округлени"
--author фильтрует по автору (подстрока по имени или почте, понимает регулярки). --since и --until - по дате, причём понимают человеческие выражения: "2 weeks ago", "yesterday", "2026-01-01". --grep ищет в тексте сообщения коммита. Хочешь найти все упоминания тикета?

Код: Выделить всё

git log --grep="JIRA-451"
. По умолчанию --grep чувствителен к регистру, добавь для игнора. Несколько --grep по умолчанию объединяются через ИЛИ; добавь

Код: Выделить всё

--all-match
, чтобы требовать совпадение всех паттернов сразу.

Важный нюанс: все эти фильтры работают по метаданным и сообщению, но не по содержимому изменений. "Найди коммит, который упомянул округление в сообщении" - это --grep. А вот "найди коммит, который реально изменил код, где есть слово round" - это уже другая, гораздо более мощная история.

Pickaxe: поиск по содержимому изменений (-S и -G) - оружие профи

Это та самая фича, которая отличает человека, умеющего git, от человека, который просто коммитит. Вопрос дежурного из вступления: "в каком коммите функция calculatePrice появилась или исчезла". Сообщения коммитов могут врать или молчать. Но diff не врёт. Pickaxe ищет по дельте изменений.

-S<строка> (pickaxe) показывает коммиты, в которых изменилось количество вхождений этой строки. То есть строку добавили или удалили. Это идеально для вопроса "когда эта функция/константа/конфиг появилась и когда пропала":

Код: Выделить всё

git log -S"calculatePrice" --oneline

Код: Выделить всё

9f3a1c7 fix: округление цены при скидке
a01d9e3 feat: добавлен расчёт цены с учётом валюты
Два коммита: в одном calculatePrice появился, в другом - количество вхождений снова изменилось (например, добавили второй вызов). Коммиты, которые трогали файл рядом, но не меняли число вхождений строки, в выдачу не попадут - в этом вся прелесть, шум отсекается.

-G<regex> - брат pickaxe, но другой логики: он показывает коммиты, в diff которых любая изменённая строка совпадает с регуляркой. Разница тонкая, но критичная. Пример: строку с calculatePrice переформатировали (отступ поправили), число вхождений не изменилось - -S это пропустит, а -G покажет, потому что строка фигурировала в diff. Эмпирика: -S отвечает на "когда сущность появилась/исчезла", -G отвечает на "когда этой строки касались вообще". Добавь --pickaxe-regex к -S, чтобы трактовать аргумент как регулярку. А -p вместе с pickaxe сразу покажет конкретные хунки:

Код: Выделить всё

git log -S"DEBUG_MODE" -p
Реальный сценарий: кто-то закоммитил

Код: Выделить всё

DEBUG_MODE = True
в прод и забыл убрать.

Код: Выделить всё

git log -S"DEBUG_MODE" -p
выдаст тебе цепочку всех касаний этой константы с diff'ами - найдёшь виновника за секунды.

Диапазоны коммитов: A..B против A...B

Это место, где спотыкаются даже опытные. Диапазон в git - это не "от строки X до строки Y в списке". Это операция над множествами достижимых коммитов.

A..B (две точки) - "коммиты, достижимые из B, но НЕ достижимые из A". По-человечески: "что есть в B, чего нет в A". Самый частый вопрос на свете: "что я наделал в своей ветке, чего ещё нет в main":

Код: Выделить всё

git log --oneline main..feature-currency

Код: Выделить всё

a01d9e3 feat: расчёт цены с учётом валюты
88c2f0b feat: таблица курсов валют
Видишь только свои два коммита - ровно то, что уйдёт в PR. Зеркально

Код: Выделить всё

git log feature-currency..main
покажет, что появилось в main, пока ты работал в ветке (полезно понять, нужен ли rebase). Кстати,

Код: Выделить всё

git log origin/main..HEAD
- это "что я ещё не запушил".

A...B (три точки) - симметрическая разность: "коммиты, которые есть либо в A, либо в B, но НЕ в обоих". То есть всё, что развело две ветки с момента их общего предка, с обеих сторон. Часто комбинируют с --left-right, который помечает каждый коммит стрелкой - из какой он стороны:

Код: Выделить всё

git log --oneline --left-right main...feature-currency

Код: Выделить всё

< 9f3a1c7 fix: округление цены при скидке
> a01d9e3 feat: расчёт цены с учётом валюты
> 88c2f0b feat: таблица курсов валют
- коммит со стороны main (левый аргумент), - со стороны feature (правый). Это лучший способ за один взгляд понять, как именно разошлись две ветки. Запомни мнемонику: две точки - "разница в одну сторону", три точки - "разошлись в обе".

История по merge-воркфлоу: --first-parent, --merges, --no-merges

В команде с pull request'ами история main часто выглядит как лапша из мелких коммитов фич, влитых через merge. Тебе на релизе не нужны эти 300 промежуточных коммитов - тебе нужны "крупные мазки": какие фичи влились в main. Для этого есть --first-parent.

У merge-коммита два родителя: первый - это состояние ветки, в которую вливали (main), второй - то, что вливали (feature). --first-parent идёт по истории, всегда выбирая только первого родителя, игнорируя содержимое влитых веток:

Код: Выделить всё

git log --oneline --first-parent main

Код: Выделить всё

7d4e9a1 Merge PR #214: расчёт цены с учётом валюты
9f3a1c7 fix: округление цены при скидке
6b1a0d8 Merge PR #209: новая система скидок
Видишь только merge-коммиты и прямые коммиты в main - чистая лента релизов вместо лапши. Это лучший друг тимлида при подготовке к выпуску. Парные фильтры: --merges оставит только merge-коммиты (у которых больше одного родителя), --no-merges - наоборот, выкинет все слияния и покажет только "содержательные" коммиты.

Код: Выделить всё

git log --no-merges --author="Oleg"
- "сколько реального кода написал Олег, без шума слияний".

git shortlog: релиз-ноуты в одну команду

Когда нужно собрать "кто что сделал в релизе", руками никто не пишет - есть git shortlog. Он группирует коммиты по авторам и показывает их сообщения:

Код: Выделить всё

git shortlog -sn v1.2.0..v1.3.0
-s (summary) - не печатать сами сообщения, только счётчик, -n - сортировать по числу коммитов:

Код: Выделить всё

    42  Marina Volkova
    31  Oleg Petrov
     8  Anna Sidorova
Вот тебе готовая строчка контрибьюторов в релиз-ноут. Без -s shortlog развернёт под каждым автором список его коммитов - почти готовый changelog. Обрати внимание на диапазон

Код: Выделить всё

v1.2.0..v1.3.0
- это те самые две точки: "всё, что появилось между двумя тегами релизов". Диапазоны и shortlog созданы друг для друга.

История одного файла и переименования: -- путь и --follow

Часто история нужна не всего проекта, а одного файла. Двойное тире -- отделяет ревизии от путей и говорит git "дальше идут файлы":

Код: Выделить всё

git log --oneline -- src/pricing.py
Покажет только коммиты, тронувшие pricing.py. Но есть ловушка: если файл когда-то переименовали (был price.py, стал pricing.py), обычный log оборвёт историю на моменте переименования - до него "этого файла не существовало". Лечит --follow:

Код: Выделить всё

git log --oneline --follow -- src/pricing.py
--follow отслеживает файл через переименования по эвристике схожести содержимого и продолжает историю в прошлое под старым именем. Ограничение: --follow работает с одним путём за раз, для нескольких файлов он не предназначен.

git grep: поиск по рабочему дереву и истории

git log -S ищет коммиты с изменением строки. А git grep отвечает на другой вопрос: "где прямо сейчас в коде лежит эта строка". Это grep, который понимает git и в разы быстрее обычного, потому что ищет только по версионируемым файлам, игнорируя .gitignore, node_modules и .git:

Код: Выделить всё

git grep -n "calculatePrice"
-n добавит номера строк. Магия в том, что git grep умеет искать не только в рабочем дереве, но и в любом коммите прошлого - просто передай ему ссылку:

Код: Выделить всё

git grep -n "DEBUG_MODE" v1.2.0
git grep -n "TODO" HEAD~10
Первая команда ищет в состоянии на теге v1.2.0, вторая - десять коммитов назад. Полезные флаги: -i - игнор регистра, -l - только имена файлов, -c - счётчик совпадений на файл, -w - искать слово целиком. Связка для расследования: git grep находит где строка живёт сейчас, а git log -S находит когда она там появилась.

Анонс: красивый алиас lg

Команду

Код: Выделить всё

git log --oneline --graph --all --decorate
набирать каждый раз - мучение. Поэтому все профи делают алиас. В отдельном уроке про конфиг и алиасы мы соберём полноценный lg с цветами и относительными датами, но вот превью, чтобы ты понимал, к чему идём:

Код: Выделить всё

git config --global alias.lg "log --graph --abbrev-commit --decorate --all --pretty=format:'%C(yellow)%h%C(reset) %C(green)(%ar)%C(reset) %s %C(blue)<%an>%C(reset)%C(auto)%d%C(reset)'"
После этого рисует цветной граф истории. Не вникай пока в каждый %C - на уроке про алиасы разберём по косточкам.

Типичные грабли
  • --graph без --all. Видишь только текущую ветку и удивляешься, "где остальные". --all обязателен, чтобы видеть всю картину.
  • Путаница A..B и A...B. Для "что в моей ветке нет в main" нужны ДВЕ точки (main..feature). Три точки дадут разницу с обеих сторон - и ты испугаешься лишних коммитов.
  • --grep вместо -S. --grep ищет в сообщении, а -S/-G - в содержимом diff. Если код менялся, а в сообщении про это ни слова, --grep промолчит. Это самая частая причина "я точно знаю, что строку добавляли, но log её не находит".
  • Забыть -- перед путём. Если у тебя есть ветка и файл с одинаковым именем, git не поймёт, что ты имел в виду. Двойное тире снимает двусмысленность.
  • --follow на нескольких файлах. Работает только с одним путём. Для нескольких - либо несколько вызовов, либо без --follow.
  • -S без кавычек на спецсимволах. Если ищешь строку со скобками или звёздочкой, бери в кавычки и помни про --pickaxe-regex, если хочешь регулярку.
Мини-лаба: расследуем историю руками

Собери учебный репозиторий и пройди весь арсенал по шагам.

Код: Выделить всё

git init lab-history && cd lab-history
git config init.defaultBranch main
printf 'def price(x):\n    return x * 100\n' > price.py
git add price.py && git commit -m "feat: базовый расчёт цены"
printf 'def price(x):\n    DEBUG = True\n    return x * 100\n' > price.py
git commit -am "wip: временный дебаг"
git checkout -b discount
printf 'def price(x):\n    return x * 90\n' > price.py
git commit -am "feat: скидка 10 процентов"
git checkout main
printf 'def price(x):\n    return x * 100\n' > price.py
git commit -am "fix: убрал дебаг"
git merge discount -m "Merge branch discount"
Теперь экспериментируй:
  • Код: Выделить всё

    git log --oneline --graph --all --decorate
    - увидь форму с merge.
  • Код: Выделить всё

    git log -S"DEBUG" --oneline
    - найди ровно два коммита, где DEBUG появился и исчез.
  • Код: Выделить всё

    git log -S"DEBUG" -p
    - посмотри сами diff'ы этих касаний.
  • Код: Выделить всё

    git log --oneline main..discount
    и наоборот discount..main - сравни выдачи.
  • Код: Выделить всё

    git log --oneline --left-right main...discount
    - кто с какой стороны.
  • Код: Выделить всё

    git log --oneline --first-parent main
    - чистая лента main без содержимого ветки.
  • Код: Выделить всё

    git grep -n "return" HEAD~3
    - поищи в прошлом состоянии.
  • Код: Выделить всё

    git shortlog -sn
    - собери список авторов.
Контрольные вопросы
  • Чем отличается результат

    Код: Выделить всё

    git log main..feature
    от

    Код: Выделить всё

    git log feature..main
    и почему порядок аргументов важен?
  • Ты точно знаешь, что строку API_KEY кто-то добавлял в код, но

    Код: Выделить всё

    git log --grep="API_KEY"
    ничего не находит. Почему и какой командой искать правильно?
  • В чём разница между -S и -G в pickaxe, и в каком случае -G найдёт коммит, а -S - нет?
  • Зачем нужен --first-parent при просмотре истории main в команде с pull request'ами?
Итог

git log - это не журнал, а инструмент обхода графа коммитов, который ты гнёшь под свой вопрос. --oneline --graph --all --decorate показывает форму истории. Фильтры --author, --since, --grep сужают по метаданным, а pickaxe -S/-G - по реальному содержимому изменений (это уровень профи, который окупается каждый раз на расследованиях). Диапазоны A..B и A...B - про множества достижимых коммитов, а не про строки в списке. --first-parent даёт чистую релизную ленту, git shortlog - готовые контрибьюторы, git grep - мгновенный поиск по коду в любой точке истории. Освой это - и история проекта перестанет быть свалкой, а станет картой, по которой ты находишь нужное за секунды.
👍2 ❤️1 🔥 😄 🤔2
Аватара пользователя
tcp_smith
Сообщения: 1
Зарегистрирован: 11 май 2026, 17:19

Re: История проекта: git log, диапазоны и поиск

Сообщение tcp_smith »

Дошло наконец про две и три точки. Раньше тыкал наугад main..feature или feature..main, теперь понял что это просто что в одной ветке нет в другой. Спасибо за зеркальный пример.
👍 ❤️ 🔥1 😄 🤔
Аватара пользователя
Mlg2
Сообщения: 1
Зарегистрирован: 22 май 2026, 15:28

Re: История проекта: git log, диапазоны и поиск

Сообщение Mlg2 »

А можно -S и --author вместе? Хочу найти все коммиты Олега где он трогал слово DEBUG_MODE. Или это уже перебор и проще через git grep?
👍 ❤️1 🔥 😄 🤔1
Ответить
← Предыдущая глава
Просмотр изменений: status, diff и индексация по частям
Следующая глава →
.gitignore: что не должно попасть в репозиторий

Все главы курса «Git профессионально: от первого коммита до внутреннего устройства»

Поделиться темой: ✈ Telegram VK
Похожие запросы: Git команды для начинающихgit bisect: найти коммит с багом

Вернуться в «Git профессионально: от первого коммита до внутреннего устройства»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость