콘텐츠로 이동

winemine-app 레포 관계도 + 안전한 직접 pull/push 가이드

Claude Code 없이 사람이 winemine-app 작업할 때 헷갈리기 쉬운 submodule·sibling 구조와 pull/push 함정을 정리. 작성일: 2026-05-20


winemine-app은 단독 레포가 아니라 2단 nested submodule 구조다. 그래서 VS Code Source Control panel에 최대 4개의 git 저장소가 동시에 보일 수 있다. 잘못된 위치에서 commit하면 commit이 detached HEAD에 묶여 사라지거나, pull 후에도 submodule 파일이 옛 상태로 남는 일이 자주 발생한다.

이 문서가 다루는 것:

  • (a) 레포들 간 sibling/참조/submodule 관계
  • (b) VS Code GUI에서 안전한 동작 / 절대 피해야 할 동작
  • (c) 시나리오별 정확한 명령어 (daily pull / 본체 작업 전파 / fresh clone)
  • (d) detached HEAD 함정과 복구
  • (e) Claude 없이 직접 작업 시 체크리스트

~/dev/
├── winemine-app/ [메인 — 모바일 앱 코드]
│ └── specs/ [submodule]
│ └── domain/
│ └── wine-research/ [nested submodule, snapshot]
├── wine-research/ [본체 — wine-research 작업 위치]
├── winemine-keyscreen/ [sibling — Phase 2 시안, frozen, read-only]
├── winemine-specs/ [본체 — specs submodule의 원본 (있을 수도, 없을 수도)]
└── winemine/ [sibling — 별개]

~/dev/wine-research/~/dev/winemine-app/specs/domain/wine-research/같은 원격을 가리키는 두 개의 클론이다. 동일 폴더가 아니라 디스크에 두 벌 존재한다.

디스크 경로원격 (origin)역할
~/dev/winemine-app/github.com/rocher71/winemine-app.git메인 (RN + Expo 앱)
~/dev/winemine-app/specs/github.com/rocher71/winemine-specs.gitsubmodule (도메인 명세)
~/dev/winemine-app/specs/domain/wine-research/github.com/rocher71/wine-research.gitnested submodule, read-only 스냅샷
~/dev/wine-research/github.com/rocher71/wine-research.git본체 — 편집·commit·push 정위치
~/dev/winemine-keyscreen/(별도)sibling, Phase 2 시안, 절대 수정 X
편집 위치
└─ ~/dev/wine-research/ ← 여기서만 편집·commit·push
│ push
github.com/rocher71/wine-research.git
│ submodule pin (특정 commit SHA로 고정)
~/dev/winemine-app/specs/domain/wine-research/ ← 자동 동기화 안 됨
│ submodule pin
~/dev/winemine-app/specs/
│ submodule pin
~/dev/winemine-app/ ← 앱 코드 작업 위치

핵심 개념:

  • submodule은 “포인터”다. 부모 레포는 자식의 commit SHA만 저장. 자식 폴더 안 파일은 별도로 동기화해야 함.
  • 본체와 submodule clone의 HEAD가 다른 게 정상이다. 본체는 latest, submodule은 부모가 pin한 옛 commit.
  • 3단 nested: winemine-app → specs → wine-research. 한 번 본체 갱신을 모두 반영하려면 3번 포인터 push가 필요.
~/dev/wine-research HEAD: a58f825 (최신, 본체)
~/dev/winemine-app/specs/domain/wine-research HEAD: 21a8c6c (옛 pin)

본체는 a58f825까지 진행됐는데 winemine-app/specs에서는 아직 21a8c6c에 머물러 있는 상태. 일반적으로 정상이며, 의도적으로 갱신할 때만 §4-2 시나리오를 돌린다.


3. VS Code GUI Source Control 안전 가이드

섹션 제목: “3. VS Code GUI Source Control 안전 가이드”

VS Code는 submodule을 자동 감지해서 panel에 별도 entry로 표시한다. 모든 entry에서 같은 버튼이 보이지만 누르면 안 되는 버튼이 있다.

순번이름 표시실제 경로권한
1winemine-app~/dev/winemine-app편집·commit·push OK
2specs~/dev/winemine-app/specs읽기 전용 취급 권장
3wine-research ...domain~/dev/winemine-app/specs/domain/wine-research읽기 전용 — detached HEAD
4wine-research ~/dev~/dev/wine-research편집·commit·push OK (본체)
동작entry 1 (메인)entry 4 (본체)
Sync Changes (pull + push)OKOK
CommitOKOK
Stage / DiscardOKOK
PushOKOK
PullOKOK

3-3. 위험한 GUI 동작 (절대 피할 것)

섹션 제목: “3-3. 위험한 GUI 동작 (절대 피할 것)”
동작entry 2 (specs) / entry 3 (nested wine-research)결과
Sync Changessubmodule이 detached HEAD인 채 원격 main으로 이동 시도부모가 pin한 commit과 mismatch 발생
Commitdetached HEAD에 commit 생성branch에 안 붙음 — checkout 바꾸면 잃어버림
Pushdetached HEAD 상태로 push 시도에러 또는 의도치 않은 branch 변경
안에서 직접 파일 편집submodule clone 안의 파일을 수정본체로 push 불가 — 변경 사라짐

