컴퓨터에서 **`diff`**는 두 개의 파일 간 차이에 대한 정보를 출력하는 파일 비교 유틸리티이다. 일반적으로 하나의 파일 버전과 동일한 파일의 다른 버전 간의 변경 사항을 보여주는 데 쓰인다. diff는 문서 파일의 줄 사이 변경 사항을 보여준다. 이 명령어로 만든 파일은 유닉스 계열 프로그램인 patch 명령어를 이용하여 출력물을 생성할 수 있다.

역사
--

diff 유틸리티는 1970년대 초에 AT&T 벨 연구소와 병합 중이던 유닉스 운영 체제 상에서 개발되었다. 최종 버전은 1974년의 유닉스 제5판에 더글라스 맥클로이(Douglas McIlroy)가 완전히 다시 쓴 판이다.

알고리즘
----

diff 명령은 최장 공통 부분 수열 문제를 해결하는 데 기반을 둔다.

이 문제에서 다음과 같이 두 개의 항목이 있다고 가정하자.

```
       a b c d f g h j q z
```

```
       a b c d e f g i j k r x y z
```

여기서 공통이 되는 가장 긴 부분은 다음과 같다.

```
       a b c d f g j z
```

두 개의 항목을 비교하여 추가(+ 기호로 표시)되거나 삭제(- 기호로 표시)되는 부분들은 다음과 같이 나타낸다.

```
       e   h i   q   k r x y
       +   - +   -   + + + +
```

사용법
---

명령 줄에 다음과 같이 두 개의 파일 이름을 지정한다.

```
diff [원본 파일 이름 또는 원본 디렉터리 이름] [새 파일 이름 또는 새 디렉터리 이름]
```

이는 원본 파일이 새 파일이 될 것임을 가리킨다. 디렉터리로 지정할 경우 diff는 두 디렉터리에 존재하는 각 파일마다 차이점을 분석한다. -r 옵션을 이용하면 하부 디렉터리까지 검색한다.

원본 파일과 새 파일의 내용이 다음과 같다고 가정하자.

|  |  |  |  |
| --- | --- | --- | --- |
|    | **원본 파일**:

```
**1**  This part of the
**2**  document has stayed the
**3**  same from version to
**4**  version.  It shouldn't
**5**  be shown if it doesn't
**6**  change.  Otherwise, that
**7**  would not be helping to
**8**  compress the size of the
**9**  changes.
**10**
**11** This paragraph contains
**12** text that is outdated.
**13** It will be deleted in the
**14** near future.
**15**
**16** It is important to spell
**17** check this dokument. On
**18** the other hand, a
**19** misspelled word isn't
**20** the end of the world.
**21** Nothing in the rest of
**22** this paragraph needs to
**23** be changed. Things can
**24** be added after it.
```
 |    | **새 파일**:

```
**1**  This is an important
**2**  notice! It should
**3**  therefore be located at
**4**  the beginning of this
**5**  document!
**6**
**7**  This part of the
**8**  document has stayed the
**9**  same from version to
**10** version.  It shouldn't
**11** be shown if it doesn't
**12** change.  Otherwise, that
**13** would not be helping to
**14** compress anything.
**15**
**16** It is important to spell
**17** check this document. On
**18** the other hand, a
**19** misspelled word isn't
**20** the end of the world.
**21** Nothing in the rest of
**22** this paragraph needs to
**23** be changed. Things can
**24** be added after it.
**25**
**26** This paragraph contains
**27** important new additions
**28** to this document.
```
 |

이 경우 diff 명령은 두 개 파일의 내용을 비교하여 다음과 같은 차이점을 만들어낸다.

```
0a1,6
> This is an important
> notice! It should
> therefore be located at
> the beginning of this
> document!
>
8,14c14
< compress the size of the
< changes.
<
< This paragraph contains
< text that is outdated.
< It will be deleted in the
< near future.
---
> compress anything.
17c17
< check this dokument. On
---
> check this document. On
24a25,28
>
> This paragraph contains
> important new additions
> to this document.

```

전통적인 출력 방식에서 **`a`**는 *added*(추가)를 가리키고, **`d`**는 *deleted*(삭제)를, 그리고 **`c`**는 *changed*(변경)을 가리킨다. 원본 파일의 줄 수는 `a`/`d`/`c` 앞에 나타나고, 수정되는 파일의 줄 수는 그 뒤에 나타난다.

기본적으로 두 개의 파일에 내용이 완전히 일치하는 줄은 나타나지 않는다.

종류
--

1975년 이후로 핵심 알고리즘의 개선, 명령어로의 유용한 기능 추가, 새로운 출력 포맷 설계 등이 변경 사항에 포함되었다.

### 편집 스크립트

