Historia commitów i analiza diffów
Przeglądanie historii commitów, wyodrębnianie zmienionych plików i podsumowywanie diffów.
Historia commitów i analiza diffów to bezpłatna lekcja AI Agents na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Agents, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Agents zawiera 4 lekcji w sumie.
Dlaczego analizować historię commitów?
Historia commitów to prawdziwa kopalnia wiedzy dla agentów: ujawnia, co się zmieniło, kiedy i przez kogo. Agenci wykorzystują historię commitów do generowania changelogów, podsumowań przeglądów kodu, raportów aktywności deweloperów, wykrywania ryzyka (duże zmiany tuż przed wydaniem) oraz zrozumienia ewolucji bazy kodu.
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()}')Filtrowanie commitów według zakresu czasu
Należy użyć parametrów since i until, aby pobrać commity z określonego zakresu dat. Oba przyjmują obiekty datetime języka Python. To podstawowe narzędzie do generowania tygodniowych lub miesięcznych raportów aktywności.
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
)Statystyki commita: dodane i usunięte wiersze
Każdy obiekt commita ma właściwość stats zawierającą liczbę zmian additions, deletions i total we wszystkich zmienionych plikach. Pozwala to szybko ocenić rozmiar zmiany.
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')Wyświetlanie plików zmienionych w commicie
commit.files to lista obiektów File — po jednym dla każdego pliku zmienionego w commicie. Właściwości: filename, status (added/modified/removed/renamed), additions, deletions, patch (tekst różnic).
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)}')Analizowanie tekstu diffu (patcha)
file.patch zawiera ujednolicony diff. Wiersze zaczynające się od + oznaczają dodane elementy, te zaczynające się od - — usunięte, a wiersze kontekstu nie mają prefiksu. Należy go przeanalizować, aby wyodrębnić dodane wiersze, na przykład w celu znalezienia nowych sekretów, komentarzy TODO lub antywzorców.
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)
Agregowanie aktywności według autora
Należy grupować commity według autora, aby tworzyć raporty aktywności deweloperów. W wybranym przedziale czasu zliczaj commity, dodane i usunięte wiersze oraz pliki zmodyfikowane przez każdego autora. Jest to przydatne podczas retrospektyw zespołu i podejmowania decyzji o przydzielaniu PR-ów.
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')Generowanie changelogu z commitów
Agent generujący changelog odczytuje commity między dwiema etykietami lub datami, grupuje je według typu (feat/fix/chore zgodnie z formatem conventional commits) i tworzy sformatowany changelog. Do poprawy jakości opisu każdego commita należy użyć LLM-a.
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')Identyfikowanie commitów wysokiego ryzyka
Commity wysokiego ryzyka to takie, które są duże, dotyczą krytycznych plików (uwierzytelnianie, płatności, bezpieczeństwo) lub zostały utworzone tuż przed wydaniem. Agent może oznaczyć je do dodatkowej kontroli przed scaleniem z gałęzią 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}')Porównywanie dwóch commitów
Należy użyć repo.compare(base, head), aby uzyskać różnice między dwoma odwołaniami (gałęziami, tagami lub identyfikatorami SHA commitów). Zwracany jest obiekt Comparison zawierający łączne statystyki, listę commitów oraz listę plików zmienionych między tymi punktami.
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')Podsumowanie commitów generowane przez LLM
Przekaż partię komunikatów commitów i nazw plików do LLM-a, aby wygenerować zrozumiałe dla człowieka podsumowanie zmian w danym okresie. To podstawa cotygodniowego bota tworzącego podsumowania prac zespołu inżynieryjnego.
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].textWykrywanie wrażliwych plików w commitach
Agent analizujący commity pod kątem bezpieczeństwa skanuje commity, które przypadkowo zawierają wrażliwe pliki: klucze prywatne, pliki .env, konfiguracje z danymi uwierzytelniającymi lub zrzuty baz danych. Należy sprawdzić commit.files pod kątem nazw plików pasujących do znanych wzorców wrażliwych danych.
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}')Szybki test: commit.files a pr.get_files()
Proszę sprawdzić swoją wiedzę na temat dostępu do diffów commitów.
Podsumowanie analizy historii commitów
Agent potrafi teraz kompleksowo analizować historię commitów:
- repo.get_commits(since, until, author, sha) — filtrowanie commitów według czasu, autora lub gałęzi
- commit.stats — liczba dodanych, usuniętych i wszystkich elementów we wszystkich zmienionych plikach
- commit.files — szczegóły poszczególnych plików, w tym patch (ujednolicony diff)
- Analizowanie
file.patchw celu skanowania dodanych wierszy pod kątem sekretów, TODO lub antywzorców - Grupowanie commitów według autora na potrzeby raportów aktywności
- repo.compare(base, head) — diff między dwoma odwołaniami (gałęziami, tagami lub identyfikatorami SHA)
- Używanie LLM-ów do generowania zrozumiałych dla człowieka podsumowań na podstawie list komunikatów commitów
- Oznaczanie commitów wysokiego ryzyka: duże zmiany + krytyczne pliki + bliskość wydania
Często zadawane pytania
Czy lekcja „Historia commitów i analiza diffów” jest bezpłatna?
Tak — pełny tekst „Historia commitów i analiza diffów” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Agents, przejdź na CoddyKit PRO. Kurs AI Agents zawiera 4 lekcji w sumie.
Co nauczysz się w „Historia commitów i analiza diffów”?
Przeglądanie historii commitów, wyodrębnianie zmienionych plików i podsumowywanie diffów. Ćwiczysz AI Agents z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć AI Agents?
Nie wymagamy żadnego doświadczenia. AI Agents w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Historia commitów i analiza diffów”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji AI Agents?
Tak. Każda lekcja AI Agents zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Przegląd GitHub REST API
- Listowanie i zarządzanie zgłoszeniami
- Automatyczne komentarze w przeglądach PR
- Historia commitów i analiza diffów