빌드노트
stash를 적용했더니 내용은 돌아왔지만 스테이징은 풀렸다
stash 적용 뒤 파일 내용과 커밋 준비 상태를 따로 확인한다. 남은 stash에서 저장 당시 staged 변경을 읽고, 이미 적용했을 때의 재선택과 다음 복원에서 --index를 쓸 조건, 실패 뒤 남은 상태를 구별한다.
git stash apply 뒤 파일 내용은 돌아왔는데, 커밋하려고 골라 둔 수정까지 전부 unstaged로 보일 수 있다. stash에 index가 저장되는 것과, 적용할 때 index까지 복원하는 것은 별개다. 저장 당시의 커밋 준비 상태도 되살리려면 적용할 때 --index를 지정한다. 이미 기본 apply를 마쳤다면 현재 수정 위에 다시 적용하기보다, 남아 있는 stash에서 원래 선택을 먼저 확인한다. Git stash 공식 문서 (새 탭에서 열림)
지금 커밋에 들어갈 내용부터 확인한다
Git과 최초 커밋이 있는 저장소에서 다음 조회로 현재 커밋 준비 상태와 미해결 충돌을 읽는다. 아래 명령의 셸 표기는 PowerShell 7에서 확인했다. 첫 명령의 경로가 의도한 저장소인지 확인하고, 하위 폴더에서 시작했다면 출력된 루트로 이동한 뒤 나머지를 실행한다. 명령이 오류를 내면 빈 출력을 정상 상태로 해석하지 않는다.
git rev-parse --show-toplevel
git status --short
git diff --cached
git diff
git ls-files -u
index는 다음 커밋에 넣을 내용을 준비하는 영역이다. git diff --cached는 HEAD, 즉 현재 커밋과 index를 비교하고, 일반 git diff는 index와 작업 파일을 비교한다. 기본 apply 뒤 cached diff는 비고 일반 diff에 저장했던 수정이 모두 보인다면, 커밋에 넣을 변경은 아직 선택되지 않은 상태다. 두 diff의 비교 대상 (새 탭에서 열림)
이를 확인하려고 새 독립 저장소에서 합성 파일 두 개를 비교했다. 최초 커밋의 ready.txt와 draft.txt는 모두 base 한 줄이었다. ready를 ready로 고쳐 이 파일만 add하고, draft는 draft로 고치되 add하지 않았다. 이 상태를 stash에 저장한 뒤 원래 커밋에서 시작하는 깨끗한 복원 지점 두 곳에 각각 적용했다. 같은 폴더에 두 번 연달아 적용한 결과가 아니다.
- 기본 apply: status는
M draft.txt,M ready.txt였다. cached diff는 비었고 일반 diff에는 draft의-base·+draft와 ready의-base·+ready가 모두 나왔다. - --index로 apply: status는 저장 전처럼
M draft.txt,M ready.txt였다. cached diff에는 ready 수정만, 일반 diff에는 draft 수정만 나왔다. 두 diff가 각각 저장 전과 같았다.
두 명령 모두 종료 코드 0이었고 디스크 파일 내용도 같았다. 달랐던 것은 다음 커밋에 준비한 내용이다. 충돌 없는 status에서 첫 칸은 index, 둘째 칸은 작업 트리 상태이므로 M 은 staged, M은 unstaged 수정이다. 공백도 출력의 일부다. Git status의 짧은 형식 (새 탭에서 열림)
남아 있는 stash에서 원래 선택을 읽는다
이미 기본 apply를 마쳤다면, 당시 staged 변경을 현재 수정과 대조할 수 있다. 아래 첫 줄로 목록을 읽고 복원했던 항목을 고른 다음 둘째 줄을 실행한다. stash@{0}은 가장 최근 항목이므로 자기 목록의 번호로 바꾼다. 새 stash를 만들면 번호가 바뀔 수 있고, PowerShell에서는 작은따옴표를 유지한다.
git stash list
git diff 'stash@{0}^1' 'stash@{0}^2'
일반적인 stash에서 첫 부모 ^1은 저장 당시 HEAD이고 둘째 부모 ^2의 트리는 저장 당시 index다. 두 부모의 diff가 저장했던 staged 변경이다. 이 조회는 현재 파일과 index를 바꾸지 않는다. stash가 저장하는 상태 (새 탭에서 열림)
실험에서는 ready의 -base·+ready만 나왔다. 기본 apply를 마친 지점에서 이 변경이 현재 파일과 같고 충돌도 없는지 확인한 뒤, ready만 다시 스테이징했다. 현재 그 파일의 수정 전체를 이번 커밋에 넣을 때 쓰는 명령이다. ready.txt를 저장소 루트 기준의 실제 파일 경로로 바꾸고, 공백이 있는 경로는 따옴표로 감싼다.
git add -- ready.txt
추가 명령이 성공한 뒤 앞의 진단 묶음으로 다시 확인하자 cached diff는 저장했던 staged diff와 같아졌고 일반 diff에는 draft 수정만 남았다. 두 파일을 모두 add했다면 다음 작업으로 남기려던 draft도 커밋 준비에 포함됐을 것이다. 한 파일 안에 이번 커밋에서 제외할 수정도 섞여 있다면 파일 전체를 add해서는 원래 구분을 보장할 수 없다. 저장된 diff와 현재 수정을 대조해 넣을 부분을 다시 골라야 한다. 이번 실험은 같은 파일의 부분 스테이징을 시험하지 않았다.
다음 복원에서는 --index를 선택한다
아직 적용하지 않은 stash의 커밋 준비 구분까지 유지하려면 --index로 복원을 시도한다. 앞의 진단과 목록 조회로 현재 작업과 대상을 먼저 확인한다. 별도 변경이 있다면 보존할 방법부터 정하고, 이미 apply한 수정 위에 아래 명령을 반복하지 않는다. 이번 성공 비교는 모두 깨끗한 상태에서 시작했다.
git stash apply --index 'stash@{0}'
$LASTEXITCODE
둘째 줄은 적용 직후의 종료 코드다. 0이어도 파일 내용만 보고 끝내지 말고 앞의 진단 묶음에서 cached diff와 일반 diff가 의도한 구분인지 읽는다. 파일 내용을 되살린 뒤 커밋 대상을 다시 고를 목적이라면 기본 apply를 사용할 수 있다. apply는 적용 뒤에도 stash를 목록에 남기므로 보관본을 유지한 채 결과를 확인할 수 있다.
최신 공식 문서에는 stash.index=true일 때 옵션 없는 apply도 --index처럼 동작한다고 되어 있다. 기본값은 false다. 이 설정은 문서로만 확인했으며, 설치된 Git에서 설정 동작을 시험하지 않았다. 옵션 생략 여부와 함께 실제 두 diff를 확인해야 하는 이유다. stash.index 설정과 --index의 조건 (새 탭에서 열림)
실패했다면 일부 반영과 적용 거절을 구별한다
깨끗한 작업 트리에서도 저장 당시와 커밋 내용이 달라졌다면 충돌할 수 있다. 별도 복원 지점에서 draft를 upstream으로 바꿔 커밋한 뒤 같은 stash를 --index로 적용했다. 종료 코드 1과 Index was not unstashed.가 나왔지만, status에는 UU draft.txt와 M ready.txt가 함께 남았다. ready 수정은 staged로 돌아왔고 draft에는 충돌 표식이 생겼다. 실패가 적용 전 상태로의 복귀를 보장하지는 않았다.
이때 앞의 git ls-files -u는 draft의 미해결 index 항목을 단계 번호 1·2·3으로 출력했다. UU는 미해결 충돌이므로 두 칸을 평소의 staged/unstaged 구분으로 읽으면 안 된다. cached diff에도 ready 수정과 함께 Unmerged path draft.txt가 나왔다. 미해결 index 항목 조회 (새 탭에서 열림)
반대로 ready를 upstream으로 바꿔 커밋한 다른 깨끗한 지점에서는 index 패치가 patch does not apply, error: conflicts in index. Try without --index.로 거절됐다. 역시 종료 코드 1이었으나 status와 두 diff, 미해결 index 조회는 모두 비었다. ready는 upstream, draft는 base인 적용 전 내용 그대로였다. UU나 충돌 표식이 없다는 이유로 복원 성공이라고 판단할 수 없다. 오류의 Try without --index도 원래 staged 구분을 복원했다는 뜻은 아니다.
실패 뒤에는 재적용부터 하지 말고 앞의 진단 묶음과 stash 목록으로 남은 상태를 읽는다. 충돌을 해결하더라도 해결한 내용까지 포함해 이번 커밋에 넣을 범위를 다시 확인해야 한다. 이번에는 두 실패 상태를 보존했으며 충돌 해결 완료까지 시험하지 않았다. 성공 두 번과 실패 두 번의 적용 뒤에도 같은 stash가 남아 있었다.
직접 실행한 환경은 Windows 11, Git 2.45.1.windows.1, PowerShell 7.6.6이다. 전역·시스템 Git 설정을 읽지 않게 하고 줄바꿈 자동 변환을 껐다. 실행 범위는 서로 다른 일반 추적 텍스트 파일의 staged/unstaged 수정과 위 두 실패 조건이다. 같은 파일의 부분 스테이징, untracked 파일, 서브모듈과 다른 운영체제는 검증하지 않았다.