Tekoälyagentit · Oppitunti

Commit-historian ja diffien analyysi

Commit-historian läpikäynti, muuttuneiden tiedostojen poiminta ja diffien tiivistäminen

Oppitunti 4/413 vaihetta

Commit-historian ja diffien analyysi on ilmainen Tekoälyagentit-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Tekoälyagentit-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Tekoälyagentit-kurssilla on yhteensä 4 oppituntia.

Miksi commit-historiaa analysoidaan

Commit-historia on agenteille aarreaitta: siitä käy ilmi, mitä muuttui, milloin ja kenen toimesta. Agentit käyttävät commit-historiaa muutoslokien luomiseen, koodikatselmointien yhteenvetoihin, kehittäjien aktiivisuusraportteihin, riskien tunnistamiseen (suuret muutokset lähellä julkaisuja) ja koodikannan kehityksen ymmärtämiseen.

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

Commitien suodattaminen aikavälin perusteella

Käytä since- ja until-parametreja hakeaksesi commitit tietyltä päivämääräväliksi. Molemmat vastaanottavat Pythonin datetime-objekteja. Tämä on ensisijainen työkalu viikoittaisten tai kuukausittaisten aktiivisuusraporttien luomiseen.

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
)

Commit-tilastot: lisäykset ja poistot

Jokaisella commit-objektilla on stats-ominaisuus, joka sisältää kaikkien muuttuneiden tiedostojen yhteenlasketut additions-, deletions- ja total-muutosmäärät. Näin muutoksen koosta saa nopeasti käsityksen.

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

Commitissa muuttuneiden tiedostojen luetteleminen

commit.files on luettelo File-objekteista, yhden jokaista commitissa muuttunutta tiedostoa kohti. Ominaisuudet ovat filename, status (added/modified/removed/renamed), additions, deletions ja patch (diff-teksti).

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

Diff- eli patch-tekstin jäsentäminen

file.patch sisältää unified diff -esityksen. Merkillä + alkavat rivit ovat lisäyksiä, merkillä - alkavat poistettavia rivejä, eikä kontekstiriveillä ole etuliitettä. Jäsennä teksti lisättyjen rivien poimimiseksi analyysia varten — esimerkiksi uusien salaisuuksien, TODO-kommenttien tai tietoturvan vastaisten käytäntöjen löytämiseksi.

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)

Aktiviteetin koostaminen tekijän mukaan

Ryhmittele commitit tekijän mukaan kehittäjien aktiivisuusraportteja varten. Laske kunkin tekijän commitit, lisätyt ja poistetut rivit sekä käsiteltyjen tiedostojen määrä tietyltä aikaväliltä. Tämä on hyödyllistä tiimien retrospektiiveissä ja PR-katselmointien jakamista koskevissa päätöksissä.

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

Muutoslokin luominen commitien perusteella

Muutoslokiagentti lukee commitit kahden tagin tai päivämäärän väliltä, ryhmittelee ne tyypin mukaan (feat/fix/chore conventional commit -muotoa käyttäen) ja luo muotoillun muutoslokin. Käytä LLM:ää kunkin commitin kuvauksen laadun parantamiseen.

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

Suuren riskin commitien tunnistaminen

Suuren riskin commitit ovat suuria, kriittisiin tiedostoihin (todennus, maksaminen, tietoturva) vaikuttavia tai hyvin lähellä julkaisua tehtyjä committeja. Agentti voi merkitä ne tarkempaa tarkastelua varten ennen yhdistämistä main-haaraan.

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

Kahden commitin vertaaminen

Käytä repo.compare(base, head)-kutsua saadaksesi kahden ref-viitteen (haaran, tagin tai commit SHA:n) välisen diffin. Tämä palauttaa Comparison-objektin, joka sisältää yhdistetyt tilastot, commit-luettelon ja näiden kahden kohdan välillä muuttuneiden tiedostojen luettelon.

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:n tuottama commit-yhteenveto

Syötä LLM:lle joukko commit-viestejä ja tiedostonimiä, jotta se voi luoda ihmisen luettavissa olevan yhteenvedon tietyn ajanjakson muutoksista. Tämä on viikoittaisen kehitystyön koontibotin ydin.

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

Arkaluonteisten tiedostojen tunnistaminen commitien joukosta

Tietoturvaan keskittyvä commit-analyysiagentti etsii committeja, joihin on vahingossa sisällytetty arkaluonteisia tiedostoja: yksityisiä avaimia, .env-tiedostoja, tunnistetietoja sisältäviä määrityksiä tai tietokantavedoksia. Tarkista commit.files tiedostonimien osalta, jotka vastaavat tunnettuja arkaluonteisten tiedostojen malleja.

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

Pikatarkistus: commit.files ja pr.get_files()

Testaa ymmärrystäsi commit-diffien käsittelystä.

Commit-historian analyysin yhteenveto

Agenttisi osaa nyt analysoida commit-historiaa kattavasti:

  • repo.get_commits(since, until, author, sha) — suodata committeja ajan, tekijän tai haaran perusteella
  • commit.stats — kaikkien muuttuneiden tiedostojen lisäykset, poistot ja kokonaismäärä
  • commit.files — tiedostokohtaiset tiedot, mukaan lukien patch (unified diff)
  • Jäsennä file.patch ja etsi lisätyiltä riveiltä salaisuuksia, TODO-kommentteja tai tietoturvan vastaisia käytäntöjä
  • Ryhmittele commitit tekijän mukaan aktiivisuusraportteja varten
  • repo.compare(base, head) — kahden ref-viitteen (haaran, tagin tai SHA:n) välinen diff
  • Luo LLM:ien avulla ihmisen luettavissa olevia yhteenvetoja commit-viestiluetteloista
  • Merkitse suuren riskin commitit: suuret muutokset + kriittiset tiedostot + läheinen ajankohta julkaisuun
Aloita maksutta

Opi Tekoälyagentit tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
60
Oppitunnit
239

Usein kysytyt kysymykset

Onko oppitunti ”Commit-historian ja diffien analyysi” ilmainen?

Kyllä – oppitunnin ”Commit-historian ja diffien analyysi” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Tekoälyagentit-kurssin, päivitä CoddyKit PROhon. Tekoälyagentit-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Commit-historian ja diffien analyysi”?

Commit-historian läpikäynti, muuttuneiden tiedostojen poiminta ja diffien tiivistäminen Harjoittelet Tekoälyagentit-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Tekoälyagentit-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Tekoälyagentit-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.

Kuinka kauan ”Commit-historian ja diffien analyysi”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Tekoälyagentit-oppitunnilla?

Kyllä. Jokainen Tekoälyagentit-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. GitHub REST API:n yleiskatsaus
  2. Issuejen luettelointi ja hallinta
  3. Automaattiset PR-katselmointikommentit
  4. Commit-historian ja diffien analyysi
← Takaisin: Tekoälyagentit