2012-06-06 3 views
6

저는 최신 개정판에 약 3500 개의 커밋과 30,000 개의 별개의 파일이있는 자식 저장소가 있습니다. 여러 사람으로부터 약 3 년 동안의 작업을 대표하며 우리는 오픈 소스로 만들 수있는 권한을 받았습니다. 나는 최신 버전 대신에 전체 역사를 발표하기 위해 열심히 노력하고있다. 이렇게하려면 "시간에 맞추기"에 관심이 있으며 파일을 만들 때 파일 맨 위에 라이센스 헤더를 삽입하는 것이 좋습니다. 실제로이 작업이 있지만 3 일 정도의 RAM 디스크에서 완전히 실행되며 수동 작업이 약간 필요합니다. 나는 그것이 훨씬 더 빨라질 수 있다는 것을 안다. 그러나 나의 git-fu는 그 일에 아주 달려 있지 않다.효율적으로 많은 역사를 재 작성 (rebase -i)

질문 : 동일한 작업을 훨씬 빨리 수행하려면 어떻게해야합니까? 나는 현재 (스크립트에서 자동화 된, 그러나 저 참아주세요 ...) 무엇을

:

  1. 확인 새 파일이 저장소에 추가 된 커밋의 모든 (단지 수줍음이 있습니다 이들 중 500 FWIW)의 :

    git whatchanged --diff-filter=A --format=oneline 
    
  2. 파일의 첫 번째 줄에 edit 단 하나의 시간 pick를 대체 내 자신의 스크립트로 환경 변수 GIT_EDITOR 정의 (당신이) 왜 곧 볼 수 있습니다.

    git rebase -i decafbad001badc0da0000~1 
    

다음 해당 파일을 추가 투입 직전부터 대화식 REBASE 호출 상기 git whatchanged의 출력으로부터 각 커밋 들어

