본문 바로가기

Trouble Shooting

[GIT 정리] 여러 조회 방법 및 충돌 파일 간단 처리

이 글은 GIT의 기초를 설명하기 보다는 흔히 현장에서 발생할 수 있는 상황에 대처할 수 있는 실용적인 방법들을 설명하기 위한 첫번째 글이다.


소스 공유 툴을 사용할 때, 모든 참여자들이 정확히 방법을 숙지하고, 정상적인 경우만 있다면 아무 문제가 없지만, 대부분은 누군가 이상한 작업을 해서 뒤죽박죽 뭔가 섞였을 때... 해결을 못하고 아마도 소스를 백업받고 아예 엎어치는 경우가 종종 있다. 이럴 때면 이걸 왜 쓰지 하는 생각이 들기도 한다.


게다가 SVN보다 GIT이 사용하기 어렵다. 솔직히 너무 어렵게 만든 것 같고 무엇보다도 명령어들이 나에게는 직관적이지 않아서 더욱 어렵다. 언젠가 확실히 이해할 필요가 있다고 생각하던 중, 시간이 되서 정리를 시작해 본다.


1. GIT 클론 저장소와 원격저장소간 차이 확인

어떤 문제가 어렵게 느껴질 때는 상황이 제대로 파악되지 못해서인 경우가 많다. 따라서 우리의 GIT 저장소에 대해 정확히 볼 수 있는 방법들을 숙지한다면, 문제는 한결 쉬워질 것이다.

git fetch 명령어를 늘 사용하자.

소스 관리자가 아니라면, 보통 원격저장소 브랜치의 클론 저장소를 생성, 사용할 것이다.이 경우 fetch 명령은 절대로 원격의 소스상태를 망치지도 않고, 내가 작업한 것들도 망치지 않는다. 그러나 원격저장소의 정보를 내 클론 저장소로 연동시켜 주기 때문에, 정확한 상태 파악을 위해서는 수시로 사용해줘야 한다.


한번에 pull명령으로 동기화 하려다 보니 원격저장소의 상태를 알아야 하는 부담이 생긴다. 그래서 원격저장소의 정보를 알기 위한 여러 어려운 방법들이 인터넷에 소개되지만 그럴 필요가 없다. 그냥 자주 아무 이유없이 fetch명령어를 쓰면 모든 것이 편해진다. 

git log <revision range> : 원격과 나와의 차이 확인

예를 들면 'git log HEAD..origin' 처럼 사용할 수 있다. 의미는 HEAD에는 없고 origin에는 있는 커밋들을 보여준다. 여기서 origin은 origin/<branch-name>을 쓸 수 있다. 앞서 fetch가 왜 중요한지 데모로 조성된 환경하에서 실행된 log명령어의 차이를 보면 알 수 있다. 데모 환경은 하나의 원격저장소에서 파생된 두개의 클론저장소에서 한 곳에서 몇개의 커밋/push작업을 했을 때, 다른 한 쪽에서 어떻게 보이는지 보여준다.

=== 하나의 클론 저장소에서 모든 커밋을 push한 후 조회한 커밋로그


prompt$ git log origin/master --oneline

4d685a3 hope to conflict

e2e8f16 commit missing files

43bae9a Other clone 3rd commit

6a8da57 Test commit while there is untracked files

69d7aba Other clone has modified readme.txt

6846f6d add some files

325b548 initialize repository


위의 로그 정보는 절대로 원격저장소에소 조회한 정보가 아니다. 바로 자신(클론)의 정보를 이용한 것이다. 이 때 다른 클론 저장소에서 'fetch'명령을 사용하지 않고 조회를 하면 자신이 갖고 있던 옛날 정보만을 보여주게 된다. 따라서 원격에 이미 커밋된 내용을 확인하지 못한다.

=== fetch 명령을 실행하지 않은 다른 클론저장소에서 ===


prompt$ git log HEAD..origin/master --oneline

prompt$ 

prompt$ git log origin/master --oneline

6846f6d add some files

325b548 initialize repository


만약 이 경우 'pull'명령어를 실행하게 되면 예상치 못한 커밋들로 인해 당황하게 된다. 항상 'fetch'를 수행하고 있었다면 아래의 로그명령을 통해 내가 반영해야될 커밋들을 정확히 파악 할 수 있다. 앞서 조회되지 않은 커밋들이 조회된다.

