diff & patch — 소스 코드 차이 비교와 패치(Patch) 적용
diff는 두 파일(또는 디렉터리)의 차이를 계산하여 사람이 읽을 수 있는 형식으로 출력하는 유틸리티이며, patch는 그 diff 출력을 원본 파일에 적용하여 변경 사항을 재현하는 유틸리티입니다. 이 두 도구는 리눅스 커널 개발을 비롯한 모든 오픈소스 협업의 기본 인프라(Infrastructure)입니다. Normal/Context/Unified 세 가지 출력 형식, 디렉터리 비교, patch의 퍼지 매칭(Fuzz Matching)과 되돌리기, patchutils 보조 도구, quilt 패치 스택 관리, Git과의 연계까지 상세히 다룹니다.
diff는 두 문서를 나란히 놓고 빨간 펜으로 다른 부분에 표시하는 것이고, patch는 그 빨간 펜 표시를 보고 원본 문서를 수정하는 것입니다.
편집자가 원고에 수정 지시를 적어 주면 저자가 그대로 반영하는 과정과 동일합니다.
핵심 요약
- diff — 두 파일의 차이를 계산합니다.
-u(Unified),-c(Context), 기본(Normal) 세 가지 출력 형식이 있으며, 커널 개발에서는 Unified 형식이 표준입니다. - patch — diff가 생성한 패치 파일을 원본에 적용합니다.
-p옵션으로 경로 깊이를 조절하고,-R로 되돌릴 수 있습니다. - Unified 형식 —
---/+++헤더와@@헝크(Hunk) 헤더로 구성되며,-는 삭제,+는 추가, 공백은 문맥(Context) 줄입니다. - 디렉터리 비교 —
diff -ruN으로 두 디렉터리 트리 전체를 재귀적으로 비교할 수 있습니다. - patchutils —
filterdiff,lsdiff,interdiff,combinediff등 패치 파일을 가공하는 보조 도구 모음입니다.
단계별 이해
- 차이 생성
diff -u old.c new.c > change.patch로 변경 사항을 패치 파일로 저장합니다. - 패치 검증
patch --dry-run -p0 < change.patch로 실제 적용 전에 결과를 미리 확인합니다. - 패치 적용
patch -p0 < change.patch로 원본 파일에 변경 사항을 적용합니다. - 패치 되돌리기
patch -R -p0 < change.patch로 적용한 패치를 원래 상태로 되돌립니다. - 커널 워크플로
커널 개발에서는git diff와git format-patch가 diff 역할을,git apply와git am이 patch 역할을 수행합니다.
diff와 patch는 POSIX 표준 유틸리티입니다.
GNU diffutils는 POSIX를 확장한 기능을 추가로 제공합니다.
diff 유틸리티 개요
diff는 1974년 Unix V5에서 Douglas McIlroy가 만든 유틸리티입니다. 초기에는 Hunt-McIlroy 알고리즘을 사용했으며, 이후 Eugene W. Myers가 1986년에 발표한 O(ND) 차이 알고리즘이 현대 diff 구현의 표준이 되었습니다. 이 알고리즘은 두 파일의 LCS(Longest Common Subsequence, 최장 공통 부분 수열)와 최단 편집 스크립트(Shortest Edit Script, SES)가 서로 연결된다는 사실을 이용해 최소 편집 경로를 찾습니다. GNU diffutils는 Myers 알고리즘 위에 가독성 보정 규칙을 더해 실제 패치 검토에 더 읽기 쉬운 헝크를 만듭니다.
# 기본 문법
diff [옵션] <원본 파일> <수정 파일>
# 종료 코드
# 0 — 차이 없음
# 1 — 차이 있음
# 2 — 오류 발생
diff/patch 역사와 발전
diff와 patch는 50년 이상의 역사를 가진 Unix 핵심 도구입니다. 각 세대마다 출력 형식과 알고리즘이 발전하면서 현대 소프트웨어 협업의 기반이 되었습니다.
patch를 만든 계기는 Usenet 뉴스그룹이었습니다.
당시 소프트웨어 업데이트를 위해 전체 소스 코드를 재배포하는 것은 네트워크 대역폭(Bandwidth) 낭비였기에, 변경 부분만 담은 diff 출력(패치)을 전송하고 수신자가 자동 적용하는 방식이 필요했습니다.
이 "패치를 보내고 적용하는" 워크플로는 오늘날 리눅스 커널의 이메일 기반 패치 리뷰 문화로 직접 이어집니다.
diff 출력 형식 비교
diff는 세 가지 주요 출력 형식을 제공합니다. 각 형식의 특성과 용도가 다르므로 상황에 맞게 선택해야 합니다.
Normal 형식 (기본)
옵션 없이 diff를 실행하면 Normal 형식으로 출력됩니다. ed 스크립트(ed script)와 유사한 형태로, 문맥 줄이 없어 가장 간결하지만 사람이 읽기에는 불편합니다.
# 예제 파일 준비
cat > old.c <<'EOF'
#include <stdio.h>
int main(void) {
printf("Hello\n");
return 0;
}
EOF
cat > new.c <<'EOF'
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
printf("Hello, %s\n", argc > 1 ? argv[1] : "World");
return EXIT_SUCCESS;
}
EOF
# Normal diff 실행
diff old.c new.c
출력 결과:
1a2
> #include <stdlib.h>
3c4
< int main(void) {
---
> int main(int argc, char *argv[]) {
4c5
< printf("Hello\n");
---
> printf("Hello, %s\n", argc > 1 ? argv[1] : "World");
5c6
< return 0;
---
> return EXIT_SUCCESS;
Normal 형식의 명령어 구문:
| 구문 | 의미 | 예시 | 설명 |
|---|---|---|---|
LaR | 추가(Append) | 1a2 | 원본 1번 줄 뒤에 수정본 2번 줄 추가 |
FdL | 삭제(Delete) | 3,5d2 | 원본 3~5번 줄 삭제 |
FcT | 변경(Change) | 3c4 | 원본 3번 줄을 수정본 4번 줄로 변경 |
Context 형식 (-c)
Context 형식은 변경 지점 주변의 문맥 줄을 함께 표시하여 사람이 변경 내용을 이해하기 쉽게 만듭니다. 1980년대 BSD에서 처음 도입되었습니다.
# Context diff 생성 (기본 문맥 3줄)
diff -c old.c new.c
# 문맥 줄 수 조절 (5줄)
diff -C 5 old.c new.c
출력 결과:
*** old.c 2024-01-01 10:00:00.000000000 +0900
--- new.c 2024-01-02 10:00:00.000000000 +0900
***************
*** 1,6 ****
#include <stdio.h>
! int main(void) {
! printf("Hello\n");
! return 0;
}
--- 1,7 ----
#include <stdio.h>
+ #include <stdlib.h>
! int main(int argc, char *argv[]) {
! printf("Hello, %s\n", argc > 1 ? argv[1] : "World");
! return EXIT_SUCCESS;
}
Context 형식의 접두사 기호:
| 기호 | 의미 | 설명 |
|---|---|---|
| (공백) | 문맥 | 변경 없는 줄 — 위치 확인용 |
! | 변경 | 원본 블록(***)과 수정본 블록(---) 양쪽에 표시 |
- | 삭제 | 원본 블록에만 존재 |
+ | 추가 | 수정본 블록에만 존재 |
Unified 형식 (-u) ★
Unified 형식은 Context 형식을 개선한 것으로, 원본과 수정본을 하나의 블록으로 합쳐 표시합니다. 가독성과 공간 효율이 모두 우수하여 리눅스 커널을 비롯한 대부분의 오픈소스 프로젝트에서 표준으로 사용합니다. git diff의 기본 출력도 Unified 형식입니다.
# Unified diff 생성 (기본 문맥 3줄)
diff -u old.c new.c
# 문맥 줄 수 조절 (5줄)
diff -U 5 old.c new.c
# 파일로 저장
diff -u old.c new.c > change.patch
출력 결과:
--- old.c 2024-01-01 10:00:00.000000000 +0900
+++ new.c 2024-01-02 10:00:00.000000000 +0900
@@ -1,6 +1,7 @@
#include <stdio.h>
+#include <stdlib.h>
-int main(void) {
- printf("Hello\n");
- return 0;
+int main(int argc, char *argv[]) {
+ printf("Hello, %s\n", argc > 1 ? argv[1] : "World");
+ return EXIT_SUCCESS;
}
diff 주요 옵션 레퍼런스
| 옵션 | 설명 | 사용 예 |
|---|---|---|
-u, -U N | Unified 형식 (문맥 N줄, 기본 3) | diff -u old new |
-c, -C N | Context 형식 (문맥 N줄, 기본 3) | diff -c old new |
-r | 디렉터리 재귀 비교(Recursive) | diff -r dir1/ dir2/ |
-N | 없는 파일을 빈 파일로 취급(New file) | diff -ruN dir1/ dir2/ |
-p | 변경된 C 함수명 표시 | diff -up old.c new.c |
-b | 공백 차이 무시(Ignore space change) | diff -ub old new |
-B | 빈 줄 차이 무시 | diff -uB old new |
-w | 모든 공백 무시 | diff -uw old new |
-i | 대소문자 무시 | diff -ui old new |
--color | 컬러 출력 (GNU diffutils 3.4+) | diff --color -u old new |
-q | 다른 파일만 이름 표시(Brief) | diff -rq dir1/ dir2/ |
-y | 나란히 비교(Side-by-side) | diff -y old new |
-W N | Side-by-side 폭 지정 | diff -y -W 120 old new |
--strip-trailing-cr | Windows CR 문자 무시 | diff -u --strip-trailing-cr old new |
-x PAT | 패턴에 맞는 파일 제외(Exclude) | diff -ruN -x '*.o' dir1/ dir2/ |
-X FILE | 제외 패턴 파일 지정 | diff -ruN -X .diffignore dir1/ dir2/ |
-L LABEL | 파일 이름 대신 라벨 표시 | diff -u -L a/file -L b/file old new |
디렉터리 비교
커널 소스 트리처럼 대규모 디렉터리를 비교할 때는 -r(재귀)과 -N(새 파일 포함) 옵션을 함께 사용합니다.
# 커널 소스 트리 비교 (전형적인 커널 패치 생성)
diff -ruNp linux-6.8/ linux-6.8-patched/ > my-kernel-fix.patch
# 옵션 분해:
# -r 재귀 비교
# -u Unified 형식
# -N 새 파일을 빈 파일 대비로 포함
# -p 변경된 C 함수명 표시
# 바이너리 파일과 빌드 산출물 제외
diff -ruNp -x '*.o' -x '*.ko' -x '.git' -x '.config' \
linux-6.8/ linux-6.8-patched/ > my-kernel-fix.patch
# 제외 패턴을 파일로 관리
cat > .diffignore <<'EOF'
*.o
*.ko
*.mod
*.cmd
.git
.config
.tmp_versions
EOF
diff -ruNp -X .diffignore linux-6.8/ linux-6.8-patched/ > my-kernel-fix.patch
# 어떤 파일이 다른지만 빠르게 확인
diff -rq linux-6.8/ linux-6.8-patched/
# 출력 예:
# Files linux-6.8/drivers/net/foo.c and linux-6.8-patched/drivers/net/foo.c differ
# Only in linux-6.8-patched/drivers/net: bar.c
diff 고급 사용법
Side-by-Side 비교 (-y)
# 터미널에서 나란히 비교
diff -y -W 120 old.c new.c
# 출력 기호:
# (빈칸) 동일한 줄
# | 변경된 줄
# < 원본에만 존재 (삭제)
# > 수정본에만 존재 (추가)
# 다른 줄만 표시
diff -y --suppress-common-lines old.c new.c
통계 정보
# diffstat: diff 출력의 통계 요약 (별도 패키지)
diff -ruNp linux-6.8/ linux-6.8-patched/ | diffstat
# 출력 예:
# drivers/net/foo.c | 15 +++++++++------
# drivers/net/bar.c | 42 ++++++++++++++++++++++++++++++++++++++++++
# include/net/baz.h | 3 ++-
# 3 files changed, 50 insertions(+), 7 deletions(-)
# git에서의 유사 기능
git diff --stat
특수 비교
# 프로세스(Process) 치환으로 명령 출력 비교
diff <(sort file1.txt) <(sort file2.txt)
# 표준 입력과 파일 비교 (- 는 stdin)
cat modified.c | diff original.c -
# 바이너리 파일 비교 (16진수 덤프)
diff <(xxd file1.bin) <(xxd file2.bin)
# 커널 .config 비교 전용 도구
scripts/diffconfig .config.old .config
# 출력 예: CONFIG_DEBUG_INFO n -> y
diff3 — 3-way 머지
diff3는 세 파일을 동시에 비교하여 3방향 머지(3-way merge)를 수행합니다. 두 사람이 같은 원본에서 각자 수정한 경우, 양쪽 변경을 합칠 때 핵심적인 도구입니다. Git의 내부 머지 엔진도 이 3-way 머지 원리를 사용합니다.
# diff3 기본 문법: diff3 MINE OLDER YOURS
# (인수 순서 주의: 내 수정본, 공통 원본, 상대 수정본)
diff3 my-version.c original.c your-version.c
# 3-way 머지 결과를 stdout으로 출력 (-m)
diff3 -m my-version.c original.c your-version.c > merged.c
# 충돌이 없으면 종료 코드 0, 충돌 시 1
diff3 -m my.c base.c yours.c > merged.c
echo $? # 0=성공, 1=충돌, 2=오류
# 충돌 발생 시 merged.c 내용:
# <<<<<<< my-version.c
# printf("Hello, World!\n");
# ||||||| original.c
# printf("Hello\n");
# =======
# printf("Hi there\n");
# >>>>>>> your-version.c
# ed 스크립트 형식 출력 (기본)
diff3 my.c base.c yours.c
# 출력 기호:
# ==== — 세 파일 모두 다름 (충돌)
# ====1 — 파일 1(mine)만 다름
# ====2 — 파일 2(base)만 다름 → A와 B가 동일하게 수정
# ====3 — 파일 3(yours)만 다름
# 겹치지 않는 변경만 자동 적용, 충돌은 표시 (-A 옵션)
diff3 -A my.c base.c yours.c
# 실전: 두 커널 패치가 같은 파일을 수정한 경우
# 1. base를 기준으로 각각 패치 적용된 파일 준비
cp base.c mine.c && patch mine.c < patch-a.diff
cp base.c yours.c && patch yours.c < patch-b.diff
# 2. 3-way 머지
diff3 -m mine.c base.c yours.c > merged.c
# 3. 충돌 확인
grep -c '<<<<<<<' merged.c # 0이면 충돌 없음
sdiff — 대화형 나란히 비교/머지
sdiff는 두 파일을 나란히(Side-by-side) 표시하며, -o 옵션으로 대화형 머지가 가능합니다.
# 나란히 비교 (diff -y와 유사)
sdiff old.c new.c
# 대화형 머지: 차이가 있는 부분마다 선택
sdiff -o merged.c old.c new.c
# 프롬프트에서:
# l — 왼쪽(old) 선택
# r — 오른쪽(new) 선택
# e — 에디터로 직접 편집
# el — 왼쪽을 에디터로 편집
# er — 오른쪽을 에디터로 편집
# eb — 양쪽을 에디터로 편집
# s — 공통 줄 무음(표시 안 함)
# v — 공통 줄 표시 (verbose)
# q — 종료
# 폭 지정
sdiff -w 160 old.c new.c
# 공백 무시
sdiff -b old.c new.c
# 출력 기호:
# (빈칸) 동일
# | 변경
# < 왼쪽에만 존재 (삭제)
# > 오른쪽에만 존재 (추가)
cmp — 바이트 단위 비교
cmp는 두 파일을 바이트 단위로 비교합니다. diff가 텍스트 줄 단위 비교라면, cmp는 바이너리 파일을 포함한 모든 파일의 바이트 수준 비교 도구입니다.
# 기본: 첫 번째 다른 바이트 위치만 출력
cmp file1 file2
# file1 file2 differ: byte 1024, line 8
# 동일하면 출력 없이 종료 코드 0
cmp -s file1 file2 && echo "동일" || echo "다름"
# 스크립트에서 파일 동일성 검사 (가장 흔한 용도)
if cmp -s before.bin after.bin; then
echo "변경 없음"
else
echo "파일이 변경됨"
fi
# 모든 다른 바이트 나열 (-l)
cmp -l file1 file2
# 출력: 바이트_위치 8진수_file1 8진수_file2
# 1024 101 142
# 1025 157 163
# 처음 N바이트만 비교
cmp -n 512 file1 file2
# 바이트 오프셋 건너뛰기
cmp -i 1024 file1 file2 # 양쪽 모두 1024바이트 건너뜀
cmp -i 512:1024 file1 file2 # file1은 512, file2는 1024 건너뜀
# 커널 빌드 결과 검증: 동일한 .config로 빌드한 vmlinux 비교
cmp -s vmlinux.old vmlinux.new && echo "재현 가능 빌드 성공"
# diff vs cmp 차이점:
# diff — 텍스트 줄 단위, 차이 내용 표시, 패치 생성 가능
# cmp — 바이트 단위, 위치만 표시, 바이너리 안전, 더 빠름
patch 유틸리티 개요
patch는 Larry Wall이 1985년에 만든 유틸리티로, diff가 생성한 차이 파일(패치)을 원본에 적용합니다. 원본 파일이 패치 생성 시점과 약간 다르더라도 퍼지 매칭(Fuzz Matching)과 오프셋(Offset) 조정(Offset Adjustment)으로 유연하게 적용할 수 있는 것이 핵심 강점입니다.
패치 파일 해부 — 멀티 파일 · 멀티 헝크
실제 커널 패치는 여러 파일에 걸쳐 여러 헝크(Hunk)를 포함합니다. 아래는 2개 파일, 총 3개 헝크로 구성된 패치의 전체 구조입니다.
퍼지 매칭 동작 시각화
원본 파일이 패치 생성 시점과 달라졌을 때, patch는 문맥 줄을 기준으로 올바른 위치를 탐색합니다. 아래 다이어그램은 오프셋(Offset)과 퍼지(Fuzz)가 어떻게 동작하는지 보여줍니다.
patch 기본 사용법
# 패치 적용 기본
patch < change.patch # 패치 파일의 헤더에서 파일명 자동 인식
patch -p0 < change.patch # 경로에서 0단계 제거
patch -p1 < change.patch # 경로에서 1단계 제거 (가장 흔함)
# -p 옵션 이해: 경로 접두사 제거 단계
# 패치 헤더: --- a/drivers/net/foo.c
# -p0 → a/drivers/net/foo.c (전체 경로 사용)
# -p1 → drivers/net/foo.c (a/ 제거) ★ 커널 패치 표준
# -p2 → net/foo.c (a/drivers/ 제거)
# 대상 파일 직접 지정
patch old.c < change.patch
# 대상 디렉터리 변경
patch -d /usr/src/linux -p1 < kernel-fix.patch
-p 옵션 (경로 접두사 제거) 상세
-p 옵션은 패치를 올바른 파일에 적용하기 위해 경로 접두사를 제거하는 핵심 옵션입니다. 이 옵션을 잘못 지정하면 파일을 찾지 못해 패치가 실패합니다.
# 커널 소스 루트에서 패치 적용 (가장 일반적)
cd /usr/src/linux
patch -p1 < /path/to/kernel-fix.patch
# diff -ruNp 로 만든 패치 (경로에 디렉터리명 포함)
# --- linux-6.8/drivers/net/foo.c
# +++ linux-6.8-patched/drivers/net/foo.c
cd linux-6.8
patch -p1 < ../kernel-fix.patch # linux-6.8/ 접두사 제거
patch 주요 옵션 레퍼런스
| 옵션 | 설명 | 사용 예 |
|---|---|---|
-p NUM | 경로 접두사 NUM단계 제거 | patch -p1 < fix.patch |
-R | 패치 되돌리기(Reverse) | patch -R -p1 < fix.patch |
--dry-run | 실제 변경 없이 시뮬레이션 | patch --dry-run -p1 < fix.patch |
-d DIR | 대상 디렉터리 지정 | patch -d /usr/src/linux -p1 < fix.patch |
-b | 백업 파일 생성 (.orig) | patch -b -p1 < fix.patch |
-V STYLE | 백업 파일 명명법 | patch -b -V numbered -p1 < fix.patch |
-o FILE | 결과를 별도 파일에 저장 | patch -o new.c old.c < fix.patch |
-F NUM | 퍼지 팩터(Fuzz factor) 설정 | patch -F 3 -p1 < fix.patch |
-l | 느슨한 공백 매칭(Loose matching) | patch -l -p1 < fix.patch |
--verbose | 상세 출력 | patch --verbose -p1 < fix.patch |
-s | 조용한 모드(Silent) | patch -s -p1 < fix.patch |
-f | 질문 없이 강제 적용(Force) | patch -f -p1 < fix.patch |
-E | 패치 후 빈 파일 삭제 | patch -E -p1 < fix.patch |
--merge | 충돌 시 merge 마커 삽입 (GNU patch 2.7+) | patch --merge -p1 < fix.patch |
퍼지 매칭과 오프셋 조정
patch가 강력한 이유는 원본 파일이 패치 생성 시점과 달라도 유연하게 적용할 수 있기 때문입니다.
오프셋 조정 (Offset)
헝크의 줄 번호가 맞지 않으면 patch는 문맥 줄을 기준으로 위아래로 검색하여 올바른 위치를 찾습니다.
# 오프셋이 발생하면 patch가 알려줍니다
patch -p1 < fix.patch
# 출력:
# patching file drivers/net/foo.c
# Hunk #1 succeeded at 127 (offset 15 lines).
# Hunk #2 succeeded at 245 with fuzz 1 (offset 22 lines).
퍼지(Fuzz)
문맥 줄이 일부 다를 때 patch는 퍼지 팩터(Fuzz Factor)만큼 문맥 줄을 무시하고 매칭을 시도합니다. 기본 퍼지 팩터는 2입니다.
# 퍼지 없이 엄격하게 적용 (커널 개발 권장)
patch -F 0 -p1 < fix.patch
# 퍼지 팩터를 3으로 늘려 느슨하게 적용
patch -F 3 -p1 < fix.patch
-F 0으로 엄격하게 적용하고, 실패 시 패치를 재생성하는 것이 안전합니다.
거부(Reject) 파일 처리
patch가 헝크를 적용하지 못하면 .rej 파일을 생성합니다.
# 패치 적용 실패 시
patch -p1 < fix.patch
# 출력:
# patching file drivers/net/foo.c
# Hunk #1 FAILED at 42.
# 1 out of 3 hunks FAILED -- saving rejects to file drivers/net/foo.c.rej
# .rej 파일 확인 → 수동으로 해결
cat drivers/net/foo.c.rej
# 내용: 적용 실패한 헝크가 그대로 들어 있음
# → 해당 부분을 수동으로 편집해야 합니다
# 정리: .rej와 .orig 파일 제거
find . -name '*.rej' -o -name '*.orig' | xargs rm -f
패치 되돌리기 (-R)
# 적용한 패치를 되돌리기
patch -R -p1 < fix.patch
# 되돌리기 전 시뮬레이션
patch -R --dry-run -p1 < fix.patch
# 이미 적용된 패치를 다시 적용하려고 하면?
patch -p1 < fix.patch
# 출력:
# Reversed (or previously applied) patch detected! Assume -R? [n]
# → patch가 자동으로 이미 적용되었음을 감지합니다
백업과 안전한 적용
# 백업 파일 생성 후 적용
patch -b -p1 < fix.patch
# drivers/net/foo.c.orig 생성 (원본 백업)
# 번호 붙은 백업
patch -b -V numbered -p1 < fix.patch
# drivers/net/foo.c.~1~ 생성
# 안전한 적용 패턴: dry-run → 백업 → 적용
patch --dry-run -p1 < fix.patch && \
patch -b -p1 < fix.patch
# 여러 패치를 순서대로 적용
for p in 0001-*.patch 0002-*.patch 0003-*.patch; do
echo "적용 중: $p"
patch -p1 < "$p" || { echo "실패: $p"; exit 1; }
done
실전 워크플로
커널 패치 생성/적용 예제
# ===== 전통적 방법: diff/patch =====
# 1. 소스 트리 복사
cp -a linux-6.8 linux-6.8-fix
cd linux-6.8-fix
# 2. 코드 수정
vim drivers/net/ethernet/intel/e1000e/netdev.c
# 3. 패치 생성
cd ..
diff -ruNp linux-6.8/ linux-6.8-fix/ > fix-e1000e-null-deref.patch
# 4. 패치 검증 (다른 머신에서)
cd linux-6.8
patch --dry-run -p1 < ../fix-e1000e-null-deref.patch
# 5. 패치 적용
patch -p1 < ../fix-e1000e-null-deref.patch
# ===== 현대적 방법: Git =====
# 1. 브랜치에서 작업
git checkout -b fix/e1000e-null-deref v6.8
# 2. 코드 수정 & 커밋
vim drivers/net/ethernet/intel/e1000e/netdev.c
git add -p
git commit -s # Signed-off-by 포함
# 3. diff 확인 (Unified 형식)
git diff v6.8..HEAD
# 4. 패치 파일 생성 (커밋 메타데이터 포함)
git format-patch -1 HEAD
# → 0001-e1000e-fix-null-deref-in-netdev-open.patch
# 5. 패치 적용 (수신자)
git apply --check 0001-*.patch # 검증만
git am 0001-*.patch # 적용 + 커밋 생성
Git의 diff/patch 관련 명령
| Git 명령 | 대응하는 전통 명령 | 설명 |
|---|---|---|
git diff | diff -u | 작업 디렉터리 변경 사항 표시 |
git diff --cached | diff -u | 스테이징된 변경 사항 표시 |
git diff HEAD~3..HEAD | diff -ruNp | 최근 3개 커밋의 차이 |
git format-patch | diff -ruNp | 커밋을 이메일 형식 패치로 변환 |
git apply | patch -p1 | 패치 파일 적용 (커밋 없음) |
git am | patch -p1 + 커밋 | 이메일 형식 패치 적용 + 커밋 생성 |
git diff --stat | diff | diffstat | 변경 통계 요약 |
git diff -w | diff -uw | 공백 무시 비교 |
git diff --color-words | — | 단어 단위 차이 하이라이트 |
# git diff 유용한 옵션들
git diff --stat # 변경 파일 목록과 통계
git diff --name-only # 변경 파일명만
git diff --name-status # 파일명 + 상태 (M/A/D/R)
git diff -w # 공백 무시
git diff --color-words # 단어 단위 하이라이트
git diff --word-diff # 단어 단위 차이 [{+추가+}] [-삭제-]
git diff --ignore-blank-lines # 빈 줄 차이 무시
git diff -U10 # 문맥 10줄로 확장
git diff -- '*.c' '*.h' # 특정 파일 패턴만
git diff --diff-filter=M # 수정된 파일만
# git apply 옵션
git apply --check patch.diff # 적용 가능 여부만 검사
git apply --stat patch.diff # 통계만 표시
git apply -3 patch.diff # 3-way 머지 시도
git apply --reject patch.diff # 실패 헝크를 .rej로 저장
git apply --whitespace=fix patch.diff # 공백 문제 자동 수정
patchutils — 패치 파일 가공 도구
patchutils 패키지는 패치 파일을 분석하고 가공하는 유틸리티 모음입니다.
# 설치
sudo apt install patchutils # Debian/Ubuntu
sudo dnf install patchutils # Fedora/RHEL
filterdiff — 패치 필터링
# 특정 파일의 변경만 추출
filterdiff -i '*/drivers/net/*' big-patch.patch > net-only.patch
# 특정 파일 제외
filterdiff -x '*/Documentation/*' big-patch.patch > code-only.patch
# 특정 헝크만 추출 (파일의 N번째 헝크)
filterdiff --hunks=1,3 -i '*/foo.c' big-patch.patch
# 패턴으로 필터링 (정규표현식)
filterdiff -I 'include.*net' big-patch.patch
lsdiff — 패치 파일 목록
# 패치에 포함된 파일 목록
lsdiff kernel-fix.patch
# 출력:
# drivers/net/foo.c
# drivers/net/bar.c
# include/net/baz.h
# 경로 접두사 제거 (patch -p1과 동일)
lsdiff -p1 kernel-fix.patch
# 통계 정보와 함께 표시
lsdiff -s kernel-fix.patch
# 출력:
# ! drivers/net/foo.c (변경)
# + drivers/net/bar.c (추가)
# - drivers/net/old.c (삭제)
# 헝크 수 표시
lsdiff -n kernel-fix.patch
interdiff — 패치 간 차이
# 두 패치 버전 간의 차이 (v1 → v2 변경 사항)
interdiff fix-v1.patch fix-v2.patch > v1-to-v2.diff
# 커널 리뷰에서 v2 변경 사항만 확인할 때 유용
# 리뷰어: "v1에서 뭐가 바뀌었나요?"
interdiff 0001-v1-fix.patch 0001-v2-fix.patch
combinediff — 패치 합치기
# 두 패치를 순서대로 합침
combinediff patch1.diff patch2.diff > combined.diff
# patch1을 적용한 후 patch2를 적용한 것과 동일한 결과
splitdiff — 패치 분할
# 파일별로 패치 분할
splitdiff -a big-patch.patch
# 출력: big-patch-drivers-net-foo.c.patch, big-patch-include-net-baz.h.patch, ...
# 디렉터리별로 분할
splitdiff -d big-patch.patch
rediff — 헝크 번호 재계산
# 패치 파일을 수동 편집한 후 줄 번호 수정
# (헝크를 직접 수정하면 줄 번호가 맞지 않을 수 있음)
rediff edited-patch.patch > fixed-patch.patch
quilt — 패치 스택 관리
quilt는 여러 패치를 스택(Stack)으로 관리하는 도구입니다. Debian 패키지 유지보수, 배포판 커널 커스터마이징, upstream에 여러 패치를 순서대로 관리할 때 유용합니다.
# 설치
sudo apt install quilt # Debian/Ubuntu
sudo dnf install quilt # Fedora/RHEL
# 초기화
cd linux-6.8
quilt init
# 새 패치 생성
quilt new fix-null-deref.patch
# 수정할 파일 등록 (수정 전에 등록해야 함)
quilt add drivers/net/foo.c
# 파일 수정
vim drivers/net/foo.c
# 패치 갱신 (변경 사항을 패치 파일에 반영)
quilt refresh
# 다른 패치 추가
quilt new add-logging.patch
quilt add drivers/net/foo.c
vim drivers/net/foo.c
quilt refresh
# 패치 스택 조작
quilt pop # 최상위 패치 제거
quilt pop -a # 모든 패치 제거
quilt push # 다음 패치 적용
quilt push -a # 모든 패치 적용
# 패치 목록과 상태
quilt series # 전체 패치 목록
quilt applied # 적용된 패치 목록
quilt unapplied # 미적용 패치 목록
quilt top # 현재 최상위 패치
# 중간 패치 수정
quilt pop fix-null-deref.patch # 해당 패치까지 되돌림
vim drivers/net/foo.c # 수정
quilt refresh # 패치 갱신
quilt push -a # 나머지 패치 재적용
대안 도구
| 도구 | 특징 | 설치 |
|---|---|---|
colordiff | diff 출력에 색상 추가 (diff 래퍼) | apt install colordiff |
icdiff | 터미널 Side-by-side 컬러 diff | pip install icdiff |
delta | git diff 페이저, 구문 하이라이트 | apt install git-delta |
difftastic | AST 기반 구조적 diff (Structural diff) | cargo install difftastic |
meld | GUI 3방향 머지 도구 | apt install meld |
vimdiff | Vim 기반 나란히 비교/머지 | Vim에 포함 |
kdiff3 | GUI 3방향 머지 도구 | apt install kdiff3 |
diffoscope | 재현 가능 빌드 검증용 심층 비교 | apt install diffoscope |
wdiff | 단어 단위 diff | apt install wdiff |
diffstat | diff 출력의 통계 요약 | apt install diffstat |
# colordiff: diff에 색상 추가
colordiff -u old.c new.c
# diff 출력을 colordiff로 파이프
diff -u old.c new.c | colordiff
# vimdiff: Vim에서 나란히 비교
vimdiff old.c new.c
# ]c — 다음 차이로 이동
# [c — 이전 차이로 이동
# do — diff obtain (상대 창에서 가져오기)
# dp — diff put (상대 창으로 보내기)
# delta: git pager로 설정
git config --global core.pager delta
git diff # 이제 delta로 보기
# difftastic: 구조적 diff (AST 기반)
difft old.c new.c
# git difftool로 사용
git config --global diff.external difft
delta 실전 설정 — 커널 리뷰 최적화
dandavison delta 0.18+는 히스토그램 알고리즘을 기본으로 사용하고, side-by-side/라인 번호/구문 하이라이트를 제공합니다.
git 기본 페이저를 대체하면 대용량 패치 리뷰가 눈에 띄게 개선됩니다.
# ~/.gitconfig
[core]
pager = delta
[interactive]
diffFilter = delta --color-only
[delta]
navigate = true ; n/N으로 hunk 이동
line-numbers = true
side-by-side = false ; 터미널 폭이 좁으면 false 유지
syntax-theme = "Monokai Extended"
features = decorations
[delta "decorations"]
commit-decoration-style = bold yellow box ul
file-style = bold yellow ul
file-decoration-style = none
hunk-header-decoration-style = cyan box ul
[merge]
conflictStyle = zdiff3 ; delta는 zdiff3 3-way 충돌 마커 이해
[diff]
algorithm = histogram ; delta + histogram 조합 (커널에 권장)
colorMoved = zebra
colorMovedWS = allow-indentation-change
difftastic 실전 설정 — 구조적 diff
Wilfred difftastic(0.60+)은 tree-sitter 파서로 AST를 생성한 뒤 Dijkstra 최단경로로 diff를 계산합니다.
순수 라인 기반 diff에서 "주석만 바뀌었는데 함수 전체가 달라 보이는" 문제를 해결합니다. 단, 대용량 파일에서는 라인 기반보다 느립니다.
# ~/.gitconfig
[diff]
tool = difftastic
[difftool]
prompt = false
[difftool "difftastic"]
cmd = difft "$LOCAL" "$REMOTE"
[pager]
difftool = true
[alias]
dft = difftool
; 사용: git dft HEAD~..HEAD -- drivers/net/
# 특정 커밋만 구조적 diff로 보기
$ git dft HEAD~1..HEAD
# 환경 변수로 외부 diff 지정 (일회성)
$ GIT_EXTERNAL_DIFF=difft git diff drivers/foo/bar.c
# 두 파일 직접 비교
$ difft old.c new.c
Myers vs Histogram vs Patience — 동일 커밋 실측
동일한 커밋에 대해 세 알고리즘으로 diff를 실행하면 결과물의 "가독성"이 달라지는 것을 확인할 수 있습니다. 커널 리뷰 경험상 histogram이 코드 이동(move)과 함수 경계 인식이 가장 좋다는 점이 JGit/libxdiff 벤치마크와 2024년 Microsoft Research 리뷰 품질 연구에서 반복 확인되었습니다.
# 세 알고리즘으로 같은 커밋 diff 생성 + 크기 비교
for algo in myers minimal histogram patience; do
lines=$(git diff --diff-algorithm=$algo HEAD~1 HEAD | wc -l)
echo "$algo: $lines lines"
done
# 결과 예 (단일 리팩터링 커밋):
# myers: 412 lines
# minimal: 398 lines
# histogram: 356 lines ← 가독성 최고
# patience: 361 lines
# 전역 기본값 변경 (권장)
$ git config --global diff.algorithm histogram
diff.algorithm=histogram을 명시적으로 설정하는 것을 권장합니다.
자세한 내용은 시퀀스 비교 알고리즘 페이지를 참고하세요.
패치 시리즈 관리
커널에 제출하는 변경이 크면 하나의 패치 대신 패치 시리즈(Patch Series)로 분리합니다. 각 패치는 독립적으로 컴파일되고 논리적 단위를 이루어야 합니다.
# ===== git format-patch로 패치 시리즈 생성 =====
# 최근 3개 커밋을 패치 시리즈로 (커버 레터 포함)
git format-patch -3 --cover-letter -v2
# 생성 파일:
# v2-0000-cover-letter.patch ← 커버 레터 (Subject/내용 직접 편집)
# v2-0001-e1000e-add-null-check.patch
# v2-0002-e1000e-add-flag-tracking.patch
# v2-0003-e1000e-add-logging.patch
# 커버 레터 편집
vim v2-0000-cover-letter.patch
# Subject: [PATCH v2 0/3] e1000e: fix null deref in open path
# --- 아래에 시리즈 설명, 변경 이력 작성 ---
# 수신자 자동 설정
scripts/get_maintainer.pl v2-000*.patch
# 전체 시리즈 전송 (커버 레터의 Message-Id를 In-Reply-To로 연결)
git send-email --to=... --cc=... v2-000*.patch
# ===== 전통적 diff/patch로 패치 시리즈 관리 =====
# 순서대로 적용
for p in 0001-*.patch 0002-*.patch 0003-*.patch; do
echo "=== 적용: $p ==="
patch --dry-run -p1 < "$p" || { echo "검증 실패: $p"; exit 1; }
patch -p1 < "$p"
done
# 역순으로 되돌리기
for p in 0003-*.patch 0002-*.patch 0001-*.patch; do
patch -R -p1 < "$p"
done
패치 파일 수동 편집
때로는 패치 파일을 직접 편집해야 합니다. 예를 들어 불필요한 변경을 제거하거나, 백포트를 위해 경로를 수정하거나, 패치에서 특정 헝크만 분리할 때입니다.
# ===== 헝크 제거 =====
# 패치에서 특정 헝크를 제거하려면:
# 1. 해당 @@ 줄부터 다음 @@ 줄(또는 파일 끝/다음 --- 줄) 직전까지 삭제
# 2. 파일 헤더의 줄 수는 보통 자동 무시됨 (patch가 실제 내용 기준)
# ===== 경로 수정 =====
# 패치의 파일 경로가 현재 소스 트리와 다른 경우
sed -i 's|a/old/path/|a/new/path/|g; s|b/old/path/|b/new/path/|g' fix.patch
# ===== 줄 번호 재계산 =====
# 헝크 내용을 수동 편집한 후 줄 번호가 맞지 않으면
rediff edited.patch > fixed.patch # patchutils 필요
# ===== 패치 분리 (filterdiff 대안) =====
# 직접 에디터로 패치를 분리하는 방법:
# 1. 원본 패치를 복사
cp big.patch part-a.patch
cp big.patch part-b.patch
# 2. part-a.patch에서 불필요한 파일 섹션 삭제
# 3. part-b.patch에서 반대로 삭제
# 4. 각각 --dry-run으로 검증
patch --dry-run -p1 < part-a.patch
patch --dry-run -p1 < part-b.patch
# ===== 주의사항 =====
# - 문맥 줄(공백 접두사)은 절대 수정하면 안 됩니다
# - +/- 줄을 수정하면 해당 헝크의 줄 수(@@ 헤더)도 수정해야 합니다
# - 문맥 줄의 첫 번째 공백 문자가 실제 내용이 아닌 접두사임에 주의
# - 편집 후 반드시 patch --dry-run으로 검증
- 탭/공백 혼동 — 문맥 줄의 접두사 공백(space)과 들여쓰기 탭(tab)을 혼동합니다. Unified 형식에서 문맥 줄의 첫 문자는 항상 공백(space)입니다.
- 줄 수 불일치 —
+줄을 추가/삭제하면@@헤더의 줄 수가 맞지 않습니다.rediff로 자동 수정하거나, 직접 수정해야 합니다. - 불완전한 줄(No newline at end of file) — 파일 끝에 개행이 없으면
\ No newline at end of file표시가 있습니다. 이 줄을 삭제하면 패치가 깨집니다.
커널 소스 내 diff/patch 관련 코드
리눅스 커널 소스 트리에는 diff/patch 워크플로를 지원하는 여러 스크립트와 도구가 포함되어 있습니다.
| 경로 | 용도 | 설명 |
|---|---|---|
scripts/checkpatch.pl | 패치 검증 | 코딩 스타일(Coding Style), 커밋 메시지, Signed-off-by 검사. checkpatch.pl *.patch로 실행 |
scripts/get_maintainer.pl | 수신자 결정 | MAINTAINERS 파일 기반으로 패치 수신자(To/Cc) 결정 |
scripts/diffconfig | .config 비교 | 두 커널 설정 파일의 차이를 "CONFIG_X n -> y" 형식으로 표시 |
scripts/bloat-o-meter | 크기 비교 | 두 vmlinux의 심볼별 크기 변화 표시. 패치의 코드 크기 영향 측정 |
scripts/decode_stacktrace.sh | 스택 디코딩 | 패치 적용 전후 oops 주소를 소스 줄로 변환 |
Documentation/process/ | 프로세스(Process) 문서 | submitting-patches.rst, email-clients.rst 등 패치 제출 가이드 |
# checkpatch.pl로 패치 검증 (제출 전 필수)
scripts/checkpatch.pl --strict v2-0001-*.patch
# ERROR: Missing Signed-off-by: line(s)
# WARNING: line over 100 characters
# 패치 파일이 아닌 소스 파일 직접 검사
scripts/checkpatch.pl --file drivers/net/foo.c
# get_maintainer.pl로 수신자 확인
scripts/get_maintainer.pl v2-0001-*.patch
# Jesse Brandeburg <...> (maintainer:INTEL ETHERNET DRIVERS)
# netdev@vger.kernel.org (open list:NETWORKING DRIVERS)
# bloat-o-meter로 패치의 코드 크기 영향 측정
scripts/bloat-o-meter vmlinux.before vmlinux.after
# add/remove: 2/0 grow/shrink: 1/0 up/down: 156/0 (156)
# Function old new delta
# e1000_open 542 698 +156
실전 레시피 모음
stable 커널 패치 백포트
# mainline 패치를 stable 커널에 적용
cd linux-6.6
patch --dry-run -p1 < ../mainline-fix.patch
# 오프셋/퍼지로 적용 가능한 경우
patch -p1 < ../mainline-fix.patch
# 충돌 발생 시: .rej 확인 후 수동 해결
patch -p1 < ../mainline-fix.patch
# → FAILED 헝크가 있으면 .rej 파일 확인
cat drivers/net/foo.c.rej
# → 해당 부분을 에디터에서 수동으로 적용
vim drivers/net/foo.c
# → 정리
rm drivers/net/foo.c.rej drivers/net/foo.c.orig
큰 패치를 파일별로 분리
# patchutils의 splitdiff 사용
splitdiff -a big-patch.patch
# 또는 filterdiff로 특정 서브시스템만 추출
filterdiff -i '*/drivers/net/*' big-patch.patch > net-changes.patch
filterdiff -i '*/include/*' big-patch.patch > header-changes.patch
이메일로 받은 패치 적용
# 이메일 본문에서 패치 추출 (git format-patch 형식)
git am patch-email.mbox
# 일반 diff 패치를 이메일에서 저장한 경우
patch -p1 < saved-patch.diff
# b4 도구로 LKML에서 패치 시리즈 가져오기
b4 am 20240101120000.12345-1-author@example.com
git am *.mbx
바이너리 파일 차이 확인
# diff는 바이너리 파일이면 "Binary files differ"만 표시
diff firmware-v1.bin firmware-v2.bin
# Binary files firmware-v1.bin and firmware-v2.bin differ
# 16진수 덤프로 비교
diff <(xxd firmware-v1.bin) <(xxd firmware-v2.bin) | head -30
# cmp: 첫 번째 다른 바이트 위치
cmp firmware-v1.bin firmware-v2.bin
# firmware-v1.bin firmware-v2.bin differ: byte 1024, line 8
# cmp -l: 모든 다른 바이트 나열
cmp -l firmware-v1.bin firmware-v2.bin
커널 .config 비교
# scripts/diffconfig (커널 소스 내장)
scripts/diffconfig .config.old .config
# 출력 예:
# CONFIG_DEBUG_INFO n -> y
# -CONFIG_DEBUG_INFO_NONE y
# +CONFIG_DEBUG_INFO_DWARF5 y
# +CONFIG_KASAN y
# 일반 diff로도 비교 가능 (주석/빈줄 제외)
diff <(grep -v '^#\|^$' .config.old | sort) \
<(grep -v '^#\|^$' .config | sort)
diff 알고리즘 이해
diff는 원본과 수정본을 줄 시퀀스(Sequence)로 보고 최단 편집 스크립트(Shortest Edit Script, SES)를 찾습니다. 전통적인 diff 모델에는 치환 연산이 따로 없으므로, 공통으로 남는 줄들의 최대 집합이 곧 LCS(Longest Common Subsequence, 최장 공통 부분 수열)입니다. 즉 LCS는 "무엇을 남길지", SES는 "무엇을 삭제하고 추가할지"를 표현한 같은 정보의 다른 관점입니다.
원본 줄 수를 n, 수정본 줄 수를 m, LCS 길이를 L이라 하면 SES 길이 D는 D = n + m - 2L입니다. 따라서 LCS가 길수록 삭제와 추가가 줄어듭니다. Myers 알고리즘은 이 관계를 바탕으로 편집 그래프(Edit Graph)에서 SES를 직접 찾아 실제 diff 출력에 쓸 수 있는 결과를 빠르게 만듭니다.
D = n + m - 2L 관계식, 편집 그래프, Myers 경로 탐색은 시퀀스 비교 알고리즘 문서에서 단계별로 설명합니다.
Git의 diff 알고리즘
Git은 Myers 외에도 가독성을 높이기 위한 여러 diff 알고리즘을 제공합니다. 최소 편집 길이만 맞는다고 항상 읽기 좋은 패치가 되는 것은 아니므로, 코드 이동이나 반복 줄이 많은 변경에서는 다른 선택지가 더 유리할 수 있습니다.
| 알고리즘 | 옵션 | 특징 |
|---|---|---|
| Myers (기본) | --diff-algorithm=myers | SES를 빠르게 찾는 기본 diff. LCS와 쌍대 관계 |
| Patience | --diff-algorithm=patience | 공통 고유 줄을 앵커로 사용, 코드 이동에 강함 |
| Histogram | --diff-algorithm=histogram | Patience 확장판. 낮은 빈도 공통 줄 처리 개선 |
| Minimal | --diff-algorithm=minimal | Myers를 더 오래 탐색해 더 작은 diff를 시도 |
# Patience diff 사용 (코드 리팩터링 시 권장)
git diff --patience
# Histogram diff (Git 기본 권장 후보)
git diff --histogram
# 기본 알고리즘 변경
git config --global diff.algorithm histogram
트러블슈팅
| 증상 | 원인 | 해결 |
|---|---|---|
can't find file to patch |
-p 값이 잘못됨 |
lsdiff -p1 patch.diff로 경로 확인 후 올바른 -p 값 사용 |
Hunk FAILED |
원본이 패치 생성 시점과 다름 | .rej 파일 확인 후 수동 적용, 또는 패치 재생성 |
Reversed patch detected |
이미 적용된 패치를 다시 적용 | -R로 되돌리거나, 이미 적용됨을 확인 |
| 줄바꿈 문제 (Windows ↔ Unix) | CR/LF vs LF 차이 | dos2unix 변환 후 diff, 또는 --strip-trailing-cr |
| 인코딩 문제 | UTF-8 vs EUC-KR 등 | iconv로 인코딩 통일 후 diff |
git apply 실패 |
공백 문제 | git apply --whitespace=warn 또는 --whitespace=fix |
patch: **** malformed patch |
패치 파일 손상 (이메일 줄바꿈 등) | git am --patch-format=mbox 또는 b4으로 재다운로드 |
| 빈 줄/공백만 다른 패치 | 에디터 설정 차이 | diff -B -w로 의미 없는 차이 무시 |
diff/patch 모범 사례
- 항상 Unified 형식(
-u)을 사용합니다. Normal/Context 형식은 레거시(Legacy)입니다. - 디렉터리 비교 시
-ruNp옵션 조합을 사용합니다 (재귀 + Unified + 새 파일 + 함수명). patch --dry-run으로 항상 먼저 검증합니다.- 커널 소스에서는
patch -p1이 표준입니다. - 퍼지(
-F)를 높이기보다 패치를 재생성하는 것이 안전합니다. .rej와.orig파일은 반드시 정리합니다.- Git 환경에서는
git format-patch/git am을 우선합니다. - 대규모 변경은 논리적 단위로 분리하여 별도 패치를 만듭니다.
참고 자료
- GNU Diffutils 매뉴얼 — diff, diff3, sdiff, cmp 공식 문서 (gnu.org)
- diff(1) 매뉴얼 페이지(Page) — POSIX diff 유틸리티 (man7.org)
- patch(1) 매뉴얼 페이지 — GNU patch 유틸리티 (man7.org)
- quilt — 패치 스택 관리 도구 (savannah.nongnu.org)
- patchutils — filterdiff, lsdiff, interdiff 도구 모음 (cyberelk.net)
- An Algorithm for Differential File Comparison — Hunt-McIlroy 알고리즘 원논문 (dartmouth.edu)
- An O(ND) Difference Algorithm and Its Variations — Eugene W. Myers 1986 논문 (xmailserver.org)
- Patience Diff Advantages — Bram Cohen의 Patience diff 설명 (livejournal.com)
- git-diff 공식 문서 — Git diff 명령 레퍼런스 (git-scm.com)
- git-apply 공식 문서 — Git apply 명령 레퍼런스 (git-scm.com)
- Submitting Patches — 커널 패치 제출 가이드 (kernel.org)
관련 문서
- 시퀀스 비교 알고리즘 — LCS, SES, Myers를 이론 중심으로 설명합니다.
- Git — 커널 개발용 Git 완전 가이드
- 패치 제출 — 커널 패치 제출 전체 과정
- 개발 도구 — 커널 개발 도구 체인
- Bash 셸 스크립팅 — 셸 스크립트에서의 diff/patch 활용
- 코딩 스타일 — checkpatch.pl 검증
- 정규표현식 — grep/sed와 함께 사용하는 패턴 매칭