이 글에서는 Git 패치를 만드는 방법과 적용하는 방법을 알아보겠습니다.
오픈소스 프로젝트에 참여하면 git을 사용하게 되고, 다른 사람으로부터 patch 파일을 전달받는 경우가 있습니다. 그럼 이 패치 파일을 내 로컬 git에 적용하고 잘 동작하는지 확인해봐야하는데요, 어떻게 나의 로컬에 패치 파일을 적용하는지, 어떻게 내 수정사항을 패치 파일로 만드는지 알아보겠습니다.
1. git에 적용된 commit의 패치 파일 만들기
다음 명령어를 사용하면 가장 최신 commit부터 입력한 commit 이전까지의 모든 commit에 대한 패치를 만들 수 있습니다.
$ git format-patch [commit-id]
예를 들어, 현재 git에 아래와 같은 commit이 적용되어 있습니다. 가장 최신 commit은 2eadda0
입니다.
$ git log
commit 2eadda0d1931632a4dcdcd2bce170f55dc9928a2 (HEAD -> master)
commit 53192f622e94f814f56352248fb33fc7bd017184
commit 31c06b3ce501bee7f9a784999337ec72ecdc1bb7
아래와 같이 패치를 만들면, 가장 최신 commit에서 31c06b3
이전 commit인 53192f6
까지 패치를 만듭니다.
생성된 파일을 보면 각각의 commit마다 패치 파일을 만듭니다.
$ git format-patch 31c06b3ce501bee7f9a784999337ec72ecdc1bb7
0001-changes.patch # => patch file for 53192f6
0002-changes2.patch # => patch file for 2eadda0
-[commit count]
옵션을 사용하면, 현재 commit부터 commit count
개수의 이전 commit 까지 패치를 만들 수 있습니다.
$ git format-patch -[commit count]
예를 들어, 다음과 같이 입력하면 최근에 반영한 commit 2개를 패치로 만들어 줍니다.
$ git format-patch -2
0001-changes.patch
0002-changes2.patch
2. 패치 파일을 로컬에 적용 및 commit으로 반영
만들어진 패치를 git 프로젝트에 적용하려면 git am [patch file]
명령어를 사용하시면 됩니다.
$ git am [patch file]
이 명령어는 패치를 git에 적용하면서 commit까지 반영합니다.
패치 적용과정에서 conflict이 없으면 다음과 같은 로그가 나오면서 완료됩니다.
$ git am 0001-changes.patch
Applying: changes
$ git am 0002-changes2.patch
Applying: changes2
merge 과정에서 conflict이 발생하면 에러 로그가 출력됩니다.
3. git의 변경 사항에 대한 패치 만들기
수정사항이 commit으로 반영되지 않았고, unstaged 영역에 수정사항으로 있는 경우(git diff 명령어로 보이는 변경사항)에도 패치 파일을 만들 수 있습니다.
아래와 같이 git diff > [patch file name]
명령어로 파일을 만들 수 있습니다.
$ git diff > patchfile
만들어진 패치 파일의 내용을 확인해보면, 아래와 같은 형식으로 보입니다.
$ cat patchfile
diff --git a/patch-515db99 b/patch-515db99
deleted file mode 100644
index e69de29..0000000
diff --git a/src/main/java/hello/HelloWorld.java b/src/main/java/hello/HelloWorld.java
index 576a0c7..650be6d 100644
--- a/src/main/java/hello/HelloWorld.java
+++ b/src/main/java/hello/HelloWorld.java
@@ -4,6 +4,7 @@ public class HelloWorld {
public static void main(String[] args) {
Greeter greeter = new Greeter();
String suffix = "@@@";
- System.out.println(greeter.sayHello() + suffix);
+ String prefix = "$$$"
+ System.out.println(prefix + greeter.sayHello() + suffix);
}
}
4. 패치 파일을 로컬에 적용 및 unstaged 영역에만 반영
patch -p[number] < [patch file name]
명령어로 패치 파일을 적용하면, unstaged 영역에만 파일이 저장되며 commit으로 반영되진 않습니다.
패치 내용을 조금 수정하고 commit을 반영하고 싶을 때 이런 방법을 사용할 수 있습니다.
바로 위에서 만든 파일을, 아래와 같은 명령어로 적용할 수 있습니다. (-p1는 file path의 첫번째 경로를 무시하고 패치를 적용한다는 의미)
$ patch -p1 < patchfile
패치 적용 후, git diff
명령어를 입력하면 unstaged 영역에만 변경사항이 반영된 것을 확인할 수 있습니다.
여기서 바로 commit을 반영해도 되고, 조금 더 코드 수정을 하고 commit을 등록할 수 있습니다.
$ git diff
diff --git a/patch-515db99 b/patch-515db99
deleted file mode 100644
index e69de29..0000000
diff --git a/src/main/java/hello/HelloWorld.java b/src/main/java/hello/HelloWorld.java
index 576a0c7..650be6d 100644
--- a/src/main/java/hello/HelloWorld.java
+++ b/src/main/java/hello/HelloWorld.java
@@ -4,6 +4,7 @@ public class HelloWorld {
public static void main(String[] args) {
Greeter greeter = new Greeter();
String suffix = "@@@";
- System.out.println(greeter.sayHello() + suffix);
+ String prefix = "$$$"
+ System.out.println(prefix + greeter.sayHello() + suffix);
}
}
4.1 -p[number] 옵션
-p[number]
는 패치 안에 있는 file path에서 number 개수만큼의 디렉토리를 무시하고 패치에 적용하겠다는 의미입니다.
패치가 적용되는 원리를 설명하면서 -p[number]
에 대해서 설명하겠습니다.
예를 들어, 아래와 같은 내용의 패치 파일이 있습니다.
diff --git a/src/main/java/hello/HelloWorld.java b/src/main/java/hello/HelloWorld.java
...
그리고 현재 터미널에서 MyProject
디렉토리 아래에 src
폴더와 patchfile이 있습니다.
../MyProject$ ls
src patchfile
이 상황에서 아래와 같이 patch -p1 < patchfile
명령어를 입력하면, 패치 파일의 a/src/main/java/hello/HelloWorld.java
에서 앞의 1개의 디렉토리를 제거한 src/main/java/hello/HelloWorld.java
파일을 현재 경로에서 찾고 그 파일에 수정사항을 적용합니다.
../MyProject$ patch -p1 < patchfile
만약 patch -p2 < patchfile
처럼 입력하면 앞의 2개의 디렉토리를 제거한 main/java/hello/HelloWorld.java
파일을 현재 작업 디렉토리에서 찾고 패치를 적용하게 되는데, MyProject
아래에 main 디렉토리가 없기 때문에 파일을 찾을 수 없어서 실패합니다.
따라서, 현재 터미널의 작업 디렉토리 경로와 패치 파일의 경로가 맞지 않을 때 -p[number]
옵션을 사용하여 조절할 수 있습니다.
참고로, patch --help
명령어를 보면 파일 패스를 벗긴다고 설명이 되어있습니다.
$ patch --help
Usage: patch [OPTION]... [ORIGFILE [PATCHFILE]]
Input options:
-p NUM --strip=NUM Strip NUM leading components from file names.
4.2 a/b path가 제거된 패치 파일 만들기
git diff --no-prefix > [patchfile]
명령어는 아래와 같이 a/b path prefix가 제거된 패치 파일을 생성합니다.
diff --git src/main/java/hello/HelloWorld.java src/main/java/hello/HelloWorld.java
이렇게 만들어진 패치 파일을 적용할 때는 -p0
옵션을 사용하면 됩니다.
$ patch -p0 < patchfile
-p0
는 패치에 보이는 file path를 생략하지 않고 적용한다는 의미입니다.
Related Posts
- Git Alias - 단축/커스텀 명령어 설정
- Git에서 .gitignore 설정 방법
- git clone 명령어로 원격 저장소를 로컬에 저장
- Git - 두개 commit의 차이점 확인 (코드 차이, 파일 차이)
- Git - 브랜치 이름 변경하는 방법
- Git - 특정 commit 삭제, 되돌리기 (reset, revert)
- Git - 리모트 브랜치 생성과 삭제
- GitHub에 ssh-key 등록 방법 (Windows10)
- git log - 특정 author의 commit 리스트만 출력
- git log - commit 리스트 간단하게 출력 (pretty)
- Git - revert 명령어(commit 되돌리기)
- git diff 명령어로 파일 변경 내용 확인
- git show 명령어로 commit 정보 확인
- Git - 브랜치 생성, 삭제, 체크아웃
- Git - 원격 저장소(remote) 추가, 해제
- Git - Staging 영역의 파일을 Untracked로 변경
- Git - commit log에서 수정된 파일 목록 보기
- Git - 마지막 commit 수정, 삭제 방법 (reset, amend 명령어)
- git commit message 에디터 변경 (Vim, Sublime, Atom, VSCode)
- Git - blame 명령어, 코드 수정한 사람과 Commit 찾기
- Git - Patch 파일 만들기 & 적용하기