우리는 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.sh
git-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
위해 일했다. 그것은 원래의 갈등 해결 커밋을 유지하고 다른 사람들을 맨 위로 스쿼시 할 수있게했습니다.
누군가를 돕는 희망!