สถานะที่เป็นอยู่
สมมติว่าเรามีพื้นที่เก็บข้อมูลที่เรียกว่าrepo-old
ซึ่งมีย่อยไดเรกทอรี sub
ที่เราอยากจะแปลงเป็นย่อยโมดูลกับ repo-sub
repo
มีวัตถุประสงค์เพิ่มเติมว่า repo เดิมrepo-old
ควรถูกแปลงเป็น repo repo-new
ที่แก้ไขโดยที่การกระทำทั้งหมดที่สัมผัสกับไดเรกทอรีย่อยที่มีอยู่ก่อนหน้านี้sub
จะชี้ไปที่คอมมิตที่เกี่ยวข้องของ repo โมดูลย่อยที่แยกออกมาของเราrepo-sub
repo
เปลี่ยนกันเถอะ
เป็นไปได้ที่จะบรรลุสิ่งนี้ด้วยความช่วยเหลือของgit filter-branch
กระบวนการสองขั้นตอน:
- การแยกไดเรกทอรีย่อยจาก
repo-old
ถึงrepo-sub
(กล่าวถึงแล้วในคำตอบที่ยอมรับ)
- การเปลี่ยนไดเร็กทอรีย่อยจาก
repo-old
ถึงrepo-new
(ด้วยการแมปคอมมิตที่เหมาะสม)
หมายเหตุ : ฉันรู้ว่าคำถามนี้เก่าและมีการพูดถึงไปแล้วว่าgit filter-branch
เป็นคำถามที่เลิกใช้แล้วและอาจเป็นอันตราย แต่ในทางกลับกันอาจช่วยให้ผู้อื่นมีที่เก็บส่วนบุคคลที่ตรวจสอบความถูกต้องหลังการแปลงได้ง่าย จึงขอเตือน ! และโปรดแจ้งให้เราทราบหากมีเครื่องมืออื่นที่ทำเช่นเดียวกันโดยไม่ถูกเลิกใช้งานและปลอดภัยต่อการใช้งาน!
ฉันจะอธิบายว่าฉันรู้ทั้งสองขั้นตอนบน linux ด้วย git เวอร์ชัน 2.26.2 ด้านล่างได้อย่างไร เวอร์ชันที่เก่ากว่าอาจใช้งานได้กับการขยาย แต่ต้องมีการทดสอบ
เพื่อประโยชน์ของความเรียบง่ายที่ผมจะ จำกัด ตัวเองให้กรณีที่มีเพียงmaster
สาขาและorigin
ห่างไกลใน repo-old
repo ขอเตือนด้วยว่าฉันใช้แท็ก git ชั่วคราวที่มีคำนำหน้าtemp_
ซึ่งกำลังจะถูกลบออกในกระบวนการ ดังนั้นหากมีแท็กที่ชื่อคล้ายกันอยู่แล้วคุณอาจต้องการปรับคำนำหน้าด้านล่าง และสุดท้ายโปรดทราบว่าฉันยังไม่ได้ทดสอบสิ่งนี้อย่างครอบคลุมและอาจมีบางกรณีที่สูตรล้มเหลว ดังนั้นโปรดสำรองข้อมูลทุกอย่างก่อนดำเนินการต่อ !
ตัวอย่าง bash ต่อไปนี้สามารถเชื่อมต่อกันเป็นสคริปต์ขนาดใหญ่หนึ่งสคริปต์ซึ่งควรดำเนินการในโฟลเดอร์เดียวกับที่ repo repo-org
อยู่ ไม่แนะนำให้คัดลอกและวางทุกอย่างลงในหน้าต่างคำสั่งโดยตรง (แม้ว่าฉันจะทดสอบสิ่งนี้สำเร็จแล้วก็ตาม)!
0. การเตรียมการ
ตัวแปร
# 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"
สคริปต์ตัวกรอง
# 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. การแยกไดเรกทอรีย่อย
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. การเปลี่ยนไดเรกทอรีย่อย
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"
หมายเหตุ:หาก repo ที่สร้างขึ้นใหม่repo-new
แฮงค์ระหว่างgit submodule update --init
นั้นให้ลองโคลนที่เก็บซ้ำอีกครั้งแทน:
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"