본문 바로가기

Trouble Shooting/episode 2. Git

Git (2). VScode에서 "변경 내용 취소"로 삭제된 파일 복구

git으로 관리 할 Local Repository를 만들었을 때

git에서 파일을 다루는 방법은 크게 두 가지로 구분이 가능하다.

tracked, untracked이다.

 

.gitignore를 통해 tracking을 하지 않으려고 하는 파일들 (보안 관련 민감 정보, node_module)을 설정하거나

새로 생긴 파일은 기본적으로 untracked 상태이다.

이를 제외하면, git은 기본적으로 해당하는 Repository에 있는 모든 파일에 대해 tracking하려고 한다.

 

만약 tracking하고 있는 파일에 변경사항이 생긴다면 modified가 되어서 변경 사항을 추가하려고 한다.

이때 우리가 git add 를 통해 변경 사항을 staging area에 올리고 

git commit을 통해 변경 사항을 저장하는 것이다.

 

그런데...

 

문제 상황) 변경 내용 취소

VSCode에서는 git의 소스와 레포지토리 관리를 위한 GUI를 제공한다.

 

"스테이징된 변경사항에 대한 취소"와 "변경 사항에 대한 취소"이다.

전자는 변경사항이 staged area에 add가 된 상태에서 다시 add가 되기 전 상태로 돌리는 것으로

이를 누르면 파일의 변경은 일어나지 않고 git add만 취소된 상태가 된다.

사실상 git reset과 같은 형태이다.

 

그런데 후자는 git을 통한 동작이 아니라

git이 아직 untracked한 파일에 대해서는 관리하지 않고 있는 형태에서

"VSCode 가 파일 시스템에서 직접 해당 파일을 삭제하는 동작"이다.

 

후자의 버튼을 누르면

위와 같은 창이 뜨면서 파일 삭제가 될 것을 경고한다.

 

현재 내 상황) 

1. 백업을 위해 원격으로의 연결을 위해 Local Repository를 새로 만들고

2. untracked된 상황에서 변경 내용 취소를 눌러버린 상태 가 되어버렸다.

 

사실 이 날 바빠서 현재 로컬 Repository 에 커밋을 통해 이미 이전 스냅샷이 저장 된 상태인 줄 착각했었다.

그런데 아니었고 나는 실제로 해당 파일들을 전부 날려버리게 된 것이다.

 

그렇다면 이렇게 커밋도 안한 상태에서 해당 파일들이 삭제되어 버린 경우, 어떻게 이를 복구할까? 에 대해 알아보자.

 

 

방법 1) VSCode의 Local History: Find Entry to Restore 명령 실행

의외로 나와 같은 상황에 놓였던 사람들이 많았나보다.

https://stackoverflow.com/questions/43541167/how-do-you-undo-discard-all-changes-in-vs-code-git

 

How do you undo "Discard all changes" in VS Code/Git

I fear I already know the answer but here goes anyway.. I accidentally clicked "Discard All Changes" in VS Code (OSX) and now a month's worth of work is gone. Poof'd. Thing is, I didn't h...

stackoverflow.com

stackoverflow에 이와 관련해 많은 사람들이 방법을 알려주고 있는데, 맨 처음 방식은 상당히 유용한 듯 하다.

 

  1. 모든 명령 표시 ( 단축키 Ctrl + Shift + P ) 를 누르면 명령 검색 창이 뜬다
  2. 로컬 기록: 복원 할 항목 찾기 (Local History: Find Entry to Restore) 를 검색
  3. 복원할 파일을 찾는다
  4. 해당 파일의 저장한 시간대를 선택
  5. 파일을 복사 한 후 복원

그 외에 ctrl + z 를 당황하지 말고 누르라던가 이런 방식이 있지만 당황한 사람은 그런게 되는지도 모를듯..

내가 사용한 방식은 아래와 같다.

 

 

방법 2) git fsck 명령 실행

git 사전 지식 1) git의 기본 자료구조 ➡️ Key-Value를 이용한 HashMap

 

git은 스냅샷 기반의 파일 구조로, 변경 사항을 저장하는 방식으로 용량을 줄이고 버전을 관리한다

라고 지난번 글에 설명한 바 있다.

더 자세히 파보면 git은 그러한 변경 사항들 및 데이터들을 Key-Value 형태로 저장하고 있다. 

git bash에서 git log를 띄워 봤을 때 다음과 같이 각각의 커밋들이 띄워진다.

commit b57d3abfc6e95a612c37dc6862c52ab464724ea9 (HEAD -> main, origin/main)
Author: namuuCY <wkdcjfdud13@gmail.com>
Date:   Tue Sep 24 17:15:00 2024 +0900

    Fix : post 방식 변경시 제목이 사라지던 버그 수정
    - text, image, link 해당 버튼을 눌렀을 때 기존의 제목이 사라지던 버그를 수정
    - useContext를 이용해 Post.jsx 에 전역 context 설정
    - 하위의 하위 component에서 useContext를 사용.

 

저기서 commit 바로 옆에 있는 16진수의 값이 이러한 데이터의 Key가 되는 것이다.

