태그 보관물: git-rebase

git-rebase

내가하고있는 모든 것이 커밋을 저지르는 것만으로 git-rebase가 병합 충돌을 일으키는 이유는 무엇입니까? 매우 비슷한 메시지가 나타납니다.

우리는 400 개가 넘는 커밋을 가진 Git 저장소를 가지고 있는데, 그 중 첫 12 개는 많은 시행 착오를 겪었습니다. 많은 커밋을 단일 커밋으로 스쿼시하여 커밋을 정리하고 싶습니다. 당연히 git-rebase는 갈 길입니다. 내 문제는 병합 충돌로 끝나고 이러한 충돌을 해결하기 쉽지 않다는 것입니다. 커밋을 삭제하거나 재 배열하지 않기 때문에 왜 충돌이 발생 해야하는지 이해하지 못합니다. 아마도 이것은 git-rebase가 과즙을 어떻게 수행하는지 완전히 이해하지 못하고 있음을 보여줍니다.

사용중인 스크립트의 수정 된 버전은 다음과 같습니다.


repo_squash.sh (실제로 실행되는 스크립트) :


rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
GIT_EDITOR=../repo_squash_helper.sh git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a

repo_squash_helper.sh (이 스크립트는 repo_squash.sh에서만 사용됨) :


if grep -q "pick " $1
then
#  cp $1 ../repo_squash_history.txt
#  emacs -nw $1
  sed -f ../repo_squash_list.txt < $1 > $1.tmp
  mv $1.tmp $1
else
  if grep -q "initial import" $1
  then
    cp ../repo_squash_new_message1.txt $1
  elif grep -q "fixing bad import" $1
  then
    cp ../repo_squash_new_message2.txt $1
  else
    emacs -nw $1
  fi
fi

repo_squash_list.txt : (이 파일은 repo_squash_helper.sh에서만 사용됨)


# Initial import
s/pick \(251a190\)/squash \1/g
# Leaving "Needed subdir" for now
# Fixing bad import
s/pick \(46c41d1\)/squash \1/g
s/pick \(5d7agf2\)/squash \1/g
s/pick \(3da63ed\)/squash \1/g

나는 “새 메시지”내용을 당신의 상상에 맡기겠습니다. 처음에는 “–strategy theirs”옵션없이이 작업을 수행했습니다 (즉, 기본 전략을 사용하여 문서를 올바르게 이해하는 경우 재귀 적이지만 어떤 재귀 적 전략이 사용되는지 잘 모르겠습니다). 작동하지 않습니다. 또한 repo_squash_helper.sh의 주석 처리 된 코드를 사용하여 sed 스크립트가 작동하는 원본 파일을 저장하고 sed 스크립트를 실행하여 원하는 작업을 수행했는지 확인했습니다. 그랬습니다). 다시 말하지만, 왜 충돌 이 일어날 지조차 알지 못하므로 어떤 전략이 사용되는지는 중요하지 않습니다. 조언이나 통찰력이 도움이 될 수 있지만 대부분이 스쿼시 작업을 원합니다.

Jefromi와의 토론에서 추가 정보로 업데이트되었습니다.

방대한 “실제”리포지토리에서 작업하기 전에 테스트 리포지토리에서 비슷한 스크립트를 사용했습니다. 매우 간단한 저장소였으며 테스트는 깨끗하게 진행되었습니다.

실패했을 때 나타나는 메시지는 다음과 같습니다.

Finished one cherry-pick.
# Not currently on any branch.
nothing to commit (working directory clean)
Could not apply 66c45e2... Needed subdir

이것은 첫 번째 스쿼시 커밋 후 첫 번째 선택입니다. 실행 git status하면 깨끗한 작업 디렉토리가 생성됩니다. 그런 다음을 수행 git rebase --continue하면 몇 가지 커밋 후에 매우 비슷한 메시지가 나타납니다. 그런 다음 다시 수행하면 수십 개의 커밋 후에 매우 비슷한 메시지가 나타납니다. 다시 한 번 수행하면 이번에는 약 100 개의 커밋을 거쳐 다음 메시지가 나타납니다.

