Community코딩 & 개발github.com

누가 변경했는가

로컬 git 기록만 사용하여 코드의 작성자, 티켓, 브랜치 및 풀 리퀘스트를 귀속시킵니다. 작성자 이름, 커밋 해시, 티켓 번호, 브랜치 이름, 날짜 그리고 커밋/PR 링크를 보고합니다.

누가 변경했는가란 무엇인가요?

이 스킬은 터미널을 떠나지 않고도 코드의 기여자를 추적할 수 있는 강력한 도구입니다. '누가 이것을 변경했는지', '이 함수를 작성한 사람', '마지막으로 이 파일을 작업한 사람'과 같은 질문에 대해 로컬 git 히스토리만으로 정확한 답변을 제공합니다. 전체 파일 모드, 라인 범위 모드, 그리고 원시 블레임 모드를 지원하며, 기본적으로 공백 무시(-w), 파일 내 이동(-M), 파일 간 복사(-C) 옵션을 사용하여 코드의 실제 작성자를 식별합니다. Bitbucket, GitHub, GitLab 리모트를 자동으로 감지하고 커밋 및 PR 링크를 제공합니다. 또한 티켓 번호를 커밋 메시지나 브랜치 이름에서 추출하며, 추측하지 않고 실제 기록에 기반하여 정보를 제공합니다. 자신감 레이블을 통해 스쿼시 병합, 로컬 전용 커밋, 통합 브랜치 등을 명확히 구분하여 보고하므로 사용자는 코드 이력을 신뢰할 수 있습니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Daniyal12333/claude-code-skills/tree/HEAD/who-changed

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

누가 변경했는가은(는) 무엇을 하나요?

"누가, 어떤 티켓으로, 언제 이것을 만들었는지" 터미널을 떠나지 않고 답합니다. 필요한 모든 것은 이미 로컬 git 기록에 있습니다. 코드 호스트가 제공하는 것은 PR 설명뿐이며, 출력 결과는 그 PR에 대한 링크를 제공합니다.

이것은 수동 루프를 대체합니다: 파일을 열고, 기능을 찾고, 블레임 뷰를 열고, 해시를 복사하고, 코드 호스트에 붙여넣고, PR로 클릭하여 이동하고, 티켓과 작성자를 읽는 과정을 한 번에 해결합니다.

사용자가 얻는 것

작성자 이름, 커밋 해시, 티켓 번호, 브랜치 이름, 날짜, PR 번호, 클릭 가능한 커밋 및 PR 링크. 범위에 대한 주요 판결 하나, 그다음 해당 범위를 건드린 모든 커밋의 테이블입니다.

스크립트 실행

bash ~/.claude/skills/who-changed/scripts/who-changed.sh <파일> [시작 끝] [raw]

스킬이 프로젝트별로 설치된 경우 경로는 .claude/skills/who-changed/scripts/who-changed.sh입니다.

  • <파일> 단독: 전체 파일 모드. 상위 기여자와 마지막 12개 커밋.
  • <파일> <시작> <끝> (또는 한 줄 번호): 라인 범위 모드. 커밋별 라인 소유권.
  • raw 추가: -w -C -M 없이 블레임. 사용자가 바이트 수준의 진실을 원할 때 사용합니다.

환경 변수 재정의: WHO_CHANGED_BASE는 PR 조회가 향하는 ref를 설정합니다 (기본값: origin/HEAD, 그다음 origin/main, origin/master, main, master); WHO_CHANGED_TICKET_REGEX는 티켓 패턴을 변경합니다 (기본값: [A-Z][A-Z0-9]+-[0-9]+).

스크립트는 탭으로 구분된 보고서를 출력하며, 저장소나 네트워크에 쓰지 않습니다. 사용자를 위해 재구성하세요 - 원시 TSV를 그대로 붙여넣지 마세요.

1단계: 대상을 라인 범위로 확인

