여러 자식 커밋을 되 돌리는 방법은 무엇입니까?


982

다음과 같은 git 저장소가 있습니다.

A -> B -> C -> D -> HEAD

나는 지점의 머리가 A를 가리 키기를 원합니다. 즉, B, C, D 및 HEAD가 사라지고 머리가 A와 동의어가되기를 원합니다.

리베이스를 시도하거나 (사이에 변경 사항을 적용했기 때문에 적용되지 않음) 되돌릴 수있는 것처럼 들립니다. 그러나 여러 커밋을 어떻게 되돌릴 수 있습니까? 한 번에 하나씩 되돌 립니까? 순서가 중요합니까?


3
리모컨 만 재설정하려면 아무 것도 사용하지 않아도됩니다. 그러나 네 번째 커밋을 전에 사용하십시오 git push -f HEAD~4:master(원격 지사가 마스터라고 가정). 예, 그런 커밋을 푸시 할 수 있습니다.
u0b34a0f6ae 1

21
사람들이 끌어 낸 경우을 사용하여 변경 사항을 되 돌리는 커밋을 만들어야합니다 git revert.
Jakub Narębski

1
git show HEAD ~ 4를 사용하여 리모컨으로 오른쪽으로 밀도록하십시오
Mâtt Frëëman


5
"주문이 중요합니까?" 예, 커밋이 같은 파일의 같은 줄에 영향을 미치는 경우. 그런 다음 가장 최근의 커밋을 되돌리기 시작하고 되돌아 가야합니다.
avandeursen

답변:


1333

댓글에 쓴 내용 확장

일반적인 규칙은 누군가가 자신의 작업을 기반으로 할 수 있기 때문에 게시 한 기록을 다시 쓰지 않아야한다는 것입니다. 기록을 다시 작성 (변경)하면 변경 내용을 병합하고 업데이트하는 데 문제가 있습니다.

이 솔루션은 만드는 것입니다 그래서 새로운 커밋 하는 되돌립니다 변경 은 없애 싶어. git revert 명령을 사용 하여이 작업을 수행 할 수 있습니다 .

다음과 같은 상황이 있습니다.

A <-B <-C <-D <-마스터 <-머리

(여기에서 화살표는 포인터의 방향을 나타냅니다. 커밋의 경우 "상위"참조, 분기 헤드의 경우 상위 커밋 (분기 참조) 및 HEAD 참조의 경우 분기 이름).

작성해야 할 것은 다음과 같습니다.

A <-B <-C <-D <-[(BCD) ^-1] <-마스터 <-헤드

여기서 "[(BCD) ^-1]"은 커밋 B, C, D의 변경 사항을 되 돌리는 커밋을 의미합니다. 수학은 (BCD) ^-1 = D ^ -1 C ^ -1 B ^ -1, 다음 명령을 사용하여 필요한 상황을 얻을 수 있습니다.

$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m "the commit message"

다른 해결책은 커밋 A의 내용체크 아웃 하고이 상태를 커밋하는 것입니다.

$ git checkout -f A -- .
$ git commit -a

그러면 다음과 같은 상황이 발생합니다.

A <-B <-C <-D <-A '<-마스터 <-머리

커밋 A '는 커밋 A와 동일한 내용을 갖지만 다른 커밋 (커밋 메시지, 부모, 커밋 날짜)입니다.

Charles Bailey가 수정 한 Jeff Ferland솔루션 은 동일한 아이디어를 기반으로하지만 git reset을 사용합니다 .

$ git reset --hard A
$ git reset --soft @{1}  # (or ORIG_HEAD), which is D
$ git commit

40
B, C 또는 D에 파일을 추가 한 경우 파일을 git checkout -f A -- .삭제하지 않으므로 수동으로 수행해야합니다. 이 전략을 지금 적용했습니다. Jakub
oma

18
이러한 솔루션은 동일하지 않습니다. 첫 번째 파일은 새로 만든 파일을 삭제하지 않습니다.
m33lky

10
@Jerry : git checkout foo계산 지점 foo ( 지점으로 전환) 또는 계산 파일 foo (색인에서)를 의미 할 수 있습니다 . --예를 들어 git checkout -- foo항상 파일에 관한 것입니다.
Jakub Narębski

87
큰 대답 외에. 이 속기는 나를 위해 작동git revert --no-commit D C B
welldan97

9
@ welldan97 : 의견 주셔서 감사합니다. 이 답변을 작성할 때 git revert여러 커밋을 수락하지 않았습니다. 그것은 아주 새로운 추가입니다.
Jakub Narębski

248

내가 찾은 깨끗한 방법

git revert --no-commit HEAD~3..

이 명령은 단 하나의 커밋으로 마지막 3 개의 커밋을 되돌립니다.

