지트 부: 다중 환매와 선형 역사 합병

며칠 전에 나는 자신에게 새로운 난제를 발명했다. 나는 내가 여러 해 동안 세운 몇 개의 라이브러리를 하나의 리포에 통합시켜 그것들을 재구성하고 싶다.언뜻 보기에는 매우 간단하다. 모든 파일을 하위 폴더에 복사하고 추가하고 제출하는 것은 다음과 같다.
$ git add .
$ git commit -m 'All together now!'
완성!
아니오, 정말이 아니에요.이것은 역사를 없앨 것이다.나는 정말 그것을 보존하고 싶다.나는 자주 돌아가서 파일에 대해 어떤 변경을 했는지 확인하고, 어떤 코드가 언제 수정되었는지, 왜 수정되었는지 '책망' 을 사용한다.나는 어떻게 빨리 완성하는지 주위를 둘러보고 싶었다.나는 비슷한 사업을 묘사하는 블로그 게시물 한 무더기를 발견했다.코드 세션, 셸, 파이톤 스크립트, 심지어 자바 프로그램까지 찾아냈다.
그 중 일부를 시도한 후에 (자바가 아니야, 아니야, 고마워) 나는 그것들이 내가 원하는 것이 아니라는 것을 깨달았다.일부 작가들은 최초의 제출 해시를 보류하려고 시도했다.일부 작가들은 원본 파일 경로를 원한다.나는 역사가 시작되었을 때의 변화를 추적할 수 있기를 바란다.
내가 발견한 대다수의 방법은 모두 나의 목표에 부합되지 않는다.보통, 사람들은 하나의 리포를 하나의 지점으로 가져오려고 시도한다. (보통 다른 리포에서 하나를 추가하는 것 remote 에서 모든 파일을 제출할 때 하위 폴더로 이동한 다음, 이 지점을 master 에 통합시킨다.이것은 큰 제출을 만들고 모든 파일을 하위 폴더로 이동합니다.그 다음에 하나의 커다란 합병 제출이 있는데, 그 중 한 지점에서 온 모든 변경 사항이 다른 지점으로 복사되었다.GitHub에서 이러한 환매 협의를 보았을 때, 문서의 역사가 파괴되었음에도 불구하고 blame.
내장 명령git subtree도 발견했는데 그 결과 내가 이전에 시도한 모든 제3자 도구와 같은 문제가 있음을 발견했다.그러니까 가지 마!여기서 바퀴를 다시 설계하고 나만의 해결 방안을 제시해야 한다.
그래서 기본적으로 나는 어떠한 합병 제출도 하지 않은 상황에서 모든 환매 협의를 합병하는 방법을 찾아야 한다.원본 파일을 하위 폴더로 이동해야 합니다.나는 두 개의 Git 도구를 생각했다. cherry-pickfilter-branch.
방주몇 년 전에 나는 업무 중에 메르쿠릴을 사용한 적이 있다. 그것은 매우 좋다.Mercurial의 사용자 체험은 사람을 놀라게 한다.나는 그것이 서서히 죽지 않고 저질 제품이 개발 현장을 인수할 수 있기를 바란다.Mercurial은 직관적이고 사용자가 우호적인 것처럼 Git는 기능이 강하고 용도가 광범위하다.Git는 마치 삐뚤어진 레고 블록과 같다. 너는 그것으로 네가 원하는 모든 것을 만들 수 있다.
계획은 다음과 같습니다.
  • 각 환매 협의를 자신의 지점에 놓는다
  • 기록을 다시 쓰고 제출할 때마다 모든 파일을 하위 폴더로 이동
  • 역사 기록을 다시 쓰고 환매 명칭을 제출 메시지에 전치
  • cherry 시간순으로 모든 지점에서 제출할 것을 선택master
  • 분기 삭제
  • 쓰레기 수집으로 환매 축소
  • 간단한 완두콩.오늘은 좀 마조히즘이 느껴지니까 베이스에서 하자.
    우선, 여느 때와 마찬가지로, 명령이 실패할 때, 스크립트 전체가 실패할 수 있도록 확보해야 한다.이것은 통상적으로 문제가 발생할 때 많은 시간을 절약할 수 있다.보통 이렇습니다.
    #!/bin/bash
    set -euo pipefail
    
    내가 가입하고 싶은 환매 협의 목록:
    repos="1password bitwarden dashlane lastpass opvault passwordbox roboform stickypassword truekey zoho-vault"
    
    처음부터 다시 시작하십시오.
    rm -rf joined
    git init joined
    cd joined
    
    지금 여기서 까다로운 문제가 하나 있다.
    for repo in $repos; do
        git remote add $repo $REPO_DIR/repo
        git fetch $repo
        git checkout -b $repo $repo/master
        echo -n "$repo: " > prefix
        git filter-branch \
            -f \
            --tree-filter "mkdir -p .original/$repo && rsync -a --remove-source-files ./ .original/$repo/" \
            --msg-filter "cat $(pwd)/prefix -" \
            --env-filter 'GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"' \
            $repo
        rm prefix
    done
    
    하나씩 하나씩 봅시다.우선, 나는 재구매를 자신의 지점에 도입할 것이다.lastpass라는 환매 협의는 최종적으로 lastpass라는 지점에서 중지되었다.지금까지 별다른 어려움은 없었다.
    git remote add $repo $REPO_DIR/repo
    git fetch $repo
    git checkout -b $repo $repo/master
    
    다음 단계에서, 제출할 때마다 파일을 하위 폴더로 이동할 수 있도록 모든 리포의 기록을 다시 쓸 것입니다.예를 들어 리포lastpass에서 온 모든 파일은 최종적으로 .original/lastpass/ 폴더에 있을 것이다.역사상의 모든 제출은 루트 디렉터리가 아닌 모든 개발이 이 폴더에서 이루어진 것처럼 변경됩니다.
    git filter-branch \
        -f \
        --tree-filter "mkdir -p .original/$repo && rsync -a --remove-source-files ./ .original/$repo/" \
        --msg-filter "cat $(pwd)/prefix -" \
        --env-filter 'GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"' \
        $repo
    
    filter-branch명령은 다기능 야수다.환매 협의가 제공하는 모든 가능한 전환을 통해 환매 협의는 식별할 수 없는 변화가 발생할 수 있다.FUBAR을 사용할 수도 있습니다.사실 이것은 매우 간단하다.Git가 refs/original/refs/heads 분기에 백업을 생성한 이유입니다.백업이 이미 존재하는 경우 -f 스위치를 사용하여 백업을 강제로 덮어씁니다.--tree-filter 스위치를 사용할 때 제출할 때마다 임시 디렉터리에 서명하고 일반적인 파일 조작을 사용하면 제출을 다시 쓸 수 있습니다.따라서 제출할 때마다 디렉터리 .original/$repo 를 만들고 rsync 를 사용하여 모든 파일을 이동합니다.--mag-filter 스위치는 내가 제출한 메시지를 다시 쓸 수 있도록 허락한다.lastpass 리포의 모든 제출이 lastpass: original commit message 처럼 보이도록 리포의 이름을 메시지에 추가하고 싶습니다.제출할 때마다 스크립트는 stdin에서 제출 메시지를 수신하고 stdout의 모든 내용은 새로운 제출 메시지가 됩니다.이 예에서 나는 cat 연결prefixstdin-를 사용한다.어떤 이유로 단순echo -n이 왜 작동하지 않는지 이해할 수 없기 때문에 메시지 접두사를 파일에 저장해야 합니다.
    마지막--env-filter은 제출 날짜를 원래 날짜(Git 용어의 작성자 날짜)로 재설정해야 합니다.만약 내가 이렇게 하지 않는다면, Git는 시간 스탬프를 현재 시간으로 변경할 것이다.그러고 싶지 않아요.
    다음 단계는 모든 제출을 master 지점에 복사해서 역사를 복원하는 것입니다.현재master 지점은 없습니다.우리 하나 하자.Git는 어떤 이유로 모든 파일을 색인에 추가하는 분기를 만들었습니다.git rm로 그들을 죽였다.
    git checkout --orphan master
    git rm -rf .
    
    제출을 복사하려면 먼저 그것들을 열거해야 합니다.이 작업은 log 명령을 통해 수행됩니다.
    git log --pretty='%H' --author-date-order --reverse $repos
    
    이 명령은 내가 이전에 만든 모든 지점에서 제출한 해시 값을 보여 주는 목록을 생성합니다. 가장 먼저부터 가장 최근까지의 정렬을 보여 줍니다.이 단계의 출력은 다음과 같습니다.
    7d62b1272b4aa37f07eb91bbf46a33609d80155f
    a8673683cb13a2040299dcb9c98a6f1fcb110dbd
    f3876d3a4900e7f6012efeb0cc06db241b0540d6
    7209ecf519475e59494504ca2a75e36ad9ea6ebe
    
    현재 나는 목록이 있는데, 나는 교체해서 cherry-pick 각각 master 에 제출한다.
    for i in $(git log --pretty='%H' --author-date-order --reverse $repos); do
        GIT_COMMITTER_DATE=$(git log -1 --pretty='%at' $i) \
            git cherry-pick $i
    done
    
    GIT_COMMITTER_DATE 환경 변수는 제출 날짜를 원래 생성 시간으로 재설정하는 데 다시 사용되며, 다음 그림과 같이 log 명령을 사용하여 다시 가져옵니다.
    git log -1 --pretty='%at' <COMMIT-HASH>
    
    이 절차 후에 저는 역사가 평탄한 환매 협의가 생겼는데 그 중에서 모든 원시 환매 협의는 .original/ 아래 자신의 하위 목록에 있습니다.나는 GitHub 파일의 기록과 탓을 사용하여 원본 파일이 태어난 이래 발생한 모든 변경 사항을 볼 수 있다.Git 곡의 이름이 바뀌었기 때문에, 나는 이 서류들을 megarepo 내의 새 집으로 옮길 수 있고, 나는 여전히 역사와 책임을 발휘할 수 있다.
    남은 유일한 일은 환매 협의를 정리하고 내가 더 이상 필요로 하지 않는 모든 지점을 삭제하며 쓰레기 수집기를 운행하여 쓰레기를 제거하는 것이다.
    for repo in $repos; do
        git branch -D $repo
        git remote remove $repo
        git update-ref -d refs/original/refs/heads/$repo
    done
    
    git gc --aggressive
    
    이로 인해 발생하는 환매 기한here.나는 내가 투입한 시간이 가치가 있다고 생각한다.이것은 나로 하여금 Git 저급 명령에 대한 더 많은 정보를 얻을 수 있게 했다.현재 나는 환매 협의가 있어서, 나는 쉽게 훑어볼 수 있으며, 내가 일부 문서의 역사를 보고 싶을 때마다 지점 사이를 돌아다닐 필요가 없다.
    내가 사용한 스크립트를 찾을 수 있다here.

    좋은 웹페이지 즐겨찾기