diff와 patch 프로그램을 사용하는 방법

방준영의 이미지

많은(?) 분들이 오픈 소스 프로젝트에 참여하고 싶어도 이 두 가지 프로그램을 어떻게 쓰는지 몰라 힘들다고 하셔서 여기다 간단히 소개합니다.

우선 diff는 말 그대로 difference, 즉 차이를 만들어 주는 프로그램입니다. 차는 두 디렉토리간일 수도 있고, 두 파일간일 수도 있습니다. 두 가지 모두 지원합니다. 가령 인터넷에서 chikichiki-2.0.tar.gz란 소스 패키지를 받아서 압축을 풀었다고 합시다. 그럼 현재 디렉토리에

chikichiki-2.0/

이라는 디렉토리가 생길 겁니다. 유닉스 세계에서는 관례상 대부분의 소스 패키지가 패키지 이름과 같은 디렉토리 밑에 소스 파일들이 전부 들어갑니다.

이제 할 일은 chikichiki-2.0의 버그를 찾아 내어 개발팀에게 보고하는 일입니다. 우선 디렉토리를 복사합니다.

cp -R chikichiki-2.0 chikichiki-2.0.orig 이러면

chikichiki-2.0/
chikichiki-2.0.orig/

처럼 같은 이름을 가졌으면서 끝에 .orig가 붙은 디렉토리가 하나 생깁니다. 이 디렉토리는 diff를 구할 때 기준(즉, 원본임)이 되는 것이므로 내용을 절대 변경하면 안됩니다. 작업은 반드시 chikichiki-2.0에서만 하도록 합니다.

그래서 며칠 동안 버그를 잡고 기능을 추가한 끝에 컴파일과 실행이 성공적으로 이루어졌습니다. 일이 끝났으니 개발팀에게 결과물을 전송해야겠죠? 그런데 chikichiki-2.0/을 다시 tar.gz로 묶어 보내면 용량도 너무 크고 개발팀쪽에서도 내가 뭘 바꿨는지 알기가 힘듭니다. 이제 드디어 diff를 써야 할 순간입니다. 8)

diff -urN chiki-2.0.orig chikichiki-2.0 > chikichiki.diff

이렇게 하면 diff 프로그램이 두 디렉토리를 탐색하면서 같은 이름의 파일끼리 비교를 한 뒤 차이를 chikichiki.diff 파일에 기록합니다. 물론 앞의 디렉토리가 원래 것이고, 뒤의 것이 고친 것입니다.

옵션을 살펴 보면, 먼저 -u 옵션은 "unified format"을 뜻하는 것으로, diff 포맷을 지정할 때 씁니다. -u 말고 -c라고 해서 "context format"도 있는데, 두 개가 모양이 다릅니다. -u를 주었을 때, -c를 주었을 때, 또는 아무 것도 안주었을 때 결과를 살펴 보면 포맷간의 차이를 쉽게 알 수 있습니다. 어느 것을 선택할지는 순전히 개인 취향의 문제입니다. 나중에 패치를 적용할 때 쓸 patch 프로그램은 세가지 포맷 모두를 자동 인식합니다. 그러나 오픈 소스 개발자중 대부분은 가장 알아보기 쉬운 unified format을 주로 씁니다.

-r은 경로로 지정한 디렉토리 안의 서브디렉토리를 전부 거슬러 들어가면서(recursive) 안에 있는 파일을 전부 비교하란 뜻입니다. 이걸 지정하지 않으면 명령행에서 지정한 디렉토리만 비교합니다.

-N 옵션은 새 파일도 diff에 포함하란 뜻입니다. 내가 고친 디렉토리에 새로 만들어 넣은 파일이 있을 경우 이걸 꼭 써야 합니다.

여기까지만 알아도 diff 사용에는 큰 문제가 없을 것입니다. 한가지 더, 위에서 디렉토리 두 개를 비교했는데, 파일끼리 비교하는 것도 물론 가능합니다. 보통 그때는 먼저 고칠 파일을 ~.orig로 복사해 놓고 고친 다음 둘간을 비교합니다. 그리고 물론 -r이나 -N 옵션은 필요가 없겠지요. 8)

그런데 또 한가지 의문점이 있습니다. 꼭 디렉토리를 .orig로 복사해서 디스크 용량을 두 배로 잡아먹어야 하느냐는 궁금함이 생길 수 있죠(물론 복사 자체가 귀찮기도 하구요). 그럴 때는 소스 패키지 대신 아예 CVS 소스 트리를 받아다 거기서 바로 작업하면 됩니다. 버전 관리 프로그램은 전부 diff 기능을 내장하고 있거든요. 버전 관리 프로그램을 쓰는 게 거의 필수인 이유중 하나도 이것 때문입니다.