-e 옵션을 이용하면 현대 버전의 diff를 통해 편집 스크립트를 만들어낼 수 있다. 결과는 이를테면 다음과 같다:

```
 24a

 This paragraph contains
 important new additions
 to this document.
 .
 17c
 check this document. On
 .
 8,14c
 compress anything.
 .
 0a
 This is an important
 notice! It should
 therefore be located at
 the beginning of this
 document!

 .

```

### 문맥 형식

BSD는 문맥 형식 (-c)을 추가하고 파일시스템 디렉터리 구조를 재귀적으로 탐색하는 기능 (-r)을 1981년 7월에 2.8 BSD에 추가하여 출시하였다. 버클리가 도입한 diff의 환경 포맷은 사소하게 변경된 소스 코드의 패치를 배포하는 데 큰 도움이 되었다.

`diff -c [원본 파일] [새 파일]`을 이용하면 다음과 같은 출력물을 만들어낸다:

```
*** /path/to/original ''timestamp''
--- /path/to/new ''timestamp''
***************
*** 1,3 ****
--- 1,9 ----
+ This is an important
+ notice! It should
+ therefore be located at
+ the beginning of this
+ document!
+
  This part of the
  document has stayed the
  same from version to
***************
*** 5,20 ****
  be shown if it doesn't
  change.  Otherwise, that
  would not be helping to
! compress the size of the
! changes.
!
! This paragraph contains
! text that is outdated.
! It will be deleted in the
! near future.

  It is important to spell
! check this dokument. On
  the other hand, a
  misspelled word isn't
  the end of the world.
--- 11,20 ----
  be shown if it doesn't
  change.  Otherwise, that
  would not be helping to
! compress anything.

  It is important to spell
! check this document. On
  the other hand, a
  misspelled word isn't
  the end of the world.
***************
*** 22,24 ****
--- 22,28 ----
  this paragraph needs to
  be changed. Things can
  be added after it.
+
+ This paragraph contains
+ important new additions
+ to this document.

```

### 통일 형식

통일 형식 (*unidiff*)은 문맥 형식의 기술적 개선을 물려받았지만, 원본 텍스트와 새 텍스트가 서로 인접하게 표시되는 더 작은 diff를 만든다. 통일 형식은 "`-u`" 옵션을 사용하여 이용할 수 있다. 이 출력은 patch 프로그램의 입력에 쓰인다. 많은 프로젝트에서 "diff"를 명확하게 통일 형식으로 제출하도록 하는데, 덕분에 통일 형식은 소프트웨어 개발자 사이에서 패치를 주고받는 가장 보편적인 형식이 되었다.

통일된 문맥의 diff는 1990년 8월 웨인 데이비슨이 unidiff라는 이름으로 처음 개발하였다. 리처드 스톨만이 1개월 뒤 GNU 프로젝트의 diff 유틸리티에 통일 diff 지원을 추가하면서 1991년 1월 GNU diff 1.15에 이 기능이 들어갔다.

통일 형식은 문맥 형식과 비슷하게 두 줄의 헤더로 시작하는데, 원본 파일은 "---"로 시작하고 새 파일은 "+++"로 시작한다. 이후에 하나 또는 그 이상의 **수정 덩어리(change hunks)**가 뒤따르는데, 파일 안에서 다른 부분을 담고 있다. 안바뀐 문맥적 전후 라인은 하나의 공백 문자로 시작하고, 추가된 라인은 하나의 "+"로 시작하고, 삭제된 라인은 "-"로 시작한다.

하나의 덩어리는 **범위 정보(range information)**로 시작하고 이후에 곧바로 추가된 라인, 삭제된 라인, 그리고 문맥적 전후 라인이 뒤따른다. 범위 정보는 "@@" 로 둘러 쌓여 있고, 문맥 형식에서 두 줄에 걸쳐 표현하던 것을 한 줄로 합쳐서 표현한다. 범위 정보 형식은 다음과 같다.

```
@@ -l,s +l,s @@ optional section heading

```
덩어리 범위 정보 (hunk range information)은 두 개의 덩어리 범위를 담고 있다. 원본 파일의 덩어리에 대한 범위는 마이너스(-) 부호로 시작하고, 새 파일의 덩어리에 대한 범위는 플러스(+) 부호로 시작한다. 각각의 덩어리 범위는 *l,s* 형식을 따르는데, *l*은 시작 라인 번호이고 *s*는 수정 덩어리가 각각의 파일에 적용되는 라인의 개수이다. GNU diff의 많은 버전에서, 각각의 범위 정보는 *s*가 1일 때 콤마와 뒤에 오는 *s* 값을 생략할 수 있다. 여기서 가장 중요한 부분은 첫 번째 범위의 *l* 값이다. 나머지 값은 diff로부터 계산해낼 수 있다.

