gitlab -> github 대용량 파일 포함 레포 이관(migration)하며 겪은 모든 오류와 해결법 : mirror clone 실패, 일반 clone + Git LFS 추적 전환, 캐시 제거, filter-repo 히스토리 삭제 (glob 패턴 & zsh 이슈 포함)

🫠·2025년 8월 7일

troubleshoot

목록 보기
3/3
post-thumbnail

[문제 1]

Gitlab 프로젝트Github로 이전하려고 한다.

저장소 전체 이관이 목적이기 때문에 .git(브랜치, 커밋 히스토리) 뿐만 아니라 remote 설정(refs, hooks, config 등)까지 전체 복제/백업할 수 있는 미러링 방식으로 다음과 같이 작업하던 중 오류가 발생했다.

# 1. Gitlab 저장소 전체 복제 (mirror clone)
git clone --mirror https://lab.ssafy.com/s11-fintech-finance-sub1/S11P21A206.git

# 2. 해당 폴더로 이동
cd S11P21A206.git

# 3. 원격 저장소(origin)를 Github로 변경
git remote set-url origin https://github.com/hi-react/S11P21A206.git

# 4. Github로 전체 push
git push --mirror

error: RPC failed; HTTP 400 curl 22 The requested URL returned error: 400
send-pack: unexpected disconnect while reading sideband packet

오브젝트 쓰는 중: 100% (11007/11007), 606.27 MiB | 158.58 MiB/s, 완료.
Total 11007 (delta 6322), reused 11007 (delta 6322), pack-reused 0
fatal: the remote end hung up unexpectedly


📌 해당 오류는 HTTP 400 에러 코드로, Github 측에서 요청을 거부하면서 연결이 끊겼음을 의미한다.

일반적으로 GitHub는 단일 파일 100MB 초과 또는 전체 push 수백 MB 이상일 경우, RPC failed, HTTP 400, unexpected disconnect 등의 메시지를 반환하며 업로드를 차단한다.

이번 프로젝트의 경우 R3F 기반 게임으로 전체 push 크기가 606MB라고 나왔기 때문에 Github 업로드 정책에 걸렸을 가능성이 높다.


[해결 1]

1. 100MB 초과하는 대용량 파일 목록 확인

일단 히스토리에 포함된 대용량 파일(blob) 목록을 확인해 보려고 한다.

아래 명령어를 이용해 전체 커밋 히스토리에서 100MB (=104,857,600 byte)를 초과하는 blob 파일들을 확인해 본 결과 아무것도 나오지 않았다.

git rev-list --objects --all \
  | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
  | awk '$1 == "blob" && $3 >= 104857600' \
  | sort -k3 -n

즉, 단일 파일로는 100MB 초과 파일이 없는 것이다.

2. blob 크기가 큰 파일 목록 확인

그렇다면 역시 전체 push 크기 때문인 것 같은데..

이번에는 blob 크기가 큰 순서대로 정렬해서 보는 명령어를 사용해 보겠다. 일단 상위 20개만 출력해 보았다.

git rev-list --objects --all \
  | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
  | awk '$1 == "blob"' \
  | sort -k3 -nr \
  | head -n 20

역시나.. 게임 에셋으로 사용한 gif가 다수 포함되어 있고..
100MB 초과는 아니지만 85MB까지 근접하게 용량이 큰 파일들이 다수 있었다.

3. Git LFS 설치하기

이처럼 .gif, .bin, .jpeg, .glb 같은 대용량 리소스를 git 히스토리에서 분리해 저장할 수 있는 git lfs (Git Large File Storage) 를 활용해 보려고 한다.

[maxOS 기준] Homebrew를 이용해 git-lfs를 설치해준다.

brew install git-lfs

4. Git LFS 초기화 (레포지토리 1회만 설정)

아래 명령어로 Gif LFS를 초기화해준다.

git lfs install

위 그림처럼 .git 내에 lfs 폴더가 생긴 것을 확인해 볼 수 있다.

잘 설치되었는지 버전 확인도 한 번 해준다.

git lfs version

5. 특정 확장자를 LFS로 추적 설정

위에서 blob 크기가 큰 상위 20개 파일을 확인했을 때,
주로 .gif, 그리고 몇개의 .bin, .jpeg, .jpg, .png가 있었으므로 해당 확장자를 모두 LFS에서 관리하도록 해주려고 한다.

git lfs track "*.gif"
git lfs track "*.bin"
git lfs track "*.jpeg"
git lfs track "*.jpg"
git lfs track "*.png"



[문제 2]

그런데 다시 문제가 발생했다.

mirror clone 했기 때문에 현재 로컬에 작업 디렉토리(= 소스 코드 파일 있는 곳)가 없어git lfs를 사용할 수가 없는 것..

⚠️ 작업 디렉토리가 왜 없나?

mirror clone 시, 복제 완료 후 새롭게 이관한 원격 저장소에서 다시 일반 clone 해서 받아와야 로컬 작업 디렉토리를 사용할 수 있다.


