0Pricing
AI Agents · Lección

Historial de commits y análisis de diferencias

Recorra el historial de commits, extraiga los archivos modificados y resuma las diferencias.

Historial de commits y análisis de diferencias es una lección gratuita de AI Agents en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Agents, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Agents incluye 4 lecciones en total.

¿Por qué analizar el historial de commits?

El historial de commits es una mina de oro para los agentes: revela qué cambió, cuándo y quién lo hizo. Los agentes utilizan el historial de commits para generar changelogs, resumir revisiones de código, elaborar informes de actividad de los desarrolladores, detectar riesgos (cambios grandes cerca de los lanzamientos) y comprender la evolución de una base de código.

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

Filtrado de commits por intervalo de tiempo

Utilice los parámetros since y until para obtener commits dentro de un intervalo de fechas específico. Ambos aceptan objetos datetime de Python. Esta es la herramienta principal para generar informes de actividad semanales o mensuales.

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
)

Estadísticas del commit: adiciones y eliminaciones

Cada objeto de commit tiene una propiedad stats con los recuentos de cambios additions, deletions y total de todos los archivos modificados. Esto permite hacerse una idea rápida del tamaño de un cambio.

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

Lista de archivos modificados en un commit

commit.files es una lista de objetos File, uno por cada archivo modificado en el commit. Propiedades: filename, status (added/modified/removed/renamed), additions, deletions, patch (el texto del diff).

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

Análisis del texto del diff (patch)

file.patch contiene un diff unificado. Las líneas que comienzan por + son adiciones, las que comienzan por - son eliminaciones y las líneas de contexto no tienen prefijo. Analícelo para extraer las líneas añadidas, por ejemplo, para encontrar secretos nuevos, comentarios TODO o antipatrones.

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)

Agregación de actividad por autor

Agrupe los commits por autor para elaborar informes de actividad de los desarrolladores. Cuente los commits, las líneas añadidas y eliminadas y los archivos modificados por cada autor durante un intervalo de tiempo. Esto resulta útil para las retrospectivas del equipo y para decidir la asignación de 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')

Generación de un changelog a partir de commits

Un agente de changelog lee los commits entre dos etiquetas o fechas, los agrupa por tipo (feat/fix/chore mediante el formato de Conventional Commits) y genera un changelog con formato. Utilice un LLM para mejorar la calidad de la descripción de cada commit.

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

Identificación de commits de alto riesgo

Los commits de alto riesgo son aquellos que son grandes, modifican archivos críticos (auth, payment, security) o se realizan muy cerca de un lanzamiento. Un agente puede marcarlos para someterlos a un análisis adicional antes de integrarlos en 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}')

Comparación de dos commits

Utilice repo.compare(base, head) para obtener el diff entre dos referencias (ramas, etiquetas o SHA de commits). Esto devuelve un objeto Comparison con las estadísticas combinadas, la lista de commits y la lista de archivos modificados entre ambos puntos.

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

Resumen de commits generado con un LLM

Proporcione a un LLM un conjunto de mensajes de commit y nombres de archivo para generar un resumen legible de los cambios realizados durante un período. Este es el núcleo de un bot de resumen semanal de ingeniería.

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

Detección de archivos sensibles en commits

Un agente de análisis de commits centrado en la seguridad busca commits que incluyan accidentalmente archivos sensibles: claves privadas, archivos .env, configuraciones con credenciales o volcados de bases de datos. Compruebe commit.files para encontrar nombres de archivo que coincidan con patrones sensibles conocidos.

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

Comprobación rápida: commit.files frente a pr.get_files()

Compruebe sus conocimientos sobre el acceso a los diffs de los commits.

Resumen del análisis del historial de commits

Su agente ya puede analizar el historial de commits de forma exhaustiva:

  • repo.get_commits(since, until, author, sha) — filtra commits por tiempo, autor o rama
  • commit.stats — adiciones, eliminaciones y total de todos los archivos modificados
  • commit.files — detalles de cada archivo, incluido el patch (diff unificado)
  • Analice file.patch para buscar secretos, TODO o antipatrones en las líneas añadidas
  • Agrupe los commits por autor para elaborar informes de actividad
  • repo.compare(base, head) — diff entre dos referencias (ramas, etiquetas o SHA)
  • Utilice LLM para generar resúmenes legibles a partir de listas de mensajes de commit
  • Marque los commits de alto riesgo: cambios grandes, archivos críticos y proximidad a lanzamientos

Preguntas frecuentes

¿La lección «Historial de commits y análisis de diferencias» es gratis?

Sí — el texto completo de «Historial de commits y análisis de diferencias» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Agents, actualiza a CoddyKit PRO. El curso de AI Agents incluye 4 lecciones en total.

¿Qué aprenderé en «Historial de commits y análisis de diferencias»?

Recorra el historial de commits, extraiga los archivos modificados y resuma las diferencias. Practicas AI Agents con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar AI Agents?

No se requiere experiencia previa. AI Agents en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Historial de commits y análisis de diferencias»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de AI Agents?

Sí. Cada lección de AI Agents incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Introducción a GitHub REST API
  2. Enumeración y gestión de incidencias
  3. Comentarios automatizados en revisiones de PR
  4. Historial de commits y análisis de diferencias
← Volver a AI Agents