perl -pi -e 's/pick/edit/ if $. == 1' $1 
  • : 이는 동작의 핵심 내 커스텀 GIT_EDITOR (그 perl one-liner)는 pick에서 edit으로 바뀌며 우리는 새로운 파일을 변경하기 위해 쉘로 버려진 다. 또 다른 간단한 header-inserter 스크립트는 내가 삽입하려고하는 헤더의 알려진 고유 한 패턴을 찾습니다 (알려진 파일 유형 (*. [chS] 만 가능)). 없으면 삽입하고 git add이 파일입니다. 이 순진 기술은 현재 커밋 중에 어떤 파일이 실제로 추가되었는지에 대한 지식이 없지만, 옳은 일을하고 멱등하고 (동일한 파일에 대해 여러 번 실행하는 것이 안전합니다.),이 전체 프로세스가 병목 현상이 발생한 부분이 아닙니다 . 이 시점에서

    우리는 우리가 현재 커밋, 업데이트 및 호출 한 것을 기쁘게 생각합니다 :

    git commit --amend 
        git rebase --continue 
    

    rebase --continue이 비싼 부분입니다. whatchanged의 출력에서 ​​모든 수정본에 대해 git rebase -i을 한 번 호출하므로 리베이스가 많이 필요합니다. 이 스크립트가 실행되는 거의 모든 시간 동안 "Rebasing (2345/2733)"카운터 증가분을 보게됩니다.

    또한 느린 것이 아닙니다. 해결해야 할 주기적으로 충돌이 있습니다. 이러한 경우는 최소한 다음과 같은 경우에 발생할 수 있습니다. (1) '새로운'파일이 실제로는 기존 파일의 사본 인 경우 (예 : #include). 이것은 진정한 충돌이지만 대부분의 경우 자동으로 해결 될 수 있습니다 (예, 해당 스크립트를 처리하십시오). (2) 파일이 삭제 될 때. 이것은 우리가 git rm으로 삭제하기를 원한다는 것을 확인함으로써 쉽게 해결할 수 있습니다. (3) diff과 같이 보이는 부분이있을뿐입니다. 예를 들어 변경 사항이 빈 줄만 추가하는 경우입니다.다른보다 합법적 인 충돌은 수동 개입이 필요하지만 전체적으로 가장 큰 병목 현상은 아닙니다. 가장 큰 병목은 "Rebasing (xxxx/yyyy)"를 쳐다 보는 것입니다.

    지금은 최신 커밋에서 이전 커밋, 즉 git whatchanged의 출력 맨 위에서 시작하여 개별 리베이스가 시작됩니다. 즉, 첫 번째 rebase가 어제의 커밋에 영향을 미침으로써 결국 3 년 전부터 커밋을 리베이스하게됩니다. "최신"에서 "이전"으로가는 것은 반 직관적 인 것처럼 보이지만, 지금까지는 rebase를 호출 할 때 을 edit으로 변경하지 않으면 문제가 있다는 것을 확신하지 못했습니다. 나는 갈등이 일어나기 때문에 두려워하며, 한 번에 모든 것을 rebase하려고 노력하는 것에서 충돌 파문의 해일을 다루기를 원하지 않는다. 누군가 그것을 피할 방법을 알고 있을까요? 나는 하나를 생각해 낼 수 없었다.

    나는 자식 객체의 내부 동작을 살펴보기 시작했습니다 1! 객체 그래프를 걷고 내가 원하는 변경을하는 훨씬 더 효율적인 방법이있는 것처럼 보입니다.

    이 저장소는 태그 또는 분기를 사용하지 않은 SVN 저장소에서 왔습니다 (나는 이미 git filter-branch를 편집했기 때문에). 우리는 직선 역사의 편리함을 가지고 있습니다. git branch 나 merges는 없다.

    중요한 정보는 빠뜨린 것이 확실하지만이 게시물은 이미 지나치게 길어 보입니다. 요청에 따라 더 많은 정보를 제공하기 위해 최선을 다할 것입니다. 결국에는 다양한 스크립트를 게시해야 할 수도 있습니다. 이는 가능성입니다. git 저장소에서 히스토리를 어떻게 재 작성하는지 알아내는 것이 나의 목표이다. 다른 실행 가능한 라이센스 및 코드 릴리스 방법을 논하는 것이 아닙니다.

    감사합니다.

    업데이트 2012-06-17 : Blog post 모든 세부 사항이 있습니다.

  • +0

    , 나는 거대한 역사의 재 작성을 할 필요가 없다,하지만 난 결코 올바른 도구는 ['git filter-branch'] (http://www.kernel.org/pub/software/scm/git/docs/v1.7.3/git-filter-branch.html)입니다. . 미안하지만 더 도움이되지 않을 것 같습니다. – KurzedMetal

    +0

    @KurzedMetal :이 릴리스와 관련이없는 (파일 시스템) 경로를 제거하기 위해이 모든 리베이스를 시작하기 전에'filter-branch'를 사용했습니다. (이 git 저장소가 생성 된 SVN 저장소는 더 크고 다루기 힘들다.) 그러나,이 모든 리베이스 작업을 수행하는 것보다'filter-branch '스크립트 변경 사항이 더 효율적일 수 있다는 지적이있을 수 있습니다. 나는 조사 할 것이다. – jonny0x5

    +0

    '여러 사람으로부터 약 3 년 동안의 작업을 대표하며 공개 소스로 만들 수있는 권한을 받았습니다. 주제는 알고 있지만 궁금합니다. P, 프로젝트 이름/홈페이지는 무엇입니까? – KurzedMetal

    답변

    4

    git filter-branch -f --tree-filter '[[ -f README ]] && echo "---FOOTER---" >> README' HEAD 
    

    기본적으로 README 파일에 바닥 글 행을 추가하고, 역사는이 파일을 만든 이후되고있다처럼 당신을 위해 충분히 효율적한다면, 나는 확실하지 않다 보일 것이다 그러나 그것을하는 올바른 방법입니다.

    맞춤 스크립트를 작성하면 좋은 프로젝트 기록으로 끝나고 너무 많은 "마법"(rebase, perl, 스크립트 편집기 등)을 실행하면 예상치 못한 방식으로 프로젝트 기록이 손실되거나 변경 될 수 있습니다.

    jon (OP)은이 기본 패턴을 사용하여 간소화 및 속도 향상과 함께 목표를 달성했습니다.

    몇 가지 성능에 중대한 영향을줍니다. 램 디스크 디렉토리에 -d <directory> 매개 변수를 가리키는 사용

    • (같은 /dev/shm/foo)는 크게 속도를 향상시킬 수 있습니다.

    • 작은 유틸리티 (예 : find)를 사용하는 동안 완료된 포크는 내장 된 언어 기능을 사용하여 단일 스크립트에서 모든 변경 작업을 수행하면 여러 번 느려질 수 있습니다. 이 문제를 피하십시오 :

      git filter-branch -d /dev/shm/git --tree-filter \ 
      'find . -name "*.[chS]" -exec perl /path/to/just-add-license.pl \{\} \;' \ 
      --prune-empty HEAD 
      

    이가 OP가 사용되는 펄 스크립트의 위생적 버전입니다 : 그것은 나를 위해 꽤 모호한입니다

    #!/usr/bin/perl -w 
    use File::Slurp; 
    use File::Find; 
    
    my @dirs = qw(aDir anotherDir nested/DIR); 
    my $header = "Please put me at the top of each file."; 
    
    foreach my $dir(@dirs) { 
        if (-d $dir) { 
        find(\&Wanted, $dir); 
        } 
    } 
    
    sub Wanted { 
        /\.c$|\.h$|\.S$/ or return; # *.[chS] 
        my $file = $_; 
        my $contents = read_file($file); 
        $contents =~ s/\r\n?/\n/g; # convert DOS or old-Mac line endings to Unix 
        unless($contents =~ /Please put me at the top of each file\./) { 
        write_file($file, {atomic => 1}, $header, $contents); 
        } 
    } 
    
    +0

    감사합니다. 이것은 올바른 방향으로 움직이는 것처럼 보입니다. 필자에게 불분명 한 점은 README 파일이 존재할 때마다 커밋마다'--- FOOTER --- '를 한 번 소개하지 않는 이유이다. '--tree-filter'의 "지능"에서 git은 오직 한 번만 명령을 실행합니다. 예를 들어,'git filter-branch -f --tree-filter 'echo "i ran">>/tmp/ran.log'HEAD'는 임시 파일에 대한 모든 커밋마다 "i ran"을 한 번 출력합니다. – jonny0x5

    +1

    그게'filter-branch'가 작동하는 방식으로, 지정한 브랜치의 모든 커밋을 "반복"합니다. 그리고 실제로 모든 커밋을해야만합니다. 여러분은 그것을 피할 수 없습니다.'git'은'diff'를 저장하지 않고, 파일 내용을 저장합니다. 그래서'A-B-C' 히스토리가 있다면, ''A '하지만 B를 쓰지 않기 위해''footer' '를 추가하면,'B''에서 삭제 한 것처럼, 퍼지지 않습니다. 그래서 모든 역사를 통해 "footer"를 유지하려면, 'filter-branch' (모든 커밋에 대해 실행)처럼'A','B' 및'C'를 커밋하도록 추가하십시오. – KurzedMetal

    +0

    이 설명에 대해 대단히 감사드립니다. 나는'git filter-branch --tree-filter'를 사용하여 나의 목표를 달성 할 수 있었고, 원하는 변경을하기 위해 하나의 짧은 (20 줄 미만의) perl 스크립트를 만들었습니다. 내 이해는 stackoverflow 모범 사례는 내 특정 질문, upvote 및 해결 표시로 해결하는 방법을 명확하게 대답을 편집 할 수 있습니다. – jonny0x5

    -1

    얼룩은 내용을 주소 지정 할 수 있습니다. 하나의 파일을 해시를 변경하지 않고 분리하여 수정할 수는 없으며, 해시를 포함하는 모든 커밋에서 참조하는 디렉토리 blob을 변경합니다. 따라서 그로부터 커밋 된 커밋을 변경합니다. 기본적으로 문제를 이해함에 따라 세상을 다시 써야합니다. 나는 각각의 객체를 한 번만 다시 작성한 원래의 객체 해시 테이블의 큰 해시 테이블을 사용하여 역 DAG 순서로이 모든 작업을 수행 한 알고리즘을 상상할 수 있다고 생각합니다.

    올바른 해결책 (이미 3 일이 걸리는 경우조차도)을 이미 가지고 있다면, 이것을 최적화하려고하면 정말 가치가 있습니까? I 실제로이 코드를 디버깅하고 정상적인 솔루션으로는 해결할 수없는 3 일 이내에 결과를 발표 할 수있을만큼 정확하게 작업하고 있다고 상상해보십시오. 사용

    +0

    에서 구할 수 있습니다. 3 일 후에 repo의 기록을 다시 쓰는 것보다 훨씬 더 많은 일을 할 수 있습니다. 컴퓨터 시대가 아닙니다. 그렇습니다. 최적화가 가능합니다. INHHO – CharlesB

    +0

    결과가 출시 될 준비가되기 전에 ** 개발 **에서 3 일 이상 소요될 것이라는 의미였습니다. –

    +0

    죄송합니다. 오산 된 내용, 내 downvote를 삭제할 수 있도록 게시물을 편집 할 수 있습니까? – CharlesB