[해결 2]

그래 다시 일반 clone 부터 해보자..

1. 일반 clone, Git LFS 초기화, 추적할 파일 확장자 지정

# 1. Gitlab 저장소를 일반 clone
git clone https://lab.ssafy.com/s11-fintech-finance-sub1/S11P21A206.git

# 2. 디렉토리 이동
cd S11P21A206

# 3. Git LFS 초기화
git lfs install

# 4. Git LFS로 관리할 대용량 파일 확장자 지정
git lfs track "*.gif"
git lfs track "*.bin"
git lfs track "*.jpeg"
git lfs track "*.jpg"
git lfs track "*.png"

여기까지 하면 아래처럼 git LFSTracking에 성공한 것을 볼 수 있다.

2. .gitattributes 변경사항 커밋

git add .gitattributes
git commit -m "Track large assets with Git LFS"

3. 현재 작업 디렉토리에서 대용량 파일 다시 한 번 확인해보기

사이즈 기준으로 상위 30개 확인

find . -type f -exec du -h {} + | sort -hr | head -n 30

확인 결과, 주로 assets 폴더 하위, omg-front/public 폴더 하위대용량 파일들이 위치한 것을 알 수 있었다.

새록 새록 그 시절 생각이 나.. 🤎을 많이 싸놨구나 과거의 나야.. ^^ 그래도 모아 두어줘서 고맙다..

작업 디렉토리에서 직접 파일들을 확인해보고, Git LFS로 추적할 파일 확장자를 추가해주었다.

git lfs track "*.gltf"
git lfs track "*.mp3"
git lfs track "*.cur"

그리고 다시 변경사항 커밋

git add .gitattributes
git commit -m "Track binary assets with Git LFS"

4. 기존 Git 인덱스에서 대용량 파일 추적 해제 (⚠️ Git 기록은 아직 남아 있음)

지금까지 Git에 커밋되어 있던 대용량 파일들을 Git 인덱스에서 제거해줘야, 이후부터 해당 파일들이 Git LFS로 관리되기 시작한다.

--cached 옵션은 Git에서만 제거하고 로컬 파일은 보존해준다.
이렇게 하면 Git LFS로 파일 추적 전환이 가능해진다.

⚠️ 다만, 주의할 점 !

"현재 커밋 이후" 부터는 Git이 해당 대용량 파일들을 직접 관리하지 않고 LFS로 추적한다는 의미이기 때문에, 이전 커밋(과거 git 히스토리)에는 여전히 대용량 파일 정보가 남아 있는 상태다.

따라서 Github에 push 할 경우 여전히 용량 초과 에러가 발생할 수 있다.
이 문제는 이후 7번째 단계에서 해결하도록 하겠다.

git rm --cached -r assets/*.gif
git rm --cached -r assets/*.png
git rm --cached -r assets/*.jpg
git rm --cached -r assets/*.jpeg
git rm --cached -r omg-front/public/**/*.gif
git rm --cached -r omg-front/public/**/*.png
git rm --cached -r omg-front/public/**/*.jpg
git rm --cached -r omg-front/public/**/*.jpeg
git rm --cached -r omg-front/public/**/*.bin
git rm --cached -r omg-front/public/**/*.gltf
git rm --cached -r omg-front/public/**/*.mp3
git rm --cached -r omg-front/public/**/*.cur

위와 같이 삭제된 것을 볼 수 있다.
일부만 캡쳐했는데, 수많은 파일 목록이 제거됨.. 중략..

이후 변경사항 다시 커밋 찍고 push 해준다.

git add .
git commit -m "Remove large files from git history and switch to LFS"

git push origin develop

5. Git LFS 잘 적용되었는지 확인

아래 명령어로 Gif LFS가 추적 중인 파일이 잘 보인다면 일단 성공!

git lfs ls-files

6. 원격 저장소(origin) Github로 변경

원본 Gitlab 저장소에 있는 대용량 파일 관련 기록은 그대로 두고,
Github에만 클린 레포를 push 하기 위해 원격 저장소(origin) 변경해줬다.

git remote set-url origin https://github.com/hi-react/S11P21A206.git

7. filter-repo 이용해 대용량 파일 제거

4번 단계에서 --cached 처리했던 파일 경로와 일치하게, 해당 대용량 파일들을 모든 Git 히스토리(모든 브랜치)에서 완전히 삭제해준다.

noglob 옵션
필자는 zsh 쉘을 사용하는데, zsh는 *.gif 같은 glob 패턴을 미리 확장하려고 시도하여, git filter-repo 명령(--path-glob)에서 의도한 것과 다르게 동작하게 만들 수 있다. 따라서 noglob 옵션으로 zsh 쉘이 *.gif 같은 걸 미리 확장하지 않게 해준다.

