Hiện trạng
Giả sử chúng ta có một kho lưu trữ được gọi là repo-oldchứa một thư mục sub con mà chúng ta muốn chuyển đổi thành một mô-đun con với kho lưu trữ của chính nó repo-sub.
Ngoài ra, còn có ý định repo-oldchuyển đổi repo gốc thành repo đã sửa đổi repo-newtrong đó tất cả các cam kết chạm vào thư mục con hiện có trước đây subsẽ trỏ đến các cam kết tương ứng của repo mô thức con được trích xuất của chúng tôi repo-sub.
Hãy thay đổi
Có thể đạt được điều này với sự trợ giúp của git filter-branchquy trình hai bước:
- Trích xuất thư mục con từ
repo-oldđến repo-sub(đã được đề cập trong câu trả lời được chấp nhận )
- Thay thế thư mục con từ
repo-oldthành repo-new(với ánh xạ cam kết thích hợp)
Lưu ý : Tôi biết rằng câu hỏi này đã cũ và nó đã được đề cập đến git filter-branchloại không được dùng nữa và có thể nguy hiểm. Nhưng mặt khác, nó có thể giúp những người khác có kho lưu trữ cá nhân dễ xác thực sau khi chuyển đổi. Vì vậy, hãy cảnh báo ! Và vui lòng cho tôi biết nếu có bất kỳ công cụ nào khác làm điều tương tự mà không bị phản đối và an toàn để sử dụng!
Tôi sẽ giải thích cách tôi nhận ra cả hai bước trên linux với git phiên bản 2.26.2 bên dưới. Các phiên bản cũ hơn có thể hoạt động với một số phần mở rộng nhưng điều đó cần được kiểm tra.
Vì đơn giản, tôi sẽ hạn chế bản thân trong trường hợp chỉ có một masternhánh và một originđiều khiển từ xa trong repo gốc repo-old. Cũng được cảnh báo rằng tôi dựa vào các thẻ git tạm thời có tiền tố temp_sẽ bị xóa trong quá trình này. Vì vậy, nếu đã có các thẻ được đặt tên tương tự, bạn có thể muốn điều chỉnh tiền tố bên dưới. Và cuối cùng xin lưu ý rằng tôi chưa thử nghiệm rộng rãi điều này và có thể có những trường hợp góc khiến công thức không thành công. Vì vậy, hãy sao lưu mọi thứ trước khi tiếp tục !
Các đoạn mã bash sau có thể được nối thành một tập lệnh lớn, sau đó sẽ được thực thi trong cùng một thư mục nơi repo repo-orgtồn tại. Không nên sao chép và dán mọi thứ trực tiếp vào cửa sổ lệnh (mặc dù tôi đã kiểm tra điều này thành công)!
0. Chuẩn bị
Biến
# Root directory where repo-org lives
# and a temporary location for git filter-branch
root="$PWD"
temp='/dev/shm/tmp'
# The old repository and the subdirectory we'd like to extract
repo_old="$root/repo-old"
repo_old_directory='sub'
# The new submodule repository, its url
# and a hash map folder which will be populated
# and later used in the filter script below
repo_sub="$root/repo-sub"
repo_sub_url='https://github.com/somewhere/repo-sub.git'
repo_sub_hashmap="$root/repo-sub.map"
# The new modified repository, its url
# and a filter script which is created as heredoc below
repo_new="$root/repo-new"
repo_new_url='https://github.com/somewhere/repo-new.git'
repo_new_filter="$root/repo-new.sh"
Tập lệnh lọc
# The index filter script which converts our subdirectory into a submodule
cat << EOF > "$repo_new_filter"
#!/bin/bash
# Submodule hash map function
sub ()
{
local old_commit=\$(git rev-list -1 \$1 -- '$repo_old_directory')
if [ ! -z "\$old_commit" ]
then
echo \$(cat "$repo_sub_hashmap/\$old_commit")
fi
}
# Submodule config
SUB_COMMIT=\$(sub \$GIT_COMMIT)
SUB_DIR='$repo_old_directory'
SUB_URL='$repo_sub_url'
# Submodule replacement
if [ ! -z "\$SUB_COMMIT" ]
then
touch '.gitmodules'
git config --file='.gitmodules' "submodule.\$SUB_DIR.path" "\$SUB_DIR"
git config --file='.gitmodules' "submodule.\$SUB_DIR.url" "\$SUB_URL"
git config --file='.gitmodules' "submodule.\$SUB_DIR.branch" 'master'
git add '.gitmodules'
git rm --cached -qrf "\$SUB_DIR"
git update-index --add --cacheinfo 160000 \$SUB_COMMIT "\$SUB_DIR"
fi
EOF
chmod +x "$repo_new_filter"
1. Trích xuất thư mục con
cd "$root"
# Create a new clone for our new submodule repo
git clone "$repo_old" "$repo_sub"
# Enter the new submodule repo
cd "$repo_sub"
# Remove the old origin remote
git remote remove origin
# Loop over all commits and create temporary tags
for commit in $(git rev-list --all)
do
git tag "temp_$commit" $commit
done
# Extract the subdirectory and slice commits
mkdir -p "$temp"
git filter-branch --subdirectory-filter "$repo_old_directory" \
--tag-name-filter 'cat' \
--prune-empty --force -d "$temp" -- --all
# Populate hash map folder from our previously created tag names
mkdir -p "$repo_sub_hashmap"
for tag in $(git tag | grep "^temp_")
do
old_commit=${tag#'temp_'}
sub_commit=$(git rev-list -1 $tag)
echo $sub_commit > "$repo_sub_hashmap/$old_commit"
done
git tag | grep "^temp_" | xargs -d '\n' git tag -d 2>&1 > /dev/null
# Add the new url for this repository (and e.g. push)
git remote add origin "$repo_sub_url"
# git push -u origin master
2. Thay thế thư mục con
cd "$root"
# Create a clone for our modified repo
git clone "$repo_old" "$repo_new"
# Enter the new modified repo
cd "$repo_new"
# Remove the old origin remote
git remote remove origin
# Replace the subdirectory and map all sliced submodule commits using
# the filter script from above
mkdir -p "$temp"
git filter-branch --index-filter "$repo_new_filter" \
--tag-name-filter 'cat' --force -d "$temp" -- --all
# Add the new url for this repository (and e.g. push)
git remote add origin "$repo_new_url"
# git push -u origin master
# Cleanup (commented for safety reasons)
# rm -rf "$repo_sub_hashmap"
# rm -f "$repo_new_filter"
Lưu ý: Nếu kho lưu trữ mới được tạo repo-newbị treo trong khi git submodule update --initđó, hãy thử sao chép lại kho lưu trữ một cách đệ quy một lần thay thế:
cd "$root"
# Clone the new modified repo recursively
git clone --recursive "$repo_new" "$repo_new-tmp"
# Now use the newly cloned one
mv "$repo_new" "$repo_new-bak"
mv "$repo_new-tmp" "$repo_new"
# Cleanup (commented for safety reasons)
# rm -rf "$repo_new-bak"