또한 기록을 다시 쓰지 않습니다.


16
이것은 간단하고 최상의 답변입니다
Julien Deniau

3
@JohnLittle 그것은 변화를 준비합니다. git commit실제로 커밋을 수행합니다.
x1a4

14
일부 커밋이 병합 커밋 인 경우에는 작동하지 않습니다.
MegaManX

5
끝에있는 두 점은 무엇을합니까?
카 다몬

5
@cardamom 범위를 지정합니다. HEAD~3..것과 동일HEAD~3..HEAD
Toine H

238

그렇게하려면 revert 명령 을 사용하여 되돌릴 커밋 범위를 지정하면됩니다.

당신의 모범을 고려할 때, 당신은 이것을해야 할 것입니다 (지사 '마스터'에 있다고 가정) :

git revert master~3..master

그러면 B, C 및 D의 역 커밋으로 로컬에 새 커밋이 만들어집니다 (이 커밋에 의해 도입 된 변경 사항이 취소됨을 의미 함).

A <- B <- C <- D <- BCD' <- HEAD

129
git revert --no-commit HEAD~2..약간 관용적 인 방법입니다. 마스터 브랜치에있는 경우 마스터를 다시 지정할 필요가 없습니다. 이 --no-commit옵션을 사용하면 git이 여러 revert commit ...메시지로 히스토리를 버리는 대신 모든 커밋을 한 번에 되돌릴 수 있습니다 (원하는 것으로 가정).
kubi

6
@ 빅터 커밋 범위를 수정했습니다. 범위의 시작은 배타적이므로 포함되지 않습니다. 따라서 마지막 3 개의 커밋을 되돌리려면 3 번째 커밋 의 부모 에서 범위를 시작해야합니다 master~3.

2
@kubi는 단일 커밋을 사용하여 커밋 메시지에 SHA를 포함시키는 방법이 없습니다 (방법은 있지만 되 돌린 커밋을 수동으로 입력하지 않아도 됨)?
Chris S

@ChrisS 내 첫 번째 생각은 사용하지 않는 것입니다 --no-commit(따라서 각 복귀마다 별도의 커밋을 얻음). 그런 다음 대화 형 리베이스로 모두 스쿼시하십시오. 결합 된 커밋 메시지에는 모든 SHA가 포함되며 원하는 커밋 메시지 편집기를 사용하여 원하는대로 정렬 할 수 있습니다.
Radon Rosborough

71

Jakub의 답변과 유사하게 되돌릴 커밋을 쉽게 선택할 수 있습니다.

# revert all commits from B to HEAD, inclusively
$ git revert --no-commit B..HEAD  
$ git commit -m 'message'

9
귀하의 솔루션은 나에게 잘 작동했지만 약간 수정되었습니다. 이 경우 Z-> A-> B-> C-> D-> HEAD가 있고 A 상태로 돌아가려면 이상하게도 git revert --no-commit Z.를 실행해야합니다. HEAD
Bogdan

3
@Bogdan에 동의하면 되돌리기 범위는 다음과 같습니다. SHA_TO_REVERT_TO..HEAD
Vadym Tyemirov

11
범위가 잘못되었습니다. 그것은해야한다 B^..HEAD, 그렇지 않으면 B는 제외됩니다.
tessus

3
@tessus에 동의하면 올바른 방법은 다음과 같습니다. git revert --no-commit B^..HEAD또는git revert --no-commit A..HEAD
Yoho

64
git reset --hard a
git reset --mixed d
git commit

그것은 한 번에 모든 사람들에게 되돌림으로 작용할 것입니다. 좋은 커밋 메시지를 제공하십시오.


4
그가 닮기 HEADA원한다면 아마도 인덱스가 일치하기를 원할 것이므로 git reset --soft D아마도 더 적합 할 것입니다.
CB Bailey

2
--soft 재설정은 인덱스를 이동시키지 않으므로 커밋 할 때 커밋이 D가 아닌 직접에서 온 것처럼 보입니다. 그러면 분기가 분할됩니다. --mixed는 변경 사항을 남기고 인덱스 포인터를 이동하므로 D는 부모 커밋이됩니다.
Jeff Ferland

5
예, git reset --keep이 내가 가진 것 같아요. 2010 년 4 월에 릴리스 된 버전 1.7.1에서 나왔으므로 그 당시에는 답이 없었습니다.
Jeff Ferland

git checkout A다음 git commit위의 나를 위해 작동하지만이 대답은하지 않았다.
SimplGy

