티스토리 뷰

기타

git diff, patch 사용법

Jaehee Jeon 2024. 2. 4. 17:02
반응형

사내에서 diff와 patch를 다룰 일이 잦아 관련 내용을 정리해보고자 한다.
git version 2.39.2 (Apple Git-143), patch 2.0-12u11-Apple에서 확인한 내용이며 버전에 따라 출력 형식은 조금 변할 수 있다.


diff?

git diff라는 명령으로 사용 가능하다.

깃의 워킹 디렉토리와 스테이징 영역을 비교하거나, 특정 커밋과 비교하거나, 로컬과 원격 깃 레포지토리를 비교하는 명령이다.

diff 사용법

diff라는 폴더에 first.txt라는 파일을 생성해 임의의 최초 커밋을 생성한 다음,

second.txt라는 파일을 생성한 뒤 git log를 확인한 결과는 아래와 같다.

# git log
commit 8d475c23f8ac7f857ca6f1ab7790f034467e8e55 (HEAD -> main)
Author: ... <...@gmail.com>
Date:   Sun Feb 4 15:51:24 2024 +0900

    second.txt

commit a9720f836dd6385594dd0e3ac53537c3f63acf03
Author: ... <...@gmail.com>
Date:   Sun Feb 4 15:47:16 2024 +0900

    create first.txt

git diff HEAD~1..HEADHEAD에 위치한 가장 최근 커밋과 첫 번째 커밋을 비교할 수 있다.
HEAD대신 원하는 커밋의 커밋 해시(SHA)를 지정하여 비교할 수도 있다.

# git diff HEAD~1..HEAD, HEAD의 1개 이전 커밋과 HEAD를 비교, 커밋 해시로도 비교 가능
diff --git a/second.txt b/second.txt
new file mode 100644
index 0000000..86a2422
--- /dev/null
+++ b/second.txt
@@ -0,0 +1 @@
+create second.txt

어떤 디렉토리에 어떤 내용의 파일이 변경되었는지 알려준다.

a/, b/ ??

실제 파일 디렉토리와 무관하게 디렉토리 앞에 a/, b/가 붙는 것을 볼 수 있다.

이는 git이 변경 사항을 관리하기 위해 임의로 붙이는 prefix로, 변경 전 디렉토리에는 a/를, 변경 후 디렉토리에는 b/를 자동으로 붙여 표현하게 된다.

이러한 prefix 없이 실제 디렉토리만을 표현하고자 한다면 --no-prefix 옵션을 붙이면 된다

--no-prefix

git diff시 출력에 디렉토리에 자동으로 변경 전, 후를 나타내기 위해 붙는 a/, b/와 같은 prefix를 제거할 수 있다.

--no-prefix를 추가해 diff를 출력한 결과는 아래와 같다.

# git diff HEAD~1..HEAD --no-prefix
diff --git second.txt second.txt # 옵션 없이는 diff --git a/second.txt b/second.txt 로 출력되었음
new file mode 100644
index 0000000..86a2422
--- /dev/null
+++ second.txt # 옵션 없이는 b/second.txt로 출력되었음.
@@ -0,0 +1 @@
+create second.txt

file mode?

새로운 변경에 대한 종류 및 권한 또한 표시되는 것을 볼 수 있다.

new file mode 100644

100644는 파일의 유형 및 권한을 8진수로 표현한 것이다.

앞의 100은 변경의 유형, 뒤의 644는 해당 파일의 권한을 나타낸다.

100은 일반 파일, 644-rw-r--r--로 소유자의 읽기 쓰기 권한, 그룹의 읽기 권한, 다른 사용자는 읽기 권한을 가진 파일임을 나타낸다.

index?

변경되는 부분이 로컬에서 어떤 해시를 부여받아 추적되는지 나타낸다.

위에서는 새로운 파일을 생성하는 경우이므로 기존 해시가 0000000였고 새로운 해시가 부여되는 것을 볼 수 있다.

git 버전에 따라 file mode와 index가 같은 행에 표시되는 경우가 존재한다.

/dev/null?

git으로 관리되는 프로젝트의 루트 디렉토리에 바로 파일을 생성했음에도 /dev/null이라는 파일이 사라지고 생성한 파일이 대체한 것으로 표현된다.

/dev/null은 유닉스 시스템에서 특별하게 다뤄지는 파일로 데이터를 버리고 아무 동작도 하지 않는 파일을 의미한다.

실제로 우리가 생성한 파일과 관계가 없다.

