빌드노트
git stash를 했는데 새 파일이 그대로 남았다
기본 git stash에는 추적하지 않는 새 파일이 빠진다. 독립 저장소에서 기본 stash와 -u, ignored 파일의 잔류, 브랜치 전환과 apply를 비교하고 보관한 내용과 복원 결과를 확인한다.
git stash를 실행했는데 새로 만든 파일이 폴더에 남고, git status에도 ??로 표시될 수 있다. 기본 stash는 추적 중인 파일의 변경을 보관하며, 아직 추적하지 않는 새 파일은 기본 범위에서 빠진다. 그 파일까지 잠시 치우려면 git stash push -u를 선택한다. 다만 ignore 규칙에 걸린 파일은 -u로도 남는다. Git stash 공식 문서 (새 탭에서 열림)
남은 파일이 ??인지 !!인지 먼저 본다
Git이 설치돼 있고 최초 커밋이 있는 저장소에서 확인한다. 아래 명령은 PowerShell 7에서 실행했다. 첫 명령의 최상위 경로가 의도한 저장소인지 확인한 뒤 상태를 읽는다.
git rev-parse --show-toplevel
git status --short --untracked-files=all --ignored
다음은 동작을 비교하려고 만든 독립 저장소의 실제 출력이다. tracked.txt는 base라는 한 줄로 커밋한 뒤 edited로 수정했다. new.txt에는 local draft를 쓰고 git add하지 않았다. cache.tmp에는 local cache를 썼으며, 미리 커밋한 .gitignore의 /cache.tmp 규칙으로 제외했다. 실제 업무에서 겪은 사건이 아니라 합성 파일을 사용한 실습이다.
M tracked.txt
?? new.txt
!! cache.tmp
M은 추적 중인 파일에 아직 스테이징하지 않은 수정이 있다는 뜻이다. 이 변경은 기본 stash에 들어간다.??는 인덱스에 등록하지 않은 새 파일이다. 이 파일도 함께 보관하려면-u가 필요하다.!!는 ignore 규칙으로 제외된 파일이다.--ignored를 붙여야 이런 파일까지 볼 수 있다.
--untracked-files=all은 새 디렉터리 안의 개별 파일도 표시한다. 파일 생성 시점만 보고 분류하지 말고 현재 상태를 읽는다. 이미 git add해서 첫 칸에 A가 표시된 새 파일은 ??와 상태가 다르다. Git status의 옵션과 상태 표기 (새 탭에서 열림)
실행 환경은 Windows 11, Git 2.45.1.windows.1, PowerShell 7.6.6이었다. 사용자 전역·시스템 Git 설정을 읽지 않도록 차단하고 줄바꿈 자동 변환을 껐다. 설치 버전에 해당하는 stash 문서는 내용 변경이 없었던 2.43.0 문서로 연결됐으며, 여기서 쓰는 -u, show, apply의 조건을 현재 문서와 함께 확인했다. 해당 버전의 stash 문서 (새 탭에서 열림)
기본 stash 뒤에 새 파일이 남는 것은 실패가 아니다
위 상태에서 다음 두 명령을 실행했다. 첫 명령이 실패하면 뒤의 결과를 정상 보관으로 해석하지 않는다. -m 뒤의 문구는 나중에 항목을 구별하기 위한 이름이다.
git stash push -m 'tracked-only'
git status --short --untracked-files=all --ignored
stash 명령은 종료 코드 0으로 끝났다. 이어 확인한 상태는 다음과 같았다.
?? new.txt
!! cache.tmp
tracked.txt의 내용은 마지막 커밋의 base로 돌아갔다. new.txt와 cache.tmp는 파일도 내용도 그대로 남았다. 이때 new.txt를 추적하는 별도 브랜치 destination으로 git switch destination을 실행하자 종료 코드 1로 차단됐다. 오류에는 untracked working tree files would be overwritten by checkout과 new.txt가 나왔다. 목적지의 같은 경로와 겹쳤기 때문이다. 새 파일이 하나라도 있으면 모든 브랜치 이동이 막힌다는 뜻은 아니다.
같은 입력으로 -u를 비교하기 위해, 먼저 git stash apply 'stash@{0}'로 기존 수정을 되살렸다. 세 파일의 상태가 처음과 같아진 것을 확인한 뒤 실행했다.
git stash push -u -m 'tracked-and-new'
git status --short --untracked-files=all --ignored
이번에도 보관 명령은 성공했고 상태에는 한 줄만 남았다.
!! cache.tmp
tracked.txt는 base로 돌아갔고 new.txt는 작업 폴더에서 없어졌다. 같은 destination 브랜치로의 이동도 이번에는 성공했다. 반면 cache.tmp는 local cache 그대로 남았다. 일반 git status --short는 빈 출력이었으므로, 빈 status가 디스크의 모든 파일을 치웠다는 뜻은 아니다.
자기 작업에서 이미 기본 stash를 했다면, 뒤늦게 -u를 실행해도 앞 항목에 새 파일이 합쳐지지는 않는다. 남아 있는 작업을 담은 별도 항목이 만들어질 수 있다. 이번 비교에서도 기본 항목을 지우지 않았으므로 최종 목록에는 두 항목이 남았다. 어떤 작업이 어느 항목에 들어갔는지 각각 확인해야 한다.
ignored 파일까지 보관하는 범위는 공식 문서의 -a 또는 --all이다. 이 실습에서는 실행하지 않았다. 남은 !!가 그대로 둘 캐시인지, 별도로 보존하고 옮겨야 할 개인 파일인지 먼저 정한다. 폴더를 비우기 위해 무조건 범위를 넓힐 필요는 없다.
새 파일을 저장했는지는 show에도 옵션을 붙여 확인한다
복원하기 전에 목록에서 메시지와 항목 번호를 확인한다. 아래 stash@{0}은 가장 최근 항목이며, PowerShell에서 그대로 전달되도록 작은따옴표로 감쌌다. 새 stash를 만들면 번호가 달라질 수 있으므로 자기 목록에서 선택한 번호로 바꾼다.
git stash list
git stash show --name-only 'stash@{0}'
git stash show --include-untracked --name-only 'stash@{0}'
git stash show --include-untracked -p 'stash@{0}'
이 실습의 최신 항목은 tracked-and-new였다. 기본 설정에서 두 번째 명령은 tracked.txt만 보여 줬다. 세 번째 명령에는 두 경로가 나왔다.
new.txt
tracked.txt
마지막 -p 출력에서는 new.txt에 추가한 +local draft와 tracked.txt의 -base, +edited를 확인했다. 경로만 있는지 보는 데서 끝내지 않고 보관하려던 내용까지 읽은 것이다. cache.tmp는 이 출력에도 없었다.
push -u는 보관할 범위를 넓히고, show --include-untracked는 이미 보관된 내용을 보여 줄 범위를 넓힌다. 조회 옵션을 붙인다고 빠진 파일을 나중에 추가하지는 않는다. 실제로 기본 stash 항목을 같은 조회 옵션으로 확인했을 때는 tracked.txt만 나왔다. 설정에 따라 untracked 파일이 기본 조회에 포함될 수도 있으므로, 확인 명령에 옵션을 명시했다.
apply 뒤 상태와 파일 내용을 함께 확인한다
다른 작업을 마치고 복원할 브랜치로 돌아오면 현재 상태부터 다시 읽는다. 다른 수정이나 복원할 파일과 같은 경로의 새 파일이 있다면, 그 작업의 보존 방법부터 정한다. 실습에서는 main으로 돌아왔고, 복원 직전에는 ignored 파일만 남아 있었다.
확인한 항목을 다음처럼 적용했다. apply가 실패하면 그 지점에서 멈추고 오류와 상태를 살펴본다.
git stash apply 'stash@{0}'
git status --short --untracked-files=all --ignored
적용은 종료 코드 0으로 끝났고 처음의 상태가 돌아왔다.
M tracked.txt
?? new.txt
!! cache.tmp
상태만 같고 내용이 다를 가능성도 있으므로 파일을 읽었다. 자기 작업에서는 아래 파일명을 실제로 보관한 경로로 바꿔 확인한다.
Get-Content -LiteralPath tracked.txt
Get-Content -LiteralPath new.txt
Get-Content -LiteralPath cache.tmp
git stash list
파일 내용은 차례로 edited, local draft, local cache였다. 목록에는 tracked-and-new와 tracked-only가 모두 남았다. cache.tmp는 복원된 파일이 아니라 실습 내내 작업 폴더에 남아 있던 파일이다.
apply는 stash 항목을 목록에 남긴다. 성공 시 항목을 제거하는 pop과 달리, 복원 내용을 확인하는 동안 보관본을 유지할 수 있다. 파일과 필요한 수정을 확인한 뒤에 해당 항목의 정리 여부를 결정한다. apply도 충돌할 수 있으므로 오류가 난 상태를 완전한 복원으로 취급하지 않는다. apply와 pop의 차이 (새 탭에서 열림)
직접 확인한 범위는 최초 커밋이 있는 독립 저장소의 일반 텍스트 파일, 기본 stash와 -u의 범위, 경로가 겹치는 브랜치 전환, 원래 브랜치에서의 충돌 없는 복원이다. -a, 충돌이 있는 apply, 부분 스테이징과 --index를 통한 스테이징 상태 복원, 서브모듈은 시험하지 않았다. 새 파일까지 함께 치우려는 경우에는 ??를 확인해 -u를 선택하고, 저장한 내용과 복원한 내용을 양쪽에서 확인한다.