맥북에서 하던 일을 맥 스튜디오에서 이어 가는 방법을 Git만으로 정리합니다. 퇴근 전 WIP 커밋과 push, 출근 후 fetch와 switch, stash가 다른 Mac으로 건너가지 못하는 이유, 두 Mac이 같은 브랜치를 만졌을 때의 pull --rebase와 --force-with-lease, 다른 Mac에 두고 온 커밋 찾기, 그리고 Mac마다 한 번씩 해 둘 인증·includeIf 설정까지 git 2.55 실제 출력으로 따라갑니다.
민지는 요즘 컴퓨터 두 대를 씁니다. 사무실 책상에는 맥 스튜디오가 있고, 가방에는 맥북이 있습니다. 낮에는 맥 스튜디오에서 Claude Code와 Codex를 두세 개씩 띄워 두고 일하다가, 저녁에는 맥북으로 집이나 카페에서 조금 더 이어 갑니다. 다음 날 아침에는 다시 맥 스튜디오 앞에 앉습니다.
처음 몇 주는 이렇게 지냈습니다.
민지: "어제 밤에 맥북에서 장바구니 기능 절반쯤 했는데… 맥 스튜디오에는 없네요. AirDrop으로 src 폴더를 통째로 보낼까요?"
도윤: "그러면 어느 쪽이 최신인지 금방 헷갈려요. 퇴근할 때 커밋해서 push만 해 두세요. 여기서는 fetch 한 번이면 됩니다."
민지: "아직 덜 끝난 건데 커밋해도 돼요? 저는 보통 git stash 해 두는데요."
도윤: "stash는 그 Mac 안에만 있어요. 맥 스튜디오에서는 안 보여요."
1편에서 세운 두 번째 원칙이 바로 이것입니다. Mac 사이 이동은 push / fetch로 한다. 폴더를 복사하거나 iCloud·Dropbox로 동기화하지 않고, 작업을 커밋으로 만들어 GitHub에 올렸다가 다른 Mac에서 받아 옵니다. 말로 하면 한 줄이지만 실제로 해 보면 질문이 줄줄이 따라옵니다.
이 글의 기준(2026년 9월) — 터미널 출력은 모두 git 2.55에서 직접 실행해 얻었고, 영어 출력(LC_ALL=C)으로 통일했습니다. 실제 Mac 두 대 대신 가짜 원격 저장소 하나(git init --bare)를 두 폴더 macbook/과 studio/에 각각 clone해서 "맥북"과 "맥 스튜디오"를 재현했습니다. 출력 속 원격 주소는 https://github.com/minji/cafe-menu.git으로, 폴더 경로는 /Users/minji/code/…로 바꿔 적었고 해시와 나머지 내용은 그대로입니다. 진행률(Rebasing (1/1))처럼 한 줄에서 덮어쓰이며 지나가는 표시는 화면에 최종적으로 남는 줄만 옮겼습니다.
1. 한 장으로 보는 길: 맥북 → GitHub → 맥 스튜디오
먼저 전체 그림입니다. 두 Mac은 서로 직접 이야기하지 않습니다. 둘 다 GitHub에 있는 같은 저장소만 바라봅니다.
비유하면 GitHub는 아파트 1층 무인 택배 보관함입니다. 맥북에서 일을 상자에 담아(커밋) 보관함에 넣어 두면(push), 맥 스튜디오가 보관함을 열어(fetch) 상자를 꺼냅니다(switch/pull). 두 Mac이 서로 주소를 알 필요도, 동시에 켜져 있을 필요도 없습니다.
여기서 중요한 사실이 하나 있습니다. 보관함에는 상자(커밋)와 상자 묶음 이름(브랜치)만 들어갑니다. 커밋하지 않은 수정, stash, 에이전트가 쓰던 worktree 폴더, .env 파일, 로컬 데이터베이스는 보관함에 들어가지 않습니다. 이번 편에서 생기는 문제의 대부분은 "보관함에 안 들어가는 것을 들어간 줄 알았다"에서 나옵니다.
무엇
push로 다른 Mac에 가나?
대신 어떻게
커밋한 변경
간다
그대로 push
브랜치 이름
간다 (push한 브랜치만)
새 브랜치는 처음에 git push -u
커밋 안 한 수정·새 파일
안 간다
WIP 커밋으로 만든 뒤 push (2절)
git stash
안 간다
WIP 커밋 (4절)
worktree 폴더 자체
안 간다
그 안의 브랜치를 push, 다른 Mac에선 새로 만든다 (8절)
.env·로컬 DB·node_modules
안 간다 (보내면 안 됨)
비밀번호 관리자·재생성 스크립트 (10절)
git 설정·SSH 키·로그인
안 간다 (Mac마다)
Mac마다 한 번 설정 (9절)
2. 퇴근 전 루틴(맥북): WIP 커밋하고 push
첫날 저녁, 민지는 맥북에서 주문 웹앱의 장바구니 기능을 만들고 있습니다. 작업은 main이 아니라 feature/order-cart 브랜치에서 합니다. 5시 10분쯤 첫 조각을 제대로 커밋해 두었습니다.
Switched to a new branch 'feature/order-cart'
[feature/order-cart fa49b3c] feat: 장바구니 담기 함수
1 file changed, 4 insertions(+)
그리고 6시 40분, 같은 메뉴를 수량으로 합치는 함수 mergeQty를 절반쯤 쓰다가 가방을 쌀 시간이 됐습니다. 테스트도 아직 안 돌렸고, 메모 파일 NOTES.md도 새로 만들어 두었습니다. 지금 상태를 봅니다.
bash
git status -sb
text
## feature/order-cart
M src/order.js
?? NOTES.md
-s는 짧게(short), -b는 브랜치 줄도 함께(branch) 보여 달라는 뜻입니다. 첫 줄 ## feature/order-cart 뒤에 아무것도 없다는 것은 이 브랜치가 아직 원격에 올라간 적이 없다는 뜻입니다(올라간 적이 있으면 ...origin/feature/order-cart가 붙습니다). M은 수정된 파일, ??는 git이 아직 모르는 새 파일입니다.
이 상태 그대로 맥북을 덮으면, 내일 아침 맥 스튜디오에는 절반짜리 mergeQty도, 브랜치도 없습니다. 그래서 퇴근 전 루틴은 세 줄입니다.
bash
git add -A
git commit -m "wip: 수량 합치기 절반, 테스트 전"
git push -u origin feature/order-cart
text
[feature/order-cart 4251e3b] wip: 수량 합치기 절반, 테스트 전
2 files changed, 5 insertions(+)
create mode 100644 NOTES.md
To https://github.com/minji/cafe-menu.git
* [new branch] feature/order-cart -> feature/order-cart
branch 'feature/order-cart' set up to track 'origin/feature/order-cart'.
한 줄씩 짚어 보겠습니다.
git add -A는 수정된 파일과 새 파일, 지운 파일까지 모두 스테이징합니다. 새 파일(NOTES.md)까지 챙겨야 하므로 -A를 씁니다. 다만 .env처럼 올리면 안 되는 파일이 .gitignore에 들어 있는지는 미리 확인해 두어야 합니다(10절). 이 저장소는 .gitignore에 .env, node_modules/, *.db가 들어 있어서 .env가 폴더에 있어도 목록에 나타나지 않았습니다.
커밋 메시지는 wip:으로 시작했습니다. WIP는 Work In Progress, "하던 중"이라는 뜻입니다. 이 커밋은 "완성품"이 아니라 "여기까지 했다는 메모"라는 표시입니다(4절에서 규칙을 정리합니다).
git push -u origin feature/order-cart에서 -u(--set-upstream)는 "앞으로 이 브랜치의 짝은 origin/feature/order-cart다"라고 기억시키는 옵션입니다. 마지막 줄 set up to track이 그 결과입니다. 처음 한 번만 붙이면 다음부터는 git push, git pull만 쳐도 됩니다(기존 시리즈 3편).
마지막으로 상태를 한 번 더 봅니다.
bash
git status -sb
text
## feature/order-cart...origin/feature/order-cart
이제 첫 줄에 ...origin/feature/order-cart가 붙었고, 뒤에 [ahead 1] 같은 꼬리표가 없습니다. "로컬과 원격이 같다"는 뜻입니다. 이 한 줄을 보고 맥북을 덮으면 됩니다.
① 본다
git status -sb — 어느 브랜치인지, 커밋 안 한 것(M·??)이 있는지
② 담는다
git add -A → git commit -m "wip: 어디까지 했는지" — 내일의 내가 읽을 메모처럼 쓴다
③ 올린다
처음이면 git push -u origin 브랜치, 두 번째부터는 git push
④ 확인한다
git status -sb 첫 줄에 [ahead N]이 없으면 끝
💡
새 브랜치마다 -u를 잊는다면 — -u 없이 git push만 치면 git 2.55는 "The current branch … has no upstream branch"라며 멈추고, 친절하게 push.autoSetupRemote 설정을 알려 줍니다(8절에서 실제 출력을 봅니다). git config --global push.autoSetupRemote true를 해 두면 첫 push에서도 -u를 자동으로 붙인 것처럼 동작합니다. 설정은 Mac마다 따로라는 점만 기억하세요.
3. 출근 후(맥 스튜디오): fetch하고 switch하면 끝
다음 날 아침 9시 10분, 맥 스튜디오 앞에 앉았습니다. 이 Mac의 cafe-menu 폴더는 며칠 전에 clone해 둔 그대로, main에 있습니다.
git branch -r는 이 Mac이 알고 있는 원격 브랜치 목록입니다(-r은 remote). 어젯밤 맥북이 올린 feature/order-cart가 없습니다. 맥 스튜디오는 아직 GitHub를 들여다보지 않았기 때문입니다. 원격 브랜치 목록은 "마지막으로 확인했을 때의 사진"이지 실시간 화면이 아닙니다.
그래서 먼저 fetch(가져와 보기)를 합니다. fetch는 원격의 새 커밋과 브랜치를 받아 오기만 하고, 내 작업 폴더의 파일은 건드리지 않는 안전한 명령입니다.
[new branch]라는 표시와 함께 origin/feature/order-cart가 생겼습니다. 이제 이 브랜치로 옮겨 갑니다. 여기서 많은 분이 git checkout -b feature/order-cart origin/feature/order-cart처럼 길게 치는데, 그럴 필요가 없습니다.
bash
git switch feature/order-cart
text
Switched to a new branch 'feature/order-cart'
branch 'feature/order-cart' set up to track 'origin/feature/order-cart'.
맥 스튜디오에는 feature/order-cart라는 로컬 브랜치가 없었는데도 git switch가 알아서 같은 이름의 원격 브랜치를 보고 로컬 브랜치를 새로 만들고, 짝(upstream)까지 연결해 주었습니다. git 문서는 이것을 --guess 동작이라고 부르며 기본으로 켜져 있습니다. 문서의 표현을 옮기면 "브랜치가 없지만 정확히 한 원격에 같은 이름의 추적 브랜치가 있으면 git switch -c <branch> --track <remote>/<branch>와 같게 취급한다"입니다. 원격이 둘 이상이고 같은 이름이 양쪽에 있으면 이 추측은 멈추고, checkout.defaultRemote 설정으로 어느 쪽을 쓸지 정할 수 있습니다.
어젯밤의 커밋이 그대로 와 있는지 봅니다.
bash
git log --oneline --decorate -4
git status -sb
text
4251e3b (HEAD -> feature/order-cart, origin/feature/order-cart) wip: 수량 합치기 절반, 테스트 전
fa49b3c feat: 장바구니 담기 함수
9e75567 (origin/main, origin/HEAD, main) feat: 주문 웹앱 첫 버전
## feature/order-cart...origin/feature/order-cart
해시 4251e3b, fa49b3c가 맥북에서 본 것과 똑같습니다. 같은 해시 = 같은 커밋이니, 맥북의 저녁 상태가 한 글자도 빠짐없이 옮겨 왔다는 증거입니다. 이제 절반짜리 mergeQty를 마저 쓰고, 이번엔 제대로 된 커밋으로 올립니다.
bash
git add -A
git commit -m "feat: 같은 메뉴 수량 합치기"
git push
text
[feature/order-cart d596e08] feat: 같은 메뉴 수량 합치기
2 files changed, 5 insertions(+), 2 deletions(-)
To https://github.com/minji/cafe-menu.git
4251e3b..d596e08 feature/order-cart -> feature/order-cart
이번에는 -u 없이 git push만 쳤습니다. git switch가 짝을 이미 연결해 두었기 때문입니다. 출력의 4251e3b..d596e08은 "원격 브랜치가 4251e3b에서 d596e08로 한 칸 전진했다"는 뜻입니다.
아래 시뮬레이터에서 이 흐름을 직접 눌러 보세요. 맥북 쪽에서 WIP 커밋 → push, 맥 스튜디오 쪽에서 fetch → switch를 차례로 누르면 원격과 두 Mac의 커밋 목록이 어떻게 맞춰지는지 보입니다. 다음 절에서 다룰 stash와, 6절의 push 거절도 같은 위젯에서 재현할 수 있습니다.
4. stash는 Mac을 건너지 못한다
같은 날 정오, 민지는 맥 스튜디오에서 메뉴판의 카페라테 가격을 4,500원에서 4,800원으로 올리고 있었습니다. 그런데 점심 약속 때문에 맥북만 들고 나가야 합니다. 손에 익은 습관대로 stash를 합니다.
bash
git status -sb
git stash push -m "메뉴 가격 올리던 중"
git stash list
git status -sb
text
## feature/order-cart...origin/feature/order-cart
M menu.md
Saved working directory and index state On feature/order-cart: 메뉴 가격 올리던 중
stash@{0}: On feature/order-cart: 메뉴 가격 올리던 중
## feature/order-cart...origin/feature/order-cart
stash는 "커밋 안 한 변경을 옆 서랍에 잠깐 넣어 두고 작업 폴더를 깨끗하게 만드는" 명령입니다. 마지막 git status -sb에 M menu.md가 사라진 것이 보입니다. 폴더는 깨끗해졌고, 변경은 stash@{0}이라는 서랍 칸에 들어갔습니다.
오후 1시, 카페에서 맥북을 엽니다. 이 맥북은 어젯밤 이후로 원격을 확인한 적이 없습니다.
bash
git status -sb
text
## feature/order-cart...origin/feature/order-cart
"원격과 같다"고 나옵니다. 하지만 이건 거짓말은 아니어도 옛날 이야기입니다.git status는 네트워크에 나가지 않고, 마지막 fetch 때 찍어 둔 origin/feature/order-cart 사진과 비교할 뿐입니다. 맥 스튜디오가 아침에 올린 커밋을 이 맥북은 아직 모릅니다. 그래서 다른 Mac에 앉으면 status보다 fetch가 먼저입니다.
bash
git fetch
git status -sb
text
From https://github.com/minji/cafe-menu
4251e3b..d596e08 feature/order-cart -> origin/feature/order-cart
## feature/order-cart...origin/feature/order-cart [behind 1]
이제야 [behind 1], 원격보다 한 커밋 뒤처져 있다고 정직하게 말합니다. 받아 옵니다.
아침에 맥 스튜디오에서 커밋한 mergeQty 완성본이 들어왔습니다. 그럼 정오에 stash한 카페라테 가격은요?
bash
git stash list
grep 카페라테 menu.md
text
- 카페라테 4500
git stash list는 아무것도 출력하지 않았습니다. 맥북의 서랍은 비어 있습니다. 메뉴판도 여전히 4,500원입니다. 원격 저장소에 무엇이 있는지 직접 들여다보면 이유가 보입니다.
bash
git ls-remote origin
text
9e7556731e6f7f07dfe826b38b536facc4807219 HEAD
d596e0804a89d7cb9bd32d44bd353caa7ddd3c6a refs/heads/feature/order-cart
9e7556731e6f7f07dfe826b38b536facc4807219 refs/heads/main
git ls-remote는 원격이 가진 이름표(ref) 목록을 보여 줍니다. refs/heads/…, 즉 브랜치만 있고 stash(refs/stash)는 없습니다. stash는 맥 스튜디오의 .git 폴더 안 refs/stash에만 기록되고, git push는 기본적으로 브랜치만 올리기 때문입니다. stash는 태생부터 "이 컴퓨터의 이 폴더에서 잠깐" 쓰는 도구입니다.
🧳
문제 — stash는 그 Mac의 서랍
맥 스튜디오에서 stash한 변경은 맥 스튜디오 .git/refs/stash에만 있습니다. push해도 올라가지 않고, 맥북의 git stash list는 비어 있습니다.
📦
해결 — Mac을 떠날 땐 WIP 커밋
자리를 뜰 때는 stash 대신 작업 브랜치에 wip: 커밋을 하고 push합니다. 커밋은 브랜치에 붙어 있으니 push와 함께 보관함으로 갑니다.
✅
결과 — stash는 "같은 Mac, 몇 분" 용도로만
pull 직전에 잠깐 치워 두기처럼 같은 폴더 안에서 금방 되돌릴 때는 stash가 여전히 편합니다. 다른 Mac, 다음 날, 다른 사람이 끼면 커밋입니다.
⚠️
"stash도 억지로 push할 수 있다던데요?" — stash 항목도 내부적으로는 커밋이라서, 이름을 지정해 브랜치처럼 밀어 올리는 요령이 인터넷에 돌아다닙니다. 하지만 받는 쪽에서 다시 stash로 되돌리는 과정이 번거롭고, 무엇이 들어 있는지 이름만 봐서는 알 수 없으며, 팀원이 보기에 정체 모를 브랜치가 생깁니다. 이 글은 권하지 않습니다. 같은 목적이라면 WIP 커밋이 더 짧고 투명합니다.
맥 스튜디오에 남겨 둔 가격 변경은 오후에 사무실로 돌아와서 이어 갑니다(6절). 그 전에 WIP 커밋을 어떻게 써야 기록이 지저분해지지 않는지부터 정리하겠습니다.
WIP 커밋 규칙: 막 해도 되지만 아무 데나 하지 않는다
"덜 끝난 걸 커밋해도 되나요?"라는 질문에 대한 답은 "된다. 단, 내 작업 브랜치에서만"입니다. 커밋은 공유 저장소에 박제되는 결재 서류가 아니라, 브랜치에서는 얼마든지 다시 쓸 수 있는 메모장입니다. 다만 메모장에도 규칙이 있어야 나중에 정리할 수 있습니다.
규칙
이렇게
이렇게는 말고
표시
제목을 wip:로 시작 — 한눈에 "정리할 커밋"임이 보임
asdf, 임시, .
내용
wip: 수량 합치기 절반, 테스트 전 — 어디까지 했고 뭐가 남았는지
wip 한 단어
장소
내 작업 브랜치(feature/…, agent/…)
main에 WIP 커밋·push
훅
pre-commit 훅이 있으면 그대로 통과시킨다. 훅이 막으면 훅이 가리킨 것을 고친다
훅을 건너뛰는 커밋 옵션으로 우회
비밀
git status -sb로 .env·키 파일이 끼지 않았는지 보고 커밋
급하다고 확인 없이 git add -A
마무리
PR 전에 squash로 정리 (rebase -i 또는 squash 머지)
WIP 커밋 그대로 main에 쌓기
main에는 왜 안 되나요? main은 팀 전체와 CI, 배포가 바라보는 브랜치입니다. main에 올라간 커밋은 다른 사람이 이미 받아 갔을 수 있어서 나중에 고쳐 쓰기(rebase, squash)가 위험해집니다. 또 main에 WIP가 올라가면 배포 파이프라인이 반쯤 만든 기능을 그대로 배포할 수도 있습니다. 실수로 main에서 작업을 시작했다면, 커밋하기 전에 git switch -c feature/새이름을 치면 됩니다. 커밋 안 한 변경은 브랜치를 옮길 때 그대로 따라오므로, 새 브랜치에서 WIP 커밋을 하면 됩니다.
훅은 왜 건너뛰지 말라고 하나요? git에는 커밋할 때 검사를 자동으로 돌리는 pre-commit 훅을 걸 수 있습니다(비밀 키가 들어갔는지, 포맷이 맞는지 등, 6편에서 다룹니다). git에는 이 훅을 건너뛰고 커밋하는 옵션도 있는데, "어차피 WIP니까" 하고 쓰기 쉽습니다. 하지만 비밀 키 검사 같은 훅은 WIP일수록 더 필요합니다. 급하게 git add -A한 커밋이야말로 .env가 섞여 들어가기 쉬운 커밋이고, push하는 순간 GitHub에 올라갑니다. 에이전트에게도 같은 규칙을 주세요. Claude Code나 Codex가 "훅이 실패해서 건너뛰고 커밋할게요"라고 하면 멈추게 하는 편이 낫습니다.
나중에 어떻게 정리하나요? 방법은 두 가지입니다.
PR을 squash로 머지하기 — GitHub의 "Squash and merge"를 쓰면 브랜치의 커밋이 몇 개든 main에는 커밋 하나로 들어갑니다. WIP 커밋이 main 기록에 남지 않습니다(기존 시리즈 6편의 squash 머지, 이 시리즈 5편에서도 씁니다).
PR 올리기 전에 git rebase -i로 직접 합치기 — 커밋 목록을 편집기로 열어 pick을 squash로 바꾸면 여러 커밋이 하나로 합쳐집니다(기존 시리즈 8편). 이 방법은 이미 push한 커밋의 해시를 바꾸므로 두 Mac이 모두 그 브랜치를 갖고 있을 때 주의가 필요합니다. 7절에서 실제로 해 보겠습니다.
5. 브랜치 이름 규칙: 사람 둘, Mac 둘, 에이전트 여럿
Mac이 한 대이고 사람이 한 명일 때는 브랜치 이름이 대충이어도 됩니다. 그런데 민지네처럼 사람 둘(민지·도윤) × Mac 여러 대 × 에이전트 여럿(Claude Code·Codex)이 한 저장소에 브랜치를 올리기 시작하면, git branch -r 목록이 금방 수십 줄이 됩니다. 이때 이름만 보고 "누가, 무엇을, 어떤 도구로" 하는 브랜치인지 알 수 있어야 합니다.
Mac 이름은 브랜치에 넣지 않습니다.feature/order-cart-macbook처럼 Mac마다 브랜치를 따로 만들면, 같은 일이 두 갈래로 쪼개져 결국 합쳐야 합니다. 브랜치는 작업 단위이고, 어느 Mac에서 만지든 같은 브랜치를 쓰는 것이 이번 편의 핵심입니다.
소문자와 하이픈만 씁니다. macOS의 기본 디스크(APFS)는 대소문자를 구분하지 않아서 Feature/Cart와 feature/cart가 같은 파일 이름으로 취급될 수 있습니다. 대소문자만 다른 브랜치 두 개가 원격에 있으면 Mac에서 이상한 일이 생깁니다.
슬래시로 묶은 이름은 폴더처럼 동작합니다.agent/search-claude가 있으면 agent라는 이름의 브랜치는 따로 만들 수 없습니다. 접두사는 접두사로만 씁니다.
3편에서 본 것처럼 Claude Code의 --worktree <이름>은 브랜치를 worktree-<이름>으로 만듭니다. 이 이름을 그대로 두어도 되고, push하기 전에 git branch -m agent/…로 바꿔 규칙에 맞춰도 됩니다(3편).
원격 브랜치 목록 정리: git fetch --prune
브랜치가 많아지면 이미 머지되고 GitHub에서 지워진 브랜치가 내 Mac의 목록에는 계속 남는 일이 생깁니다. 며칠 뒤 feature/order-cart PR이 squash로 머지되고 GitHub에서 브랜치가 삭제되었다고 해 봅시다. 순서상 이 장면은 6~7절을 모두 마친 뒤의 일이라 해시도 그 뒤의 것입니다(재현에서는 원격에서 squash 머지 후 브랜치를 지우는 것으로 GitHub의 PR 머지를 흉내 냈습니다). 맥북에서 보면 이렇습니다.
bash
git fetch
git branch -r
text
From https://github.com/minji/cafe-menu
9e75567..524c07f main -> origin/main
origin/HEAD -> origin/main
origin/agent/search-claude
origin/feature/order-cart
origin/main
main이 새로 받아졌는데도 origin/feature/order-cart가 여전히 목록에 있습니다. 평범한 git fetch는 새로 생긴 것은 가져오지만, 원격에서 사라진 것을 지우지는 않기 때문입니다. --prune(가지치기)을 붙입니다.
[deleted] 줄과 함께 목록이 실제 GitHub와 같아졌습니다. 로컬 브랜치는 어떻게 되었을까요?
bash
git branch -vv
text
+ agent/search-claude a4dac4e (/Users/minji/code/cafe-menu-search-claude) [origin/agent/search-claude] wip: 메뉴 검색 뼈대
* feature/order-cart 4ffac00 [origin/feature/order-cart: gone] fix: 제목 문구
main 9e75567 [origin/main: behind 1] feat: 주문 웹앱 첫 버전
git branch -vv(자세히, 두 번)는 로컬 브랜치마다 짝이 된 원격 브랜치와 앞섬/뒤처짐을 보여 줍니다. [origin/feature/order-cart: gone]은 "짝이던 원격 브랜치가 사라졌다", 즉 PR이 머지되고 지워졌다는 신호입니다. main으로 돌아가 최신화하고 로컬 브랜치도 지웁니다.
bash
git switch main
git pull
git branch -D feature/order-cart
text
Switched to branch 'main'
Your branch is behind 'origin/main' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)
Updating 9e75567..524c07f
Fast-forward
NOTES.md | 1 +
README.md | 1 +
index.html | 2 +-
menu.md | 2 +-
src/order.js | 11 +++++++++++
test/order.test.js | 2 ++
6 files changed, 17 insertions(+), 2 deletions(-)
create mode 100644 NOTES.md
create mode 100644 README.md
create mode 100644 test/order.test.js
Deleted branch feature/order-cart (was 4ffac00).
squash 머지된 브랜치는 원래 커밋들이 main에 그대로 들어간 것이 아니라서 -d(소문자)가 거절하므로 -D를 씁니다(기존 시리즈 6편 FAQ). 매번 --prune을 치기 귀찮다면 git config --global fetch.prune true를 해 두면 모든 fetch가 --prune처럼 동작합니다. 역시 Mac마다 한 번입니다.
6. 같은 브랜치를 두 Mac이 만졌을 때: 거절 → pull --rebase → push
이제 이번 편에서 가장 자주 만나는 장면입니다. 4절의 그 카페, 오후 1시 40분. 민지는 맥북에서 mergeQty 테스트를 추가해 커밋하고 push했습니다.
3시에 사무실로 돌아와 맥 스튜디오에서 정오에 stash해 둔 가격 변경을 꺼냅니다. 이번에는 끝까지 마무리해서 커밋하고 push합니다. fetch는 잊었습니다.
bash
git stash pop
git add menu.md
git commit -m "fix: 카페라테 가격 4800원"
git push
text
On branch feature/order-cart
Your branch is up to date with 'origin/feature/order-cart'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: menu.md
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (6bf0254a148d7244adf98ac32eed1c2c311e672d)
[feature/order-cart 4152c91] fix: 카페라테 가격 4800원
1 file changed, 1 insertion(+), 1 deletion(-)
To https://github.com/minji/cafe-menu.git
! [rejected] feature/order-cart -> feature/order-cart (fetch first)
error: failed to push some refs to 'https://github.com/minji/cafe-menu.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
stash pop의 첫 줄 "Your branch is up to date"는 4절에서 본 그 "옛날 사진" 이야기입니다. 그리고 push가 거절(rejected)되었습니다. 이유는 힌트에 그대로 쓰여 있습니다. "원격에 당신이 로컬에 갖고 있지 않은 작업이 있습니다. 보통은 다른 저장소가 같은 ref에 push했기 때문입니다." 그 "다른 저장소"가 바로 1시 40분의 맥북입니다.
이 거절은 git이 여러분을 지켜 주는 장치입니다. 만약 그냥 올라갔다면 맥북이 올린 테스트 커밋(630fa29)이 원격에서 사라졌을 겁니다. 무슨 일이 벌어졌는지 정확히 봅시다.
fetch 전에는 [ahead 1](내가 한 칸 앞섬)만 보였는데, fetch 후에는 [ahead 1, behind 1]입니다. 내 쪽에만 있는 커밋 1개(가격 수정), 원격에만 있는 커밋 1개(맥북의 테스트). 브랜치가 두 갈래로 갈라졌다(diverged)는 뜻입니다.
d596e08 feat: 같은 메뉴 수량 합치기 (두 Mac 모두 가진 마지막 커밋)
630fa29 · 맥북 test: 수량 합치기 테스트 원격에 있음 → behind 1
4152c91 · 맥 스튜디오 fix: 카페라테 가격 4800원 로컬에만 있음 → ahead 1
해결은 기존 시리즈 8편에서 배운 git pull --rebase입니다. 원격의 커밋을 먼저 받아 깔고, 내 커밋만 그 위에 다시 붙입니다.
bash
git pull --rebase
git log --oneline --graph -5
text
Successfully rebased and updated refs/heads/feature/order-cart.
* aef9566 fix: 카페라테 가격 4800원
* 630fa29 test: 수량 합치기 테스트
* d596e08 feat: 같은 메뉴 수량 합치기
* 4251e3b wip: 수량 합치기 절반, 테스트 전
* fa49b3c feat: 장바구니 담기 함수
그래프가 한 줄로 펴졌습니다. 맥북의 테스트 커밋 630fa29는 그대로이고, 맥 스튜디오의 가격 커밋은 그 위로 옮겨지면서 해시가 4152c91에서 aef9566으로 바뀌었습니다. rebase는 커밋을 새로 만들기 때문입니다. 하지만 이 커밋은 아직 어디에도 push되지 않았으므로 해시가 바뀌어도 아무에게도 피해가 없습니다. 이제 push합니다.
bash
git push
git status -sb
text
To https://github.com/minji/cafe-menu.git
630fa29..aef9566 feature/order-cart -> feature/order-cart
## feature/order-cart...origin/feature/order-cart
--force 같은 것은 전혀 필요 없었습니다. 평범한 push가 통과했습니다.
🧭
왜 그냥 git pull(merge)이 아니라 --rebase인가 — 두 Mac을 오가는 사람은 하루에도 몇 번씩 이 상황을 만납니다. 매번 merge로 받으면 "Merge branch 'feature/order-cart' of https://github.com/…" 같은 머지 커밋이 브랜치에 계속 쌓여, 나중에 PR을 볼 때 실제 작업 커밋이 묻힙니다. 같은 사람의 같은 작업이 두 Mac에서 조금씩 엇갈린 것이니 한 줄로 펴는 편이 자연스럽습니다. 팀 전체가 쓰는 규칙으로 정하고 싶다면 git config --global pull.rebase true(Mac마다)를 해 두세요. 충돌이 나면 기존 시리즈 8편의 「rebase 중 충돌」 절과 똑같이 풀면 됩니다.
이 장면은 앞의 시뮬레이터에서도 재현됩니다. 두 Mac 모두 파일 고치기 → WIP 커밋을 한 뒤, 한쪽만 먼저 push하고 다른 쪽에서 push를 눌러 보세요. 거절 메시지가 나오면 pull --rebase → push로 풀립니다.
7. 기록을 다시 쓸 때: squash 후 --force-with-lease, 그리고 다른 Mac 뒷정리
그다음 날 아침, PR을 올리기 전에 맥북에서 브랜치를 정리하려고 합니다. 지금 브랜치에는 main 위로 커밋이 여섯 개 있습니다.
그런데 그 사이 맥 스튜디오에서도 한 일이 있었습니다. 9시 5분에 도윤의 부탁으로 제목 문구를 고쳐 push했습니다(2f1f8e2 fix: 제목 문구). 맥북은 이 사실을 아직 모릅니다. 이 상태에서 맥북이 squash를 합니다.
bash
git log --oneline main..feature/order-cart
text
713b42d docs: README 한 줄 소개
aef9566 fix: 카페라테 가격 4800원
630fa29 test: 수량 합치기 테스트
d596e08 feat: 같은 메뉴 수량 합치기
4251e3b wip: 수량 합치기 절반, 테스트 전
fa49b3c feat: 장바구니 담기 함수
git rebase -i main을 치면 편집기에 커밋 목록이 pick 해시 # 제목 형식(git 2.55, 오래된 커밋이 위)으로 열립니다. 첫 줄만 pick으로 두고 나머지를 squash로 바꾸면(아래는 바꾼 뒤의 모습) 여섯 커밋이 하나로 합쳐지고, 이어서 합친 커밋의 메시지를 쓰는 편집기가 열립니다.
text
pick fa49b3c # feat: 장바구니 담기 함수
squash 4251e3b # wip: 수량 합치기 절반, 테스트 전
squash d596e08 # feat: 같은 메뉴 수량 합치기
squash 630fa29 # test: 수량 합치기 테스트
squash aef9566 # fix: 카페라테 가격 4800원
squash 713b42d # docs: README 한 줄 소개
text
[detached HEAD 0e0d726] feat: 장바구니와 같은 메뉴 수량 합치기
Date: Fri Oct 9 17:10:00 2026 +0900
5 files changed, 16 insertions(+), 1 deletion(-)
create mode 100644 NOTES.md
create mode 100644 README.md
create mode 100644 test/order.test.js
Successfully rebased and updated refs/heads/feature/order-cart.
bash
git log --oneline main..feature/order-cart
git status -sb
커밋 하나로 깔끔해졌습니다. 그런데 상태가 [ahead 1, behind 6]입니다. 원격의 커밋 여섯 개(squash 전 원본들)가 로컬에는 "없는" 것으로 보이고, 로컬의 새 커밋 하나는 원격에 없습니다. 기록을 다시 썼으니 당연한 결과입니다. 평범한 push는 당연히 거절됩니다.
bash
git push
text
To https://github.com/minji/cafe-menu.git
! [rejected] feature/order-cart -> feature/order-cart (fetch first)
error: failed to push some refs to 'https://github.com/minji/cafe-menu.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
여기서 git pull을 하면 squash가 무의미해집니다(원본 여섯 개가 다시 섞여 들어옵니다). 이럴 때 쓰는 것이 --force-with-lease입니다. --force가 "원격을 무조건 내 것으로 덮어라"라면, --force-with-lease는 "원격이 내가 마지막으로 본 그 상태 그대로일 때만 덮어라"입니다. lease는 "임대 계약"이라는 뜻으로, 내가 알고 있는 상태라는 계약이 유효할 때만 덮어쓰기를 허락합니다.
bash
git push --force-with-lease
text
To https://github.com/minji/cafe-menu.git
! [rejected] feature/order-cart -> feature/order-cart (stale info)
error: failed to push some refs to 'https://github.com/minji/cafe-menu.git'
거절되었습니다. 그리고 이게 정답입니다.(stale info)는 "네가 아는 정보가 낡았다"는 뜻입니다. 맥북이 마지막으로 본 원격은 713b42d였는데, 실제 원격은 맥 스튜디오가 올린 2f1f8e2까지 가 있습니다. 만약 --force를 썼다면 맥 스튜디오의 제목 수정 커밋은 아무 경고 없이 원격에서 사라졌을 것입니다. 무엇이 새로 왔는지 확인합니다.
bash
git fetch
git status -sb
git log --oneline feature/order-cart..origin/feature/order-cart -1
[feature/order-cart 4ffac00] fix: 제목 문구
Date: Sun Oct 11 09:05:00 2026 +0900
1 file changed, 1 insertion(+), 1 deletion(-)
To https://github.com/minji/cafe-menu.git
+ 2f1f8e2...4ffac00 feature/order-cart -> feature/order-cart (forced update)
4ffac00 fix: 제목 문구
0e0d726 feat: 장바구니와 같은 메뉴 수량 합치기
9e75567 feat: 주문 웹앱 첫 버전
이번에는 (forced update)로 올라갔습니다. fetch로 원격의 최신 상태(2f1f8e2)를 보고 난 뒤, 그 내용을 내 쪽에 반영한 뒤에 덮었기 때문에 lease가 유효했고, 잃어버린 커밋도 없습니다.
다른 Mac(맥 스튜디오)의 뒷정리
이제 맥 스튜디오 차례입니다. 맥 스튜디오의 로컬 브랜치에는 squash 전 원본 일곱 개가 그대로 있습니다.
bash
git fetch
git status -sb
git pull
text
From https://github.com/minji/cafe-menu
+ 2f1f8e2...4ffac00 feature/order-cart -> origin/feature/order-cart (forced update)
## feature/order-cart...origin/feature/order-cart [ahead 7, behind 2]
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint: git config pull.rebase false # merge
hint: git config pull.rebase true # rebase
hint: git config pull.ff only # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: Need to specify how to reconcile divergent branches.
fetch 출력의 +와 (forced update)가 "누군가 이 브랜치의 기록을 다시 썼다"는 신호입니다. 이때 git pull로 merge해 버리면 squash 전 원본이 다시 섞입니다. 먼저 맥 스튜디오에만 있는 것이 정말 없는지 확인합니다.
2f1f8e2 fix: 제목 문구
713b42d docs: README 한 줄 소개
aef9566 fix: 카페라테 가격 4800원
630fa29 test: 수량 합치기 테스트
d596e08 feat: 같은 메뉴 수량 합치기
4251e3b wip: 수량 합치기 절반, 테스트 전
fa49b3c feat: 장바구니 담기 함수
일곱 개 모두 이미 squash된 커밋에 들어간 원본들입니다(2f1f8e2도 맥북이 cherry-pick해 갔습니다). 맥 스튜디오에서만 새로 한 일이 없음을 확인했으니, 로컬 브랜치를 원격에 맞춥니다.
bash
git reset --hard origin/feature/order-cart
git status -sb
text
HEAD is now at 4ffac00 fix: 제목 문구
## feature/order-cart...origin/feature/order-cart
⚠️
기록을 다시 쓰는 push의 세 가지 규칙
① 내 브랜치에서만. 도윤도 받아 간 브랜치, main, release 브랜치에는 절대 쓰지 않습니다. GitHub의 브랜치 보호 규칙으로 main의 강제 push를 아예 막아 두세요(6편).
② --force 말고 --force-with-lease. stale info로 거절되면 "다른 Mac이 뭔가 올렸구나" 하고 fetch부터 합니다.
③ 다른 Mac에서는 reset --hard 전에 git log origin/브랜치..브랜치를 꼭 봅니다. 그 Mac에만 있는 새 커밋이 있으면 먼저 cherry-pick으로 옮겨야 합니다. reset --hard는 커밋 안 한 변경도 지우므로 git status도 함께 확인합니다.
squash를 GitHub의 "Squash and merge"에 맡기면 이 절의 과정이 통째로 필요 없어집니다. 두 Mac을 오가는 사람에게는 그쪽이 더 편한 경우가 많습니다.
8. "다른 Mac에 뭘 두고 왔지?" 찾는 법
맥 스튜디오 앞에서 "어제 맥북에서 한 게 다 올라갔나?"를 확인하는 방법은 사실상 없습니다. 보관함에 넣지 않은 것은 보관함에서 보이지 않기 때문입니다. 그래서 확인은 항상 떠나는 Mac에서, 떠나기 전에 합니다. 6절이 끝난 그날 저녁 6시 20분, 맥북의 상황을 일부러 복잡하게 만들어 봤습니다.
feature/order-cart에 README 커밋을 하나 했지만 push는 안 했다.
에이전트용으로 worktree를 하나 만들어agent/search-claude 브랜치에서 메뉴 검색 뼈대를 커밋했다. 이 브랜치도 push 안 했다.
"원격에는 없고 로컬에만 있는 커밋"입니다. 반대로 feature..origin/feature는 "받아야 할 커밋"입니다. 방향만 기억하면 됩니다. 점 두 개 오른쪽에 있고 왼쪽에 없는 것이 나옵니다.
도구 3: git branch -vv — 모든 로컬 브랜치를 한 번에
bash
git branch -vv
text
+ agent/search-claude a4dac4e (/Users/minji/code/cafe-menu-search-claude) wip: 메뉴 검색 뼈대
* feature/order-cart 713b42d [origin/feature/order-cart: ahead 1] docs: README 한 줄 소개
main 9e75567 [origin/main] feat: 주문 웹앱 첫 버전
세 줄에 모든 게 들어 있습니다.
*는 지금 이 폴더에서 쓰는 브랜치, +는 다른 worktree에서 체크아웃 중인 브랜치이고 괄호 안이 그 폴더입니다.
feature/order-cart에는 [… ahead 1] — push 안 한 커밋 1개.
agent/search-claude에는 대괄호 자체가 없습니다. 짝(upstream)이 없다, 즉 한 번도 push되지 않은 브랜치라는 뜻입니다. 이게 가장 놓치기 쉬운 경우입니다. git status -sb는 지금 폴더의 브랜치만 보여 주므로 이 브랜치는 status로는 절대 안 보입니다.
도구 4: git log --branches --not --remotes — 어디에도 안 올라간 커밋 전부
bash
git log --branches --not --remotes --oneline
text
a4dac4e wip: 메뉴 검색 뼈대
713b42d docs: README 한 줄 소개
이 한 줄이 "떠나기 전 최종 점검"으로 가장 좋습니다. 모든 로컬 브랜치(--branches)의 커밋 중에서 어떤 원격 브랜치(--remotes)에도 없는 것을 모아 보여 줍니다. 브랜치가 몇 개든, worktree가 몇 개든 한 번에 잡힙니다. 아무것도 안 나오면 이 Mac의 모든 커밋이 원격에 있다는 뜻입니다.
worktree는 Mac마다 따로입니다. worktree 목록은 각 Mac의 .git 폴더 안에 기록되고, push로 전달되지 않습니다(2편). 맥북에서 에이전트가 쓰던 worktree는 맥 스튜디오에 없고, 필요하면 맥 스튜디오에서 브랜치를 fetch한 뒤 새로 만들어야 합니다. 그러려면 먼저 그 브랜치가 push되어 있어야 합니다. 맥 스튜디오에서 확인해 봐도 agent/search-claude는 흔적도 없습니다.
bash
git branch -vv
git fetch
git branch -r
text
* feature/order-cart aef9566 [origin/feature/order-cart] fix: 카페라테 가격 4800원
main 9e75567 [origin/main] feat: 주문 웹앱 첫 버전
origin/HEAD -> origin/main
origin/feature/order-cart
origin/main
다 올리기
다음 날 아침, 맥북에서 두 브랜치를 모두 올립니다. 에이전트 worktree 안에서 그냥 git push를 치면 이렇게 됩니다.
bash
cd ../cafe-menu-search-claude
git push
text
fatal: The current branch agent/search-claude has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin agent/search-claude
To have this happen automatically for branches without a tracking
upstream, see 'push.autoSetupRemote' in 'git help config'.
처음 올리는 브랜치라 짝이 없어서입니다. 안내대로 합니다.
bash
git push -u origin agent/search-claude
text
To https://github.com/minji/cafe-menu.git
* [new branch] agent/search-claude -> agent/search-claude
branch 'agent/search-claude' set up to track 'origin/agent/search-claude'.
아래 체크리스트는 이 절의 내용을 "자리 뜨기 전 3분"으로 묶은 것입니다. 해당하는 항목을 체크하면 지금 칠 명령을 순서대로 만들어 줍니다.
9. Mac마다 한 번: 새 Mac 설정 체크리스트와 includeIf
지금까지는 저장소 안의 이야기였습니다. 그런데 Mac을 바꿔 앉을 때 생기는 문제의 절반은 저장소 밖, 즉 각 Mac의 설정에서 나옵니다. 설정은 push로 가지 않으므로 Mac마다 한 번씩 해야 합니다.
① GitHub 인증: Mac마다 따로
HTTPS를 쓴다면 gh auth login. GitHub CLI의 도움말은 이 명령이 기본적으로 브라우저로 로그인하고 "토큰을 시스템의 자격 증명 저장소에 안전하게 보관한다"고 설명합니다. 저장소를 찾지 못하면 평문 파일에 쓴다는 단서도 있으니 gh auth status로 보관 위치를 한 번 확인하세요. 이어서 gh auth setup-git을 치면 git이 push/fetch할 때 gh를 자격 증명 도우미로 쓰도록 설정됩니다.
SSH를 쓴다면 Mac마다 키를 새로 만듭니다. 한 Mac의 비밀 키를 다른 Mac으로 복사하지 마세요. Mac마다 키를 따로 두면, 맥북을 잃어버렸을 때 GitHub 설정에서 맥북 키만 지우면 됩니다. GitHub 문서가 안내하는 macOS 명령은 아래와 같습니다. -C의 설명 문구에 Mac 이름을 넣어 두면 GitHub 키 목록에서 어느 Mac 것인지 구분하기 쉽습니다.
자격 증명 도우미(credential helper) — HTTPS로 push할 때 비밀번호(토큰)를 매번 묻지 않게 해 주는 것이 credential helper입니다. macOS에서는 키체인에 보관하는 osxkeychain이 흔히 기본으로 잡혀 있습니다. 이 Mac에서 확인해 보니 Homebrew로 설치한 git과 Xcode에 딸린 git 모두 기본 설정 파일에 osxkeychain이 들어 있었습니다(아래는 임시 HOME에서 확인한 결과).
이 두 줄을 새 Mac에서 잊으면, 그 Mac에서 만든 커밋만 이상한 이름(예: minji@Minjis-Mac-Studio.local처럼 Mac 이름이 섞인 주소)으로 기록되거나 커밋 자체가 거부됩니다. 두 Mac의 커밋이 GitHub에서 다른 사람처럼 보이는 가장 흔한 원인입니다.
③ 회사 메일과 개인 메일 나누기: includeIf
민지는 한 Mac에서 회사 저장소(cafe-menu)와 개인 블로그 저장소를 모두 다룹니다. 회사 저장소 커밋에는 회사 메일이, 개인 저장소에는 개인 메일이 찍혀야 합니다. 저장소마다 git config user.email을 따로 해도 되지만, 새로 clone할 때마다 잊기 쉽습니다. 폴더 기준으로 자동으로 바뀌게 하는 것이 includeIf(조건부 포함)입니다.
아래는 사용자 설정을 건드리지 않도록 임시 HOME 폴더를 따로 만들어 재현한 결과입니다(경로는 /Users/minji로 바꿔 적었습니다).
bash
cat ~/.gitconfig
cat ~/.gitconfig-work
text
[user]
name = Minji Kim
email = minji.personal@example.com
[init]
defaultBranch = main
[includeIf "gitdir:~/work/"]
path = ~/.gitconfig-work
[user]
email = minji@cafe-company.example
(위 출력에서 마지막 두 줄이 ~/.gitconfig-work 파일의 내용입니다.) 뜻은 이렇습니다. "기본 이메일은 개인 메일. 단, 저장소의 .git 폴더가 ~/work/ 아래에 있으면~/.gitconfig-work도 읽어라." 나중에 읽힌 설정이 앞의 것을 덮으므로 회사 메일이 이깁니다. 이제 폴더마다 확인해 봅니다.
bash
cd ~/personal/blog && git config user.email
cd ~/work/cafe-menu && git config user.email
git config --show-origin user.email
~/work/notes처럼 git 저장소가 아닌 폴더에서 git config user.email
개인 메일이 나옴
gitdir은 이름 그대로 .git 폴더의 위치를 봅니다. git init 후에는 회사 메일로 바뀌었습니다
~/work/cafe-menu의 worktree를 ~/elsewhere/에 만든 경우
회사 메일이 나옴
linked worktree의 git 폴더는 원래 저장소의 .git/worktrees/…이므로 원래 저장소 위치로 판단됩니다
패턴을 "gitdir:~/work"처럼 끝 / 없이
~/work/cafe-menu에서도 개인 메일이 나옴
끝이 /이면 git이 **를 붙여 "그 아래 전부"가 됩니다. 항상 /로 끝내세요
폴더 대신 원격 주소로 나눌 수도 있습니다. git 문서의 hasconfig:remote.*.url: 조건입니다. 아래 설정을 추가하고 ~/personal/ 아래에 회사 원격(https://github.com/cafe-company/pos.git)을 가진 저장소를 만들어 보니 회사 메일이 나왔습니다.
두 Mac의 폴더 구조가 다를 수밖에 없다면 이 방식이 편합니다. 또 macOS의 기본 디스크는 대소문자를 구분하지 않으므로, ~/Work/와 ~/work/를 섞어 쓸 가능성이 있다면 대소문자를 무시하는 gitdir/i:를 쓰는 것도 방법입니다.
아래 위젯에 폴더(또는 원격 주소)와 이메일을 넣으면 두 Mac에 똑같이 붙여 넣을 설정 조각을 만들어 줍니다.
④ 같은 도구 버전: 6편 예고
맥북에는 Node 22, 맥 스튜디오에는 Node 24가 깔려 있으면 "맥북에서는 되던 테스트가 맥 스튜디오에서 깨지는" 일이 생깁니다. 에이전트도 마찬가지입니다. Claude Code나 Codex가 실행하는 npm test는 그 Mac에 깔린 버전으로 돕니다. 각 Mac에 무엇을 설치할지 사람이 기억하는 대신, 저장소 안의 mise.toml에 도구 버전을 적어 두고 각 Mac에서 mise install 한 번으로 맞추는 방법을 6편에서 다룹니다. 1편의 세 번째 원칙, "개발환경은 저장소 안의 파일로 재현"이 바로 이것입니다.
인증
gh auth login → gh auth setup-git, 또는 이 Mac 전용 SSH 키 생성·GitHub 등록
신원
git config --global user.name / user.email + includeIf로 회사 폴더 분리
두 Mac 모두 ~/work/cafe-menu처럼 같은 경로에 clone — includeIf와 문서 속 경로가 그대로 맞음
도구
저장소의 mise.toml로 버전 맞추기 (6편) · 비밀과 로컬 데이터는 다음 절
10. Git으로 가지 않는 것들: .env, 로컬 DB는 어떻게 옮기나
마지막으로 1절 표의 아래쪽, 보관함에 넣으면 안 되는 것들입니다.
.env 같은 비밀 파일. API 키와 비밀번호가 든 .env는 .gitignore에 넣어 저장소에 들어가지 않게 합니다. 그 대신 키 이름만 적힌 .env.example을 저장소에 둡니다(6편). 실제 값은 비밀번호 관리자(팀이 함께 쓰는 금고)에 보관하고, 새 Mac에서는 거기서 꺼내 .env를 만듭니다. 여러 비밀번호 관리자가 명령줄 도구를 제공해 금고의 값을 파일이나 환경 변수로 바로 채우는 기능을 갖추고 있으니, 팀이 쓰는 도구의 문서를 확인해 보세요. 피해야 할 것은 메신저로 .env를 보내기, 메모 앱에 붙여 두기, 저장소 폴더째 클라우드 드라이브로 동기화하기입니다. 어느 쪽이든 비밀이 통제할 수 없는 곳에 사본으로 남습니다.
로컬 데이터베이스(SQLite 파일 등). 개발용 DB 파일(*.db)도 .gitignore에 넣었습니다. DB 파일은 앱이 켜져 있는 동안 계속 바뀌고, 두 Mac에서 각각 바뀐 파일은 git이 합칠 수 없습니다. 그래서 데이터 자체를 옮기지 말고 "만드는 법"을 저장소에 둡니다. 테이블을 만드는 마이그레이션 파일과, 개발용 샘플 데이터를 넣는 시드(seed) 스크립트를 커밋해 두면, 어느 Mac에서든 한 명령으로 똑같은 개발 DB가 만들어집니다. 에이전트에게도 "DB 파일을 고치지 말고 마이그레이션을 추가하라"고 지시할 수 있습니다. 정말 특정 데이터를 옮겨야 한다면, 실제 개인 정보가 없는지 확인한 뒤 두 Mac 사이에서 암호화된 경로(예: 같은 네트워크에서 SSH로 파일 복사)로 한 번 옮기고, 그 사본을 계속 동기화하지는 않습니다.
node_modules·빌드 결과물. 옮길 필요가 없습니다. 각 Mac에서 npm install, npm run build로 다시 만들면 됩니다. Mac마다 CPU 종류나 도구 버전이 다르면 오히려 복사한 것이 문제를 일으킵니다.
✅
새 Mac에서 첫날 해 보기 — clone → .env.example을 보고 비밀번호 관리자에서 값을 채워 .env 만들기 → npm install → 마이그레이션·시드 실행 → 테스트 통과. 이 순서가 30분 안에 끝나지 않는다면, 저장소에 빠진 "만드는 법"이 있다는 뜻입니다. 빠진 것을 찾아 저장소에 채워 넣는 것이 6편의 주제입니다.
AI 에이전트와 함께 쓸 때
에이전트는 Mac을 옮겨 다니지 않지만, 에이전트가 만든 브랜치는 Mac에 두고 온 채로 잊히기 가장 쉽습니다. 사람은 자기가 한 일을 기억하지만, 에이전트 세션을 닫고 나면 그 worktree에 무엇이 남았는지 기억하는 사람이 없습니다. 민지는 에이전트에게 작업을 맡길 때 아래 문장을 함께 줍니다.
여러 Mac에서 일할 때 에이전트에게 주는 지시
브랜치
"이 작업은 agent/search-claude 브랜치에서만 해 줘. main에는 커밋하지 마."
끝낼 때
"끝났거나 중간에 멈출 때는 변경을 wip: 커밋으로 남기고 이 브랜치를 push해 줘. stash는 쓰지 마. 커밋 훅이 실패하면 건너뛰지 말고 이유를 알려 줘."
점검
"마지막에 git log --branches --not --remotes --oneline 결과를 보여 줘. 비어 있어야 해. 강제 push는 하지 말고, 필요하면 나한테 먼저 물어봐."
에이전트가 권한 설정 때문에 push를 못 한다면, 세션을 닫기 전에 사람이 8절의 도구로 점검하고 직접 push하면 됩니다. Claude Code가 worktree 격리로 일했다면, 세션 종료 시 변경이 남은 worktree는 지우기 전에 확인을 받는다는 점도 기억해 두세요(3편). 그 worktree는 그 Mac에만 있습니다.
자주 하는 질문(FAQ)
Q1. WIP 커밋이 GitHub에 올라가면 남들이 다 보잖아요. 창피하지 않나요?
작업 브랜치의 커밋은 "작업 중"이라는 것을 모두가 압니다. PR이 main에 들어갈 때 squash 머지로 합치면 main 기록에는 정리된 커밋 하나만 남습니다. 공개 저장소라서 정말 신경 쓰인다면 PR을 올리기 전에 rebase -i로 정리하고 --force-with-lease로 올리면 됩니다(7절). 부끄러운 WIP보다 맥북에만 남아 사라진 작업이 훨씬 아깝습니다.
Q2. 두 Mac을 동시에 켜 두고 같은 브랜치를 번갈아 만져도 되나요?
됩니다. 규칙은 두 가지입니다. 앉자마자 git pull --rebase(또는 fetch 후 pull), 일어나기 전에 커밋하고 push. 두 Mac에서 동시에 에이전트를 돌려야 한다면 같은 브랜치 대신 브랜치를 나누세요(agent/search-claude는 맥 스튜디오, feature/order-cart는 맥북처럼). 같은 브랜치를 두 곳에서 동시에 고치면 6절의 거절이 끊임없이 반복됩니다.
Q3. git pull --rebase 도중에 충돌이 나면 어떻게 하나요?
기존 시리즈 8편의 「rebase 중 충돌」과 똑같습니다. 충돌 난 파일을 고치고 git add → git rebase --continue. 도저히 모르겠으면 git rebase --abort로 pull 전 상태로 돌아갈 수 있습니다. 두 Mac에서 같은 파일의 같은 줄을 고쳤을 때만 충돌이 나므로, 자주 push하고 자주 pull할수록 충돌은 작고 드물어집니다.
Q4. iCloud Drive에 저장소를 두면 이런 거 다 필요 없지 않나요?
1편에서 자세히 다뤘듯이 권하지 않습니다. 동기화 서비스는 .git 폴더 안의 수많은 작은 파일을 git이 모르는 순서로 옮기므로, 두 Mac에서 동시에 git 명령을 치면 저장소가 깨질 수 있습니다. 또 이번 편에서 본 "누가 먼저 올렸는지 확인하고 거절해 주는" 안전장치가 전혀 없습니다. 파일은 동기화 서비스에 두더라도, 저장소는 각 Mac의 로컬 폴더에 두고 Git으로 주고받으세요.
Q5. git status가 "up to date"라는데 왜 다른 Mac의 커밋이 없나요?
git status는 네트워크에 나가지 않고 마지막 fetch 때의 원격 사진과 비교합니다(4절). 다른 Mac에 앉으면 git fetch를 먼저 치거나, 아예 git pull로 시작하세요. git status -sb에 [behind N]이 나타나는 것은 fetch 이후입니다.
Q6. 새 Mac을 샀는데, 옛 Mac의 설정을 통째로 옮기면 안 되나요?
~/.gitconfig처럼 비밀이 없는 설정 파일은 복사해도 됩니다(설정을 저장소 하나에 모아 관리하는 dotfiles 방식을 쓰는 사람도 많습니다). 하지만 SSH 비밀 키와 로그인 토큰은 복사하지 말고 새로 만드세요. Mac마다 따로 두어야 한 대를 잃어버렸을 때 그 Mac의 키만 지울 수 있습니다.
gh auth login/SSH 키 · user.name/email · includeIf · pull.rebase·fetch.prune·push.autoSetupRemote
git config --show-origin user.email
다음 편 예고
Mac 두 대 사이를 오가는 길이 생겼으니, 다음에는 에이전트 두 명입니다. 5편에서는 같은 기능(메뉴 검색)을 Claude Code에는 agent/search-claude, Codex에는 agent/search-codex 브랜치로 동시에 맡긴 뒤, git diff A...B와 git range-diff, 테스트로 두 결과를 비교하고 좋은 것만 골라 합치는 법을 다룹니다. 이번 편에서 본 브랜치 이름 규칙과 squash가 거기서 그대로 쓰입니다.
출처
Git 공식 문서 — git-config 「Conditional includes」(gitdir, gitdir/i, hasconfig:remote.*.url, 끝 /와 ** 규칙), push.autoSetupRemote, fetch.prune: git-scm.com
Git 공식 문서 — git-switch --guess, checkout.defaultRemote: git-scm.com