git reset --mixed D필요한가요? 구체적으로 왜 reset? D로 재설정하지 않고 HEAD가 A를 가리키고 B, C 및 D가 "중지"되고 가비지 수집되기 때문에 이것이 그가 원하는 것이 아니기 때문입니까? 그런데 왜 --mixed? 당신은 이미 "대답 --soft이 방법 a를 - 작업 디렉토리가 A의 변경을 포함하면서,이 수단 지수, D의 변화를 포함 할 인덱스를 이동하여 그래서 재설정이 ... 인덱스를 이동하지 않습니다" git status또는 git diff색인 [D]에 비교하는 ( 작업 디렉토리 [A])는 물질을 보여줄 것입니다; 그 사용자는 D에서 A로 다시 가고 있습니까?
The Red Pea

39

먼저 작업 사본이 수정되지 않았는지 확인하십시오. 그때:

git diff HEAD commit_sha_you_want_to_revert_to | git apply

그런 다음 커밋하십시오. 되돌리기 이유를 문서화하는 것을 잊지 마십시오.


1
나를 위해 일했다! 기능 브랜치가 개발 브랜치 (일부 버그 수정)에서 작성된 변경 사항이 더 이상 사용되지 않아서 기능 브랜치가 개발에서 작성된 모든 변경 사항 (일부 파일 삭제 포함)을 덮어 써야했습니다.
silentser

1
바이너리 파일로는 작동하지 않습니다 :error: cannot apply binary patch to 'some/image.png' without full index line error: some/image.png: patch does not apply
GabLeRoux

2
이것은 허용 된 답변보다 훨씬 유연한 솔루션입니다. 감사!
Brian Kung

2
다시 : 이진 파일은 --binary 옵션을 사용합니다 : git diff --binary HEAD commit_sha_you_want_to_revert_to | git apply
weinerk

2
병합 커밋이 포함 된 커밋 범위를 되돌리려는 경우에도 작동합니다. 사용하는 경우 git revert A..Z당신은 얻을 것error: commit X is a merge but no -m option was given.
율리 Gonera

35

이 질문에 답할 수 없을 정도로 실망했습니다. 다른 모든 질문은 올바르게 되돌리고 역사를 보존하는 방법과 관련이 있습니다. 이 질문은 "지점장이 A를 가리 키기를 원합니다. 즉, B, C, D 및 HEAD가 사라지고 헤드가 A와 동의어가되기를 원합니다."

git checkout <branch_name>
git reset --hard <commit Hash for A>
git push -f

나는 Jakub의 게시물을 많이 배웠지 만 회사의 일부 직원 (Pull-Request없이 "테스트"지점에 푸시 할 수있는 액세스 권한)은 5 개의 커밋 전에 실수를 수정하고 수정하려고 시도하는 5 개의 나쁜 커밋처럼 밀렸습니다. 그뿐만 아니라 하나 또는 두 개의 풀 요청이 수락되었으므로 이제는 나빴습니다. 잊어 버렸습니다. 마지막으로 좋은 커밋 (abc1234)을 발견하고 기본 스크립트를 실행했습니다.

git checkout testing
git reset --hard abc1234
git push -f

이 리포지토리에서 일하는 다른 5 명에게 지난 몇 시간 동안의 변경 사항과 최신 테스트에서 Wipe / Re-Branch를 더 잘 기록한다고 말했습니다. 이야기의 끝.


2
커밋을 게시하지 않았으므로 이것이 필요한 대답이었습니다. 감사합니다, @Suamere.
Tom Barron

이 작업을 수행하는 더 좋은 방법은입니다.이 명령은 git push --force-with-lease증발하려는 커밋 이후 또는 그 범위 내에 다른 누군가가 분기에 커밋하지 않은 경우에만 기록을 다시 작성합니다. 다른 사람들이 지점을 사용했다면 그 역사는 다시 쓰지 않아야하며 커밋은 단순히 눈에 띄게 되돌려 져야합니다.
frandroid

1
@frandroid는 "역사를 다시 쓰지 말아야한다"고 시스 만 절대적으로 다루었 다. 이 스레드의 문제와 내 대답의 요점은 특정 시나리오의 경우 모든 기록을 지워야한다는 것입니다.
Suamere

@Suamere 물론, 그게 문제입니다. 그러나 당신의 대답은 당신이 다른 사람들에게 말해야 할 것에 대해 언급했듯이, 문제의 가능성이 있습니다. 개인적인 경험을 통해 push -f는 다른 사람들이 당신이 지우려고하는 일을 저질렀다면 코드베이스를 망칠 수 있습니다. -포스와 임대는 동일한 결과를 얻습니다. 단, 레포를 엉망으로 만들려고하면 엉덩이가 절약됩니다. 왜 기회를 잡아? --force-with-lease가 실패하면 어떤 커밋이 방해가되는지 확인하고 올바르게 평가하고 조정 한 후 다시 시도하십시오.
frandroid

