Skip to content

Source Code Map

Won-Kyu Park edited this page Aug 28, 2026 · 3 revisions

소스 코드 지도

패키지 구성

simple_rcs/
  simple_rcs.py          핵심 클래스 (2344줄) — 여기가 사실상 라이브러리 전체
  codec.py                포맷 프리미티브 (바이너리 인코딩, 해시, 이스케이핑)
  matchers.py             diff 백엔드 레지스트리 (import 시점에 해석, 폴백 포함)
  gitpatch.py             git 호환 바이너리 패치 출력 (내보내기 전용)
  pydifflib.py             프로덕션 diff 엔진 (StreamSequenceMatcher)
  pybsdiff.py              바이너리 델타 (BSDIFF40 호환)
  myersdiff.py             순수 파이썬 Myers 알고리즘 (참고/벤치마크용)
  myersdiff_ses.py          Myers SES 변형
  myersdiff_dmp.py          Myers, diff-match-patch 스타일 offset 버전
  _myersdiff_ses.pyx       위 SES를 Cython으로 포팅
  _myersdiff_dmp.pyx        위 DMP를 Cython으로 포팅
  simple_rcs_gpg.py         GPG 서명/검증 콜백
  adapters.py               psycopg2 large object용 스트림 어댑터

tools/
  srcs_commit.py     파일을 커밋하는 CLI
  srcs_log.py         히스토리/서명 목록 출력
  srcs_diff.py        버전 간, 혹은 엔진 간 unified diff
  srcs_blame.py       라인별 저자/버전 귀속
  srcs_verify.py      해시체인 + GPG 서명 검증
  srcs_sign_head.py    현재 HEAD에 GPG 서명 추가
  bench_diff.py        diff 엔진 벤치마크 (시간 + 메모리)
  compare_memory_usage.py  메모리 사용량 비교 유틸

tests/unit_tests/    pytest 스위트 (186개 테스트)
docs/                 설계/벤치마크 메모
scripts/              일회성 실험용 벤치마크 스크립트 (위키 백엔드 등)

simple_rcs.py — 핵심

SimpleRCS 클래스 하나가 라이브러리의 사실상 전부다. 공개 메서드는 이 정도다:

메서드 역할
commit(content, author, log, ...) 새 버전 저장 (텍스트/바이너리, 스냅샷 옵션)
checkout(ver_num=None) 특정 버전 복원 (기본은 HEAD)
log(limit=None, reverse=False) 히스토리 메타데이터 목록
diff(ver_a, ver_b) 두 버전 간 unified diff
blame(depth=None) HEAD의 각 라인이 어느 버전/저자 것인지
sign_head(signer_callbacks) HEAD에 GPG 서명 추가
verify(verifier_callbacks=None) 해시체인 + 서명 전체 검증
verify_block_signature(...) 블록 하나의 서명만 검증
get_content() 스트림 전체를 문자열로 반환
get_bytes() 스트림 전체를 바이트 그대로 반환 (raw/base85 블록이 섞여 있으면 이쪽)

생성자는 durable(기본 True)을 받는다 — 파일 경로 백엔드에서 커밋을 fsync할지 여부다.

내부적으로는 스트림을 끝에서부터 역방향으로 스캔하는 로직 (_load_head, _get_prev_block)이 제일 중요한 부분이다. 파일 전체를 메모리에 올리지 않고 필요한 블록만 읽어오기 때문에 히스토리가 길어져도 메모리 사용량이 늘지 않는다. 블록 경계는 ver @ 마커를 찾은 뒤 @ 패리티로 확정한다 (Storage Format).

쓰기 쪽은 _rewrite_head가 갈림길이다. 파일 경로 백엔드는 _atomic_rewrite_head(prefix 복사 → 임시 파일 → os.replace)로 가고, 메모리·호출자 스트림은 _rewrite_head_in_place로 간다. 파괴적 쓰기가 코드베이스에 한 곳만 있도록 두 커밋 경로(commit/sign_head)가 모두 여기를 지난다.

gitpatch.py — git 호환 바이너리 패치

우리 저장 포맷(BSDIFF + 블록)은 다른 도구가 읽지 못한다. 이 모듈은 내보내기 경로다 — 두 리비전을 복원한 뒤 git diff --binary가 쓰는 형태 (GIT binary patch + literal 블록, zlib + git식 base85 줄 프레이밍)로 감싸서 git apply가 소비할 수 있게 한다. tools/srcs_diff.py --binary가 유일한 호출자다.

