2012-04-16 3 views
13

확인을 눌러 약간의 혼란을 겪었습니다. 외관상으로는, 나의 기계에 집에서, 개발 분지는 새롭게하지 않았다. 나는 커밋을하고 밀었다. 결과적으로 실제 출처/개발 지점이 내 지역 개발 지점에 병합되었습니다. 이는 어떤 이유로 다른 지점으로 간주되었습니다!밀어 넣은 병합 실행 취소

사실, 어떻게 이런 일이 있었는지 이해하지 못하고 둘째로,이 일을 취소 할 수 있습니까? 그것을 설명하기 위해

는 네트워크가 지금과 같은 :

내가 정말 원하는 무엇
local-develop ---------------------------------- C*--- M - 
origin/develop --- C --- C --- C --- C --- C ---------/

는/대신 가지를 병합의 개발은 C *이 기원하기 위해 최선을 다하고 것이라고했다.

내가 말했듯이, 이것은 이미 밀려났다. 변경 사항을 삭제하고 원하는대로 커밋 할 수 있습니까? 예를 들어

내가 할 :

git reset --hard HEAD~1 

나는이 병합을 취소합니다 있는지 확실하지 않습니다와 나는 서로 다른 두 개의 개발이 다음 병합 등 삭제 ...?

답변

26

병합시 밀어 넣기가 발생하지 않으므로 git merge (물론, pull)에서 발생하지만 실제로는 병합 + 병합이므로 병합시 병합됩니다. pullfetch가 (지금에있는 모든 새로운 커밋을 얻을 의미하기 때문에,

<get copy from origin/develop> 
<wait around for remote's origin/develop to acquire new commits> 
git checkout develop  # get onto (local) develop branch 
<edit> 
git commit -m message  # create some new commit(s) 
git push origin develop # attempt to push -- but this fails! 
git pull 

그것은 병합 (위 M를) 커밋 만듭니다 마지막 단계입니다 :

그것은 당신이 이런 일을했다는 가능성이 보인다 origin/develop), merge (로컬 develop을 가져 와서 방금 가져온 새로운 커밋과 병합하십시오.) 당신이하지 git push 에디션이 새로운 결과가있는 경우

, 다음 원격 REPO는 중 해당 지역의 커밋, 사람이 분류 한 C*M이 없습니다. 이 경우, 당신은 좋은 모습입니다! (다시 원격에서 하나, 다음에 무엇을 볼 수 git log origin/develop을하고 해당 지역의 repo의 origin/develop 일치하는지 확인하기 위해 git fetch을 실행하여 확인할 수 있습니다.)

그것은 두 개의 완전히 분리 된 자식의 repos 여기에 있다는 것을 기억하는 데 도움이 될 수 있습니다 당신의 물건들; 그리고 전화 한 사람 origin 여기. (그 기계를 X라고 부르 자.) 만약 X에 로그온했다면, 그것 자신의 별도의 커밋 내역과 브랜치 이름 등을 가지고있다. 저기서 디렉토리를 repo로 변경하고 git log -1 develop을 실행하고 해당 분기의 끝 부분에있는 내용을 확인하십시오.

이제 X에서 로그 오프하고 다시 자신의 컴퓨터에있는 경우 git log -1 origin/develop을 실행할 수 있습니다. 그 내용이 X에서 본 것과 같으면 git fetch은 실제로 X에 로그온하여 develop에있는 내용을보고 있기 때문에 아무 것도 업데이트하지 않습니다. Xorigin/develop, fetch이 없으면 origin/develop에 추가됩니다.이제 X과 동기화되었습니다. X 님은 내 콘텐츠가 없지만 나만 보유하고 있습니다.

당신은 다음이 할 경우, 병합 커밋합니다 ...을 A (pull에 의해 암시 포함) merge, 자식을하는 별도의 조치를 취할하지만이 모든에서, 당신의의 repo에있는 경우 가지의 끝 (여전히이 경우 develop). 이 병합 커밋을 X (또는 누군가 X에있는 사용자가 커밋을 가져 오지만 지금은 무시합니다 .-))까지 밀어 넣기 전까지는 X에 커밋되지 않습니다.

