이 글은 GIT의 기초를 설명하기 보다는 흔히 현장에서 발생할 수 있는 상황에 대처할 수 있는 실용적인 방법들을 설명하기 위한 두번째 글이다.
프로젝트를 진행하다보면 뒤늦게, 혹은 실수로 이미 소스저장소에 올려진 파일을 .gitignore에 등록해야 할 때가 생긴다. GIT에 대한 이해가 적을 때, 이 작업은 무지 난해한 작업이었다. 그러나 자세히 정리해보니 생각보다 간단한 작업이었다.
1. .gitignore 파일이 정확히 동작하는지 확인하기
- 클론저장소는 클론-1 과 클론-2 두개가 존재한다.
- dir1/sub.txt 파일은 이미 등록되서 관리중인 파일로 이미 2개의 클론저장소 모두에 공유된 상태이다.
- dir1/sub.out 파일은 클론-1에서 새로 만들어진 파일로 등록되지 않은 상태이다.
- 클론-1에서 2,3항의 'sub.txt', 'sub.out'파일을 .gitignore에 등록 했다.
- 불행히도 클론-2가 'sub.txt'파일을 수정하고 있다.
clone-1$ git status -s --ignored
?? .gitignore
!! dir1/sub.out
느낌표 두개는 소스관리 대상이 아님을 표시한다. 즉, .gitignore등에 등록된 파일이다. 그러나 'sub.txt'정보는 누락되었다.
check-ignore
새로 만든 파일인지 혹은 이미 등록된 파일인지 상관없이 모든 파일을 대상으로 .gitignore설정을 테스트해 보려면 아래의 2가지 방법중의 하나를 사용하면 된다.
clone-1$ git check-ignore -v --no-index **/*
.gitignore:2:*.out dir1/sub.out
.gitignore:1:sub.txt dir1/sub.txt
clone-1$
clone-1$ git ls-files -ico --exclude-from=.gitignore
dir1/sub.out
dir1/sub.txt
'check-ignore' 명령은 모든 ignore설정을 사용해서 테스트해준다. 그런데 '--no-index' 옵션에 주의하자 이 옵션의 뜻은 "인덱스처리(파일)을 반영하지 않고" 이다. 그래서 이 옵션이 포함되면 인덱스된 파일도 보여주고 빼면 새로 등록된 파일들만 보여준다. 이런 부분이 GIT을 어렵게 만든다. 전혀 직관적이지 않고 명령어마다 해석이 다른 옵션이다. 'ls-files' 명령은 늘 쓰는 명령이 될 것이니, 이 명령어만 알고 있어도 조금은 불편하지만 상관없을 듯하다. 여기서 '-ico'옵션은 "ignore AND (cached OR other)"의 의미이다. '-ic', '-io'로 구분해서 사용할 수도 있다. 다만 불편한 점은 '--exclude-from'옵션을 반드시 사용해줘야 한다.
테스트해본 결과 .gitignore파일은 잘 설정이 된 듯하다.
2. 파일의 삭제, 삭제된 파일 병합 처리
diff-index
=== 인덱스 정보만 지운다. ===
clone-1$ git rm --cached dir1/sub.txt
rm 'dir1/sub.txt'
=== 실재파일은 그대로 있다. ====
clone-1$ ls dir1
binary.png sub.out sub.txt
clone-1$
clone-1$
clone-1$ git status -s
D dir1/sub.txt
clone-1$
clone-1$
clone-1$ git diff-index HEAD
:100644 000000 c98fd588a704f4712904a46916d56115605f8f2e 0000000000000000000000000000000000000000 D dir1/sub.txt
clone-1$
clone-1$ git diff-index origin/master
:100644 000000 186f4046b662f2f091b9e6fa9021203f25421003 0000000000000000000000000000000000000000 D dir1/sub.txt
clone-1$
=== commit을 한다. ===
clone-1$ git commit -m "remove index of sub1.txt"
인덱스에서 정보가 사라진 파일은 실재로는 삭제되지 않았지만, 소스관리 정보상으로는 삭제된 것으로 보여진다. 'diff-index'명령의 결과를 보면 파일의 상태가 '100644'에서 '000000'으로 차이가 있음을 보여준다.
그럼 이 상황에서 클론-2에서 눈치없이 'sub.txt'파일을 수정하고 commit&push까지 했다고 가정하자, 이런 상황에서 병합이 되면 'sub.txt'파일에서 충돌이 발생한다. 'ls-files' 명령어를 이용해서 충돌을 조회해보면
clone-1$ git ls-files -u
100644 c98fd588a704f4712904a46916d56115605f8f2e 1 dir1/sub.txt
100644 186f4046b662f2f091b9e6fa9021203f25421003 3 dir1/sub.txt
clone-1$
로컬에서는 비록 인덱스상에서이지만, 파일을 삭제했기 때문에 stage번호가 '2'가 누락된 '1','3'만 보여진다. 만약 stage '3'으로 충돌을 복구하면 클론-2에서 작업한 내용이 반영된다. 그리고 삭제작업 상태 또한 그대로 유지된다. 그 이유는 충돌이 났을 때, 작업디렉토리에 있는 파일은 인덱스된 것들과 별개의 것이기 때문이다. 이런 파일을 'status'명령으로 보면 아래와 같다.
clone-1$ git status -s
DU dir1/sub.txt
clone-1$
=== 그냥 'reset'명령어를 내리면 충돌이 처리된 것으로 된다. ===
clone-1$ git reset
clone-1$
reset
clone-1$ ls
dir1 dir2 readme.txt
clone-1$
clone-1$
clone-1$ rm readme.txt
clone-1$ ls
dir1 dir2
clone-1$
=== 삭제된 파일을 되돌릴 수 있다. / 필요시 특정파일만을 지정할 수 있다.===
clone-1$ git reset --hard
HEAD is now at ab18bd2 change sub2.txt
clone-1$ ls
dir1 dir2 readme.txt
3. 상대방은 어찌 되는가?
사실 클론-1보다는 클론-2가 더 문제이다. 왜냐하면 병합을 하게 되면 실재로 파일이 삭제되기 때문이다. 따로 관련된 파일들을 다른 곳에 복사해 둔 다음에 삭제작업이 모두 병합되고 '.gitignore'파일도 완전히 연동된 후 다시 파일을 복구하는게 가장 쉽고 빠른 듯 하다.
checkout-index
만약 파일을 미처 백업해두지 못했었다면 이전에 커밋된 곳에서 파일을 복구할 수 있다.
clone-2$ git merge -m "resut of gitifnore"
Updating 15cf3fb..53ddffa
Fast-forward (no commit created; -m option ignored)
.gitignore | 2 ++
dir1/sub.txt | 4 ----
2 files changed, 2 insertions(+), 4 deletions(-)
create mode 100644 .gitignore
delete mode 100644 dir1/sub.txt
clone-2$
=== sub.txt파일이 작업디렉토리에서도 삭제된다. ===
clone-2$ ls dir1
binary.png
=== 당황하지 말고 최종본이 있는 <commit>에서 파일을 복구하자 ===
clone-2$ git checkout 15cf3fb -- dir1/sub.txt
clone-2$
clone-2$ ls dir1
binary.png sub.txt
GIT은 다양한 명령어 덕분(?)에 하나의 일을 하는데, 여러가지 방법이 있는 듯 하다. 방금의 경우도 'update-index --assume-unchanged'를 사용하는 방식도 있지만 도긴개긴인 듯하다.
마치며
'.gitignore' 파일이 꼬이면 번거로운 작업을 해야 되지만 생각보다 쉬운 작업이라고 본다. 한쪽에서는 인덱스만 삭제/commit/push를 해주고 다른 쪽에서는 병합후 삭제된 파일을 복구해주면 된다.
'Trouble Shooting' 카테고리의 다른 글
안드로이드 뒤로가기 버튼(백버튼) 보이기 및 가리기 (0) | 2017.10.18 |
---|---|
안드로이드 EditText 항목 쓰기방지(read-only)로 만들기 (0) | 2017.09.27 |
[GIT 정리] 여러 조회 방법 및 충돌 파일 간단 처리 (0) | 2017.08.26 |
안드로이드 시뮬레이터, root권한 없이 SQLite DB 파일 접근 (0) | 2017.08.14 |
안드로이드 기기를 사용하는 단위테스트 첫발 (0) | 2017.07.26 |