현상 유지
자체 저장소가 있는 하위 모듈 로 변환하려는 repo-old
하위 디렉토리 를 포함 하는 저장소가 있다고 가정 해 보겠습니다.sub
repo-sub
.
또한 원래의 저장소 repo-old
는 수정 된 저장소로 변환되어야하며 repo-new
, 이전에 존재하는 하위 디렉토리를 터치하는 모든 커밋은 sub
이제 추출 된 하위 모듈 저장소의 해당 커밋을 가리 킵니다.repo-sub
.
바꾸자
git filter-branch
두 단계의 프로세스 를 통해이를 달성 할 수 있습니다.
- 에서 하위 디렉토리 추출
repo-old
에 repo-sub
(이미 허용에 언급 대답 )
- 에서
repo-old
로 repo-new
(적절한 커밋 매핑 사용)로 하위 디렉터리 교체
비고 :이 질문은 오래되었고 이미 git filter-branch
사용되지 않으며 위험 할 수있는 것으로 이미 언급되었습니다 . 그러나 다른 한편으로는 변환 후 검증하기 쉬운 개인 리포지토리로 다른 사람들에게 도움이 될 수 있습니다. 그래서 경고하십시오 ! 그리고 더 이상 사용되지 않고 사용하기에 안전한 다른 도구가 있으면 알려주세요!
아래 git 버전 2.26.2를 사용하여 Linux에서 두 단계를 모두 실현 한 방법을 설명하겠습니다. 이전 버전은 일부 확장 될 수 있지만 테스트가 필요합니다.
단순함을 위해 원래 저장소에 master
브랜치와 origin
리모트 만있는 경우로 제한하겠습니다 repo-old
. 또한 temp_
프로세스에서 제거 될 접두사가 있는 임시 git 태그에 의존한다는 점에 유의 하십시오. 따라서 유사한 이름의 태그가 이미있는 경우 아래 접두사를 조정하는 것이 좋습니다. 그리고 마지막으로 이것을 광범위하게 테스트하지 않았으며 레시피가 실패하는 코너 케이스가있을 수 있습니다. 계속하기 전에 모든 것을 백업 하십시오 !
다음 bash 스 니펫을 하나의 큰 스크립트로 연결 한 다음 저장소가있는 동일한 폴더에서 실행해야 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-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"