Git에서 마지막 커밋을 두 개로 나누는 방법


277

나는이 개 작업 지점,이 마스터포럼 난 그냥 몇 가지 수정했습니다 포럼 난에 벚꽃 선택하고 싶은 것을, 분기를 마스터 . 그러나 불행히도, 체리 피킹하려는 커밋에는 원하지 않는 수정 사항이 포함되어 있습니다.

해결책은 어쩌면 잘못된 커밋을 삭제하고 두 개의 별도 커밋으로 바꾸는 것입니다. 하나는 마스터에서 선택하고 싶은 변경 사항이 있고 다른 하나는 커밋에 속하지 않습니다.

나는 노력했다

git reset --hard HEAD^

모든 변경 사항을 삭제 했으므로 다시 돌아 가야했습니다.

git reset ORIG_HEAD

내 질문은 마지막 커밋을 두 개의 별도 커밋으로 나누는 가장 좋은 방법 은 무엇입니까?

답변:


332

색인을 사용해야합니다. 혼합 재설정 ( " git reset HEAD ^")을 수행 한 후 첫 번째 변경 사항 세트를 색인에 추가 한 후 커미트하십시오. 그런 다음 나머지는 커밋하십시오.

" git add "를 사용 하여 파일의 모든 변경 사항을 색인에 넣을 수 있습니다 . 파일에서 수행 된 모든 수정 사항을 스테이징하지 않으려면 그 중 일부만 "git add -p"를 사용할 수 있습니다.

예를 보자. 다음 텍스트가 포함 된 myfile이라는 파일이 있다고 가정 해 봅시다.

something
something else
something again

마지막 커밋에서 수정하여 이제 다음과 같이 보입니다.

1
something
something else
something again
2

이제 두 개로 나누고 첫 번째 줄을 첫 번째 커밋에 넣고 마지막 줄을 두 번째 커밋에 넣기를 원합니다.

먼저 HEAD의 부모로 돌아가서 파일 시스템의 수정 사항을 유지하고 싶습니다. 그래서 인수없이 "git reset"을 사용합니다 (소위 "혼합"재설정).

$ git reset HEAD^
myfile: locally modified
$ cat myfile
1
something
something else
something again
2

이제 "git add -p"를 사용하여 커밋하려는 변경 사항을 인덱스에 추가합니다 (= 스테이징). "git add -p"는 색인에 추가해야 할 파일 변경 사항을 묻는 대화식 도구입니다.

$ git add -p myfile
diff --git a/myfile b/myfile
index 93db4cb..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,5 @@
+1
 something
 something else
 something again
+2
Stage this hunk [y,n,a,d,/,s,e,?]? s    # split this section into two!
Split into 2 hunks.
@@ -1,3 +1,4 @@
+1
 something
 something else
 something again
Stage this hunk [y,n,a,d,/,j,J,g,e,?]? y  # yes, I want to stage this
@@ -1,3 +2,4 @@
 something
 something else
 something again
+2
Stage this hunk [y,n,a,d,/,K,g,e,?]? n   # no, I don't want to stage this

그런 다음이 첫 번째 변경 사항을 커밋합니다.

$ git commit -m "Added first line"
[master cef3d4e] Added first line
 1 files changed, 1 insertions(+), 0 deletions(-)

이제 다른 모든 변경 사항 (즉, 마지막 줄에있는 숫자 "2")을 커밋 할 수 있습니다.

$ git commit -am "Added last line"
[master 5e284e6] Added last line
 1 files changed, 1 insertions(+), 0 deletions(-)

로그를 확인하여 커밋이 무엇인지 확인하십시오.

$ git log -p -n2 | cat
Commit 5e284e652f5e05a47ad8883d9f59ed9817be59d8
Author: ...
Date: ...

    Added last line

Diff --git a/myfile b/myfile
Index f9e1a67..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -2,3 +2,4 @@
 something
 something else
 something again
+2

Commit cef3d4e0298dd5d279a911440bb72d39410e7898
Author: ...
Date: ...

    Added first line

Diff --git a/myfile b/myfile
Index 93db4cb..f9e1a67 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,4 @@
+1
 something
 something else
 something again