해시를 사용한다는 것은 데이터의 무결성, 즉 데이터의 Key값이 같으면 이에 해당하는 Value인 데이터가 정확한지를

보장하는 작업이 필요하다. 이러한 무결성을 점검해주는 명령이 바로 git fsck이다.

 

 

git 사전 지식 2) Value ;  3가지 형태가 존재

그러면 Value에 해당하는 파일들은 어떤 형태로 저장하고 있는지 알아보자.

  1. 대부분의 단일 파일들을 Blob(Binary Large Object)이라는 개체(문서에 object라고 되어 있지만,
    프로그래밍에서 다루는 "객체"와는 다른 듯 하다. 공식적으로 "개체"로 언급하고 있다.)로 저장한다.
    다만, 이러한 파일들의 이름이나 확장자, 타입이나 디렉토리와 같은 메타데이터에 대한 내용은 없다.
    오직 파일의 내용만을 저장하고 있다.
  2. git은 스냅샷의 형태로 변경 사항 위주로 자료를 저장한다고 했었다.
    그렇다면 해당하는 파일의 원래 모습(이름, 이전, 이후 모습 등)을 저장하기 위한 메타데이터가 필요하다.
    이러한 파일에 대한 메타 데이터를 저장하는 개체를 Tree 라 한다.
    다양한 것을 저장하는데, 공식 문서에 소개된 내용은 아래와 같다.
    1. 하나, 또는 여러 개의 파일 이름
    2. Blob 개체 혹은 하위 Tree 개체의 Key값
    3. 파일 모드, 개체 타입 등
  3. Tree를 통해서 이전과 이후의 파일들의 서로 다른 스냅샷을 "만들어 낼 수"는 있다. 
    그렇지만 이 스냅샷을 불러오려면 해당하는 이전과 이후의 Key 값을 저장하기 위한 작업이 필요하다.
    그리고 누가, 언제, 왜 저장했는지에 대한 정보도 필요하다.
    이를 저장하는 개체가 바로 Commit 개체이다. 우리가 알고 있는, 위에 bash로 설명한 부분의 개체이다.
  4. 이 세 가지 개체를 통해 Git이 파일을 저장하는 방식을 요약하자면 이렇다.
    1. Git은 변경된 파일을 Blob 개체로 저장하고 현 Index에 따라서 Tree 개체를 만든다.
    2. 그리고 이전 커밋 개체와 최상위 Tree 개체를 참고해서 커밋 개체를 만든다.
    3. 즉 Blob, Tree, 커밋 개체가 Git의 주요 개체이고 이 개체는 전부 .git/objects 디렉토리에 저장된다.

 

무결성, Git의 개체 구성과 파일의 복구가 대체 무슨 상관인가?

상관있다. git fsck는 무결성(어떤 두 개의 데이터에 대해 Key 값이 같다면, Value도 같아야 한다)을 검사해준다.

그리고 vscode에서 제공하는 "변경 사항에 대한 취소" 기능은 이러한 무결성을 해칠 가능성이 존재한다.

 

만약 당신이 git add 를 통해 파일을 추가해서 staging 상태까지 만들었지만,

이를 제거했다면 일단 해당 파일들에 대한 blob은 형성이 되었을 것이다.

 

그렇기에 git fsck를 통해 무결성 검사를 실행할 경우,

무결성이 깨지거나 해서 Git 저장소 내에 참조되지 않는 개체를 찾아낸다.

이러한 개체를 dangling 개체라고 하는데, 검사를 통해 찾아낸 dangling 개체를

Git은 로컬 Repository의 .git/lost-found/ 디렉토리에 저장한다.

이때 참조 되지 않는 파일은 lost-found/other 디렉토리에 Blob의 형태로 저장이 된다.

 

다시 말해, 내가 실수로 날려버린 파일들이( 정확히는 파일의 내용만이 ) 그 곳에 존재하는 것이다.

 

문제 해결)

제목이 16진수 Key로 되어있는 Blob 파일들이다. 확장자마저 보이지 않는다.
이처럼 Blob개체는 코드의 내용만이 남아 있다.

 

이제 이거 다 하나 하나 내용 복붙으로 복원시켜주면 된다.

당시에는 실제로 그렇게 문제를 해결했다.

 

참고로, 위의 stackoverflow에서의 방법이 제일 좋아 보이고

git history와 같은 vscode extension을 설치해서 이런 문제가 생겼을 때

손쉽게 이전 history를 볼 수 있는 방식이 제일 적절해 보인다.

 

https://git-scm.com/book/ko/v2/Git%EC%9D%98-%EB%82%B4%EB%B6%80-Git-%EA%B0%9C%EC%B2%B4

 

Git - Git 개체

여러분이 사용하는 쉘이 어떤 것인가에 따라 master^{tree} 표현식이 오류를 일으킬 수도 있다. Windows 에서 CMD는 ^ 문자는 이스케이프 기호로 사용한다. ^ 문자를 제대로 사용하려면 git cat-file -p master

git-scm.com