규칙: entry 2·3은 panel에서 펼쳐 변경 사항을 확인만 한다. 작업은 entry 1(메인) 또는 entry 4(본체)에서.


4-1. Daily pull (다른 사람이 winemine-app에 push했음)

섹션 제목: “4-1. Daily pull (다른 사람이 winemine-app에 push했음)”
Terminal window
# 1) 메인 받기 (포인터까지 같이 받음)
cd ~/dev/winemine-app
git pull
# 2) submodule 폴더 안 파일을 새 포인터에 맞춰 동기화
git submodule update --init --recursive

git pull만 하면 specs/, specs/domain/wine-research/ 폴더 내용은 옛 상태 그대로다. git statusmodified: specs (new commits) 같은 표시가 뜨면 step 2를 안 한 상태. step 2 후엔 깨끗해진다.

GUI로 step 2 대체: 없음. 터미널 필수.

4-2. 본체 wine-research 편집 → winemine-app까지 전파 (3단 push)

섹션 제목: “4-2. 본체 wine-research 편집 → winemine-app까지 전파 (3단 push)”

본체에서 글을 쓰고 winemine-app 앱이 그 새 글을 참조하게 만드는 전체 흐름.

Terminal window
# === Step 1: 본체에서 작업 + push ===
cd ~/dev/wine-research
# 편집 작업...
git add .
git commit -m "docs: add new wine note"
git push # 본체 origin (wine-research.git) 갱신
# === Step 2: specs 안 submodule 포인터 갱신 ===
cd ~/dev/winemine-app/specs
git submodule update --remote domain/wine-research
# ↑ 이 명령은 specs/domain/wine-research/ 의 HEAD를 본체 origin의 main 최신으로 끌어옴
git add domain/wine-research # 포인터 변경 stage
git commit -m "chore: bump domain/wine-research submodule"
git push # specs origin (winemine-specs.git) 갱신
# === Step 3: winemine-app 안 specs 포인터 갱신 ===
cd ~/dev/winemine-app
git submodule update --remote specs
git add specs
git commit -m "chore: bump specs submodule"
git push # winemine-app origin 갱신

3단 모두 안 하면 다음 사람이 winemine-app pull해도 새 글이 안 보인다.

단축 옵션 (모든 submodule을 한 번에 최신으로):

Terminal window
cd ~/dev/winemine-app
git submodule update --remote --recursive
git add specs # 변경된 포인터만 stage
git commit -m "chore: bump all submodules"
git push
# 이 경우에도 specs 자체 push (step 2 후반부)는 별도로 해줘야 함

4-3. Fresh clone 셋업 (다른 머신에서 처음 clone)

섹션 제목: “4-3. Fresh clone 셋업 (다른 머신에서 처음 clone)”
Terminal window
cd ~/dev
git clone --recursive https://github.com/rocher71/winemine-app.git
# ↑ --recursive 가 없으면 specs/, specs/domain/wine-research/ 둘 다 빈 폴더로 옴
# 이미 --recursive 없이 clone했다면 사후 복구:
cd ~/dev/winemine-app
git submodule update --init --recursive
# 본체 wine-research도 별도 작업할 거면 sibling으로 따로 clone
cd ~/dev
git clone https://github.com/rocher71/wine-research.git
# 결과: ~/dev/wine-research/ 가 본체로 생성됨

다른 사람이 본체에 push한 글을 받기:

Terminal window
cd ~/dev/wine-research
git pull

winemine-app 쪽 submodule 포인터는 영향 없음. 본체 갱신을 winemine-app 앱에 반영하려면 §4-2 step 2·3 추가 필요.


submodule 폴더(specs/, specs/domain/wine-research/)는 부모가 특정 commit SHA로 고정하기 때문에 항상 detached HEAD 상태다. branch가 없는 게 정상.

문제는 이 폴더 안에서 파일을 수정하고 commit해버리는 경우. commit은 만들어지지만 branch에 안 묶여서:

  • 다른 branch checkout 시 사라짐
  • git pull 시 덮어씌워짐
  • push할 곳이 없음 (fatal: You are not currently on a branch)
Terminal window
cd ~/dev/winemine-app/specs/domain/wine-research
git status
# HEAD detached at 21a8c6c
# Changes not staged for commit:
# modified: site/src/content/docs/some-file.md

여기서 commit하면 위험.

submodule 폴더에서 작업하기 전 항상:

Terminal window
git status
# HEAD detached at ... 가 보이면 → 이 폴더에서 commit 금지
# 본체로 가서 같은 파일 편집

5-4. 이미 detached HEAD에 commit해버린 경우 복구