1
나는 지난 주 반 동안 Mercurial에서 git에 익숙해 졌으며 재설정 후 git reset [--patch|-p] <commit>해야 할 어려움을 덜어 주는 편리한 바로 가기 명령 이 있습니다 git add -p. 내가 맞아? 자식 사용 1.7.9.5.
trojjer

2
: 여기 이전 커밋하거나 M의 커밋에 N의 커밋을 변경할 필요가 있다면 리베이스를 포함하여,이 기술에 좀 더 emmanuelbernard.com/blog/2014/04/14/...을 .
Chris Westin

84

목표 :

  • 과거 커밋 ( splitme)을 두 개로 나누고 싶습니다 .
  • 커밋 메시지유지하고 싶습니다 .

계획:

  1. 이전부터 대화식으로 리베이스하십시오 splitme.
  2. 편집하십시오 splitme.
  3. 두 번째 커밋으로 분할되도록 파일을 재설정하십시오.
  4. 커밋을 수정하고 메시지를 유지 관리하고 필요에 따라 수정하십시오.
  5. 첫 번째 커밋에서 분리 된 파일을 다시 추가하십시오.
  6. 새 메시지로 커밋하십시오.
  7. 리베이스를 계속하십시오.

rebase 단계 (1 & 7)는 splitme가장 최근의 커밋 인 경우 건너 뛸 수 있습니다 .

git rebase -i splitme^
# mark splitme commit with 'e'
git reset HEAD^ -- $files
git commit --amend
git add $files
git commit -m "commit with just some files"
git rebase --continue

분할 파일을 먼저 커밋하려면 -i를 다시 rebase하고 순서를 전환하십시오.

git rebase -i splitme^
# swap order of splitme and 'just some files'

1
git reset HEAD^퍼즐의 빠진 조각이었다. 잘 작동합니다 -p. 감사!
Marius Gedminas

10
에 대한 -- $files인수를 기록하는 것이 중요 합니다 git reset. 경로가 전달되면 git reset해당 파일을 참조 된 커밋 상태로 복원하지만 커밋은 변경하지 않습니다. 경로를 벗어나면 다음 단계에서 수정하려는 커밋을 "손실"합니다.
duelin markers

2
이 방법을 사용하면 허용 된 답변과 비교하여 첫 번째 커밋 메시지를 다시 복사하여 붙여 넣을 필요가 없습니다.
Calvin

또한 : 모든 파일을 재설정하려면을 사용하십시오 git reset HEAD^ -- .. 놀랍게도 이것은 정확히의 동작 이 아닙니다git reset HEAD^ .
allidoiswin

52

현재 커밋을 두 개의 커밋으로 변경하려면 다음과 같이 할 수 있습니다.

어느 한 쪽:

git reset --soft HEAD^

이것은 마지막 커밋을 취소하지만 모든 것이 준비된 상태로 둡니다. 그런 다음 특정 파일을 스테이지 해제 할 수 있습니다.

git reset -- file.file

선택적으로 해당 파일의 일부를 복원하십시오.

git add -p file.file

새로운 첫 커밋을 만드십시오 :

git commit

두 번째 커밋에서 나머지 변경 사항을 준비하고 커밋합니다.

git commit -a

또는:

마지막 커밋의 모든 변경 사항을 취소하고 단계를 해제하십시오.

git reset HEAD^

첫 번째 변경 라운드를 선택적으로 준비하십시오.

git add -p

범하다:

git commit

나머지 변경 사항을 적용하십시오.

git commit -a

(두 단계 모두에서 새 파일을 추가 한 커밋을 원치 않고 두 번째 커밋에 추가하려는 commit -a경우 이미 추적 된 파일의 변경 사항을 단계별로만 수동으로 추가해야 합니다.)


22

을 실행 git gui하고 "마지막 커밋 수정"라디오 버튼을 선택한 다음 첫 커밋에 들어 가지 않으려는 변경 사항을 커밋 해제 (커밋> 언 스테이지에서 언 스테이지 또는 Ctrl- U)하십시오. 나는 그것이 가장 쉬운 방법이라고 생각합니다.

