Automation/Github

[Automation] Git LFS를 이용한 Github 대용량 프로젝트 이관하기 (Gitlab → Github)

[앙금빵] 2024. 8. 3.


1. 개요

Git은 기본적으로 텍스트 파일, 주로 코드 파일을 효율적으로 관리하고 추적할 수 있도록 설계된 버전 관리 시스템(VCS)이다.

 

그렇기에 Github은 대용량 파일이나 바이너리 파일에 대해서 별도로 용량제한을 걸어두고 있으며 50MiB가 넘으면 Warning, 100MiB를 넘어가는 파일들에 대해서는 push를 진행할 때 에러가 발생한다.

 

이는 대용량 파일로 인해 발생할 수 있는 성능 저하와 리소스 낭비를 방지하기 위한 조치이다.

참조: https://docs.github.com/ko/repositories/working-with-files/managing-large-files/about-large-files-on-github

 

제한을 초과하는 파일을 추적하려면 Git 대용량 파일 스토리지(Git LFS) 사용해야 하며, 아래 링크에서 OS별 LFS 설치를 진행할 수 있다.

https://github.com/git-lfs/git-lfs

 

GitHub - git-lfs/git-lfs: Git extension for versioning large files

Git extension for versioning large files. Contribute to git-lfs/git-lfs development by creating an account on GitHub.

github.com


2. 본문

2-1. 단일 대상

# Gitlab으로 부터 소스 가져오기
git clone "https://oauth2:<<Gitlab_Token>>@<<your_gitlab_repo_url>>"
cd large_project

#용량 큰 대상 확인
find . -type f -size +50M

# Git LFS 실행
git lfs install

# 캐시 삭제
git rm --cached large_file_A
git rm --cached large_file_B

# git lfs track
git lfs track "large_file_A"
git lfs track "large_file_B"


# git add & commit
git add .gitattributes
git add large_file_A
git add large_file_B

git commit -m "Large file migration with Git LFS"

# 히스토리 초기화
git filter-repo --strip-blobs-bigger-than 50M --force

# Remote & Push
git remote add github "https://<<github_token>>@github.com/xxxxxxxxx.git"
git push --force github master

# 브랜치 이관
git branch -r | grep -v '\->' | while read remote; do
  git branch --track "${remote#origin/}" "$remote"
done
git push --all github

# 태그 이관
git push --tags github

 

2-2. 복수 대상

필자 경우 용량이 큰 레포들이 수십개가 넘어가는 상황이였기에 일일이 용량큰 파일에 대하여 git lfs 를 적용하기에는 많은 시간이 소요되어졌다. 그렇기에 스크립트를 통해 조치를 진행하였다.

#!/bin/bash

# GitHub credentials 정보
GITHUB_USERNAME="your-username"
GITHUB_TOKEN="ghp_123456xxxxxxxx"
GITHUB_ORG="<<your_github_org>>"

# GitLab credential
GITLAB_TOKEN="glpat-123456xxxxxxxx"
GITLAB_URL="<<your_gitlab_repo>>"

# 용량 큰 레포 리트
REPOSITORIES=(
		'groupA/example_project1'
		'groupA/example_project2'
		'groupB/example_project3'
		'groupB/example_project4'
)

migrate_repository() {
    local repo_path=$1
    local project_name=$(basename "$repo_path")

    echo "---------- Creating GitHub repository: $project_name ----------"
    curl -u "$GITHUB_USERNAME:$GITHUB_TOKEN" https://api.github.com/orgs/$GITHUB_ORG/repos -d "{\"name\":\"$project_name\", \"private\": true}"

    echo "---------- Cloning repository from GitLab: $repo_path ----------"
    git clone https://oauth2:$GITLAB_TOKEN@$GITLAB_URL/$repo_path.git
    cd $project_name || exit

    echo "---------- Identifying files larger than 50MB, ignoring .git directory ----------"
    large_files=$(find . -type f -size +50M | grep -v '^./\.git/')

    echo "---------- Initializing Git LFS ----------"
    git lfs install

    for file in $large_files; do
        echo "---------- Processing large file: $file ----------"
        git rm --cached "$file"
        git lfs track "$file"
    done

    echo "---------- Adding and committing changes ----------"
    git add .gitattributes
    git add .
    git commit -m "Large file migration with Git LFS"

    echo "---------- Rewriting history to use Git LFS ----------"
    git filter-repo --strip-blobs-bigger-than 50M --force

    echo "---------- Setting the remote and pushing to GitHub ----------"
    git remote add github https://$GITHUB_TOKEN@github.com/$GITHUB_ORG/$project_name.git
    git push --force github master

    echo "---------- Migrating branches ----------"
    git branch -r | grep -v '\->' | while read -r remote; do
        git branch --track "${remote#origin/}" "$remote"
    done

    git push --all github

    echo "---------- Migrating tags ----------"
    git push --tags github

    echo "---------- Cleanup project ----------"
    cd ..
    rm -rf $project_name

    echo "---------- Repository $project_name migration completed ----------"
}

# 전체 진행리스트 파악
total_repos=${#REPOSITORIES[@]}
done_repos=0

for repo in "${REPOSITORIES[@]}"; do
    migrate_repository "$repo"
    done_repos=$((done_repos + 1))
    echo "Done repositories: $done_repos / $total_repos" # 전체 진행리스트 파악
done

echo "All repositories have been processed."

3. 실패 및 에러

기본적인 git lfs 사용법은 깃헙 공식문서에 기재되어져 있다. 그러나 필자의 상황 경우 git lfs 설정을 진행하더라도 Push시 용량 에러가 계속 발생하고 있었다.

 

입력 명령어는 아래와 같이 진행하였으나 용량 관련 에러가 발생하였다.

 

입력 명령어 (더보기 클릭)

더보기
--------------------------------
# 다운로드
git lfs install

# lfs track
git lfs track "largefileA"

# git add
git add .gitattributes
git add largefileA
git commit -m "migrating large files"

#Rewrite Histroy
git lfs migrate import --include="largefileA"

#Git remote add
git remote add github https://<<GITHUB_TOKEN>>github.com/xxxxxxxx.git

#make the changes
git push --force github master
--------------------------------

 

에러

Uploading LFS objects: 100% (40/40), 978 MB | 0 B/s, done.                                                            
Enumerating objects: 30178, done.
Counting objects: 100% (30178/30178), done.
Delta compression using up to 2 threads
Compressing objects: 100% (17803/17803), done.
Writing objects: 100% (30178/30178), 450.50 MiB | 16.75 MiB/s, done.
Total 30178 (delta 11952), reused 30103 (delta 11936), pack-reused 0
remote: Resolving deltas: 100% (11952/11952), done.

remote: error: Trace: b434f44f017efd308a1044108eb33bcf78d8b943afc79a08d12f2eec422d2c9b
remote: error: See https://gh.io/lfs for more information.
remote: error: File xxxxxxxxx is 305.17 MB; this exceeds GitHub's file size limit of 100.00 MB
remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com.
To https://github.com/org_name/xxxxxxxxxx.git
 ! [remote rejected]   master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://github.com/org_name/xxxxxxxxxx.git'
You have mail in /var/spool/mail/root

 

이 이슈는 히스토리에서도 큰 용량을 포함하고 있었기에 발생한 문제이며 아래 구문을 통해 이슈가 해결되었다. (참조: git filter-repo)

git filter-repo --strip-blobs-bigger-than 50M --force

Reference

https://newsight.tistory.com/330

 

댓글