codec.py — 상태 없는 포맷 프리미티브

SimpleRCS 인스턴스와 무관하게 독립적으로 테스트 가능한 순수 함수들. 바이너리 페이로드 인코딩/디코딩(encode_binary/decode_binary, base64/base85/raw 지원), @...@ 값 안의 @ 이스케이핑, v2 블록 해시 계산이 여기 있다. SimpleRCS는 자기 설정(hash_algo, encoding)을 인자로 넘겨서 이 함수들을 호출한다.

diff 관련 모듈들 — 왜 이렇게 많은가

  • pydifflib.pyStreamSequenceMatcher — 실제로 커밋 경로에서 쓰이는 유일한 엔진. 해시 기반 그리디 매칭 + 교체 구간만 표준 difflib으로 다듬는 하이브리드. 최단 편집 거리를 보장하진 않지만 빠르다.
  • myersdiff*.py — 정통 Myers O(ND) 알고리즘의 순수 파이썬 구현. SES(최단 편집 스크립트) 변형과 diff-match-patch 스타일 offset 기반 변형 두 가지가 있다. 둘 다 최단 편집 거리를 보장하지만, 서로 다른 입력 특성에서 성능이 갈린다.
  • _myersdiff_{ses,dmp}.pyx — 위 두 개를 Cython으로 포팅한 것. 빌드돼 있으면 이게 기본 백엔드다 (matchers.py가 import 시점에 dmp_cythonses_cythonstream 순으로 해석). 실측으로 폴백인 StreamSequenceMatcher보다 ~14배, 같은 알고리즘의 순수 파이썬 구현보다 ~300배 빠르다. 순수 파이썬 쌍둥이는 폴백 대상이 아니라 이름으로만 선택된다.

왜 이렇게 여러 벌이 존재하는지, 실제로 뭘 써야 하는지는 Diff Engines에서 더 깊게 다룬다.

pybsdiff.py — 바이너리 델타

텍스트가 아닌 콘텐츠(bytes)를 커밋할 때 쓰인다. BSDIFF40 포맷과 호환되도록 만들어져 있어서, 시스템에 설치된 네이티브 bsdiff/bspatch 도구로도 패치를 만들고 적용할 수 있다.

simple_rcs_gpg.py — 서명/검증

gpg 바이너리를 서브프로세스로 호출해서 서명을 만들고 검증한다. SRCS_GPGSIGN_PATH 환경변수로 gpg 실행 파일 경로를 바꿀 수 있다. SimpleRCS.sign_head()/verify()가 콜백 형태로 이 모듈의 함수들을 받아서 쓰는 구조라, GPG가 아닌 다른 서명 방식으로 바꾸고 싶으면 같은 콜백 시그니처로 새로 만들어 끼우면 된다.

adapters.py — DB에 직접 붙이기

SimpleRCS가 기대하는 건 BinaryIO뿐이라, psycopg2의 large object처럼 BinaryIO를 상속하지 않는 파일 비슷한 객체는 그대로 못 넘긴다. PsycopgLargeObjectAdapter가 그 틈을 메워서, PostgreSQL large object를 .srcs 파일 대신 그대로 백엔드로 쓸 수 있게 해준다. 이 어댑터가 왜 특별히 잘 맞는지는 Wiki Backend Design에서 다룬다.

tools/ — 명령줄 도구

각 스크립트는 SimpleRCS를 라이브러리로 가져다 쓰는 얇은 CLI 래퍼다. uv run tools/<이름>.py로 저장소 루트에서 바로 실행한다. 라이브러리 코드는 logging만 쓰고 절대 print하지 않는 게 원칙이지만, tools/ 아래 스크립트들은 CLI 출력이 본업이라 이 규칙에서 예외다. 각 도구가 정확히 뭘 하는지는 CLI Tools 참고.

빌드

pyproject.toml[build-system]setuptools + Cython을 쓰도록 설정돼 있고, ext-modules_myersdiff_ses/_myersdiff_dmp 두 Cython 확장이 등록돼 있다. uv sync할 때 같이 빌드된다. .pyx 파일을 고치면 다시 빌드해야 반영된다.

Clone this wiki locally