당신이 할 수있는 또 다른 일은 커밋 ( git cherry-pick -n) 하지 않고 git gui변경 사항 을 선택한 다음 커밋하기 전에 수동으로 또는 원하는 변경 사항 을 선택하는 것입니다.



13

아무도 제안하지 않은 것에 놀랐습니다 git cherry-pick -n forum. 이렇게하면 최신 forum커밋 에서 변경 사항이 준비 되지만 커밋되지는 않습니다. 그런 다음 reset필요하지 않은 변경 사항 을 제거하고 유지하려는 내용을 커밋 할 수 있습니다.


3

이중 복귀 스쿼시 방법

  1. 원치 않는 변경을 제거하는 다른 커밋을 만듭니다. (파일 단위 인 경우이 작업은 매우 쉽습니다. git checkout HEAD~1 -- files with unwanted changesgit commit. 그렇지 않은 경우 변경 내용이 혼합 된 파일을 부분적으로 준비 git reset file하고 git add -p file중간 단계로 진행할 수 있습니다 .)이를 되돌리기라고 합니다.
  2. git revert HEAD– 원치 않는 변경 사항을 다시 추가하는 또 다른 확약을 작성하십시오. 이것은이다 더블 되돌리기
  3. 커밋 한 커밋 2 개 중 첫 커밋을 스쿼시 ( git rebase -i HEAD~3)합니다. 이 커밋은 이제 두 번째 커밋에있는 변경 사항을 원하지 않습니다.

혜택

  • 커밋 메시지를 유지
  • 분할 커밋이 마지막 것이 아닌 경우에도 작동합니다. 원하지 않는 변경 사항이 나중에 커밋과 충돌하지 않아야합니다.

1

체리 따기이므로 다음을 수행 할 수 있습니다.

  1. cherry-pick그것은 함께 --no-commit옵션 추가.
  2. reset사용 add --patch, add --edit아니면 그냥 add유지하려는 어떤 단계에.
  3. commit 단계적 변화.
    • 원래 커밋 메시지를 재사용 하기 위해 명령에 --reuse-message=<old-commit-ref>또는 --reedit-message=<old-commit-ref>옵션을 추가 할 수 있습니다 commit.
  4. 로 단계적 변경 사항을 제거하십시오 reset --hard.

원본 커밋 메시지를 보존하거나 편집하는 다른 방법 :

  1. cherry-pick 원래 커밋은 정상입니다.
  2. 원하지 않는 변경 사항을 add되돌리고 반전을 준비하는 데 사용 하십시오.
    • 이 단계는 추가 한 내용을 제거하는 경우 쉽지만 제거한 내용을 추가하거나 변경 사항을 되 돌리는 경우 약간 까다 롭습니다.
  3. commit --amend 체리 고른 커밋에 대한 반전에 영향을 미칩니다.
    • 동일한 커밋 메시지가 다시 표시되며 필요에 따라 유지 또는 수정할 수 있습니다.

0

이는 커밋이 많고 적은 양의 파일을 새 커밋으로 이동해야하는 경우를위한 또 다른 솔루션 일 수 있습니다. <path>HEAD에서 마지막 커밋에서 파일 세트를 추출하여 모두 새 커밋으로 이동 한 경우에 작동합니다. 여러 커밋이 필요한 경우 다른 솔루션을 사용할 수 있습니다.

먼저 수정 전과 수정 후 각각의 코드를 되돌릴 수있는 변경 사항이 포함 된 단계적 영역과 비 단계적 영역으로 패치를 만듭니다.

git reset HEAD^ <path>

$ git status
On branch <your-branch>
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   <path>

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   <path>

무슨 일이 일어날 지 이해하려면 (화살표와 설명은 명령의 일부가 아닙니다) :

git diff --cached   -> show staged changes to revert <path> to before HEAD
git diff            -> show unstaged changes to add current <path> changes

<path>마지막 커밋의 변경 사항 되돌리기 :

git commit --amend  -> reverts changes on HEAD by amending with staged changes

<path>변경 사항으로 새로운 커밋을 만듭니다 .

git commit -a -m "New Commit" -> adds new commit with unstaged changes

이는 마지막 커밋에서 추출 된 변경 사항이 포함 된 새 커밋을 만드는 효과가 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.