Git Filter-Repo는 다른 곳에서는 찾을 수 없었던 기능을 포함하여 기록을 다시 작성하기위한 다양한 도구입니다. 그것은 GIT 필터 브랜치와 동일한 공간에 속하지만, 훨씬 더 많은 기능을 갖추고, 더 많은 기능을 갖추고, 사소한 재 작성 사례를 넘어서는 유용성을 확장하는 설계를 통해 항복 유도 성능이 좋지 않습니다. Git Filter-Repo는 이제 GIT 필터 브랜치 대신 GIT 프로젝트에서 권장합니다.
대부분의 사용자는 아마도 필터 레포를 간단한 명령 줄 도구 (몇 가지 플래그 만 사용하는 것)로 사용하지만 코어 필터 레포에는 히스토리 재 작성 도구를 만들기위한 라이브러리가 포함되어 있습니다. 따라서 전문 요구가있는 사용자는이를 활용하여 완전히 새로운 기록 재 작성 도구를 신속하게 만들 수 있습니다.
필터 리포가 필요합니다.
git-filter-repo
단일 파일 파이썬 스크립트로, 많은 시스템에서 기본적으로 사용하기 위해 설치하기 위해 수행되었습니다. 해당 파일을 $ 경로로 배치하십시오.
기본 사용 또는 특별 사례를 초과하는 것들은 install.md를 참조하십시오. 다음 중 하나가 적용되는 경우에만 관련된 지침이 더 필요합니다.
포괄적 인 문서 :
예에서 배우는 것을 선호하는 경우 :
이것은 필터 리포의 Git Rev 뉴스 기사에서 더 자세히 설명했지만 주요 경쟁자들에게는 일부 하이라이트가 있습니다.
필터 브랜치는 사소한 리포지토리의 경우 매우 느리게 느리게 (보다 여러 차례 느리게)입니다.
필터 브랜치에는 재 작성을 조용히 손상 시키거나 적어도 "정리"노력을 정력하게 부패시킬 수있는 Gotchas로 가득 차 있습니다.
필터 브랜치는 약간의 사소한 재 작성에 사용하기에 매우 번거 롭습니다.
GIT 프로젝트는 필터 브랜치의 위의 문제를 호환 적으로 수정할 수 없다고 밝혔다. 필터 브랜치 사용을 중단하는 것이 좋습니다
필터 브랜치의 다이 하드 팬은 필터-평방 (일명 필터 브랜치)에 관심이있을 수 있으며, 필터-레포를 기반으로 필터 브랜치를 다시 구현하는 필터-브랜치의 재 구현 (필터만큼 빠르거나 안전하지는 않지만 필터- 레포).
필터 브랜치 매뉴얼의 예제 명령을 Filter-Repo 명령으로 변환하는 방법을 보여주는 치트 시트를 사용할 수 있습니다.
당시의 훌륭한 도구이지만 일부는 간단하지만 몇 가지 종류의 재 작성로 제한됩니다.
아키텍처는 더 많은 유형의 재 작성을 처리 할 수 없습니다.
그것의 아키텍처는 의도 된 usecase에 대해서도 몇 가지 단점과 버그를 제시합니다.
BFG의 팬은 BFG에 대한 BFG-ISH, BFG에 대한 몇 가지 새로운 기능 및 버그 수정을 포함하는 필터 레포를 기반으로 BFG의 상환에 관심이있을 수 있습니다.
BFG Repo Cleaner 매뉴얼의 예제 명령을 Filter-Repo 명령으로 변환하는 방법을 보여주는 치트 시트를 사용할 수 있습니다.
그 조각을 다른 더 큰 저장소로 병합하려는 의도로 저장소 조각을 추출하고 싶다고 가정 해 봅시다. 추출을 위해 우리는 다음을 원합니다.
필터 레포 로이 작업을 수행하면 다음 명령만큼 간단합니다.
git filter-repo --path src/ --to-subdirectory-filter my-module --tag-rename ' ' : ' my-module- '
(단일 따옴표는 불필요하지만 빈 문자열을 my-module-
로 접두사로 대체하고 있음을 인간에게 명확하게 만듭니다.)
BFG Repo Cleaner는 이러한 종류의 다시 작성할 수 없습니다. 실제로, 세 가지 유형의 원하는 변경 사항은 모두 기능을 벗어납니다.
필터 브랜치는 필요한 호출을 알아 내면 다음과 같은 경고가 있습니다 (아래에 더 자세히 설명).
git filter-branch
--tree-filter ' mkdir -p my-module &&
git ls-files
| grep -v ^src/
| xargs git rm -f -q &&
ls -d *
| grep -v my-module
| xargs -I files mv files my-module/ '
--tag-name-filter ' echo "my-module-$(cat)" '
--prune-empty -- --all
git clone file:// $( pwd ) newcopy
cd newcopy
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git gc --prune=now
일부는 위의 필터 브랜치 호출이 -tree-filter를 사용하여 실제로 느리게 될 것임을 알 수 있습니다. 또는 위의 명령을 다음과 같이 변경하는 -Index-Filter 옵션을 사용하여 다음을 변경할 수 있습니다.
git filter-branch
--index-filter ' git ls-files
| grep -v ^src/
| xargs git rm -q --cached;
git ls-files -s
| sed "s%$(printf \t)%&my-module/%"
| git update-index --index-info;
git ls-files
| grep -v ^my-module/
| xargs git rm -q --cached '
--tag-name-filter ' echo "my-module-$(cat)" '
--prune-empty -- --all
git clone file:// $( pwd ) newcopy
cd newcopy
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git gc --prune=now
그러나 필터 브랜치 명령 중 하나의 경우 경고가 있습니다. 첫째, 일부는 필터 브랜치에 대한 5 개의 명령을 왜 여기에 나열하는지 궁금 할 것입니다. -all 및-tag-name filter를 사용하고 필터 브랜치의 맨 페이지에도 불구하고 클론이 오래된 개체를 제거하기에 충분하다고 주장하는 필터 브랜치 맨 페이지에도 불구하고 다른 태그를 삭제하고 다른 GC를 수행하는 추가 단계는 여전히 필요합니다. 오래된 물건을 청소하고 어딘가에 밀기 전에 새롭고 오래된 역사를 섞지 마십시오. 기타 경고 :
다음과 같은 것과 함께 이것을 해킹 할 수 있습니다.
git fast-export --no-data --reencode=yes --mark-tags --fake-missing-tagger
--signed-tags=strip --tag-of-filtered-object=rewrite --all
| grep -vP ' ^M [0-9]+ [0-9a-f]+ (?!src/) '
| grep -vP ' ^D (?!src/) '
| perl -pe ' s%^(M [0-9]+ [0-9a-f]+ )(.*)$%1my-module/2% '
| perl -pe ' s%^(D )(.*)$%1my-module/2% '
| perl -pe s%refs/tags/%refs/tags/my-module-%
| git -c core.ignorecase=false fast-import --date-format=raw-permissive
--force --quiet
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git reset --hard
git reflog expire --expire=now --all
git gc --prune=now
그러나 이것은 불쾌한 경고와 한계가 있습니다.
기존 저장소 필터링 도구 중 어느 것도 내가 원하는 것을 수행하지 않았습니다. 그들은 모두 내 필요로 짧게 나타났습니다. 내가 원했던 아래의 첫 8 가지 특성 중 하나는 제공되지 않았으며, 마지막 4 가지 특성 중 2 개 이상을 제공 한 도구는 다음과 같습니다.
[시작 보고서] 사용자에게 리포 분석을 제공하여 다른 도구를 추측하거나 찾을 수있는 도구를 찾는 대신 자두 또는 이름을 바꾸는 것을 시작하는 데 도움이됩니다. (예를 들어 -Analyze와 같은 특별한 깃발로 처음으로 실행하여 트리거되었습니다.)
[유지 대 제거] 사용자가 선택한 경로를 쉽게 제거 할 수있는 방법을 제공하는 대신 사용자가 특정 경로 만 유지할 수있는 플래그를 제공합니다. 물론 사용자는 보관하려는 경로 이외의 모든 경로를 제거하도록 지정하여이를 해결할 수 있지만 저장소의 모든 버전에 존재 했던 모든 경로를 지정해야 할 필요성은 때때로 상당히 고통 스러울 수 있습니다. 필터 브랜치의 경우 git ls-files | grep -v ... | xargs -r git rm
합리적인 해결 방법이지만 다루기 어려울 수 있으며 사용자에게는 간단하지 않습니다. 또한 이러한 명령은 종종 시스템에 따라 다릅니다 (내가 제공 한 스 니펫에서 GNUISM을 발견 할 수 있습니까?).
[Renaming] 경로 이름을 쉽게 바꾸어야합니다. 예를 들어, 일부 하위 디렉토리를 리포지토리의 루트로 취급 할 수있는 것 외에도 사용자가 저장소의 루트를 하위 디렉토리가되도록 옵션을 제공합니다. 더 일반적으로 파일과 디렉토리의 이름을 쉽게 바꿀 수 있습니다. 이름 변경으로 인해 여러 파일이 동일한 경로에서 존재하는지 사전 확인을 제공합니다. (그리고 커밋이 단순히 OldName-> newName을 수정하지 않고 복사하는 경우, OldName-> NewName을 필터링하지 않도록 특수 처리를 추가하십시오.
[보다 지능적인 안전] 원래 Ref의 사본을 Repo 내의 특수 네임 스페이스에 쓰는 것은 사용자 친화적 인 복구 메커니즘을 제공하지 않습니다. 많은 사람들이 그것을 사용하여 회복하기 위해 고군분투 할 것입니다. 내가 본 것 중 거의 모든 사람들이 리포지토리 필터링 작업을 수행했습니다. 오류의 경우 클론을 닦는 것은 훨씬 쉬운 복구 메커니즘이기 때문입니다. 사용자가 -force를 재정의하지 않는 한, 우리가 신선한 클론에 있지 않으면 검출하고 구제함으로써 워크 플로우를 강력하게 장려하십시오.
[자동 축소] 오래된 CRUFT를 자동으로 제거하고 필터링 후 사용자의 저장소를 다시 포장합니다 (재정의하지 않는 한). 이것은 사용자의 물건을 단순화하고, 오래된 역사와 새로운 역사를 함께 혼합하지 않으며, 맨 페이지에 문서화 된 레포를 축소하기위한 다단계 프로세스가 실제로 작동하지 않는 문제를 피합니다. (나는 당신을보고 있습니다, 필터 브랜치.)
[깨끗한 분리] 오래된 리포지토리와 다시 작성한 repo를 함께 섞어 혼란스러워하는 사용자 (그리고 오래된 물건의 우발적 인 재 푸치를 방지하지 않도록)를 피하십시오. (이것은-tag-name filter 옵션을 사용할 때 필터 브랜치의 문제이며, 때로는 분기의 서브 세트 만 필터링 할 때 문제이기도합니다.)
[Versatility]는 사용자에게 도구를 확장하거나 기존 기능을 활용하는 새로운 도구를 작성하고 (a) 별도의 프로세스 (성능을 파괴 할 수있는), (b)를 피할 필요가없는 방식 으로이 확장 성을 제공 할 수 있습니다. 사용자가 OS 의존적 쉘 명령 (사용자가 서로 명령을 공유하지 못하게하는 것을 방지)을 지정하지 않으면 (c) 풍부한 데이터 구조 (해시, 딕트, 목록 및 배열이 쉘에서는 엄청나게 어렵 기 때문에)와 ( d) 합리적인 문자열 조작 기능 (쉘에 심하게 부족한)을 제공합니다.
[Old Commit References]는 사용자가 새로운 저장소와 함께 이전 커밋 ID를 사용할 수있는 방법을 제공합니다 (특히 refs/ rows/ references가있는 오래된에서 새 해시까지의 매핑을 통해).
[커밋 메시지 일관성] 커밋 메시지가 ID로 다른 커밋을 참조하는 경우 (예 : "Commit 0013deadbeef9a ..."에서 "Commit 01234567890ABCDEF", "Commit Commit 01234567890ABCDEF")를 참조하면 해당 커밋 메시지를 다시 작성하여 새 커밋 ID를 참조해야합니다.
[비어있는 가지 치기] 필터링으로 인해 비어있는 커밋은 가지 치기를해야합니다. 커밋의 부모가 가지 치기가있는 경우, 첫 번째로 정리되지 않은 조상은 새로운 부모가되어야합니다. 비교되지 않은 조상이없고 커밋이 병합되지 않았다면 새로운 루트 커밋이됩니다. 비교되지 않은 조상이없고 커밋이 병합 인 경우, 합병은 부모가 적을 것입니다 (따라서 자체 파일 변경이 없으면 자체적으로 정리 될 수있는 비자수 커밋이 될 가능성이 높습니다). . 여기서 주목해야 할 특별한 한 가지는 비어있는 커밋이 비어 있고 공허한 커밋이 아니라 커밋을 잘라냅니다. 일부 프로젝트는 의도적으로 버전 작성 또는 게시 이유에 대해 빈 커밋을 생성하며이를 제거해서는 안됩니다. (특별한 경우에, 비워지기 시작했지만 부모가 잘라낸 커밋은 "비어있게 된"것으로 간주 될 것입니다.)
비어있는 커밋의 가지 치기는 잠재적으로 토폴로지 변화를 일으킬 수 있으며 많은 특별한 경우가 있습니다. 일반적으로, 합병 커밋은 그래프 토폴로지를 보존하기 위해 필요하기 때문에 제거되지 않지만 부모와 다른 조상의 가지 치기는 궁극적으로 하나 이상의 부모를 상실 할 수 있습니다. 간단한 사례는 이미 위에서 언급되었습니다. 병합 커밋이 충분한 부모가 무너지지 않은 커밋이되기에 충분한 부모를 잃고 파일 변경이 없으면 정리 할 수 있습니다. 병합 커밋은 또한 퇴보하는 토폴로지를 가질 수 있습니다. 그것은 두 부모로서의 MERGE_BASE로 끝날 수 있습니다 (원래 리포의 모든 중재 커밋이 가지 치기 된 경우). 조상. 그러한 경우, 병합에 자체 파일 변경이 없으면 합병 커밋도 잘라낼 수도 있습니다. 그러나 우리가 빈 가지 치기와 마찬가지로 우리는 퇴보를 시작한 병합 커밋을 가지지 않습니다 (-no-ff merge와 같이 의도적 일 수 있음을 나타냅니다). 그들 자신.
[속도] 필터링은 합리적으로 빠르야합니다
기고 가이드 라인을 참조하십시오.
필터 레포 커뮤니티의 참가자는 GIT 프로젝트와 동일한 표준을 준수 할 것으로 예상되므로 GIT 행동 강령이 적용됩니다.
필터 레포와 전임자에 대한 작업은 필터 레포가 작업을 수행하는 데 필요한 것들을 기반으로 핵심 GIT에서 빠른 수출 및 빠른 수입 (및 때로는 다른 명령)에 대한 수많은 개선을 주도했습니다.