Git을 사용하여 가장 최근의 커밋을 새로운 브랜치로 옮깁니다.

git git-branch branching-and-merging


마지막 브랜치 몇 가지 커밋을 새 브랜치로 마스터하고 커밋하기 전에 다시 마스터로 가져 가고 싶습니다. 불행히도 내 Git-fu는 아직 충분히 강하지 않습니다.

즉, 나는 이것을 어떻게 갈 수 있습니까

master A - B - C - D - E

이에?

newbranch     C - D - E
             /
master A - B 



Answer 1 Duc Filan


기존 지점으로 이동

커밋을 기존 브랜치 로 옮기려면 다음과 같이 보일 것입니다.

git checkout existingbranch
git merge master         # Bring the commits here
git checkout master
git reset --keep HEAD~3  # Move master back by 3 commits.
git checkout existingbranch

--keep 유사 무엇을 - 옵션은 해당 변경 내용을 덮어했을 경우 관련없는 파일되거나 중단에있을 수 있음을 커밋되지 않은 변경 보존 git checkout 수행합니다. 중단되면 git stash 를 변경하고 다시 시도하거나 --hard 를 사용 하여 변경 사항을 잃어 버리 십시오 (커밋간에 변경되지 않은 파일에서도)!

새로운 지점으로 이동

이 방법은 첫 번째 명령 ( git branch newbranch ) 으로 새 분기를 작성 하지만 전환하지는 않습니다. 그런 다음 현재 지점 (마스터)을 롤백하고 새 지점으로 전환하여 작업을 계속합니다.

git branch newbranch      # Create a new branch, containing all current commits
git reset --keep HEAD~3   # Move master back by 3 commits (Make sure you know how many commits you need to go back)
git checkout newbranch    # Go to the new branch that still has the desired commits
# Warning: after this it's not safe to do a rebase in newbranch without extra care.

그러나 얼마나 많은 커밋을 되돌릴 수 있는지 확인하십시오. 또는 HEAD~3 대신 간단히 되돌릴 커밋의 해시 (또는 origin/master 와 같은 참조)를 제공 할 수 있습니다 . 예 :

git reset --keep a1b2c3d4

경고 : 함께 망할 놈의 버전 2.0 및 나중에 경우 나중에, git rebase 원래의 (에 새로운 지점을 master ) 지점, 당신은 명시 적으로해야 할 수도 있습니다 --no-fork-point 당신이에서 이동 커밋 손실을 방지하기 위해 REBASE 동안 옵션을 마스터 브랜치. 데 branch.autosetuprebase always 설정하면이 가능성이 있습니다. 자세한 내용은 John Mellor의 답변 을 참조하십시오.




Answer 2 Ryan Lundy


왜 그것이 효과가 있는지 궁금해하는 사람들을 위해 :

C로 돌아가서 D와 E를 새 분기로 이동하려고합니다. 처음에는 다음과 같습니다.

A-B-C-D-E (HEAD)
        ↑
      master

git branch newBranch 후 :

    newBranch
        ↓
A-B-C-D-E (HEAD)
        ↑
      master

git reset --hard HEAD~2 후 --hard HEAD ~ 2 :

    newBranch
        ↓
A-B-C-D-E (HEAD)
    ↑
  master

브랜치는 단지 포인터이기 때문에 마스터 는 마지막 커밋을 가리 켰습니다. newBranch 를 만들었 을 때 마지막 커밋에 대한 새로운 포인터를 만들었습니다. 그런 다음 git reset 을 사용 하여 마스터 포인터를 두 커밋으로 다시 이동했습니다 . 그러나 newBranch를 이동하지 않았 으므로 여전히 원래 커밋을 가리 킵니다.




Answer 3 Ivan


일반적으로 ...

이 경우 sykora에 의해 노출되는 방법이 가장 좋습니다. 그러나 때로는 가장 쉬운 방법이 아니며 일반적인 방법이 아닙니다. 일반적인 방법으로 git cherry-pick을 사용하십시오 .

OP가 원하는 것을 달성하기 위해 2 단계 프로세스는 다음과 같습니다.

1 단계- newbranch 원하는 마스터에서 커밋하는 메모

Execute

git checkout master
git log

newbranch 에서 원하는 (예를 들어 3) 커밋의 해시를 주목하십시오 . 여기서는 다음을 사용합니다.
C 커밋 : 9aa1233
D 커밋 : 453ac3d
전자 커밋 : 612ecb3

참고 : 처음 7 자 또는 전체 커밋 해시를 사용할 수 있습니다

2 단계 - 온을 넣어 newbranch

git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233

또는 (Git 1.7.2 이상에서 사용 범위)

git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233

git cherry-pick 은 세 가지 커밋을 newbranch에 적용합니다.




Answer 4 John Mellor


대부분의 이전 답변은 위험합니다!