어쨌든 리모컨 (여기서는 X)에 병합 완료가없는 한 사용자는 황금색입니다. 부터이 없으므로 다른 사람도 마찬가지입니다. develop 지점의 rebase을 수행하여 origin/develop 위에 커밋 (C*)을 올릴 수 있습니다. 그러면 병합 커밋 (M)이 제거되고 간단한 빨리 감기를 origin/develop으로 푸시 할 수 있습니다.

하면 X않습니다이 병합 커밋 - 즉, 당신이 pull 에드 후 밀어 얻은 경우 그 병합 - 당신은 아마도 다른 사람이 X에 액세스 할 수 있으며 현재 사용하고 있기 때문에 (어느 정도) 막히다 병합 커밋. git resetgit rebase 등으로 자신의 저장소에서 수행 할 수있는 것과 비슷한 방식으로 X에있는 저장소를 롤백 할 수는 있지만 일반적으로 좋지 않습니다.


지금, 당신은 사실이 가정은 (기계 X에) 다른 REPO에 푸시,하지만 당신은 다른 확신 아무도 아직 변경 사항을 볼 수 없다있어, 당신은 확실히 그들을 다시 것보다는 되 돌리는 것 그들 (되돌리기는 "fessing up"에 이르고 screwup-record는 뒤에 남겨 두어 다른 사람들이 쉽게 복구 할 수있게하지만 오류를 볼 수도 있습니다 :-)). 여기에 1

트릭 : 당신이 먼저 C7 이전에서 당신의 고유의 다이어그램에서 "지점 devel의 끝이 C7을 커밋한다"고 말을 기계 X의 REPO를 얻을 필요가, 단지 있도록 다시 번호 I 각 커밋의 이름을 다르게 지정할 수 있습니다.

--------------------------- C*--- M 
--- C4 --- C5 --- C6 --- C7 ----/

그래서 어떻게 할 수 있습니까? 음, 한 가지 방법은 X, cd에 로그인하여 (--bare 인 경우에도) Repo에 로그인하고 git update-ref을 사용하는 것입니다. C7의 SHA1이 실제로 50db850 ("git log"에 표시된 것처럼)이라고 가정 해 보겠습니다.

localhost$ ssh X 
X$ cd /path/to/repo.git 
X$ git update-ref refs/heads/develop 50db850 

을하지만 X에 로그인 할 수없는 경우, 또는 그냥 당신이 git push -f과 같은 작업을 수행 할 수 있습니다 싶지 않은 : 그럼 당신은이 작업을 수행 할 수 있습니다. 3는 (이것은 다른 장점이 있습니다 특히, 당신의 자식의 repo는 push -f이 성공적으로 완료되면 origin/develop가, 되감기되었음을 알 수 있습니다.) 그냥 오른쪽을 가리키는 로컬 가지 팁 커밋합니다 4

localhost$ git branch resetter 50db850 
localhost$ git log resetter   # make sure it looks right 
... 
localhost$ git push -f origin resetter:develop 
Total 0 (delta 0), reused 0 (delta 0) 
To ssh://[redacted] 
+ 21011c9...50db850 resetter -> develop (forced update) 
localhost$ git branch -d resetter # we don't need it anymore 

이 작업을 완료하면 컴퓨터 X가 원하는 상태로 돌아오고 원하지 않는 병합을 푸시 한 적이없는 것처럼 계속 진행할 수 있습니다.당신이 push -f을 수행 할 때 다른 사람이 M의 상단에 새로운 커밋을 만든 경우, 사람들은 는 "기술적으로 커밋 병합과 함께, 거기에 아직도 있지만, 그들이 (보이지 않는 될 것으로

주 잃어버린 "은 lost+found의 느낌이 git fsck --lost-found이고, 몇 달 후에 그들은 정말로 영원히 사라질 것입니다. 5