=== 아래 명령어에서 'FETCH_HEAD'는 'origin' 혹은 'origin/<branch name>'으로 변경가능하다. ===


prompt$ git log HEAD..FETCH_HEAD --oneline

4d685a3 hope to conflict

e2e8f16 commit missing files

43bae9a Other clone 3rd commit

6a8da57 Test commit while there is untracked files

69d7aba Other clone has modified readme.txt

prompt$

prompt$ git log HEAD..origin/master --oneline

4d685a3 hope to conflict

e2e8f16 commit missing files

43bae9a Other clone 3rd commit

6a8da57 Test commit while there is untracked files

69d7aba Other clone has modified readme.txt



2. 파일충돌 해결방법

파일에서 충돌 발생.

어떤 파일이 두군데 이상의 곳에서 변경되었고, 잘 합쳐지지 않게 되면 충돌이 난다. GIT에서는 'fetch'명령어를 실행했는지 여부와 아무 상관없이 항상 'commit'을 할 수 있다. 그러나 결국 'fetch'된 정보는 'merge'명령을 통해 자신의 클론 저장소에 반영해야 된다. 아래는 다른 곳에서 커밋된 것이 'fetch'되고 자신이 수정한 것이 커밋된 것이 공존하는 경우를 보여준다.

=== 나에게는 반영안된 커밋들 ===

prompt$ git log HEAD..FETCH_HEAD --oneline

4d685a3 hope to conflict

e2e8f16 commit missing files

43bae9a Other clone 3rd commit

6a8da57 Test commit while there is untracked files

69d7aba Other clone has modified readme.txt


=== 내가 커밋했지만 원격으로 push안한 것들 ===

prompt$

prompt$ git log FETCH_HEAD..HEAD --oneline

f190b1a try to commit while there is a unmerged fatch


위에 조회된 것은 다른 곳에서, 아래 조회된 목록은 자신이 커밋한 것들이다. 이 상황에서 'merge'명령을 내리면, 충돌난 파일이 생길 수 있다. 먼저 충돌난 파일을 조회하는 방법을 알아보자 

충돌난 파일 조회 및 처리방법

prompt$ git status -s

A  dir2/dir2-other.txt

A  dir2/dir2.txt

UU readme.txt

prompt$ 

prompt$ git ls-files -u

100644 633cb9b8d004e779c4ddc13be0a13016ed502ef3 1 readme.txt

100644 afe66fea12a7cf0e62eff1eee061391af1e49c15 2 readme.txt

100644 16643f3ad2efbcb106fa2a3a9c510c2df666608d 3 readme.txt


주로 사용하는 'status'명령어로 조회 가능하다. 조회 내용을 보면 readme.txt파일에서 충돌이 발생했다. 이런 경우 보통 충돌난 파일을 편집기에서 열어서 충돌난 부분을 수정/저장할 수 도 있다. 그러나 의외로 원래 파일로 되돌리거나, 자신의 파일로 덮어쓰거나, 상대방의 파일로 엎어쓰거나 할 때도 많다. 그럴 때는 'ls-files'명령어가 쓸모 있다. 위의 예제에서 '-u'옵션은 합병실패(unmerged)한 파일들을 보여준다. 이 옵션은 '--stage'옵션을 포함한다. 그래서 출력되는 형식이 위의 예제같이 조금 다르다. 어떤 특정 파일을 명시할 수 도 있다.

prompt$ git ls-files --stage -- readme.txt

100644 633cb9b8d004e779c4ddc13be0a13016ed502ef3 1 readme.txt

100644 afe66fea12a7cf0e62eff1eee061391af1e49c15 2 readme.txt

100644 16643f3ad2efbcb106fa2a3a9c510c2df666608d 3 readme.txt


