Hiện trạng
Giả sử chúng ta có một kho lưu trữ được gọi là repo-old
chứ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-old
chuyển đổi repo gốc thành repo đã sửa đổi repo-new
trong đó tất cả các cam kết chạm vào thư mục con hiện có trước đây sub
sẽ 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-branch
quy 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-old
thà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-branch
loạ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 master
nhá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-org
tồ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-new
bị 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"