그럼 이번에는 patch 프로그램을 써서 사용자들이 보내온 패치를 트리에 어떻게 적용하는지 알아 봅시다. diff로 구한 패치 모양은 대략 다음과 비슷합니다:

Index: config/util/lndir.c
===================================================================
--- config/util/lndir.c     (revision 9) <-- !여기 적힌 경로명에 주목! 
+++ config/util/lndir.c     (working copy) <-- !여기도!
@@ -222,6 +222,8 @@
                        continue;
                    if (!strcmp (dp->d_name, "CVS.adm"))
                        continue;
+                   if (!strcmp (dp->d_name, ".svn"))
+                       continue;
                }
                ocurdir = rcurdir;
                rcurdir = buf;

이 패치는 XFree86을 빌드할 때 쓰는 lndir이란 프로그램이 서브버전 관리 디렉토리인 .svn을 무시하도록 해줍니다. XFree86의 최상위 디렉토리는 xc이므로 거기로 가서

patch -p0 < /path/to/lndir.diff

이렇게 하면 화면에 패치 결과가 주루룩 나옵니다. failed... 하면서 같은 이름의 .rej (rejected, 즉 거부된 패치라는 뜻) 파일이 생성되지만 않는다면 성공한 것입니다. 한 파일의 여러 군데를 고치는 패치의 경우 어떤 것은 적용되고 어떤 것은 적용되지 않는 경우가 가끔 있으니 유심히 봐야 합니다.

patch는 항상 표준 입력으로부터 입력을 받습니다. 그래서 패치 파일을 '<'을 써서 입력해 주었습니다. 그리고 아주 중요하고도 유일한(?) 옵션으로 -p 옵션이 있습니다. 이 옵션은 주어진 패치의 경로에서 몇 단계를 벗길 것인가를 지정합니다. -p0이라고 지정하면 하나도 벗기지 말라가 되겠죠? 위 패치를 보면 경로가 config/util/lndir.c로 되어 있습니다(!주목! 부분). 그리고 patch 프로그램을 실행하는 디렉토리가 xc 였으니까 상대 경로가 딱 맞습니다. 즉

$ pwd
/home/junyoung/xc
$ ls config/util/lndir.c
config/util/lndir.c
$ patch -p0 < ~/lndir.c.diff

처럼 하게 됩니다.

그럼 -p0 말고 -p1, -p2, ... 등의 옵션은 언제 써야 할까 하는 의문이 생깁니다. 0이외는 숫자는 아래와 같을 때 씁니다.

$ pwd
/home/junyoung/xc
$ cd config/util
$ ls lndir.c
lndir.c
$ patch -p2 < ~/lndir.c.diff

즉, 어떤 이유로 config/util 디렉토리 안으로 들어가 패치를 적용할 필요가 있을 때 -p2 옵션을 쓰면 패치 안에 적힌 경로(!주목!)에서 앞의 두 단계를 벗겨내라는 뜻입니다(config와 util).

patch 프로그램 사용법도 이게 전부입니다. 그밖에 옵션이 더 있는 것 같은데 한번도 써본 적이 없어서 모르겠구요, 아마 -p 옵션만 알면 다른 건 몰라도 전혀 문제 없을 겁니다.

아무튼 그렇게 해서 공헌자가 패치를 제출하면 개발자가 패치를 적용하는 식으로 작업이 진행되는 것이 오픈 소스 프로젝트의 전형적인 과정입니다. 가령 위의 패치를 실제로 XFree86 프로젝트에

http://www.mail-archive.com/devel%40xfree86.org/msg04824.html

로 제출했더니 다음 날 바로

http://www.mail-archive.com/cvs-commit@xfree86.org/msg03121.html

처럼 커밋을 해주더군요. 보잘 것 없는 패치지만 XFree86 프로젝트에 이름이 올라가는 걸 보니 기분이 좋더군요. 아직 경험해 보지 않으셨다면 여러분도 같은 기쁨을 얼마든지 느껴볼 수 있습니다! 지금 참여합시다. 8)

Forums: 
pyrasis의 이미지

http://wiki.kldp.org/wiki.php/DiffAndPatch

제가 문서를 복사해서 KLDPWiki에 만들었습니다.

PHPBB의 특성상 다른 글이 올라와 버리면 뭍혀 버리기 때문에..

KLDPWiki의 개발자 코너의 맨 앞에 배치 했습니다.

맹고이의 이미지

좋은 글 감사합니다~ :D