섹션 제목: “5-4. 이미 detached HEAD에 commit해버린 경우 복구”
Terminal window
cd ~/dev/winemine-app/specs/domain/wine-research
# 1) 잃어버리기 직전인 commit SHA 확인
git log --oneline -5
# ↑ 맨 위 SHA를 복사 (예: abc1234)
# 2) 본체에 같은 변경을 적용
cd ~/dev/wine-research
git checkout main
git cherry-pick abc1234
# ↑ 또는 변경 내용을 수동 복붙
# 3) 본체에서 정상 commit + push
git push
# 4) submodule 폴더는 원래 pin으로 되돌리기
cd ~/dev/winemine-app
git submodule update --init --recursive

복구 안 하고 그냥 두면 다음 git submodule update 호출 시 commit이 사라진다.

5-5. submodule 폴더 내용이 dirty 표시될 때

섹션 제목: “5-5. submodule 폴더 내용이 dirty 표시될 때”

~/dev/winemine-app에서 git status:

modified: specs (modified content)

원인 후보:

  • (a) submodule 폴더에서 누군가 수동 편집 → 본체로 옮긴 뒤 git submodule update --init --recursive로 reset
  • (b) submodule이 detached HEAD에서 새 commit이 있음 → §5-4 복구
  • (c) 본체에 새 commit이 push됐고 포인터를 의도적으로 갱신하려는 중 → §4-2 step 2 진행

확인 명령:

Terminal window
cd ~/dev/winemine-app
git diff specs # 포인터 SHA 변화만 보여줌
cd specs
git status # 내부 상태 자세히

6. Claude 없이 직접 작업할 때 체크리스트

섹션 제목: “6. Claude 없이 직접 작업할 때 체크리스트”
  • pwd로 현재 디렉터리 확인. submodule 내부에 있지는 않은가?
  • git status로 detached HEAD 여부 확인. detached면 위치 이동.
  • 편집할 파일이 본체 소속인지, 메인 앱 소속인지 명확히 판단
    • 도메인 문서 (site/src/content/docs/...) → ~/dev/wine-research/
    • 앱 코드 (app/, src/, supabase/) → ~/dev/winemine-app/
    • 도메인 명세 (specs/api/, specs/domain/policies/) → 본체 winemine-specs 따로 clone 후 거기서 (또는 PR로)
  • winemine-app pull → git submodule update --init --recursive 항상 함께
  • GUI Sync 버튼은 entry 1·4에서만 사용
  • entry 2·3 panel에서 발견되는 변경은 건드리지 말고 정체부터 파악
  • git status로 어느 레포에 commit되는지 확인 (On branch main 이어야 안전)
  • HEAD detached at 표시면 STOP — §5-3 절차로 진행
  • 메시지 컨벤션: winemine-app은 conventional commits (feat:, fix:, chore:, docs:)
  • 본체 → submodule → 메인 순서로 push (§4-2)
  • git push --force 금지 (특히 main)
  • CLAUDE.md §4 규칙 위반 점검:
    • emoji 금지 (npm run lint:emoji)
    • hardcoded hex 금지 (npm run lint:hex)
    • .env 또는 secret 포함 여부

6-5. Fresh clone 또는 새 머신 셋업 시

섹션 제목: “6-5. Fresh clone 또는 새 머신 셋업 시”
  • git clone --recursive 로 가져옴 (또는 직후 git submodule update --init --recursive)
  • ~/dev/wine-research/ 도 별도 clone (sibling으로)
  • ~/dev/winemine-keyscreen/ 필요하면 별도 clone (read-only)
  • .env 파일 생성 (.env.example 참고)
  • npm installnpx expo start 동작 확인
  • git status 깨끗한지
  • git submodule status — 모든 submodule이 의도한 SHA를 가리키는지
  • 원격 push 후 GitHub에서 winemine-app 메인 페이지에 submodule SHA 링크가 올바른지

Q. VS Code panel에서 entry 2·3을 숨길 수 있나? A. .vscode/settings.json"git.detectSubmodules": false. 다만 submodule 내부 변경(읽기 전용으로 봐야 할 경우조차) 인지 못 하게 되니 신중. 권장은 그대로 두고 §3 규칙으로 손대지 않는 것.

Q. entry 4 (~/dev/wine-research)는 왜 panel에 보이나? 분명 winemine-app workspace인데. A. VS Code workspace에 ~/dev/wine-research 폴더를 추가했거나, git.openRepositoryInParentFolders 설정이 부모 탐색을 허용해서. 의도된 멀티-루트면 그대로 유용 (본체 작업하기 편함). 원하지 않으면 workspace 설정에서 제거.

Q. submodule pin을 origin의 main이 아닌 다른 branch로 고정하고 싶다. A. .gitmodulesbranch = <name> 추가 후 git submodule update --remote 동작이 그 branch를 따라감.

Q. 본체에서 commit했는데 winemine-app 앱 동작에 반영이 안 된다. A. submodule 포인터 갱신을 안 했기 때문 (§4-2 step 2·3 누락). 또는 메인 앱이 specs/domain/wine-research/ 콘텐츠를 런타임에 참조하지 않는 경우라면 정상 (대부분의 경우 specs는 사람용 문서지 앱 런타임 input이 아님).

Q. --recursive 없이 clone했는데 폴더가 비어있다. A. git submodule update --init --recursive 한 번이면 복구.