원본에 대한 덩어리 범위는 모든 문맥적/삭제(변경 포함) 덩어리 라인의 합이어야 한다. 새 파일에 대한 덩어리 범위는 모든 문맥적/추가(변경 포함) 덩어리 라인의 합이어야 한다. 만약 덩어리 크기 정보가 덩어리 안의 라인의 개수랑 안맞으면, 그 diff는 유효하지 못한 것으로 판단되어 거절된다.

덩어리 범위 뒤에는 그 덩어리가 속한 섹션 또는 함수의 이름이 올 수도 있다. 이러면 diff를 읽기가 쉬워진다. GNU diff로 diff를 만들 때는, 헤딩은 정규 표현식에 의해 인식된다.

만약 한 라인이 수정되었다면, 이 수정은 한 줄의 삭제와 한 줄의 추가로 표현된다. 원본과 새 파일의 덩어리가 같은 덩어리에 나타나기 때문에, 이런 변경은 서로 인접해서 나타난다. 다음 예시를 보면 안다.

```
-check this dokument. On
+check this document. On

```
`diff -u [원본 파일] [새 파일]`을 이용하면 다음과 같은 출력물을 만들어낸다:

```
--- /path/to/original ''timestamp''
+++ /path/to/new ''timestamp''
@@ -1,3 +1,9 @@
+This is an important
+notice! It should
+therefore be located at
+the beginning of this
+document!
+
 This part of the
 document has stayed the
 same from version to
@@ -5,16 +11,10 @@
 be shown if it doesn't
 change.  Otherwise, that
 would not be helping to
-compress the size of the
-changes.
-
-This paragraph contains
-text that is outdated.
-It will be deleted in the
-near future.
+compress anything.

 It is important to spell
-check this dokument. On
+check this document. On
 the other hand, a
 misspelled word isn't
 the end of the world.
@@ -22,3 +22,7 @@
 this paragraph needs to
 be changed. Things can
 be added after it.
+
+This paragraph contains
+important new additions
+to this document.

```
여기서, diff 의 출력에 색깔이 칠해져 있으면 더 읽기 쉽다. diff 유틸리티 도구는 색이 있는 출력을 뱉진 않는다. 출력은 순수한 텍스트다. 하지만, 많은 도구가 여기에 문법 강조를 통해 색깔을 칠해준다.

참고로 파일 이름과 타임 스탬프를 잘 구분하기 위해서 이 둘 사이에는 탭 문자가 구분자로 쓰이고 있다. 이것은 스크린 상에서는 안보이기 때문에 diff 결과를 콘솔/터미널 사이에서 복사 붙여넣기 하다보면 잃어버릴 수도 있다.

통일 형식에 추가적인 수정이나 확장을 통해 특정 문맥에서 특정 프로그램이 이해할 수 있도록 한 것도 있다. 예를 들어, 서브버전과 같은 몇몇 버전 컨트롤 시스템은 버전, "작업중인 카피(working copy)", 또는 diff의 헤더 섹션에 추가적인 코멘트를 명시하기도 한다.

몇몇 도구는 서로 다른 여러 개의 파일에 대한 diff를 하나로 합치는 것을 허용하기도 하는데, 수정된 각각의 파일에 대해서 다음과 같은 헤더를 사용한다.

```
Index: path/to/file.cpp

```
줄바꿈 문자로 끝나지 않는 파일의 특별한 경우는 처리되지 않는다. unidiff 유틸리티 도구 뿐만 아니라 POSIX diff 표준에서도 이러한 유형의 파일을 처리하는 방법을 정의하고 있지 않다. (사실, 이런 파일은 엄밀한 POSIX 정의에 의하면 "텍스트" 파일조차 아니다.) 

### 기타

이 밖에도 sdiff과 diffmk가 존재한다.

참고 문헌
-----

1. ↑  David MacKenzie; Paul Eggert; Richard Stallman (1997). 《Comparing and Merging Files with GNU Diff and Patch》. Bristol: Network Theory. ISBN 0-9541617-5-0.  `|title=`에 외부 링크가 있음 (도움말)

* Paul Heckel (1978). "A technique for isolating differences between files". 《Communications of the ACM》 **21** (4): 264–268. doi:10.1145/359460.359467.  더 이상 지원되지 않는 변수를 사용함 (도움말)
* A generic implementation of the Myers SES/LCS algorithm with the Hirschberg linear space refinement (C source code)

자유 파일 비교 도구
-----------

* cmp
* comm
* diff3
* Kompare
* tkdiff
* WinMerge (윈도)
* UCC
* meld
* Diff Checker (웹 기반)
* Web Diff (웹 기반)
* Pretty Diff (웹 기반)