다시 매우 중요한: "공유의 repo의 롤백"이런 종류의 그래서 당신이 그것을하기 전에 괜찮아요 정말 반드시 해당 공유 REPO의 다른 사용자에게 큰 고통이다.


1도 되돌릴 필요가없는 사소한 병합 이런 종류의. 거기에 병합을 남기는 것이 근본적으로 잘못된 것은 아닙니다. 더 심각한 실수 합병을 다루는 경우, "oops"기록 외에 병합을 되 돌리는 데 또 다른 단점이 있습니다. 나중에 "병합"에서 변경 사항을 "다시 실행"하는 것이 조금 더 어렵습니다. 목적에 따라 "이전 합병과 생각을 볼 수 있습니다 : 확인을 다시 병합 할 필요가 없습니다 변경. 대신 "되돌리기 되돌리기"를해야합니다. 보기 (git log) 당신은 당신이 추진하려고하는 것은 당신이 밀어 의도가 무엇인지 확인하기 위해을 밀어 넣기 전에 :

나는 여기에 바로 테이크 아웃의 교훈이라고 생각합니다.

더 쉽고 간단 : git ls-remote. 이것은 원격 프로토콜의 내용을보기 위해 fetch protocol 코드를 사용합니다. 그러나 그것은 내가 여기에서 할 주제를 숨 깁니다. 리모콘은 당신처럼 레포입니다!

3 곧 자식 버전이 출시 git push -f으로 가능한 것입니다 새로운 "안전 기능"을 가지고있다. 그러나 그들은 아직 빠져 나오지 않았으므로 도움이되지 않습니다. 나는 이것을 나중에 다시 편집 할 것입니다. 그때까지는 이 실제로입니다. 공유 저장소에 새로운 항목을 푸시하려고하는 다른 사용자와 경쟁하고 있습니다.

원시 SHA-1 ID로도이 작업을 수행 할 수 있습니다. 분기 이름은 한 번에 여러 번 올바르게 입력하는 것이 더 쉽습니다.

보유 및 만료는 git의 "reflogs"를 통해 이루어집니다. 공유 서버가 모든 참조 업데이트를 로깅하지 않을 수 있습니다. 그렇지 않다면, 새로운 커밋을 이미 가지고있는 private repos만이 그 커밋을 유지할 것입니다. 가장 짧은 기본 만료 기간은 분기 팁에서 더 이상 도달 할 수없는 커밋에 대해 30 일 (약 1 개월)입니다. 하지만 공유 저장소에서 강제로 "작업을 잃어 버린"후 "잃어버린"커밋에 대해 저장소를 통해 모든 사람이 검색 할 수 있도록 만드는 것은 재미없고 놀랄만 한 스크램블입니다.

+0

와우, 덕분에이 광범위한 답변을 많이. 불행히도 X로 밀어 넣었습니다. 아무도 새로운 가져 오기를 만들지 않았으므로이 변경 사항을 취소 할 수 있는지 궁금합니다. 정말 명확하지 않은 한 가지 : 다시 두 걸음을 다시 설정하면 분기 중 하나 또는 두 가지 모두를 남겨두고 있습니까? – dmeu

+0

좋아, 음, 밀고 난 후에, 정말로 확실하다면 : *) ... 당신이하고 싶은 것은 분기의 끝점에 대한 X의 생각을 조금 뒤로 되 감는 것이다. 대답을 수정하겠습니다 ... – torek

+2

아, 그리고 :'git reset HEAD ~ 2'를 "두 걸음 뒤로"이동 시키려면, 이것은 정말 자식에게 말하고 있습니다 : "내가 지금 가고있는 가지의 끝을 설정하십시오"- 아마도 이것은 당신의' 개발 "-"HEAD ~ 2를 보면서 얻을 수있는 커밋 ID로 이동 " 커밋이 무엇인지 확인하려면 'git show HEAD ~ 2'를 먼저 실행하십시오. 위의 그림에서'M'을 선택하면'HEAD ~ 2'는'M'과'C * '를 모두 백업하는'M ~ 2'입니다. 아마도 당신이 원하는 것이 아닙니다. – torek

관련 문제