[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/python/python-docs-de/3.14/tools/sync_branches.py [Back]  [Original]

#!/usr/bin/env python3
"""Gleicht Uebersetzungen zwischen zwei Branches ab -- Eintrag fuer Eintrag.

Unser Arbeitsmodell: gearbeitet wird in 3.15, von dort wird nach 3.14
zurueckgespiegelt. Dieses Skript macht den Abgleich nachvollziehbar, statt
eine Datei pauschal zu ueberschreiben.

Warum nicht einfach pomerge? pomerge kennt nur Quelle und Ziel. Es sieht
nicht, WER einen Eintrag zuletzt angefasst hat, und ueberschreibt deshalb
auch Arbeit, die nur im Zielbranch existiert. Dieses Skript vergleicht
beide Seiten gegen ihren gemeinsamen Vorfahren und unterscheidet damit:

    nur die Quelle hat sich bewegt  -> wird uebertragen
    nur das Ziel hat sich bewegt    -> bleibt unangetastet
    beide haben sich bewegt         -> Konflikt, wird gemeldet
    Quelle ist leer oder fuzzy      -> nie uebertragen

Der letzte Punkt ist wichtig: ein msgmerge kann eine Uebersetzung im
Zielbranch entfernt haben. Mit --leere-ziele werden genau diese Luecken
aus der Quelle wieder gefuellt.

Aufruf (im Repo, das ZIEL muss ausgecheckt sein):

    python tools/sync_branches.py --von 3.15            # Trockenlauf
    python tools/sync_branches.py --von 3.15 --apply
    python tools/sync_branches.py --von 3.15 --leere-ziele --apply
    python tools/sync_branches.py --von 3.15 --nur reference,library
    python tools/sync_branches.py --von 3.15 -o konflikte.md

Ohne --apply wird nichts geschrieben. Konflikte werden nie automatisch
entschieden -- sie landen im Bericht und gehoeren von Hand geprueft.
"""

import argparse
import os
import subprocess
import sys
import tempfile

try:
    import polib
except ImportError:
    sys.exit("polib fehlt. Im aktiven venv installieren: pip install polib")


def git(*args, repo="."):
    e = subprocess.run(["git", "--no-optional-locks", *args], cwd=repo,
                       capture_output=True, text=True)
    return e.stdout if e.returncode == 0 else None


def karte(text):
    """{msgid: msgstr} aus einem PO-Text; fuzzy zaehlt als unuebersetzt."""
    with tempfile.NamedTemporaryFile("w", suffix=".po", encoding="utf-8",
                                     delete=False) as f:
        f.write(text)
        tmp = f.name
    try:
        po = polib.pofile(tmp)
        return {e.msgid: ("" if "fuzzy" in e.flags else e.msgstr)
                for e in po if not e.obsolete and e.msgid}
    finally:
        os.unlink(tmp)


def po_dateien(ref):
    aus = git("ls-tree", "-r", "--name-only", ref) or ""
    return {z for z in aus.split("\n") if z.endswith(".po")}


def main() -> None:
    p = argparse.ArgumentParser(
        description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    p.add_argument("--von", required=True, metavar="BRANCH",
                   help="Quellbranch, aus dem uebertragen wird (z. B. 3.15)")
    p.add_argument("--apply", action="store_true",
                   help="tatsaechlich schreiben (ohne dies nur ein Trockenlauf)")
    p.add_argument("--leere-ziele", action="store_true", dest="leere_ziele",
                   help="auch Eintraege fuellen, die im Ziel leer sind, "
                        "obwohl sich die Quelle nicht bewegt hat")
    p.add_argument("--nur", help="nur diese Pfadanfaenge, kommagetrennt")
    p.add_argument("-o", "--ausgabe", metavar="DATEI",
                   help="Konfliktbericht als Markdown schreiben")
    args = p.parse_args()

    ziel = (git("rev-parse", "--abbrev-ref", "HEAD") or "").strip()
    if not ziel:
        sys.exit("Kein Git-Repository -- bitte im python-docs-de-Verzeichnis starten.")
    if ziel == args.von:
        sys.exit(f"Quelle und Ziel sind derselbe Branch ({ziel}).")
    # Nur geaenderte .po-Dateien sind gefaehrlich: sie wuerden mit den
    # uebertragenen Eintraegen vermischt. Unversionierte Dateien und
    # geaenderte Skripte stoeren nicht.
    schmutzig = [z[3:] for z in (git("status", "--porcelain", "--untracked-files=no")
                                 or "").splitlines() if z[3:].endswith(".po")]
    if schmutzig and args.apply:
        sys.exit(f"{len(schmutzig)} .po-Datei(en) sind ungespeichert geaendert, "
                 f"z. B. {schmutzig[0]}.\nBitte erst committen oder stashen.")

    vorfahr = (git("merge-base", ziel, args.von) or "").strip()
    if not vorfahr:
        sys.exit(f"Kein gemeinsamer Vorfahr von {ziel} und {args.von} gefunden.")

    dateien = sorted(po_dateien(ziel) & po_dateien(args.von))
    if args.nur:
        anfaenge = tuple(a.strip() for a in args.nur.split(",") if a.strip())
        dateien = [d for d in dateien if d.startswith(anfaenge)]

    print(f"{ziel} 

Web Proxy Viewer  |  New URL  |  Original Page