git-coda — 원클릭 Ship 워크플로우
git-coda는 프로젝트 이름입니다; coda(종곡)는 악장의 마무리 부분으로, 전체 개발 이력을 하나의 음표로 수렴하여 main으로 돌려보냅니다. 워크플로우의 동사는 여전히 ship입니다.
목표 및 성공 기준
현재 작업 공간의 변경 사항을 다음 경로로 완전히 실행합니다:
현재 변경 → 최신 main 기반 새 브랜치 → 커밋 → PR → squash merge → main 복귀
사용자가 ship을 호출하면 전체 워크플로우 실행을 승인한 것으로 간주합니다. 프로세스는 연속적으로 완료되어야 하며, 브랜치 이름, 커밋 메시지, PR 제목, PR 본문, 충돌 해결 방법, 검증 성공 여부 또는 계속 진행 여부에 대한 확인을 요청하지 않습니다.
이 승인에는 다음이 포함됩니다:
- 최신
main의 온라인 기능과 호환되도록 현재 기능 코드를 수정합니다. - 이번 변경 또는 충돌로 인한 테스트, 린트, 타입, 빌드 및 CI 문제를 수정합니다.
- 필요한 수정 커밋을 만들고 반복적으로 푸시합니다.
- 병합 전에 최신
main을 동기화하고, 새로 발생한 충돌을 해결하고 재검증합니다.
다음 조건이 모두 충족되어야만 완료로 간주합니다:
- PR이
main에 성공적으로 병합되었습니다. "PR이 생성됨" 또는 "자동 병합이 활성화됨"을 완료로 간주하지 않습니다. - 필수 검사가 통과되었거나, 리포지토리에 실행 가능한 검사가 없습니다.
- 로컬에서
main으로 돌아와 병합된 커밋까지 최대한 fast-forward합니다.
핵심 실행 원칙
ship의 기본 결과는 "배포 성공"이지, "문제 발견 후 사용자에게 문제를 넘김"이 아닙니다.- 명령이 실패하면 먼저 원인을 진단하고 워크플로우를 복구합니다; 동일한 실패 명령을 기계적으로 반복하지 않습니다.
- 최신
origin/main의 기존 온라인 기능과 현재 변경 사항의 제품 목표를 공동 제약 조건으로 삼고, 둘 다 우선적으로 보존합니다. - 근본 원인을 수정하고, 테스트 건너뛰기, 단정문 삭제, 규칙 비활성화 또는
--no-verify사용으로 가짜 통과를 만들지 않습니다. - force push,
reset --hard, 커밋 폐기 또는 사용자 변경 사항 덮어쓰기 등 파괴적인 수단을 사용하지 않습니다. - 인증/권한 부족, 타인 승인 필요, 플랫폼 또는 네트워크 지속적 불가, 요구 사항 의도를 코드와 컨텍스트에서 정말로 판단할 수 없는 경우에만 일시 중지하고 사용자에게 처리해야 할 유일한 사항을 설명합니다.
0단계: 정보 수집
시작하기 전에 현재 상태를 확인합니다:
git status --short --branch
git branch --show-current
git diff --stat
git diff --cached --stat
git remote -v
gh auth status
동시에 현재 브랜치, 커밋되지 않은 변경 사항 및 푸시되지 않은 커밋을 기록하여 사용자 작업이 누락되지 않도록 합니다. 커밋되지 않은 변경 사항이 없으면 현재 브랜치가 origin/main에 비해 배포할 커밋이 있는지 계속 확인합니다; 둘 다 없으면 배포할 내용이 없다고 알리고 중지합니다.
다음 정보를 자동으로 결정합니다:
1. 브랜치 이름
- 사용자가 이미 제공한 경우 직접 사용합니다.
- 그렇지 않으면 변경 내용에 따라
<type>/<short-desc>형식으로 추론합니다. - 대상 리포지토리의 기존 브랜치 접두사 또는 명명 규칙을 따릅니다; 규칙이 없으면 의미 있는 이름을 사용합니다.
- 로컬 또는 원격에 동일한 이름의 브랜치가 이미 있는 경우, 충돌로 인해 중단되지 않도록 자동으로 짧은 접미사를 추가합니다.
2. 커밋 메시지
- 사용자가 이미 제공한 경우 직접 사용합니다.
- 그렇지 않으면 전체 diff를 기반으로 Conventional Commits 형식으로 생성합니다:
<type>(<scope>): <description>.
3. PR 제목 및 본문
- PR 제목은 기본적으로 커밋 메시지를 사용합니다.
- PR 본문은 diff를 기반으로 2~3개의 요약, 변경 파일 개요 및 검증 결과를 자동 생성합니다.
- PR 이름이나 본문을 별도로 묻지 않습니다.
결정 후 사용자에게 곧 사용할 브랜치와 커밋을 간략히 알린 후, 답변을 기다리지 않고 즉시 계속합니다.
1단계: 메인 브랜치 동기화
작업 공간 변경 사항이 있으면 먼저 고유 식별자가 있는 stash를 만들고 해당 참조를 기록합니다; 변경 사항이 없으면 빈 stash를 만들지 않습니다.
git stash push --include-untracked -m "git-coda: <timestamp>"
git fetch --prune origin
fetch 후 0단계에서 기록된 원래 브랜치와 원래 HEAD를 기준으로 배포할 커밋이 있는지 확인합니다. 원래 기능 브랜치에 이미 커밋이 있으면 해당 브랜치를 유지하고 origin/main을 직접 병합한 다음 이번 stash를 복원합니다; main으로 전환한 후 원래 브랜치에 커밋이 있는지 결정할 수 없습니다. 원래 브랜치에 배포할 커밋이 없는 경우에만 origin/main에서 배포 브랜치를 새로 만듭니다.
먼저 원격 HEAD 또는 리포지토리 설정에서 대상 메인 브랜치를 확인합니다; 아래 main은 자리 표시자 이름이며, 리포지토리가 master 등의 이름을 사용하는 경우 모두 실제 브랜치로 대체합니다.
사용자에게 최신 메인 브랜치의 커밋 해시를 알립니다.
로컬 main을 fast-forward할 수 없는 경우:
- 로컬에만 있는 커밋을 확인하고, 삭제하거나 덮어쓰지 않습니다.
- 최신
origin/main에서 직접 배포 브랜치를 생성하고 배포 프로세스를 계속합니다. - 최종적으로
main으로 돌아갈 때 이러한 로컬 커밋을 유지하고 로컬main이 덮어쓰이지 않았음을 명확히 설명합니다; 이는 이미 완료된 온라인 병합에 영향을 미치지 않습니다.
2단계: 브랜치 생성
git checkout -b <branch-name> origin/main
현재 작업이 배포할 커밋이 포함된 기능 브랜치에 이미 있는 경우 계속 사용할 수 있습니다; 먼저 최신 origin/main을 병합하고 "충돌 처리 절차"에 따라 문제를 해결합니다. 동일한 변경 사항을 중복 커밋하지 않습니다.
3단계: 변경 사항 복원 및 커밋
git stash apply <recorded-stash>
git add -A
git commit -m "<commit-message>"
stash apply에 충돌이 발생하면 "충돌 처리 절차"에 따라 직접 해결합니다. 모든 변경 사항이 복원 및 커밋되었는지 확인한 후에만 이번에 생성된 stash를 삭제합니다; 사용자의 기존 stash를 실수로 삭제하지 않습니다.
커밋 훅 실패는 검증 실패에 해당합니다: 근본 원인을 수정하고, 다시 스테이징한 후 다시 커밋합니다. --no-verify를 사용하지 않습니다.
충돌 처리 절차
다음 절차는 stash apply, 최신 main 병합, 원격 브랜치의 non-fast-forward 및 PR 병합 충돌에 적용됩니다.
-
사실을 확인합니다:
git status git diff --name-only --diff-filter=U git log --oneline --left-right origin/main...HEAD -
충돌 파일의 base, 현재 브랜치 및 상대방 버전을 개별적으로 검토하고 관련 호출자, 테스트, 타입, 구성 및 최근 커밋을 읽습니다. 충돌 마커만 보지 않고, 파일 전체를 맹목적으로
ours또는theirs로 선택하지 않습니다. -
양쪽의 의도를 판단합니다:
origin/main은 현재 온라인 동작을 나타내며, 이번 기능이 명시적으로 이를 대체하지 않는 한 반드시 유지해야 합니다.- 현재 브랜치는 이번 기능 목표를 나타내며, 충돌을 해소하기 위해 조용히 삭제해서는 안 됩니다.
- 양쪽 모두 유효한 기능을 추가하는 경우, 인터페이스, 데이터 흐름 및 경계 조건을 통합하여 두 기능이 동시에 성립하도록 합니다.
- 테스트 충돌은 최종 제품 동작에 따라 업데이트합니다; 잠금 파일 및 생성 파일은 리포지토리에서 지정한 도구를 사용하여 다시 생성합니다.
-
충돌 마커가 없도록 편집하고
git add를 실행한 후git diff --name-only --diff-filter=U가 비어 있는지 확인합니다. -
충돌 파일과 관련된 최소 검증을 실행한 다음, 리포지토리에서 요구하는 전체 검증을 실행합니다.
-
병합 또는 수정 커밋을 만들고 원래 프로세스를 계속합니다.
충돌 블록만으로 판단할 수 없는 경우, 구현, 테스트, 호출 체인, 커밋 기록 및 프로젝트 문서에서 증거를 계속 수집해야 합니다. 두 동작이 상호 배타적이고 제품 의도를 여전히 판단할 수 없는 경우에만 사용자에게 질문하고, 옵션, 영향 및 권장 결론을 한 번에 명확히 설명합니다.
3.5단계: 로컬 검증
현재 변경 사항 및 리포지토리 설명에 따라 기존 검증 명령을 찾습니다:
- 우선
AGENTS.md,CONTRIBUTING.md및README.md를 읽습니다. - 그런 다음
package.json,pyproject.toml,Makefile, CI 구성 등 프로젝트 파일을 확인합니다. - 필요한 경우 잠금 파일에 따라 프로젝트의 기존 패키지 관리자를 사용하여 종속성을 설치하며, 종속성을 함부로 업그레이드하지 않습니다.
- 먼저 변경 범위와 직접 관련된 검사를 실행한 다음, 리포지토리에서 요구하는 lint, typecheck, test 또는 build를 실행합니다.
- 신뢰할 수 있는 명령을 찾을 수 없는 경우, 어떤 검증을 건너뛰었는지와 그 이유를 사용자에게 명확히 알리고, 명령을 임의로 만들지 않습니다.
검증 실패 수정 루프
검증 실패 시 중단하지 말고 다음 루프에 따라 처리합니다:
- 전체 오류 출력을 유지하고 첫 번째 근본 원인을 찾습니다. 마지막 연쇄 오류만 수정하지 않습니다.
- 실패가 제품 코드, 테스트 예상, 타입, 형식, 종속성, 구성 또는 환경에서 비롯된 것인지 판단합니다.
- 온라인 기능과 현재 기능을 충족하는 최소한의 올바른 수정을 수행합니다:
- 우선 리포지토리의 기존 formatter 또는 lint autofix를 사용한 다음, 나머지 문제를 처리합니다.
- 코드 결함은 코드를 수정합니다.
- 테스트는 이번 기능이 의도적으로 동작을 변경하고 기존 예상이 더 이상 유효하지 않은 경우에만 업데이트합니다; 유효한 테스트를 약화시키거나, 건너뛰거나, 삭제하지 않습니다.
- 환경에 종속성이 누락된 경우 프로젝트 잠금 파일에 따라 환경을 복원합니다; 관련 없는 업그레이드를 수행하지 않습니다.
- 먼저 실패한 최소 검사를 다시 실행한 다음, 전체 필수 검사를 다시 실행합니다.
- 새로운 실패를 발견하면 계속 진단 및 수정하여 모두 통과할 때까지 반복합니다.
첫 번째 push 전에 발생한 수정은 현재 커밋에 포함할 수 있습니다; 첫 번째 push 후에 발생한 수정은 새 커밋을 만들고 정상적으로 push합니다. 히스토리를 정리하기 위해 force push하지 않습니다. 매 라운드마다 새로운 증거나 실질적인 진전이 있으면 계속 진행하며, 실패 횟수가 많다고 포기하지 않습니다.
4단계: 푸시 및 PR 생성
git push -u origin <branch-name>
푸시가 거부되면 먼저 분류하여 처리합니다:
- 원격에 동일한 이름의 브랜치에 관련 없는 내용이 있는 경우: 자동으로 고유한 브랜치 이름으로 변경하여 푸시합니다.
- 원격에 동일한 이름의 브랜치가 동일한 기능에 속하고 새 커밋이 있는 경우: fetch 후 원격 브랜치를 병합하고, 충돌을 해결하고, 검증한 후 다시 푸시합니다.
- 네트워크 또는 GitHub의 일시적 실패: 짧게 재시도하고 서비스 복구 상태를 확인합니다.
- 인증 또는 쓰기 권한이 누락된 경우: 전체 작업 현장을 보존하고 사용자에게 권한 완료를 요청합니다.
gh pr create를 사용하여 PR을 생성합니다:
- base는
main - head는 새 브랜치
- 제목은 커밋 메시지 사용
- 본문에는 2~3개의 요약, 변경 파일 개요, 검증 결과 및
🤖 Finale via git-coda포함
PR이 이미 존재하면 재사용하고 중복 생성하지 않습니다. 생성 성공 후 사용자에게 PR URL을 보여주고, 답변을 기다리지 않고 계속합니다.
5단계: CI 대기 및 수정
gh pr checks <pr-number> --watch 등 리포지토리에서 사용 가능한 명령을 사용하여 필수 검사를 기다립니다.
검사 실패 시:
gh pr checks,gh run view --log-failed등의 명령으로 실패한 작업과 로그를 읽습니다.- 로컬에서 재현 가능하면 먼저 재현하고, "검증 실패 수정 루프"에 따라 근본 원인을 수정합니다.
- 수정 사항을 커밋하고 정상적으로 푸시한 후 CI를 다시 기다립니다.
- 모든 필수 검사가 통과될 때까지 반복합니다.
검사가 장기간 pending 상태이면 workflow, 동시성 큐 및 GitHub 상태를 확인합니다; 실패한 작업을 다시 실행할 수 있으면 직접 처리합니다. 승인 권한이 있는 사람이 반드시 승인해야 하거나 외부 서비스가 지속적으로 사용 불가능한 경우에만 사용자 개입을 요청합니다.
6단계: 최신 main 동기화 및 Squash 병합
병합 전에 메인 브랜치를 다시 동기화합니다:
git fetch origin main
git merge --no-edit origin/main
충돌이 있으면 "충돌 처리 절차"에 따라 해결하고, 로컬 검증을 다시 실행하고, push하고, CI를 기다립니다. 그런 다음 직접 병합합니다:
gh pr merge <pr-number> --squash --delete-branch
병합 시 base 업데이트 또는 충돌이 발생하면 동기화, 해결, 검증, push, CI 대기의 루프를 다시 실행하고 다시 병합합니다. 리포지토리가 병합 큐를 지원하면 큐에 들어간 후 PR이 실제로 병합될 때까지 계속 대기합니다.
브랜치 보호를 우회하지 않습니다. 필수 승인, 서명 또는 권한이 누락된 경우 이는 외부 장애입니다: GitHub의 정확한 반환 정보를 표시하고, 완료된 단계와 사용자가 완료해야 하는 작업만 설명합니다.
7단계: main으로 복귀 및 결과 확인
git checkout main
git pull --ff-only origin main
git status --short --branch
gh pr view <pr-number> --json state,mergedAt,mergeCommit,url
PR 상태가 merged인지 확인하고 최종 main 커밋 해시를 출력합니다. 로컬 main에 사전에 존재하는 고유 커밋이 있어 fast-forward할 수 없는 경우, 이를 유지하고 파괴적인 처리를 하지 않습니다; 여전히 원격 PR이 병합되었는지 확인하고 로컬 상태를 명확히 설명합니다.
최종 상태를 출력합니다:
🚀 Ship 완료!
✓ 브랜치 <branch-name>이(가) main에 병합됨
✓ 로컬 검증 및 필수 CI 통과
✓ 현재 main에 있으며, 최신 코드 동기화 완료 (<commit-hash>)
✓ PR: <pr-url>
오류 처리 원칙
- 각 단계 작업 전에 수행 중인 작업을 간략히 설명합니다.
- 명령이 실패하면 먼저 진단, 수정 및 재시도하여 성공적인 병합을 목표로 계속합니다.
- force push,
reset --hard또는 기타 파괴적인 복구 방식을 사용하지 않습니다. - 충돌을 적극적으로 해결하고, 최신 온라인 기능과 현재 기능 목표를 동시에 보호합니다.
- 테스트, 린트, 타입 검사, 빌드, 커밋 훅 및 CI 실패를 적극적으로 수정합니다.
- 자동 추론된 브랜치 이름, 커밋 메시지, PR 제목, PR 본문 또는 계속 실행에 대한 확인을 요청하지 않습니다.
- 수정 가능한 실패, 여러 라운드의 디버깅 필요 또는 프로세스 지연을 장애로 간주하지 않습니다.
- 현재 환경에서 실제로 해결할 수 없는 외부 조건에서만 일시 중지합니다; 계속 작업할 수 있는 브랜치, 커밋 및 로그를 보존합니다.
- 일시 중지 시 명확하고 최소한의 실행 가능한 사용자 작업만 제공하며, 완료 후 원래 위치에서 계속할 수 있습니다.
일반적인 경계 상황
| 상황 | 처리 방식 |
|---|---|
| 현재 non-main 브랜치에 있고 작업 공간 변경 사항만 있음 | stash → origin/main 기반 새 브랜치 생성 → apply → 충돌 해결 |
| 현재 브랜치에 배포할 커밋이 이미 있음 | 커밋 유지, 최신 origin/main 병합 후 계속 |
| 커밋되지 않은 변경 사항 없음 | origin/main에 대한 배포할 커밋 확인; 둘 다 없으면 중지 |
| 브랜치 이름이 이미 존재함 | 자동으로 고유 접미사 생성; 동일 기능 원격 브랜치면 재사용 및 통합 |
stash apply 또는 병합 충돌 | 양쪽 기능 의도 분석, 통합 구현, 검증 후 계속 |
| lint/test/typecheck/build 실패 | 근본 원인 수정 및 루프 검증, 실패를 사용자에게 넘기지 않음 |
| CI 실패 | 로그 읽기, 로컬 재현, 수정, push, 다음 CI 라운드 대기 |
| 병합 전 main 업데이트 | 최신 origin/main 병합, 충돌 해결 및 검증 루프 재실행 |
gh 미로그인 | gh auth login 실행 안내 |
| 필수 수동 승인 또는 쓰기 권한 없음 | 현장 보존, 사용자가 완료해야 할 유일한 외부 작업 설명 |
사용자가 --no-squash 지정 | --merge 사용 |