pynoos의 이미지

diff 포맷은 제 경험상 두가지 문제가 있었습니다.

aix, hpux에서는 patch가 unified 포맷을 인식하지 못합니다.
대부분의 개발자들은 linux,bsd,solaris에서 개발 하겠지만,
저런 OS를 쓰는 사람들에게는 GNU patch를 꼭설치해야만하지요.

또 aix, hpux 에서의 context format 도, directory간 diff에서 나오는

diff 어쩌고..
Only in 어쩌고..

요런 말이 들어가 있는 context format을 인식지 못합니다.

저 두 단어로 시작하는 줄은 모두 grep -v 으로 빼야잘 되더군요.

혹시 멀티 플랫폼 패치 파일 만드실 분들은 참고하세요.

McKabi의 이미지

좋은 글입니다. 조금 살찌워 볼까요?

방준영 wrote:
그런데 또 한가지 의문점이 있습니다. 꼭 디렉토리를 .orig로 복사해서 디스크 용량을 두 배로 잡아먹어야 하느냐는 궁금함이 생길 수 있죠(물론 복사 자체가 귀찮기도 하구요). 그럴 때는 소스 패키지 대신 아예 CVS 소스 트리를 받아다 거기서 바로 작업하면 됩니다. 버전 관리 프로그램은 전부 diff 기능을 내장하고 있거든요. 버전 관리 프로그램을 쓰는 게 거의 필수인 이유중 하나도 이것 때문입니다.

cvs를 보기로 들어 보겠습니다.

$ pwd
/home/tody/moin/MoinMoin
$ cat CVS/Root
:pserver:anonymous@cvs.sourceforge.net:/cvsroot/moin
$ vi cgimain.py
$ cvs diff -u3 -p cgimain.py
Index: cgimain.py
===================================================================
RCS file: /cvsroot/moin/MoinMoin/cgimain.py,v
retrieving revision 1.65
diff -u -3 -p -r1.65 cgimain.py
--- cgimain.py  9 Nov 2003 21:00:48 -0000       1.65
+++ cgimain.py  13 Jan 2004 09:31:19 -0000
@@ -140,8 +140,13 @@ def run(properties={}):
                 try:
                     pagename = unicode(pagename, 'UTF-8').encode(config.charset)
                 except UnicodeError:
-                    # give up, use URI value literally and see what happens
-                    pass
+                    try:
+                        pagename = unicode(pagename, 'EUC-KR').encode(config.charset)
+                    except UnicodeError:
+                        pass
+
+#                    # give up, use URI value literally and see what happens
+#                    pass

         if request.form.has_key('filepath') and request.form.has_key('noredirect'):
             # looks like user wants to save a drawing
$ cvs diff -u3 -p cgimain.py > moinmoin-20040113-dirty.patch

cvs diff 명령은 diff과 거의 비슷하지만, 비교할 원본 파일 이름을 넣을 필요는 없습니다. cvs가 서버에 있는 원본 파일과 비교를 해 준 뒤 결과를 보여주기 때문입니다. 실행 결과를 보면 어떤 명령으로 무엇과 비교를 했는지 잘 나와 있기 때문에 이 결과를 그대로 파일로 만들어 본디 개발자에게 보내주면 자잘한 설명은 필요없고 패치의 필요성만 말해주면 보통은 잘 받아들여줍니다.

Quote:
RCS file: /cvsroot/moin/MoinMoin/cgimain.py,v
retrieving revision 1.65
diff -u -3 -p -r1.65 cgimain.py
--- cgimain.py 9 Nov 2003 21:00:48 -0000 1.65
+++ cgimain.py 13 Jan 2004 09:31:19 -0000

ㄲ ㅏ ㅂ ㅣ / M c K a b i / 7 7 r b i / T o D y

McKabi의 이미지

pyrasis wrote:
http://wiki.kldp.org/wiki.php/DiffAndPatch
제가 문서를 복사해서 KLDPWiki에 만들었습니다.
PHPBB의 특성상 다른 글이 올라와 버리면 뭍혀 버리기 때문에..
KLDPWiki의 개발자 코너의 맨 앞에 배치 했습니다.

이제야 봤습니다. 뒷 얘기는 KLDP 위키에서... :lol:

ㄲ ㅏ ㅂ ㅣ / M c K a b i / 7 7 r b i / T o D y

oosap의 이미지


http://ko.wikipedia.org/wiki/Diff

diff 포멧 3가지에 대해 잘 설명되어있습니다.
도움될 것 같아 답글 남겨놓습니다.

Thanks for being one of those who care for people and mankind.
I'd like to be one of those as well.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.