История коммитов и анализ различий
Просмотр истории коммитов, извлечение изменённых файлов и обобщение различий.
«История коммитов и анализ различий» — бесплатный урок AI Agents на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Agents, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Agents содержит 4 уроков всего.
Зачем анализировать историю коммитов
История коммитов — богатый источник информации для агентов: она показывает, что изменилось, когда и кем. Агенты используют её для создания журналов изменений, сводок проверок кода, отчётов об активности разработчиков, выявления рисков — например, крупных изменений незадолго до выпусков — и понимания развития кодовой базы.
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
# Get recent commits on the default branch
commits = repo.get_commits()
print(f'Total commits: {commits.totalCount}')
# Preview the last 5
for commit in commits[:5]:
print(f'{commit.sha[:8]} | {commit.commit.author.name}')
print(f' {commit.commit.message.split(chr(10))[0][:60]}')
print(f' {commit.commit.author.date.date()}')Фильтрация коммитов по диапазону времени
Используйте параметры since и until, чтобы получить коммиты за определённый диапазон дат. Оба параметра принимают объекты Python datetime. Это основной инструмент для создания еженедельных и ежемесячных отчётов об активности.
import datetime
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
# Last 7 days of commits
now = datetime.datetime.utcnow()
last_week = now - datetime.timedelta(days=7)
recent_commits = repo.get_commits(
since=last_week,
until=now
)
print(f'Commits in last 7 days: {recent_commits.totalCount}')
# By specific author
author_commits = repo.get_commits(
since=last_week,
author='alice' # GitHub username
)
print(f'Alice\'s commits last week: {author_commits.totalCount}')
# On a specific branch
branch_commits = repo.get_commits(
sha='feature/new-api',
since=last_week
)Статистика коммита: добавления и удаления
У каждого объекта коммита есть свойство stats с количеством additions, deletions и total изменений во всех изменённых файлах. Это позволяет быстро оценить размер изменения.
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
commit = repo.get_commit('abc12345sha')
# Overall stats
print(f'SHA: {commit.sha[:8]}')
print(f'Message: {commit.commit.message[:80]}')
print(f'Author: {commit.commit.author.name}')
print(f'Date: {commit.commit.author.date}')
print(f'Additions: +{commit.stats.additions}')
print(f'Deletions: -{commit.stats.deletions}')
print(f'Total changes: {commit.stats.total}')
# Large commit warning
if commit.stats.total > 500:
print('WARNING: Large commit — careful code review needed')Список файлов, изменённых в коммите
commit.files — это список объектов File для каждого файла, изменённого в коммите. Свойства: filename, status (добавлен/изменён/удалён/переименован), additions, deletions, patch (текст различий).
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
commit = repo.get_commit('abc12345sha')
print(f'Files changed: {len(commit.files)}')
for f in commit.files:
print(f' [{f.status.upper():<8}] {f.filename}')
print(f' +{f.additions} -{f.deletions}')
if f.previous_filename: # for renamed files
print(f' Renamed from: {f.previous_filename}')
# Find changed Python files
python_changes = [
f for f in commit.files
if f.filename.endswith('.py')
]
print(f'\nPython files changed: {len(python_changes)}')Разбор текста различий (патча)
file.patch содержит унифицированные различия. Строки, начинающиеся с +, обозначают добавления, с - — удаления, а контекстные строки не имеют префикса. Разберите их, чтобы извлечь добавленные строки для анализа — например, найти новые секреты, комментарии TODO или небезопасные шаблоны.
def extract_added_lines(patch):
if not patch:
return []
added = []
for line in patch.split('\n'):
if line.startswith('+') and not line.startswith('+++'):
added.append(line[1:]) # strip the '+' prefix
return added
def scan_for_secrets(added_lines):
import re
PATTERNS = [
(r'api[_-]?key\s*=\s*["\'][^"\']{20,}', 'potential API key'),
(r'password\s*=\s*["\'][^"\']{6,}', 'potential hardcoded password'),
(r'sk-[a-zA-Z0-9]{20,}', 'potential OpenAI key'),
(r'ghp_[a-zA-Z0-9]{36}', 'potential GitHub token'),
]
findings = []
for i, line in enumerate(added_lines, 1):
for pattern, label in PATTERNS:
if re.search(pattern, line, re.IGNORECASE):
findings.append({'line': i, 'type': label, 'preview': line[:80]})
return findings
# --- demo ---
patch = (
'@@ -1,2 +1,3 @@\n'
' def connect():\n'
'+ api_key = "sk-abcdefghijklmnopqrstuvwx"\n'
'+ password = "hunter2222"\n'
)
added = extract_added_lines(patch)
print('Added lines:', added)
for finding in scan_for_secrets(added):
print(finding)
Группировка активности по автору
Сгруппируйте коммиты по автору, чтобы создавать отчёты об активности разработчиков. Подсчитайте количество коммитов, добавленных и удалённых строк, а также затронутых файлов для каждого автора за определённый период. Это полезно для командных ретроспектив и принятия решений о назначении PR.
import datetime
from collections import defaultdict
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
last_month = datetime.datetime.utcnow() - datetime.timedelta(days=30)
author_stats = defaultdict(lambda: {'commits': 0, 'additions': 0, 'deletions': 0, 'files': set()})
for commit in repo.get_commits(since=last_month):
author = commit.commit.author.name
author_stats[author]['commits'] += 1
author_stats[author]['additions'] += commit.stats.additions
author_stats[author]['deletions'] += commit.stats.deletions
for f in commit.files:
author_stats[author]['files'].add(f.filename)
print('Author Activity (last 30 days):')
for author, stats in sorted(author_stats.items(), key=lambda x: -x[1]['commits']):
print(f'{author}: {stats["commits"]} commits, +{stats["additions"]} -{stats["deletions"]}, {len(stats["files"])} files')Создание журнала изменений из коммитов
Агент для создания журнала изменений читает коммиты между двумя тегами или датами, группирует их по типу — новые функции, исправления и служебные изменения — с использованием общепринятого формата коммитов и создаёт оформленный журнал изменений. Используйте LLM, чтобы улучшить качество описания каждого коммита.
import re
import datetime
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
def get_commits_since_tag(repo, tag_name):
try:
ref = repo.get_git_ref(f'tags/{tag_name}')
tag_sha = ref.object.sha
tag_commit = repo.get_commit(tag_sha)
since = tag_commit.commit.author.date
return list(repo.get_commits(since=since))[:-1] # exclude the tag commit
except Exception:
return []
def parse_conventional_commit(message):
match = re.match(r'^(feat|fix|chore|docs|test|refactor|perf|style)(\(.+?\))?: (.+)', message)
if match:
return match.group(1), match.group(3)
return 'other', message.split('\n')[0][:60]
commits = get_commits_since_tag(repo, 'v1.2.0')
by_type = {'feat': [], 'fix': [], 'other': []}
for c in commits:
msg = c.commit.message
ctype, desc = parse_conventional_commit(msg)
by_type.get(ctype, by_type['other']).append(desc)
print(f'Parsed {len(commits)} commits')Выявление коммитов высокого риска
К коммитам высокого риска относятся крупные коммиты, затрагивающие критически важные файлы — аутентификацию, платежи или безопасность, — а также коммиты, созданные непосредственно перед выпуском. Агент может отметить их для дополнительной проверки перед слиянием с основной веткой.
import datetime
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
CRITICAL_PATHS = [
'src/auth', 'src/payment', 'src/security',
'config/', '.env', 'Dockerfile', 'requirements.txt'
]
def is_high_risk(commit):
reasons = []
# Large change
if commit.stats.total > 300:
reasons.append(f'Large change: {commit.stats.total} lines')
# Touches critical files
for f in commit.files:
for path in CRITICAL_PATHS:
if f.filename.startswith(path):
reasons.append(f'Touches critical: {f.filename}')
break
return reasons
now = datetime.datetime.utcnow()
for commit in repo.get_commits(since=now - datetime.timedelta(days=1)):
risks = is_high_risk(commit)
if risks:
print(f'HIGH RISK: {commit.sha[:8]} - {commit.commit.message[:50]}')
for r in risks:
print(f' - {r}')Сравнение двух коммитов
Используйте repo.compare(base, head), чтобы получить различия между двумя ссылками — ветками, тегами или SHA коммитов. Возвращается объект Comparison с общей статистикой, списком коммитов и списком файлов, изменённых между двумя points.
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
# Compare two branches
comparison = repo.compare('main', 'feature/new-api')
print(f'Commits ahead: {comparison.ahead_by}')
print(f'Commits behind: {comparison.behind_by}')
print(f'Status: {comparison.status}') # ahead/behind/identical/diverged
print(f'Total commits in diff: {len(comparison.commits)}')
print(f'Files changed: {len(comparison.files)}')
# Summary of what changed
for f in comparison.files:
print(f' {f.status}: {f.filename} +{f.additions} -{f.deletions}')
# Compare two tags
tag_comparison = repo.compare('v1.0.0', 'v1.1.0')
print(f'Changes between v1.0.0 and v1.1.0: {len(tag_comparison.commits)} commits')Сводка коммитов с помощью LLM
Передайте LLM набор сообщений коммитов и имён файлов, чтобы создать понятную человеку сводку изменений за определённый период. Это основа бота, который еженедельно рассылает инженерные дайджесты.
import anthropic
import os
client = anthropic.Anthropic(api_key=os.environ['ANTHROPIC_API_KEY'])
def summarize_commits(commits, time_period='last week'):
commit_data = []
for commit in commits[:30]: # limit to avoid context overflow
commit_data.append(
f'- {commit.commit.message.split(chr(10))[0][:80]} '
f'(+{commit.stats.additions}/-{commit.stats.deletions})'
)
commit_list = '\n'.join(commit_data)
prompt = (
f'Summarize what happened in this codebase {time_period} '
f'based on these commit messages. Be concise (3-5 bullet points). '
f'Focus on user-facing changes and significant technical work.\n\n'
f'Commits:\n{commit_list}'
)
response = client.messages.create(
model='claude-opus-4-5',
max_tokens=400,
messages=[{'role': 'user', 'content': prompt}]
)
return response.content[0].textВыявление чувствительных файлов в коммитах
Агент для анализа коммитов с акцентом на безопасность ищет коммиты, в которые случайно попали чувствительные файлы: закрытые ключи, файлы .env, конфигурации с учётными данными или дампы баз данных. Проверьте commit.files на наличие имён файлов, соответствующих известным чувствительным шаблонам.
import re
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
SENSITIVE_PATTERNS = [
r'\.env$', r'\.pem$', r'\.key$', r'id_rsa', r'credentials\.json',
r'secret', r'password', r'\.p12$', r'keystore', r'\btoken\b'
]
def is_sensitive_filename(filename):
for pattern in SENSITIVE_PATTERNS:
if re.search(pattern, filename, re.IGNORECASE):
return pattern
return None
import datetime
last_day = datetime.datetime.utcnow() - datetime.timedelta(days=1)
for commit in repo.get_commits(since=last_day):
for f in commit.files:
matched = is_sensitive_filename(f.filename)
if matched and f.status in ('added', 'modified'):
print(f'ALERT: Sensitive file in commit {commit.sha[:8]}')
print(f' File: {f.filename} (matched: {matched})')
print(f' Author: {commit.commit.author.name}')
print(f' Link: {commit.html_url}')Быстрая проверка: commit.files и pr.get_files()
Проверьте, насколько хорошо Вы понимаете доступ к различиям коммитов.
Повторение: анализ истории коммитов
Теперь Ваш агент умеет всесторонне анализировать историю коммитов:
- repo.get_commits(since, until, author, sha) — фильтровать коммиты по времени, автору или ветке
- commit.stats — количества добавлений, удалений и всех изменений во всех изменённых файлах
- commit.files — сведения о каждом файле, включая патч (унифицированные различия)
- Разбирать
file.patch, чтобы искать в добавленных строках секреты, TODO или небезопасные шаблоны - Группировать коммиты по автору для отчётов об активности
- repo.compare(base, head) — различия между двумя ссылками: ветками, тегами или SHA
- Использовать LLM для создания понятных человеку сводок на основе списков сообщений коммитов
- Отмечать коммиты высокого риска: крупные изменения, критически важные файлы и близость к выпускам
Часто задаваемые вопросы
Урок «История коммитов и анализ различий» бесплатный?
Да — полный текст урока «История коммитов и анализ различий» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Agents, подпишись на CoddyKit PRO. Курс AI Agents содержит 4 уроков всего.
Чему я научусь в уроке «История коммитов и анализ различий»?
Просмотр истории коммитов, извлечение изменённых файлов и обобщение различий. Ты практикуешь AI Agents с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Agents?
Предыдущий опыт не требуется. AI Agents на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «История коммитов и анализ различий»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Agents?
Да. Каждый урок AI Agents включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Обзор GitHub REST API
- Просмотр и управление задачами
- Автоматические комментарии к проверке PR
- История коммитов и анализ различий