1
@Suamere 감사합니다! 나는 그 질문이 역사를 다시 쓰고 싶다고 분명히 말하고있다. 나는 당신과 같은 상황에 있으며 누군가가 우연히 (휴가 중일 때) 우연히 수십 번의 추악한 복귀와 이상한 커밋과 복귀를 한 상태에서 OP를 추측하고 상태를 되돌릴 필요가 있습니다. 건강에 좋은 경고와 함께 어떤 경우에도 이것이 정답입니다.
리 리처드슨

9

이것은 Jakub의 답변에서 제공되는 솔루션 중 하나의 확장입니다.

롤백해야 할 커밋이 다소 복잡하고 커밋 중 일부가 병합 커밋 인 상황에 직면하여 기록을 다시 쓰지 않아야했습니다. git revert추가 된 복귀 변경 사항 사이에 충돌이 발생 하여 일련의 명령 을 사용할 수 없었 습니다. 나는 다음 단계를 사용하여 끝났습니다.

먼저, 분기의 끝에 HEAD를 남겨두고 대상 커밋의 내용을 확인하십시오.

$ git checkout -f <target-commit> -- .

-는 <target-commit>파일이 아닌 커밋으로 해석됩니다.은 현재 디렉토리를 나타냅니다.

그런 다음 롤백중인 커밋에 추가 된 파일을 확인하여 삭제해야합니다.

$ git diff --name-status --cached <target-commit>

추가 된 파일은 줄의 시작 부분에 "A"로 표시되어야하며 다른 차이점은 없어야합니다. 이제 파일을 제거해야하는 경우 제거 할 파일을 준비하십시오.

$ git rm <filespec>[ <filespec> ...]

마지막으로 복귀를 커밋하십시오.

$ git commit -m 'revert to <target-commit>'

원하는 경우 원하는 상태로 돌아갑니다.

$git diff <target-commit> <current-commit>

차이가 없어야합니다.


당신은 git지점의 끝으로 머리를 할 수 있습니까?
Suamere

2
광산에서 병합 커밋을했기 때문에 이것은 훨씬 더 나은 해결책이었습니다.
sovemp

3

공유 저장소에서 커밋 그룹을 되 돌리는 쉬운 방법 (사람들이 사용하고 기록을 보존하려는 경우)은 git revertgit과 함께 사용하는 것 입니다 rev-list. 후자는 커밋 목록을 제공하며, 전자는 커밋을 수행합니다.

두 가지 방법이 있습니다. 단일 커밋에서 여러 커밋을 되돌리려면 다음을 사용하십시오.

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert -n $i; done

이것은 필요한 커밋 그룹을 되돌릴 것이지만 작업 트리에 모든 변경 사항을 남겨두면 평소대로 커밋해야합니다.

또 다른 옵션은 되 돌린 변경 당 단일 커밋을 갖는 것입니다.

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert --no-edit -s $i; done

예를 들어 커밋 트리가 있다면

 o---o---o---o---o---o--->    
fff eee ddd ccc bbb aaa

eee 에서 bbb로 변경 사항을 되돌리려면 다음을 실행하십시오.

for i in `git rev-list eee^..bbb`; do git revert --no-edit -s $i; done

방금 이것을 사용했습니다. 감사!
Ran Biron

2

그 중 어느 것도 나를 위해 일하지 않았으므로 되돌릴 3 가지 커밋 (마지막 3 커밋)이 있었으므로 다음과 같이했습니다.

git revert HEAD
git revert HEAD~2
git revert HEAD~4
git rebase -i HEAD~3 # pick, squash, squash

매력처럼 일했습니다 :)


2
변경 사항이 아직 푸시되지 않은 경우에만 가능한 옵션입니다.
kboom

0

내 의견으로는 매우 쉽고 깨끗한 방법은 다음과 같습니다.

A로 돌아가다

git checkout -f A

마스터의 머리를 현재 상태로 향하게

git symbolic-ref HEAD refs/heads/master

저장

git commit

1
downvoting의 이유를 설명해 주시겠습니까?
nulll

이것은 잘 작동합니다. 이 유용한 답변에 대해 공감대를 내리는 요점은 무엇입니까? 아니면 모범 사례가 무엇인지 설명 할 수 있습니까?
Levent Divilioglu

그것과 동일 git checkout master; git reset --hard A합니까? 아니면 이것이 무엇을하는지 좀 더 설명 할 수 없다면?
MM

그냥 작동하지만 symbolic-ref HEAD는 "안전한"명령이 아닌 것 같습니다
Sérgio

오류 수정 마스터를 원하지 않습니다. 분기를 수정하고 싶습니다
Sérgio

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