Git 히스토리가 복잡해질수록 터미널에서 커밋 관계를 파악하기 어려워져요. 브랜치가 여러 개 얽혀있거나 머지 포인트가 많으면 더욱 그래요. 이런 상황을 해결하는 파이썬 CLI 도구를 직접 만들어보면서 Git 로그 시각화의 핵심을 파악해보려고 해요.
기본 Git 명령어부터 시작하기
macOS 터미널에서 가장 간단한 방법은 Git 자체 기능을 활용하는 거예요. 먼저 기본 명령어를 파이썬으로 래핑해서 더 편하게 사용해봐요.
#!/usr/bin/env python3
# git_visual.py - Git 로그 시각화 헬퍼
import subprocess
import sys
def run_git_log(options=""):
"""Git log 명령어를 실행하고 결과를 반환해요"""
cmd = f"git log --graph --oneline --all --decorate {options}"
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
return result.stdout
# 기본 실행
if __name__ == "__main__":
# 최근 20개 커밋만 보기
print(run_git_log("-20"))
이 스크립트를 실행하면 브랜치 구조가 ASCII 아트로 표현돼요. 하지만 색상이 없어서 구분이 어려워요. 색상을 추가해볼게요.
def colorize_output(text):
"""Git 출력에 색상을 추가해요"""
import re
# 브랜치명에 색상 추가
text = re.sub(r'\(([^)]+)\)', r'\033[33m(\1)\033[0m', text)
# 커밋 해시에 색상 추가
text = re.sub(r'^([*| ]+)([a-f0-9]{7})', r'\1\033[36m\2\033[0m', text, flags=re.MULTILINE)
return text
# 색상 있는 버전으로 출력
output = run_git_log("-20")
print(colorize_output(output))
GitPython으로 커밋 데이터 분석하기
Git 명령어 래핑만으로는 한계가 있어요. GitPython 라이브러리를 사용하면 커밋 데이터를 직접 다룰 수 있어요. 먼저 설치가 필요해요.
# macOS에서 pip 설치
pip3 install gitpython
이제 커밋 히스토리를 분석하는 도구를 만들어봐요.
from git import Repo
from datetime import datetime
import os
class GitVisualizer:
def __init__(self, repo_path="."):
"""현재 디렉토리 또는 지정된 경로의 Git 저장소를 열어요"""
self.repo = Repo(repo_path)
def analyze_commits(self, max_count=30):
"""커밋 통계를 분석해요"""
commits_data = []
for commit in self.repo.iter_commits(max_count=max_count):
# 각 커밋의 세부 정보 수집
data = {
'hash': commit.hexsha[:7],
'author': str(commit.author),
'date': datetime.fromtimestamp(commit.committed_date),
'message': commit.message.strip().split('\n')[0], # 첫 줄만
'files_changed': len(commit.stats.files)
}
commits_data.append(data)
return commits_data
def print_commit_tree(self):
"""커밋 트리를 예쁘게 출력해요"""
commits = self.analyze_commits()
for commit in commits:
# 시간대별로 다른 아이콘 사용
hour = commit['date'].hour
if 6 <= hour < 12:
icon = "🌅" # 아침
elif 12 <= hour < 18:
icon = "☀️" # 낮
else:
icon = "🌙" # 저녁/밤
print(f"{icon} [{commit['hash']}] {commit['author']}")
print(f" 📝 {commit['message']}")
print(f" 📁 {commit['files_changed']} files changed")
print()
# 사용 예시
viz = GitVisualizer()
viz.print_commit_tree()
이렇게 하면 커밋 시간대별로 다른 아이콘이 표시되고, 변경된 파일 수도 한눈에 볼 수 있어요. 개발자의 작업 패턴을 파악하는 데 유용해요.
Matplotlib로 인터랙티브 그래프 만들기
텍스트 기반 시각화의 한계를 넘어서 실제 그래프를 그려봐요. matplotlib를 사용하면 터미널에서도 간단한 그래프를 볼 수 있어요.
# macOS에서 matplotlib 설치
pip3 install matplotlib
import matplotlib.pyplot as plt
from collections import defaultdict
from git import Repo
class GitGraphPlotter:
def __init__(self, repo_path="."):
self.repo = Repo(repo_path)
def plot_commit_activity(self):
"""시간대별 커밋 활동을 그래프로 그려요"""
hourly_commits = defaultdict(int)
daily_commits = defaultdict(int)
# 최근 100개 커밋 분석
for commit in self.repo.iter_commits(max_count=100):
dt = datetime.fromtimestamp(commit.committed_date)
hourly_commits[dt.hour] += 1
daily_commits[dt.strftime('%A')] += 1
# 두 개의 서브플롯 생성
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
# 시간대별 커밋 그래프
hours = list(range(24))
counts = [hourly_commits[h] for h in hours]
ax1.bar(hours, counts, color='skyblue')
ax1.set_xlabel('Hour of Day')
ax1.set_ylabel('Number of Commits')
ax1.set_title('Commit Activity by Hour')
# 요일별 커밋 그래프
days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
day_counts = [daily_commits[d] for d in days]
ax2.bar(days, day_counts, color='lightgreen')
ax2.set_xlabel('Day of Week')
ax2.set_ylabel('Number of Commits')
ax2.set_title('Commit Activity by Day')
ax2.tick_params(axis='x', rotation=45)
plt.tight_layout()
# 터미널에서 바로 보기 위해 임시 파일로 저장
plt.savefig('/tmp/git_activity.png')
print("📊 그래프가 /tmp/git_activity.png에 저장됐어요")
# macOS에서 이미지 열기
os.system('open /tmp/git_activity.png')
plotter = GitGraphPlotter()
plotter.plot_commit_activity()
브랜치 관계를 네트워크 그래프로 표현하기
더 복잡한 브랜치 구조를 시각화하려면 네트워크 그래프가 필요해요. networkx와 graphviz를 활용해봐요.
# macOS에서 필요한 패키지 설치
pip3 install networkx
brew install graphviz # Homebrew 필요
pip3 install graphviz
import networkx as nx
from graphviz import Digraph
from git import Repo
class BranchVisualizer:
def __init__(self, repo_path="."):
self.repo = Repo(repo_path)
self.graph = Digraph('G', filename='/tmp/git_branches.gv')
self.graph.attr(rankdir='LR') # 왼쪽에서 오른쪽으로
def build_branch_graph(self):
"""브랜치 관계를 그래프로 구축해요"""
# 모든 브랜치 가져오기
branches = list(self.repo.branches)
for branch in branches:
# 브랜치 노드 추가
self.graph.node(branch.name,
shape='box',
style='filled',
fillcolor='lightblue' if branch == self.repo.active_branch else 'white')
# 최근 커밋 몇 개만 표시
for commit in list(branch.commit.iter_parents())[:3]:
commit_id = commit.hexsha[:7]
self.graph.node(commit_id,
label=f"{commit_id}\n{commit.message[:20]}...",
shape='ellipse')
self.graph.edge(branch.name, commit_id)
def render(self):
"""그래프를 이미지로 렌더링해요"""
self.build_branch_graph()
self.graph.render('/tmp/git_branches', format='png', view=True)
print("🌳 브랜치 그래프가 생성됐어요")
# 실행
visualizer = BranchVisualizer()
visualizer.render()
실시간 모니터링 CLI 도구 만들기
마지막으로 모든 기능을 통합한 실시간 모니터링 도구를 만들어봐요. curses 라이브러리를 사용하면 터미널에서 인터랙티브한 UI를 구현할 수 있어요.
import curses
from git import Repo
import time
class GitMonitor:
def __init__(self, stdscr):
self.stdscr = stdscr
self.repo = Repo(".")
curses.curs_set(0) # 커서 숨기기
def run(self):
"""메인 실행 루프예요"""
while True:
self.stdscr.clear()
height, width = self.stdscr.getmaxyx()
# 헤더 표시
header = f"📊 Git Repository Monitor - {self.repo.working_dir}"
self.stdscr.addstr(0, 0, header, curses.A_BOLD)
# 현재 브랜치 정보
current_branch = self.repo.active_branch
self.stdscr.addstr(2, 0, f"🌿 Current Branch: {current_branch}")
# 최근 커밋 5개 표시
self.stdscr.addstr(4, 0, "📝 Recent Commits:", curses.A_UNDERLINE)
y_pos = 5
for commit in list(self.repo.iter_commits(max_count=5)):
if y_pos >= height - 2:
break
commit_info = f" [{commit.hexsha[:7]}] {str(commit.author)[:15]} - {commit.message[:40]}"
self.stdscr.addstr(y_pos, 0, commit_info[:width-1])
y_pos += 1
# 브랜치 목록
self.stdscr.addstr(y_pos + 1, 0, "🔀 Branches:", curses.A_UNDERLINE)
y_pos += 2
for branch in self.repo.branches:
if y_pos >= height - 2:
break
marker = "→" if branch == current_branch else " "
self.stdscr.addstr(y_pos, 0, f" {marker} {branch.name}")
y_pos += 1
# 하단 정보
self.stdscr.addstr(height-1, 0, "Press 'q' to quit, 'r' to refresh")
self.stdscr.refresh()
# 키 입력 처리
key = self.stdscr.getch()
if key == ord('q'):
break
elif key == ord('r'):
continue
time.sleep(1) # 자동 새로고침
# curses 래퍼로 실행
def main(stdscr):
monitor = GitMonitor(stdscr)
monitor.run()
if __name__ == "__main__":
curses.wrapper(main)
이렇게 만든 도구들은 각각 다른 상황에서 유용해요. 간단한 히스토리 확인은 첫 번째 방법으로, 통계 분석이 필요하면 matplotlib를 활용하고, 복잡한 브랜치 구조는 graphviz로 시각화하면 돼요. 실시간 모니터링이 필요하면 curses 기반 도구를 사용하세요.
파이썬의 강력한 라이브러리 생태계 덕분에 Git 로그를 다양한 방식으로 시각화할 수 있어요. 각자의 프로젝트 특성에 맞는 방법을 선택해서 커스터마이징하면 더욱 효과적인 도구를 만들 수 있을 거예요.