Automatic cherry-pick failed.  After resolving the conflicts,
mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'
Could not apply f1de3bc... Incremental

그런 다음을 실행하면 다음을 git status얻습니다.

# Not currently on any branch.
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
# modified:   repo/file_A.cpp
# modified:   repo/file_B.cpp
#
# Unmerged paths:
#   (use "git reset HEAD <file>..." to unstage)
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified:      repo/file_X.cpp
#
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted:    repo/file_Z.imp

“수정 된”비트는 이상하게 들립니다. 왜냐하면 이것은 선택의 결과 일뿐입니다. “충돌”을 살펴보면 하나의 버전이 [탭] 문자로 시작하고 다른 하나는 4 개의 공백이있는 한 줄로 요약됩니다. 구성 파일을 설정하는 방법에 문제가있는 것처럼 들리지만 그 안에는 아무것도 없습니다. (core.ignorecase가 true로 설정되어 있지만 git-clone이 자동으로 수행 한 것으로 나타났습니다. 원래 소스가 Windows 시스템에 있다는 점을 고려해도 전혀 놀랍지 않습니다.)

file_X.cpp를 수동으로 수정하면 나중에 다른 충돌로 인해 실패합니다. 이번에는 한 버전이 존재한다고 생각하는 파일 (CMakeLists.txt)과 한 버전이 존재하지 않아야한다고 생각합니다. 내가이 파일을 원한다고 말 함으로써이 갈등을 고치면 (약간의 커밋) 나중에 약간의 변경 사항이있는 또 다른 갈등 (이 동일한 파일에서)이 발생합니다. 그것은 여전히 ​​갈등의 약 25 %입니다.

또한이 프로젝트가 svn 저장소에서 시작되었다는 것이 매우 중요하기 때문에 지적해야합니다. 초기 기록은 해당 svn 저장소에서 가져온 것 같습니다.

업데이트 # 2 :

나는 Jefromi의 의견에 영향을받는 종달새에서 repo_squash.sh를 다음과 같이 변경하기로 결정했습니다.

rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a

그런 다음 원래 항목을 그대로 수락했습니다. 즉, “리베이스”는 변경되지 않아야합니다. 그것은 앞에서 설명한 것과 같은 결과로 끝났습니다.

업데이트 # 3 :

또는 전략을 생략하고 마지막 명령을 다음과 같이 바꾸면

git rebase -i bd6a09a484b8230d0810e6689cf08a24f26f287a

더 이상 “커밋 할 사항 없음”rebase 문제가 발생하지 않지만 여전히 다른 충돌이 남아 있습니다.

문제를 재현하는 장난감 저장소로 업데이트하십시오.

test_squash.sh (실제로 실행되는 파일) :

#========================================================
# Initialize directories
#========================================================
rm -rf test_squash/ test_squash_clone/
mkdir -p test_squash
mkdir -p test_squash_clone
#========================================================

#========================================================
# Create repository with history
#========================================================
cd test_squash/
git init
echo "README">README
git add README
git commit -m"Initial commit: can't easily access for rebasing"
echo "Line 1">test_file.txt
git add test_file.txt
git commit -m"Created single line file"
echo "Line 2">>test_file.txt
git add test_file.txt
git commit -m"Meant for it to be two lines"
git checkout -b dev
echo Meaningful code>new_file.txt
git add new_file.txt
git commit -m"Meaningful commit"
git checkout master
echo Conflicting meaningful code>new_file.txt
git add new_file.txt
git commit -m"Conflicting meaningful commit"
# This will conflict
git merge dev
# Fixes conflict
echo Merged meaningful code>new_file.txt
git add new_file.txt
git commit -m"Merged dev with master"
cd ..