파일 생성, 수정, 삭제, 이름 변경 시의 diff

파일 생성, 수정, 삭제 시의 diff 형태를 한눈에 보면 아래와 같다.

기존의 first.txt는 삭제, second.txt는 수정, third.txt라는 파일은 새로 생성했다.

# 생성, 수정, 삭제 시의 diff
diff --git a/first.txt b/first.txt
deleted file mode 100644
index cbd2c53..0000000
--- a/first.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-create first file
-
diff --git a/second.txt b/second.txt
index 86a2422..3dcf956 100644
--- a/second.txt
+++ b/second.txt
@@ -1 +1,5 @@
+prefix
+
 create second.txt
+
+postfix
diff --git a/third.txt b/third.txt
new file mode 100644
index 0000000..a1c5d2c
--- /dev/null
+++ b/third.txt
@@ -0,0 +1,2 @@
+create third.txt
+
# 이름 변경 시의 diff
diff --git a/second.txt b/second_new_name.txt
similarity index 100%
rename from second.txt
rename to second_new_name.txt


patch

git diff를 통해 얻어낸 변경 사항을 patch 라는 별도의 프로그램을 통해 git과 무관하게 적용할 수 있다.

patch를 사용하면 변경 내역파일로 관리, 손쉽게 변경을 적용할 수 있다.

두 사람의 git디렉토리에 first.txt만 있는 상황을 가정,

나의 로컬에서의 변경점을 원격 git 레포지토리 활용 없이 타인의 로컬에 반영하려고 한다.

#ls
first.txt

어떠한 작업을 수행하여 결과로 second.txt가 생성되었고 그 내용을 커밋하였다.

diff를 출력하면 다음과 같다.

# git diff HEAD~1..HEAD
diff --git a/second.txt b/second.txt
new file mode 100644
index 0000000..86a2422
--- /dev/null
+++ b/second.txt
@@ -0,0 +1 @@
+create second.txt

diff를 단순히 콘솔에 출력하지 않고 파일의 형태로 만들어내면 patch프로그램에 이를 그대로 적용 가능하다.

diff가 곧 patch file이 된다.

# 패치 파일 생성
git diff HEAD~1 HEAD > ../patch_file # 생성을 원하는 디렉토리 및 파일 명 입력

당연히 파일의 내용은 diff로 출력한 내용과 동일하다.

이제 이 패치 파일을 타인의 로컬에 그대로 전달하고,
아래와 같이 패치 파일 표준 입력으로 넣어주기만 하면 된다.

# patch 적용 전 ls
first.txt
# patch 적용
# patch -p1 < ../patch_file
patching file second.txt
# patch 적용 결과 ls
first.txt  second.txt

파일의 형태로 변경 사항을 가볍게 주고받고 적용할 수 있다.

-p1?

왜 굳이 p1 옵션을 붙이는 것일까?
이것은 패치 파일(diff)에 존재하는 디렉토리 정보에서 경로 정보의 상위 몇 번째 디렉토리까지 무시하고 적용할지 알리는 옵션이다.

위에서 설명했듯 --no-prefix 옵션 없이 패치 파일을 생성(diff)하면

a/, b/와 같이 git이 prefix로 별도 디렉토리 정보를 추가한 결과가 출력되므로,

diff --git a/second.txt b/second.txt # 실제 디렉토리 정보가 아닌 `a/`, `b/`가 포함됨.
new file mode 100644
index 0000000..86a2422
--- /dev/null
+++ b/second.txt
@@ -0,0 +1 @@
+create second.txt

patch 프로그램이 이를 실제로 적용할 때 디렉토리에 붙은 접두어 한 단계를 무시하고 적용하도록 하는 것이다.

당연하게도 만약 --no-prefix옵션을 주어 패치 파일을 생성했다면,
패치 적용 시에는 patch -p0 < ${patch_file}p0옵션을 주어야 한다.

패치 파일과 패치 적용 시의 디렉토리 옵션이 맞지 않는 경우 사용자가 직접 이 충돌을 해소해주어야 하며 실패하는 경우 Oops.rej가 남는다.


참고

단순 파일에 대한 변경의 경우 패치 파일에 권한 및 로컬 깃 인덱스 정보가 누락되어도 적용은 가능하다.

# 파일 권한 및 인덱스 정보가 없는 아래의 약식 패치 파일도 적용이 가능하다.
--- /dev/null
+++ b/second.txt
@@ -0,0 +1 @@
+create second.txt
반응형