0Pricing
AI Agents · レッスン

コミット履歴と差分の分析

コミット履歴をたどり、変更されたファイルを抽出し、差分を要約します。

「コミット履歴と差分の分析」はCoddyKit上の無料AI Agentsレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これは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(added/modified/removed/renamed)、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')

コミットから変更履歴を生成する

変更履歴生成エージェントは、2つのタグまたは日付の間にあるコミットを読み取り、種類(conventional commit形式によるfeat/fix/chore)ごとにグループ化して、整形済みの変更履歴を生成します。各コミットの説明の品質を高めるには、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')

高リスクのコミットを特定する

高リスクのコミットとは、規模が大きいもの、重要なファイル(認証、決済、セキュリティ)に変更を加えるもの、またはリリースの直前に作成されたものです。エージェントを使って、mainへのマージ前に追加の精査が必要なコミットとして指摘できます。

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}')

2つのコミットを比較する

repo.compare(base, head)を使用すると、2つのref(ブランチ、タグ、コミットSHA)の間の差分を取得できます。戻り値はComparisonオブジェクトで、両方の統計情報、コミットの一覧、2つの時点の間で変更されたファイルの一覧が含まれます。

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) — 2つのref(ブランチ、タグ、SHA)の間の差分を取得します
  • LLMを使用して、コミットメッセージのリストから人間が読みやすい要約を生成します
  • 高リスクのコミット(大規模な変更、重要なファイル、リリース間近)を指摘します

よくある質問

「コミット履歴と差分の分析」レッスンは無料ですか?

はい。「コミット履歴と差分の分析」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Agentsコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Agentsコースには全4レッスンが含まれています。

「コミット履歴と差分の分析」で何を学びますか?

コミット履歴をたどり、変更されたファイルを抽出し、差分を要約します。 ブラウザで直接実行するハンズオンコードでAI Agentsを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

AI Agentsを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAI Agentsは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「コミット履歴と差分の分析」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAI Agentsレッスンでコードを書いて実行できますか?

はい。すべてのAI Agentsレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. GitHub REST API概要
  2. Issueの一覧表示と管理
  3. PRレビューコメントの自動化
  4. コミット履歴と差分の分析
← AI Agentsに戻る