#========================================================
# Save off a clone of the repository prior to squashing
#========================================================
git clone test_squash test_squash_clone
#========================================================

#========================================================
# Do the squash
#========================================================
cd test_squash
GIT_EDITOR=../test_squash_helper.sh git rebase -i HEAD@{7}
#========================================================

#========================================================
# Show the results
#========================================================
git log
git gc
git reflog
#========================================================

test_squash_helper.sh (test_sqash.sh에서 사용) :

# If the file has the phrase "pick " in it, assume it's the log file
if grep -q "pick " $1
then
  sed -e "s/pick \(.*\) \(Meant for it to be two lines\)/squash \1 \2/g" < $1 > $1.tmp
  mv $1.tmp $1
# Else, assume it's the commit message file
else
# Use our pre-canned message
  echo "Created two line file" > $1
fi

추신 : 네, 이맥스를 폴백 편집기로 사용하는 것을 보았을 때 여러분 중 일부가 울부 짖는 것을 알고 있습니다.

PPS : 리베이스 후 기존 리포지토리의 모든 클론을 날려 버려야한다는 것을 알고 있습니다. ( “저장소를 게시 한 후에 저장소를 리베이스하지 않아야한다”는 내용과 함께)

PPPS : 누구나 현상금을 추가하는 방법을 알려줄 수 있습니까? 편집 모드 또는보기 모드에 관계 없이이 화면의 어느 곳에서도 옵션이 표시되지 않습니다.



답변

좋아, 나는 대답을 버릴만큼 확신합니다. 어쩌면 편집해야하지만 문제가 무엇인지 알고 있습니다.

토이 저장소 테스트 케이스에 병합이 포함되어 있습니다. 더 나쁜 것은 충돌과 병합 된 것입니다. 그리고 당신은 합병에 걸쳐 rebasing하고 있습니다. -p(완전히 작동하지 않는) 없이는 -i병합이 무시됩니다. 즉 , rebase가 다음 커밋을 체리 픽하려고 할 때 충돌 해결에서 수행 한 작업 이 없으므로 패치가 적용되지 않을 수 있습니다. ( git cherry-pick원래 커밋, 현재 커밋 및 공통 조상 사이에 3 방향 병합을 수행하여 패치를 적용 할 수 있기 때문에 이것이 병합 충돌로 표시됩니다 .)

불행하게도,로 우리가 코멘트에 언급, -i그리고 -p아주 잘 따라하지 않는다 (병합을 보존). 편집 / 리워드 기능이 작동하고 재정렬이 효과가 없다는 것을 알고 있습니다. 그러나 나는 그것이 과즙과 잘 작동 한다고 생각 합니다. 이것은 문서화되어 있지 않지만 아래에서 설명하는 테스트 사례에서 효과적이었습니다. 귀하의 사례가 길고, 더 복잡한 경우, 여전히 가능하지만 원하는 일을하는 데 많은 어려움을 겪을 수 있습니다. (이야기의 교훈 : 병합 rebase -i 하기 전에 정리하십시오 .)

A, B 및 C를 함께 스쿼시하려는 매우 간단한 경우가 있다고 가정 해 봅시다.

- o - A - B - C - X - D - E - F (master)
   \             /
    Z -----------

이제 말했듯이 X에 충돌이 없으면 git rebase -i -p예상대로 작동합니다.

충돌이 있으면 상황이 조금 까다로워집니다. 잘 스쿼시하지만 병합을 다시 만들려고 할 때 충돌이 다시 발생합니다. 다시 해결하고 인덱스에 추가 한 다음 계속해서 사용해야 git rebase --continue합니다. 물론 원래 병합 커밋에서 버전을 확인하여 다시 해결할 수 있습니다.