이 작업을 수행하지 마십시오 :

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

다음에 git rebase (또는 git pull --rebase ) 를 실행할 때,이 3 개의 commit은 newbranch 에서 자동으로 폐기됩니다 ! (아래 설명 참조)

대신 이것을하십시오 :

git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
  • 먼저 가장 최근의 커밋 3 개를 버립니다 ( --keep--hard 와 비슷하지만 커밋되지 않은 변경 사항을 버리는 대신 실패하므로 더 안전합니다).
  • 그런 다음 newbranch 를 분기합니다 .
  • 그런 다음 해당 3 개의 커밋을 newbranch 로 다시 선택합니다 . 그들은 더 이상 지점에서 참조하고 있기 때문에, 자식의 사용하여 해당하지 않습니다 reflog를 : HEAD@{2} (가) 커밋이다 HEAD 는 우리 1. 체크 아웃하기 전에이 작업 전에, 즉 참조하는 데 사용 newbranch 2. 사용 git reset 3 개의 커밋을 버리려면 리셋 하십시오.

경고 : reflog는 기본적으로 활성화되어 있지만 수동으로 비활성화하면 (예 : "bare"git 저장소를 사용하여) git reset --keep HEAD~3 실행 한 후 3 개의 커밋을 다시 가져올 수 없습니다. 3 .

reflog에 의존하지 않는 대안은 다음과 같습니다.

# newbranch will omit the 3 most recent commits.
git checkout -b newbranch HEAD~3
git branch --set-upstream-to=oldbranch
# Cherry-picks the extra commits from oldbranch.
git cherry-pick ..oldbranch
# Discards the 3 most recent commits from oldbranch.
git branch --force oldbranch oldbranch~3

(원하는 경우 oldbranch 대신 @{-1} -이전에 체크 아웃 한 분기 ) 를 작성할 수 있습니다.


기술적 설명

git rebase 가 첫 번째 예제 후에 3 개의 커밋을 버릴까요 ? 때문입니다 git rebase 인수없이이 가능 --fork-point 상류 지점이 강제로 밀려에 대한 견고하려고 로컬 reflog를 사용하여 기본적으로 옵션을.

커밋 M1, M2, M3이 포함 된 원점 / 마스터에서 분기 한 다음 세 개의 커밋을 직접했다고 가정 해 봅시다.

M1--M2--M3  <-- origin/master
         \
          T1--T2--T3  <-- topic

그러나 누군가가 M2를 제거하기 위해 강제 푸시 원점 / 마스터로 기록을 다시 작성합니다.

M1--M3'  <-- origin/master
 \
  M2--M3--T1--T2--T3  <-- topic

로컬 reflog를 사용하여 git rebase 는 원점 / 마스터 브랜치의 초기 화신에서 분기되었으며 M2 및 M3 커밋이 실제로 토픽 브랜치의 일부가 아님을 알 수 있습니다. 따라서 M2가 업스트림 브랜치에서 제거되었으므로 토픽 브랜치가 리베이스 된 후에는 더 이상 토픽 브랜치에서 원하지 않습니다.

M1--M3'  <-- origin/master
     \
      T1'--T2'--T3'  <-- topic (rebased)

이 동작은 의미가 있으며 일반적으로 리베이스 할 때해야 할 일입니다.

따라서 다음 명령이 실패하는 이유는 다음과 같습니다.

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

리플 로그를 잘못된 상태로두기 때문입니다. Git은 newbranch 가 3 개의 커밋을 포함하는 개정판에서 업스트림 브랜치를 분기 한 것으로보고 , reset --hard 는 커밋을 제거하기 위해 업스트림의 히스토리를 다시 작성하므로 다음에 git rebase 를 실행 하면 다른 커밋처럼 버립니다. 업스트림에서 삭제되었습니다.

그러나이 특별한 경우에 우리는이 3 개의 커밋이 토픽 브랜치의 일부로 간주되기를 원합니다. 이를 위해서는 3 개의 커밋이 포함되지 않은 이전 수정 버전에서 업스트림을 분기해야합니다. 그것이 내가 제안한 솔루션이하는 일이므로 둘 다 reflog를 올바른 상태로 둡니다.

자세한 내용 은 git rebasegit merge-base docs 에서 --fork-point 정의를 참조하십시오 .




Answer 5 aragaer


단 두 개의 명령을 사용하여이를 수행하는 또 다른 방법입니다. 또한 현재 작업 트리를 그대로 유지하십시오.

git checkout -b newbranch # switch to a new branch
git branch -f master HEAD~3 # make master point to some older commit

이전 버전 - git branch -f 에 대해 배우기 전에 -f

git checkout -b newbranch # switch to a new branch
git push . +HEAD~3:master # make master point to some older commit 

push 할 수 . 좋은 요령입니다.