이 글에서는 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.patch2. 패치 파일을 로컬에 적용 및 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: changes2merge 과정에서 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 파일 만들기 & 적용하기