--path-glob 옵션
Git은 .gitignore처럼 glob 패턴 (**/*.jpg)을 인식하지 못하기 때문에, filter-repo에서 공식 지원하는 --path-glob 옵션을 이용해 glob 패턴을 인식시켜 준다.

--invert-paths 옵션
지정한 경로를 제외하고 모두 유지한다는 의미. 즉, 작성한 경로만 삭제된다.

noglob git filter-repo \
  --path-glob assets/*.gif \
  --path-glob assets/*.png \
  --path-glob omg-front/public/**/*.gif \
  --path-glob omg-front/public/**/*.png \
  --path-glob omg-front/public/**/*.jpg \
  --path-glob omg-front/public/**/*.jpeg \
  --path-glob omg-front/public/**/*.bin \
  --path-glob omg-front/public/**/*.gltf \
  --path-glob omg-front/public/**/*.mp3 \
  --path-glob omg-front/public/**/*.cur \
  --invert-paths



[문제 3]

이쯤 되니.. 진짜 지겨워 죽을 것 같다..^^ 그래도 다 옴 !

❌ Aborting: Refusing to destructively overwrite repo history since
this does not look like a fresh clone.
(expected freshly packed repo)

Please operate on a fresh clone instead. If you want to proceed
anyway, use --force.

git filter-repo가 기존 커밋/브랜치 작업이 된 더럽혀진(fatigued) 저장소에서는 히스토리 덮어쓰기를 막아주신단다.


⚠️ 그렇다면 "더럽다(fatigued)"란 무엇인가 ..

일단, git filter-repo는 과거 커밋 히스토리까지 삭제해버리는 위험한(?) 명령어기 때문에 작업의 안정성이 매우 중요하다.

그래서 다음 중 하나라도 해당하면 "더럽다" 판단하고 작업이 거부된다.

  1. rebase, merge, reset 등 복잡한 이력이 있는 경우
  2. .git/refs에 참조가 너무 많거나, GC(Garbage Collection) 가 제대로 수행되지 않은 경우
  3. packfile이 조각난 경우 (loose object들이 다수 존재하는 상태)
  4. .git 디렉토리에 불필요한 메타 데이터나 잔여 파일이 많은 경우
  5. 이전에 누군가가 작업하거나 필터링 시도했던 레포를 재사용하는 경우
  6. 커밋/브랜치 작업 이력이 많아 .git 내부가 복잡한 경우

필자의 경우, git lfs track 하고, git rm --cached로 인덱스에서 제거하고, 커밋도 여러 번 했으니.. 충분히 더러운(fatigued) 상태였나 보다.

fresh clone 하면 ?

Git은 전체 히스토리를 한 번에 packfile로 정리하고, .git 디렉토리를 정돈된 상태로 구성해준다. 필터링하기 가장 이상적인 깔끔한 상태가 되기 때문에, 히스토리를 안전하게 조작할 수 있다.

📌 아무튼 filter-repo는 귀찮아도 꼭 fresh clone에서 하는 걸로 !


[해결 3]

1. Gitlab 저장소 새로 clone, filter-repo로 대용량 파일 삭제

# 1. Gitlab 저장소 새로 clone
git clone https://lab.ssafy.com/s11-fintech-finance-sub1/S11P21A206.git

# 2. 디렉토리 이동
cd S11P21A206

# 3. 원격 저장소(origin) Github로 변경
git remote set-url origin https://github.com/hi-react/S11P21A206.git

# 4. git filter-repo 이용해 히스토리에서 대용량 파일 삭제
noglob git filter-repo \
  --path-glob assets/*.gif \
  --path-glob assets/*.png \
  --path-glob omg-front/public/**/*.gif \
  --path-glob omg-front/public/**/*.png \
  --path-glob omg-front/public/**/*.jpg \
  --path-glob omg-front/public/**/*.jpeg \
  --path-glob omg-front/public/**/*.bin \
  --path-glob omg-front/public/**/*.gltf \
  --path-glob omg-front/public/**/*.mp3 \
  --path-glob omg-front/public/**/*.cur \
  --invert-paths

NOTICE: Removing 'origin' remote;
Parsed 1373 commits
New history written in 0.34 seconds; now
Repacking your repo and cleaning out old unneeded objects
Remove large files from git history and switch to LFS
Completely finished after 0.76 seconds.

으악!

1,373개 커밋 파싱하고, 새로운 Git 히스토리를 쓰고, 레포를 repack/clean 한 뒤, 대용량 파일은 LFS로, 그리고 성공 ! 🥹

안전 상의 이유로 origin 까지 제거 되었으므로 재연결해주면 된다!

2. Github 원격 저장소(origin) 재연결, 브랜치명 변경 후 강제 push

# 1. Github 원격 저장소(origin) 연결
git remote add origin https://github.com/hi-react/S11P21A206.git

# 2. 브랜치명 develop -> main 변경
git branch -m develop main

# 3. Github에 강제 push
git push origin main --force



🥹 최최최종 성공 ㅠㅠㅠㅠㅠ 고생했수다 !

0개의 댓글