조회된 파일을 보면 3개의 파일이 인덱스에 존재한다. 그리고 자신의 클론저장소 디렉토리 즉, work-tree에도 파일이 존재한다. 이 파일은 등록되지 않은 파일이다. 즉 총 4개의 파일이 존재한다. 인덱스에 있는 파일은 위에 조회된 내용을 보면 1, 2, 3 으로 숫자가 매겨져 있다. 각각 파일의 내용은 아래와 같다.
  • 1 : 자신과 상대방이 서로 수정하기 전 공통으로 받은 파일(내용이 같았던) 내용 
  • 2 : 자신이 수정한 내용
  • 3 : 다른 곳에서 수정한 내용 
만약 내가 수정한 내용으로 덮어 쓰기를 하고 싶다면 아래의 명령을 내리면 된다. '-f' 옵션은 기존파일이 있어도 덮어쓰게 한다.

prompt$ git checkout-index --stage=2 -f -- readme.txt


'--stage'옵션의 숫자를 1~3사이의 값을 사용해서 원본으로 되돌릴 수도 자신의 것 혹은 다른 곳에서 수정한 것으로 덮어쓸 수도 있다. 또한 work-tree에 있는 파일 자체를 수정하거나 다른 파일로 대체할 수 있다. 모든 작업이 끝나면 'add'명령어를 사용해서 충돌사항이 처리되었음을 알린다.


충돌난 파일을 처리한 후 상대방은?

상대방이 처리된 내용을 'fetch'해서 'merge'하게 되면 상대방 또한 처리된 내용으로 덮어 써지게 된다. 그런데 상대방이 그 사이에 또 수정했다. 그 때 상대방은 이 것을 어떻게 처리할 수 있을까... 이런 복잡한 경우 아래 명령어를 사용하면 상황을 파악하는데 도움이 된다. 여전히 우리는 항상 'fetch'명령을 수행한 상태이다.

prompt$ git log  --graph --oneline --all

*   d7c47fd merge OKmore readme.txt

|\  

| * dde8191 Other change readme.txt once more

* | 55030ae change readme.txt once more

|/  

*   44b1a6d OK overwrite

|\  

| * b76dcd7 insert line into readme.txt

* | 52b2e3b change readme.txt

|/  

*   046ad98 merge other clone's changes

|\  

| * 4d685a3 hope to conflict

| * e2e8f16 commit missing files

| * 43bae9a Other clone 3rd commit

| * 6a8da57 Test commit while there is untracked files

| * 69d7aba Other clone has modified readme.txt

* | f190b1a try to commit while there is a unmerged fatch

|/  

* 6846f6d add some files

* 325b548 initialize repository


'-n 10' 같은 옵션을 추가해서 내용이 많은 경우, 보여지는 갯수를 제한할 수 있다.예는 10개로 제한한 경우이다. 현재 상황을 보면 최종 커밋인 'd7c47fd'는 아래 두개의 서로 분기된 커밋의 충돌을 해소하고 병합된 커밋이다.


이 커밋을 받는 곳에서 또 수정을 했을 때는 아래처럼 또 분기된다.

prompt$ git log --graph --oneline --all

* e100f0b fix merge too

| *   d7c47fd merge OKmore readme.txt

| |\  

| |/  

|/|   

* | dde8191 Other change readme.txt once more

| * 55030ae change readme.txt once more

|/  

*   44b1a6d OK overwrite

|\  

| * b76dcd7 insert line into readme.txt

* | 52b2e3b change readme.txt

|/  

*   046ad98 merge other clone's changes

|\  

| * 4d685a3 hope to conflict

| * e2e8f16 commit missing files

| * 43bae9a Other clone 3rd commit

| * 6a8da57 Test commit while there is untracked files

| * 69d7aba Other clone has modified readme.txt

* | f190b1a try to commit while there is a unmerged fatch

|/  

* 6846f6d add some files

* 325b548 initialize repository


'--graph'옵션은 많은 도움이 된다. 출력된 내용을 보면 커밋의 흐름이 한 눈에 많이 들어온다. 이 출력들은  OSX에서 돌린 결과이다. 다른 플랫폼에서는 어떤지 모르곘다.


마치며

충돌파일에 대해 비로소 정확히 알게 되었다. 다른 내용도 준비되는데로 정리할 예정이다. 물론 사용자인터페이스가 좋은 툴 을 사용할 수 있다면 이럴 필요는 없다. 그러나... 이런 내용이 필요할 때가 꼭 생긴다.