새소식

Programming/VCS

[git] 수정된 소스의 일부분만 추가 및 커밋하기

  • -

수정된 소스가 특정 위치마다 커밋의 내용이 크게 다른데 소스 수정하다 보면 한꺼번에 여러 곳을 고치게 되는 경우가 자주 있습니다. 이럴 때 일일이 이 부분은 버그 커밋 내용, 기능 추가 내용, 소스 정리 내용 등으로 구분해서 커밋을 할 수 있다면 소스 수정을 겁내지 않고 할 수 있습니다. git에서는 심지어 하나의 파일도 사용자가 지정해서 원하는 부분만 커밋할 수 있는 기능을 제공합니다.


git add -p 또는 git add --patch 명령을 내리면 'Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?'라는 질문이 뜨면서 입력을 기다리게 됩니다.

뭔가 복잡해 보이는데 간단하게 맨 앞의 y,n은 현재 보이는 코드 덩어리(chunk)를 추가 하겠습니다(y) 안하겠습니다(n)를 지정하는 명령입니다.

3번째 옵션인 q종료를 뜻합니다. 종료를 해도 q를 선택하기 전까지 y, e 등의 명령으로 추가(add)한 부분은 스테이지에 반영되어 있습니다. git status 명령을 내리면 동일한 파일 이름이 두 개 뜨는 것을 확인할 수 있습니다.

4번째 5번째 옵션인 ad이후부터 모두 추가-a(dd) 또는 모두 삭제-d(elete)하라는 명령입니다. a를 누르면 이후 남은 부분이 모두 커밋 대상에 추가되고 d를 누르면 남은 부분은 추가되지 않고 대화 프롬프트가 종료됩니다. q와 동일한 동작을 합니다.

6번째 옵션인 /정규식을 사용해서 일치하는 덩어리를 찾습니다. 사용하면 검색할 정규식을 물어보는데 여기서 사용되는 정규식을 아직 파악하지 못했습니다. 이 부분은 찾아 이해하는대로 내용을 추가하겠습니다.

7번째 옵션인 j는 현재 덩어리를 어떻게 할지 결정하지 않고 결정되지 않는 다음 덩어리를 보여줍니다.

8번째 옵션인 J는 현재 덩어리를 어떻게 할지 결정하지 않고 다음 덩어리를 보여줍니다. j와 비슷한데 약간 다르네요.

목록에는 나오지 않았지만 k또는 K를 사용하면 각각 j 와 J 기능을 수행하지만 다음 덩어리가 아닌 이전 덩어리를 찾는다는 점이 다릅니다.

9번째 옵션인 g는 git이 인식한 덩어리(hunk) 목록을 보여주고 추가할지 말지를 판단할 덩어리를 골라 작업할 수 있게 해 줍니다.

10번째 옵션인 e는 덩어리를 수동으로 편집합니다. 이것은 수동 모드이고 목록에는 나오지 않았지만 s 명령을 사용하면 현재 덩어리를 알아서 더 작게 잘라줍니다. s 명령을 사용해도 더 잘라지지 않거나 덩어리 상태가 복잡한 경우에는 e 명령을 쓰는게 좋을 것 같습니다.

11번째 옵션인 ?는 명령 목록을 나열해주고 간단한 도움말을 보여줍니다.


간단하게 사용하는 예제를 살펴보겠습니다. 여기에서는 e 명령을 사용하겠습니다.

제 git은 vim이 기본 편집기(git config --list 명령으로 확인 가능, core.editor 값)로 지정되어 있어서 vim이 실행된 결과입니다.

'#'으로 시작하는 줄이 주석인데 맨 첫줄에 수동으로 덩어리를 편집하는 중이라는 메시지가 있습니다(Manual hunk edit mode). 이 화면에서 언급된 소스를 보면 '-'로 시작하는 줄이 있고 '+'로 시작하는 줄이 있습니다. diff -u 명령을 자주 써보신 분들에겐 익숙한 화면일 겁니다. '-'는 고치면서 없어진 내용이고 '+'는 고치면서 추가된 내용입니다. data란 변수 이름과 내용이 바뀌고, print_data란 함수의 이름이 바뀐 것을 확인할 수 있습니다.

여기에서 print_data => print_text라고 바뀐 부분만 적용하고 다른 부분은 아직 그대로 두고 싶으면  '-'로 시작하는 부분이 바뀌기 전 내용이니까 하단 주석의 친절한 설명대로 '-'를 빈 칸(' ')으로 바꿔줍니다.


요 부분이 바뀐 부분('+') 인 unsigned char text 로 시작하는 줄을 삭제해 줍니다.


이렇게 바꾸니 printl_packet 함수가 print_text 로 바뀐 부분만 남았습니다. 이제 더 바꿀 부분이 없다면 알아서 종료되었을 것이고 뒷 내용들이 더 있다면 q 명령으로 빠져나와 제대로 적용되었는지 'git diff --cached' 명령으로 확인해봅니다.


커밋 대상으로 잘 지정된 것(staged)을 확인할 수 있습니다. git diff 명령으로 커밋되지 않고 남은 내용을 확인할 수 있습니다.


git status 명령을 내리면 일부만 추가되고 일부는 추가되지 않았음을 확인할 수 있습니다.

커밋 대상으로 지정되었으니 git commit 명령으로 커밋하면 됩니다. 남은 내용들 추가로 커밋 대상으로 지정해도 되고 전에 지정한 내용이 맘에 안들면 git reset 명령으로 커밋 대상을 취소하고 다시 지정을 시작하거나 내용을 수정해도 됩니다.

git add -p 대신에 git add -e 명령을 내리면 바뀐 내용 전체에 대해서 e 명령을 내린 것처럼 수동으로 편집할 수 있습니다. 일일이 덩어리 단위로 편집하기 귀찮고 난 바뀔 내용 다 알고 있어 하는 경우에는 git add -e 명령을 사용하는게 편할 것 같습니다.

사족을 달자면 임시로 보관함에 내용을 저장할 수 있는 git stash 명령에도 -p 옵션을 사용해서 이처럼 원하는 부분만 추가할 수 있습니다. 하지만 이 명령을 사용하면 다시 복귀할 때 귀찮아지므로(stash pop 또는 git stash apply 명령으로 보관함의 내용을 다시 적용할 때는 편집중인 내용이 없이 깨끗해야 합니다.) 사용하지 않는 것을 추천합니다. 이 명령을 사용했다가 보관함의 내용을 적용할 때 다음 오류 메시지를 만나는 경우가 있습니다.

이럴 땐 잠시 언급된 파일(여기서는 test.cpp)을 git add test.cpp 명령으로 추가시키고 git stash pop 또는 git stash apply 명령으로 보관함의 내용을 적용한 다음 git reset 명령으로 git add 명령을 취소하면 잃는 내용 없이 적용할 수 있습니다.


자세한 부분은 참고 글을 확인하시기 바랍니다. 


참고: http://stackoverflow.com/questions/5506339/how-can-i-git-stash-a-specific-file

http://nuclearsquid.com/writings/git-add/

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.