당신이 한 일이 있다면 rerere(당신의 repo에서 사용할 rerere.enabled자식이 할 수있을 것입니다 – True로 설정), 이것은 쉬운 방법이 될 것입니다 다시 사용 다시 유선 원래 충돌을했을 때의 솔루션을, 그리고 당신이 모두는 그것을 검사 할 제대로 작동하는지 확인하려면 파일을 색인에 추가하고 계속하십시오. (한 단계 더 나아가서을 켜면 rerere.autoupdate추가 할 수 있으므로 병합이 실패하지 않습니다). 그러나 나는 당신이 결코 rerere를 활성화하지 않았다고 추측하고 있습니다. 그래서 당신은 스스로 갈등 해결을해야 할 것입니다. *


또는 rerere-train.shgit-contrib에서 “기존 병합 커밋에서 데이터베이스를 다시 가져 오기”를 시도 하는 스크립트를 시도 할 수 있습니다. 기본적으로 모든 병합 커밋을 체크 아웃하고 병합을 시도하며 병합에 실패하면 결과를 가져 와서에 표시합니다 git-rerere. 시간이 오래 걸리고 실제로 사용한 적이 없지만 매우 도움이 될 수 있습니다.


답변

새 브랜치를 생성하는 것이 마음에 들지 않으면 다음과 같이 문제를 해결했습니다.

메인에 있음 :

# create a new branch
git checkout -b new_clean_branch

# apply all changes
git merge original_messy_branch

# forget the commits but have the changes staged for commit
git reset --soft main

git commit -m "Squashed changes from original_messy_branch"

답변

나는 비슷한 요구 사항을 찾고 있었다. 즉, 개발 브랜치의 intermeiate 커밋을 폐기하는 것이이 절차가 나를 위해 효과적이라는 것을 알았다.
내 작업 지점에서

git reset –hard mybranch-start-commit
git checkout mybranch-end-commit . // files only of the latest commit
git add -a
git commit -m”New Message intermediate commits discarded”

비올라 우리는 지점의 시작 커밋에 최신 커밋을 연결했습니다! 병합 충돌 문제가 없습니다! 나의 학습 실습에서 나는이 단계에서이 결론에 도달했다. 목적을위한 더 나은 접근법이 있는가?


답변

수동 개입을 최소화하는 위의 @ hlidka 의 위대한 대답을 바탕으로 지점에없는 마스터에 대한 새로운 커밋을 유지하는 버전을 추가하고 싶었습니다.

필자는이 git reset예제 의 단계에서 쉽게 잃을 수 있다고 생각합니다 .

# create a new branch
# ...from the commit in master original_messy_branch was originally based on. eg 5654da06
git checkout -b new_clean_branch 5654da06

# apply all changes
git merge original_messy_branch

# forget the commits but have the changes staged for commit
# ...base the reset on the base commit from Master
git reset --soft 5654da06

git commit -m "Squashed changes from original_messy_branch"

# Rebase onto HEAD of master
git rebase origin/master

# Resolve any new conflicts from the new commits

답변

참고 -X대화 형 REBASE에 사용하면 무시 전략 옵션을 제공합니다.

커밋 db2b3b820e2b28da268cc88adff076b396392dfe (2013 년 7 월, git 1.8.4+)를 참조하십시오 .

대화식 리베이스에서 병합 옵션을 무시하지 마십시오

병합 전략 및 옵션은에서 지정할 수 git rebase있지만을 사용 -- interactive하면 완전히 무시됩니다.

서명 : Arnaud Fontaine

-X, 일반 rebase뿐만 아니라 대화식 rebase에서도 작동하고 전략이 작동하므로 초기 스크립트가 더 잘 작동 할 수 있습니다.


답변

나는 1) 로컬 브랜치에서 병합 충돌을 해결하고 2) 더 많은 커밋을 추가하는 작업을 계속했다 .3) 리베이스하고 병합 충돌을 원했다.

나를 git rebase -p -i master위해 일했다. 그것은 원래의 갈등 해결 커밋을 유지하고 다른 사람들을 맨 위로 스쿼시 할 수있게했습니다.

누군가를 돕는 희망!


답변