Chúng tôi có một kho lưu trữ Git với hơn 400 cam kết, vài chục đầu tiên trong số đó có rất nhiều lỗi và thử. Chúng tôi muốn dọn sạch những cam kết này bằng cách ép nhiều người vào một cam kết duy nhất. Đương nhiên, git-rebase dường như là con đường để đi. Vấn đề của tôi là nó kết thúc bằng xung đột hợp nhất, và những xung đột này không dễ giải quyết. Tôi không hiểu tại sao nên có bất kỳ xung đột nào cả, vì tôi chỉ thực hiện các cam kết (không xóa hoặc sắp xếp lại). Rất có thể, điều này chứng tỏ rằng tôi không hoàn toàn hiểu cách git-rebase thực hiện các cú đánh của nó.
Đây là phiên bản sửa đổi của tập lệnh tôi đang sử dụng:
repo_squash.sh (đây là tập lệnh thực sự đang chạy):
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 (tập lệnh này chỉ được sử dụng bởi 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: (tệp này chỉ được sử dụng bởi 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
Tôi sẽ để lại nội dung "tin nhắn mới" cho trí tưởng tượng của bạn. Ban đầu, tôi đã làm điều này mà không có tùy chọn "--strargety thems" (nghĩa là sử dụng chiến lược mặc định, nếu tôi hiểu tài liệu chính xác là đệ quy, nhưng tôi không chắc chiến lược đệ quy nào được sử dụng), và nó cũng không t làm việc Ngoài ra, tôi nên chỉ ra rằng, bằng cách sử dụng mã nhận xét trong repo_squash_helper.sh, tôi đã lưu tệp gốc mà tập lệnh sed hoạt động và chạy tập lệnh sed để chống lại nó để đảm bảo rằng nó đang làm những gì tôi muốn ( nó là) Một lần nữa, tôi thậm chí không biết tại sao lại có sẽ có một cuộc xung đột, vì vậy nó dường như không quan trọng lắm đến việc sử dụng chiến lược nào. Bất kỳ lời khuyên hoặc cái nhìn sâu sắc sẽ hữu ích, nhưng chủ yếu tôi chỉ muốn làm cho việc nghiền nát này hoạt động.
Cập nhật với thông tin bổ sung từ cuộc thảo luận với Jefromi:
Trước khi làm việc trên kho lưu trữ "thực" khổng lồ của chúng tôi, tôi đã sử dụng các tập lệnh tương tự trên kho lưu trữ thử nghiệm. Đó là một kho lưu trữ rất đơn giản và thử nghiệm đã hoạt động sạch sẽ.
Thông điệp tôi nhận được khi thất bại là:
Finished one cherry-pick.
# Not currently on any branch.
nothing to commit (working directory clean)
Could not apply 66c45e2... Needed subdir
Đây là lựa chọn đầu tiên sau lần cam kết đầu tiên. Chạy git status
mang lại một thư mục làm việc sạch sẽ. Nếu sau đó tôi làm một git rebase --continue
, tôi nhận được một tin nhắn rất giống sau một vài lần cam kết. Nếu sau đó tôi làm lại, tôi nhận được một tin nhắn rất giống nhau sau vài chục lần cam kết. Nếu tôi làm điều đó một lần nữa, lần này nó sẽ trải qua khoảng một trăm cam kết và mang lại thông điệp này:
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
Nếu sau đó tôi chạy git status
, tôi nhận được:
# 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
Bit "cả hai sửa đổi" nghe có vẻ kỳ lạ đối với tôi, vì đây chỉ là kết quả của một lựa chọn. Cũng đáng lưu ý rằng nếu tôi nhìn vào "xung đột", nó sẽ chuyển thành một dòng duy nhất với một phiên bản bắt đầu bằng ký tự [tab] và một phiên bản khác có bốn khoảng trắng. Điều này nghe có vẻ như là một vấn đề với cách tôi thiết lập tệp cấu hình của mình, nhưng không có gì thuộc loại này. (Tôi đã lưu ý rằng core.ignorecase được đặt thành đúng, nhưng rõ ràng git-clone đã tự động làm điều đó. Tôi không hoàn toàn ngạc nhiên khi xem xét rằng nguồn ban đầu là trên máy Windows.)
Nếu tôi sửa thủ công file_X.cpp, thì nó sẽ thất bại ngay sau đó với một xung đột khác, lần này là giữa một tệp (CMakeLists.txt) mà một phiên bản cho rằng nên tồn tại và một phiên bản cho rằng không nên. Nếu tôi khắc phục xung đột này bằng cách nói rằng tôi muốn tệp này (mà tôi làm), một vài lần cam kết sau tôi sẽ nhận được một xung đột khác (trong cùng tệp này) khi hiện tại có một số thay đổi không tầm thường. Nó vẫn chỉ còn khoảng 25% trong các cuộc xung đột.
Tôi cũng nên chỉ ra, vì điều này có thể rất quan trọng, rằng dự án này đã bắt đầu trong một kho lưu trữ svn. Lịch sử ban đầu rất có thể đã được nhập từ kho svn đó.
Cập nhật số 2:
Trên một vỏ cây (bị ảnh hưởng bởi ý kiến của Jefromi), tôi đã quyết định thay đổi repo_squash.sh của mình thành:
rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
Và sau đó, tôi chỉ chấp nhận các mục gốc, như là. Tức là, "rebase" không nên thay đổi một điều. Nó đã kết thúc với cùng một kết quả mô tả trước đó.
Cập nhật số 3:
Ngoài ra, nếu tôi bỏ qua chiến lược và thay thế lệnh cuối cùng bằng:
git rebase -i bd6a09a484b8230d0810e6689cf08a24f26f287a
Tôi không còn gặp phải vấn đề nổi loạn "không có gì để cam kết", nhưng tôi vẫn còn những mâu thuẫn khác.
Cập nhật với kho đồ chơi tái tạo vấn đề:
test_squash.sh (đây là tệp bạn thực sự chạy):
#========================================================
# 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 (được sử dụng bởi 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
PS: Vâng, tôi biết một số bạn co rúm người lại khi thấy tôi sử dụng emacs làm biên tập viên dự phòng.
PPS: Chúng tôi biết rằng chúng tôi sẽ phải thổi bay tất cả các bản sao của kho lưu trữ hiện có sau cuộc nổi loạn. (Dọc theo dòng "bạn sẽ không khởi động lại một kho lưu trữ sau khi nó được xuất bản".)
PPPS: Bất cứ ai cũng có thể cho tôi biết làm thế nào để thêm một tiền thưởng cho điều này? Tôi không thấy tùy chọn ở bất cứ đâu trên màn hình này cho dù tôi đang ở chế độ chỉnh sửa hay chế độ xem.
rebase --interactive
- đó là một danh sách các hành động để git thử. Tôi đã hy vọng bạn có thể giảm điều này xuống một quả bí đao gây ra xung đột và tránh tất cả sự phức tạp thêm của các tập lệnh trợ giúp của bạn. Thông tin còn thiếu khác là khi xảy ra xung đột - khi git áp dụng các bản vá để tạo thành quả bí, hoặc khi nó cố gắng di chuyển qua quả bí và áp dụng bản vá tiếp theo? (Và bạn có chắc chắn không có gì xấu xảy ra với loại bùn GIT_EDITOR của mình không? Một phiếu bầu khác cho trường hợp thử nghiệm đơn giản.)
rebase -p
)