스크립트는 숫자를 받으므로 함수 이름을 먼저 범위로 변환해야 합니다. Git의 -L :함수이름: 단축키는 언어별 함수 패턴에 의존하며, 많은 언어(특히 TypeScript 클래스 멤버)에서 no match로 실패합니다. 직접 범위를 확인하세요:

  1. 선언부를 grep으로 찾으세요. 예를 들어 ^\s*(public|private|protected)?\s*(async\s+)?<이름>\s*[(<], 그리고 화살표 함수 속성의 경우 <이름>\s*[:=]\s*(function|\(). HTML이나 CSS의 경우 리터럴 선택자, 키 또는 블록을 grep하세요.
  2. 해당 줄부터 시작하여 첫 번째 {에서 괄호 짝을 맞춰 닫는 줄을 찾습니다.
  3. 이름이 두 군데 이상 일치하면 일치 항목을 나열하고 어느 것인지 물어보세요. 절대 첫 번째를 조용히 선택하지 마세요 - 흔한 이름은 여러 파일에 존재하는 경우가 많습니다.

줄 번호는 작업 트리에서 가져오므로 사용자가 편집기에서 보는 것과 일치합니다.

명시적 범위(285-300, 285, L285-L300)가 주어지면 그대로 사용합니다. 파일 경로만 주어지면 전체 파일 모드를 사용합니다.

2단계: 출력 읽기

## meta 블록은 base(PR 조회가 향하는 ref), base_tip_date, web, host(링크 베이스, git remote에서 파생), 블레임 모드, 파일에 커밋되지 않은 변경 사항이 있는지 여부를 전달합니다.

## commits의 행은 소유한 줄 수로 정렬됩니다. 판결은 첫 번째 행입니다 - 해당 범위의 대부분을 소유한 커밋입니다. 범위가 진정으로 공유된 경우(3년에 걸쳐 4명이 만든 함수는 한 사람의 소유로 보고되어서는 안 됨) 차점자를 언급하세요.

스크립트는 Bitbucket, GitHub 및 GitLab 리모트에 대해 commit_url과 pr_url을 이미 빌드합니다. -인 경우 인식 가능한 리모트가 없음을 의미합니다 - 그렇게 말하고 링크는 생략하고 호스트를 임의로 만들지 마세요.

다음과 같이 제시하세요 (아래 이름은 가상입니다):

### calculateTotal - src/cart/cart.service.ts:42-61

**Alice Moreno** · `a1b2c3d` · 2023-02-08 · 20줄 중 13줄 소유
티켓 SHOP-88 · 브랜치 `SHOP-88-cart-totals` · PR #41

| 줄 | 작성자 | 커밋 | 날짜 | 티켓 | 브랜치 | PR |
|---|---|---|---|---|---|---|
| 13 | Alice Moreno | a1b2c3d | 2023-02-08 | SHOP-88 | SHOP-88-cart-totals | #41 |
| 6 | Ben Carter | 9f8e7d6 | 2026-03-11 | SHOP-310 | SHOP-310-rounding-fix | 병합되지 않음 |
| 1 | Chen Wei | 4c5d6e7 | 2024-06-20 | — | release-2024-06 | #112 |

그런 다음 링크, 신뢰도 플래그가 발생한 경우 주의 사항 줄을 추가합니다.

신뢰도 레이블 및 각각에 대해 말할 내용

레이블의미사용자에게 알리기
ok티켓 브랜치가 PR을 통해 병합됨추가 필요 없음
local-only커밋이 아직 base에 없음"아직 기본 브랜치에 병합되지 않음" - PR이 없음
no-pr-merge메인라인에 있지만, 이를 전달하는 PR 병합이 없음메인라인에 직접 커밋되었거나, 병합 제목이 규칙을 위반함
squash(#123) 접미사에서 PR 번호를 읽음스쿼시 병합됨, 따라서 해당 PR 내의 줄별 기록이 평탄화됨
low-no-ticket메시지나 브랜치에 티켓 없음티켓이 기록되지 않았다고 명시하세요. 추측하지 마세요
low-integration-branch브랜치 이름에 티켓이 없는 브랜치를 통해 병합됨PR은 통합 브랜치가 도착한 곳이며, 코드가 작성된 곳이 아님. 신뢰도 낮음
uncommitted아직 커밋되지 않음로컬 미커밋 편집; 판결에서 제외됨

오래된 코드는 종종 장기 통합 브랜치(develop, release-*, ui-dev)를 통해 도착합니다. 이러한 경우 정직한 답변은 "PR #112를 통해 메인라인에 병합되었지만, 해당 PR은 통합 브랜치입니다 - 이것이 코드가 작성된 PR이 아닙니다."

-w -C -M이 기본값인 이유

-w는 공백을 무시하고, -M은 파일 내에서 이동된 줄을 추적하며, -C는 다른 파일에서 복사된 줄을 추적합니다. 이는 단순한 장식이 아닙니다. 2023년에 작성되고 2026년에 다시 들여쓰기된 블록을 예로 들어보겠습니다 (예시 숫자):

  • 스마트 (-w -C -M): 9줄 중 9줄이 원저자(2023)에게 감 - 정확함
  • 원시: 9줄 중 8줄이 다시 들여쓰기한 사람(2026)에게 감 - 오래된 코드를 인정함

따라서 명명된 작성자는 바이트를 마지막으로 건드린 사람이 아니라 동작을 마지막으로 변경한 사람입니다. 중요할 때 그렇게 말하고 raw를 후속 조치로 제안하세요.

정직성 규칙

  • 절대 티켓 ID를 만들지 마세요. 커밋 메시지나 브랜치에 티켓이 없으면 —를 보고하고 티켓이 기록되지 않았다고 말하세요.
  • 절대 작성자를 만들지 마세요. 블레임이 결정적이지 않으면 어떤 커밋이 후보인지 말하세요.
  • "~을 통해 병합됨"과 "~에서 작성됨"을 구별하세요. 낮은 신뢰도 플래그가 발생할 때마다.
  • base_tip_date가 오래된 것으로 보이면 기본 ref가 최근에 가져오지 않았으며 최근 병합이 누락될 수 있다고 언급하세요. git fetch origin을 제안하세요 - 요청 없이 실행하지 마세요.

관련될 때 언급할 가치가 있는 한계

  • "이유"는 git에 없습니다. PR 설명, 리뷰어 및 티켓 제목은 코드 호스트 또는 트래커의 API와 자격 증명이 필요합니다. PR 링크가 연결 지점입니다.
  • 스쿼시 병합은 기록을 평탄화합니다. PR이 스쿼시된 경우 하나의 커밋이 전체 기능을 소유하며 그 안의 줄별 속성은 사라집니다.
  • --ancestry-path는 커밋을 메인라인으로 가져온 첫 번째 병합을 선택합니다. 중첩된 기능 브랜치의 경우 중간 병합일 수 있으며 티켓 자체의 PR이 아닐 수 있습니다.
  • 전체 파일 기여자는 이름 변경을 추적하지 않습니다. git shortlog에는 --follow가 없습니다. 커밋 목록은 이름 변경을 추적하지만 기여자 집계는 추적하지 않습니다.
  • -C는 때때로 복사-붙여넣기된 코드를 원본 파일에 귀속시켜 놀라게 할 수 있습니다.
  • PR 감지는 세 가지 병합 메시지 스타일을 인식합니다: Bitbucket (Merged in <브랜치> (pull request #N)), GitHub (Merge pull request #N from ...), 스쿼시 (... (#N)). 다른 스타일은 no-pr-merge로 처리됩니다.

스크립트를 사용할 수 없는 경우의 대체 방법

git blame -w -C -M -L <시작>,<끝> --date=short -- <파일>
git log -1 --date=short --pretty=format:"%H%n%an%n%ad%n%s%n%b" <sha>
git merge-base --is-ancestor <sha> origin/main     # 조상이 아님 -> 병합되지 않음
git log --merges --ancestry-path --reverse --pretty=format:"%h|%s" <sha>..origin/main | grep -m1 "pull request"
git branch --contains <sha>                        # 병합되지 않은 경우

병합 제목을 파싱하여 브랜치와 PR 번호를 얻습니다. 두 가지 주의사항: git shortlog는 명시적인 HEAD가 필요하며, 그렇지 않으면 조용히 stdin을 읽고 아무것도 반환하지 않으며, 브랜치 이름에 접두사가 붙을 수 있으므로(feature/SHOP-88-...) 티켓 정규식을 문자열 시작에 고정하지 마세요.

관련 스킬