자식 저장소에서 이전 기록을 제거하려면 어떻게합니까?


208

이 특정 시나리오와 비슷한 것을 찾지 못했습니다.

500 개 이상의 지점, 500 개 이상의 태그, 2007 년 중반으로 거슬러 올라가는 역사가 많은 자식 저장소가 있습니다. ~ 19,500 개의 커밋이 포함되어 있습니다. 2010 년 1 월 1 일 이전에 모든 기록을 제거하여 더 작고 다루기 쉽도록 만들었습니다 (기록의 전체 사본을 보관 저장소에 보관함).

나는 새로운 저장소의 뿌리가되고 싶다는 결심을 알고 있습니다. 그러나 해당 커밋으로 시작하기 위해 저장소를 자르는 올바른 git mojo를 알아낼 수는 없습니다. 나는 몇 가지 변형을 추측하고있다.

git filter-branch

이식편과 관련된 것이 필요하다; 또한 우리가 별도로 유지하려는 200여 가지의 각각의 치료 후 (내가 뭔가 다시 함께 REPO을 패치 할 필요가 있습니다 않습니다 어떻게 해야할지).

누구든지 이런 일을 한 적이 있습니까? 중요한 경우 자식 1.7.2.3이 있습니다.

답변:


118

새로운 루트 커밋의 부모에 대한 이식편 을 부모가없는 곳 (또는 저장소의 실제 루트 커밋과 같은 빈 커밋)으로 만드 십시오. 예 :echo "<NEW-ROOT-SHA1>" > .git/info/grafts

이식편을 만든 후에는 즉시 적용됩니다. git log원하지 않는 오래된 커밋이 사라진 것을 보고 볼 수 있어야합니다 .

$ echo 4a46bc886318679d8b15e05aea40b83ff6c3bd47 > .git/info/grafts
$ git log --decorate | tail --lines=11
commit cb3da2d4d8c3378919844b29e815bfd5fdc0210c
Author: Your Name <your.email@example.com>
Date:   Fri May 24 14:04:10 2013 +0200

    Another message

commit 4a46bc886318679d8b15e05aea40b83ff6c3bd47 (grafted)
Author: Your Name <your.email@example.com>
Date:   Thu May 23 22:27:48 2013 +0200

    Some message

모든 것이 의도 한 것처럼 보이면 간단하게 간단 git filter-branch -- --all하게 만들 수 있습니다.

주의 : filter-branch 단계를 수행 한 후에는 모든 커밋 ID가 변경되므로 이전 저장소를 사용하는 사람은 새 저장소를 사용하는 사람과 병합해서는 안됩니다.


6
git filter-branch --tag-name-filter cat -- --all태그를 업데이트 해야 했습니다. 그러나 삭제하려는 오래된 기록을 가리키는 오래된 태그도 있습니다. 기존 태그를 모두 제거하려면 어떻게해야합니까? 삭제하지 않으면 이전 기록이 사라지지 않고로 계속 볼 수 있습니다 gitk --all.
Craig McQueen

9
"어떤 부모에게도 새로운 루트 커밋의 부모에 대한 이식을 만들려면"약간의 설명이 필요합니다. 나는 그것을 시도하고 "부모 없음"에 대한 구문을 이해하지 못했습니다. 매뉴얼 페이지는 부모 커밋 ID가 필요하다고 주장합니다. 모든 0을 사용하면 오류가 발생합니다.
Marius Gedminas

6
다른 사람이 정확히 어떻게 작동하는지 궁금해하는 경우에는 매우 쉽습니다.echo "<NEW-ROOT-HASH>" > .git/info/grafts
friederbluemle

3
나는 이식편이 유용한 것 이상을 설명 할 것에 동의한다
Charles Martin

4
이식편의 링크 된 wiki 페이지에서 인용했습니다. "Git 1.6.5부터는 더 유연한 git replace가 추가되어 어떤 객체를 다른 객체로 교체 할 수 있고 repos 사이에서 밀고 당길 수있는 refs를 통해 연결을 추적 할 수 있습니다." 따라서이 답변 최신 버전의 git에서 오래 되었을 수 있습니다.
ThorSummoner

130

답글을 게시하기에는 너무 늦었을 수 있지만이 페이지가 Google의 첫 번째 결과이므로 여전히 도움이 될 수 있습니다.

git repo에서 공간을 확보하고 싶지만 모든 커밋 (rebase 또는 graft)을 다시 작성하지 않고 전체 저장소가있는 사람들의 푸시 / 풀 / 병합을 계속하려면 git을 사용할 수 있습니다 복제 얕은 복제본 ( --depth 매개 변수).

; Clone the original repo into limitedRepo
git clone file:///path_to/originalRepo limitedRepo --depth=10

; Remove the original repo, to free up some space
rm -rf originalRepo
cd limitedRepo
git remote rm origin

다음 단계에 따라 기존 리포지토리를 좁힐 수 있습니다.

; Shallow to last 5 commits
git rev-parse HEAD~5 > .git/shallow

; Manually remove all other branches, tags and remotes that refers to old commits

; Prune unreachable objects
git fsck --unreachable ; Will show you the list of what will be deleted
git gc --prune=now     ; Will actually delete your data

모든 자식 로컬 태그를 제거하는 방법은 무엇입니까?

추신 : 이전 버전의 git은 clone / push / pull을 얕은 저장소에서 지원하지 않았습니다.


9
+1이다 망할 놈의 새 버전 정답. (아, PPCG돌아와 주세요!)
wizzwizz4

6
cd방금 삭제 한 폴더로 어떻게 이동할 수 있습니까? 여기에 누락 된 정보가있는 것 같습니다. 또한 이러한 변경 사항을 원격 저장소에 적용하는 방법이 있습니까?
Trogdor 2016 년

4
@Jez 다른 최고 투표 답변입니다. 당신은 역사를 영구적으로 제거하려는 경우이 대답은 당신을위한 것이 아닙니다. 거대한 역사 를 다루는 데 사용 됩니다.
아무도

4
내 자신의 질문에 대답하려면 : git clone file:///Users/me/Projects/myProject myClonedProject --shallow-since=2016-09-02매력처럼 작동합니다!
Micros

5
@Jez 당신은 얕은 repo를 정상으로 변환 할 수있다 git filter-branch -- --all. 이것은 모든 해시를 변경하지만 그 후에는 새로운 저장소로 푸시 할 수 있습니다
Ed'ka

61

방법 은 이해하기 쉽고 잘 작동합니다. 스크립트 ( $1)에 대한 인수 는 히스토리를 유지하려는 커밋에 대한 참조 (태그, 해시 등)입니다.

#!/bin/bash
git checkout --orphan temp $1 # create a new branch without parent history
git commit -m "Truncated history" # create a first commit on this branch
git rebase --onto temp $1 master # now rebase the part of master branch that we want to keep onto this branch
git branch -D temp # delete the temp branch

# The following 2 commands are optional - they keep your git repo in good shape.
git prune --progress # delete all the objects w/o references
git gc --aggressive # aggressively collect garbage; may take a lot of time on large repos

참고 된 태그가 여전히 유지됩니다; 수동으로 제거해야 할 수도 있습니다.

비고 : 나는이 거의 @yoyodin 같은 aswer 알고 있지만, 여기에 몇 가지 중요한 추가 명령과 정보가 있습니다. 답변을 편집하려고했지만 @yoyodin의 답변이 크게 변경되었으므로 편집이 거부되었으므로 여기에 정보가 있습니다!


git pruneand git gc명령에 대한 설명에 감사드립니다 . 스크립트의 나머지 명령에 대한 설명이 있습니까? 그대로, 어떤 인수가 전달되는지와 각 명령이 수행하는 작업은 명확하지 않습니다. 감사.
user5359531

2
@ user5359531 의견을 보내 주셔서 감사합니다. 각 명령에 대해 의견을 더 추가했습니다. 도움이 되었기를 바랍니다.
Chris Maes

4
모든 곳에서 충돌을 병합 ...별로 유용하지는 않음
Warpzit

3
@Warpzit 다른 답변에서 제안한 것처럼 명령 에 추가 -p하여 병합 충돌을 제거했습니다. rebase
leonbloy

1
나는 이것을 정확하게 따랐고, 내가 얻은 모든 것은 이전과 동일한 역사로 정리하고 싶었던 커밋에서 시작하는 새로운 지점으로 이전과 동일한 역사였습니다. 기록이 제거되지 않았습니다.
DrStrangepork

51

이 방법을 시도하십시오 git history 자르는 방법 :

#!/bin/bash
git checkout --orphan temp $1
git commit -m "Truncated history"
git rebase --onto temp $1 master
git branch -D temp

다음은 $1SHA-1의 인 당신은 유지하고 스크립트가 사이의 모든 커밋이 포함되어 새로운 지점이 생성됩니다 원하는 커밋 $1하고 master모든 이전의 역사를 삭제합니다. 이 간단한 스크립트는라는 기존 분기가 없다고 가정합니다 temp. 또한이 스크립트는 이전 히스토리에 대한 자식 데이터를 지우지 않습니다. git gc --prune=all && git repack -a -f -F -d당신이 진정으로 모든 역사를 잃고 싶다는 것을 확인한 후에 실행하십시오 . rebase --preserve-merges해당 기능의 git 구현이 완벽하지 않다는 경고가 필요할 수도 있습니다 . 사용하는 경우 결과를 수동으로 검사하십시오.


22
나는 이것을 시도했지만 rebase단계 에서 병합 충돌이 발생했습니다 . 이상-이러한 상황에서 병합 충돌이 발생할 수있을 것으로 기대하지 않았습니다.
Craig McQueen

2
git commit --allow-empty -m "Truncate history"체크 아웃 한 커밋에 파일이없는 경우 사용하십시오 .
friederbluemle

2
이것을 원격 마스터로 다시 푸시하려면 어떻게합니까? 그렇게 할 때 나는 오래된 역사와 새로운 역사로 끝납니다.
rustyx

1
'임시'는 무엇입니까? 이것에 대한 논쟁으로 무엇을 전달해야합니까? 실제로 실행할 때 이러한 명령의 모양에 대한 예가 있습니까? 감사.
user5359531

1
나는 $ 1이 커밋 해시라고 생각합니다. 링크 된 기사에 자세한 내용이 있습니다.
Chris Nolet

34

기록을 다시 쓰는 대신, 이 기사에서와 같은 Pro Git 사용 git replace을 고려 하십시오 . 논의 된 예제는 부모 커밋을 대체하여 트리의 시작을 시뮬레이션하는 동시에 전체 히스토리를 보호를 위해 별도의 브랜치로 유지합니다.


네, 만약 당신이 분리 된 전체 히스토리 브랜치를 누비면 우리가 원하는 것을 할 수 있다고 생각합니다. (우리는 저장소를 축소하려고했습니다.)
ebneter

1
나는 그 응답이 오프 사이트라는 것에 낙담했다. 그러나 그것은 GitScm 사이트에 링크되어 있으며 링크 된 튜토리얼은 매우 잘 작성되어 있으며 OP의 질문에 직접적으로 보입니다.
ThorSummoner

@ThorSummoner 죄송합니다! 나는 조금 더 온전한 현장에서 답을 개발할 것입니다
Jeff Bowman

불행히도 이것은 기록을 다시 쓰는 대안이 아닙니다. 기사의 시작 부분에 아마도이 인상을 주었던 혼란스러운 문장이 있습니다. 이 답변에서 제거 할 수 있습니까? 이 기사에서 저자는 잘린 분기의 히스토리를 다시 작성하지만을 사용하여 기존 "히스토리"분기를 다시 첨부하는 방법을 제안합니다 git replace. 나는 이것이 당신이 대답을 게시 한 다른 질문에서 수정되었다고 생각합니다.
Mitch

1
의 논의 git replace비교가 git graft에서 만들어 stackoverflow.com/q/6800692/873282
koppor

25

업스트림 저장소를 전체 히스토리유지 하지만 로컬 소규모 체크 아웃 을 유지 하려면을 사용 하여 얕은 복제를 수행하십시오 .git clone --depth=1 [repo]

커밋을 푸시하면 할 수 있습니다

  1. git fetch --depth=1오래된 커밋을 정리합니다. 이로 인해 이전 커밋과 해당 개체에 도달 할 수 없습니다.
  2. git reflog expire --expire-unreachable=now --all. 이전 커밋 및 해당 객체를 모두 만료하려면
  3. git gc --aggressive --prune=all 오래된 물건을 제거하기 위해

커밋 후 로컬 자식 기록을 제거하는 방법을 참조하십시오 . .

이 "shallow"저장소를 "shallow update not allowed"라는 다른 위치로 푸시 할 수 없습니다. Git 원격 URL을 변경 한 후 원격 거부 (얕은 업데이트는 허용되지 않음)를 참조하십시오 . 당신이 그것을 원한다면, 접목을 고수해야합니다.


1
포인트 번호 1. 나를 위해 차이를 만들었습니다. 건배
clapas

21

내가하고있는 일을 이해하기 위해 몇 가지 답변과 다른 정보를 읽어야했습니다.

1. 특정 커밋보다 오래된 모든 것을 무시하십시오.

파일 .git/info/grafts은 커밋에 대한 가짜 부모를 정의 할 수 있습니다. 커밋 ID 만있는 줄은 커밋에 부모가 없다고 말합니다. 지난 2000 개의 커밋에만 관심이 있다고 말하고 싶다면 다음과 같이 입력하면됩니다.

git rev-parse HEAD~2000 > .git/info/grafts

git rev-parse는 현재 커밋의 2000 번째 부모의 커밋 ID를 제공합니다. 위의 명령은 이식편 파일이 있으면 덮어 씁니다. 먼저 있는지 확인하십시오.

2. 힘내 역사를 다시 쓰십시오 (선택 사항)

이식 된 가짜 부모를 실제 부모로 만들려면 다음을 실행하십시오.

git filter-branch -- --all

모든 커밋 ID가 변경됩니다. 이 저장소의 모든 사본은 강제로 업데이트해야합니다.

3. 디스크 공간 정리

사본이 업스트림과 호환되도록하려면 2 단계를 수행하지 않았습니다. 디스크 공간을 절약하고 싶었습니다. 이전 커밋을 모두 잊어 버리려면 :

git prune
git gc

대안 : 얕은 사본

다른 저장소의 얕은 사본이 있고 디스크 공간을 절약하려는 경우 업데이트 할 수 있습니다 .git/shallow. 그러나 이전부터 커밋을 가리키는 것이 아무것도 없도록주의하십시오. 따라서 다음과 같이 실행할 수 있습니다.

git fetch --prune
git rev-parse HEAD~2000 > .git/shallow
git prune
git gc

얕은 항목은 이식편처럼 작동합니다. 그러나 이식편과 얕음을 동시에 사용하지 않도록주의하십시오. 적어도 동일한 항목이 없으면 실패합니다.

오래된 커밋을 가리키는 오래된 참조 (태그, 분기, 원격 헤드)가 여전히 있으면 정리되지 않으며 더 많은 디스크 공간을 절약하지 않습니다.


<GIT_DIR> / info / grafts에 대한 지원은 더 이상 사용되지 않으며 향후 Git 버전에서 제거 될 예정입니다.
danny

git replace대신 사용 을 고려하십시오 . 참조 stackoverflow.com/questions/6800692/...
조엘 AZEMAR

3

경우 REBASE 또는 푸시헤드 / 마스터는 이 오류가 발생있다

remote: GitLab: You are not allowed to access some of the refs!
To git@giturl:main/xyz.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'git@giturl:main/xyz.git'

자식 대시 보드에서이 문제를 해결하려면 "보호 된 분기" 에서 마스터 분기를 제거해야합니다.

여기에 이미지 설명을 입력하십시오

그런 다음이 명령을 실행할 수 있습니다

git push -f origin master

또는

git rebase --onto temp $1 master

0

현재 답변이 너무 많으며 그 결과를 완전히 설명하지 못하는 답변이 너무 많습니다. 최신 git 2.26을 사용하여 기록을 정리하는 데 도움이 된 것은 다음과 같습니다.

먼저 더미 커밋을 만듭니다. 이 커밋은 잘린 리포지토리의 첫 번째 커밋으로 나타납니다. 이 커밋은 보관하는 기록에 대한 모든 기본 파일을 보유하기 때문에 이것이 필요합니다. SHA는 유지하려는 커밋이전 커밋 (이 예에서는 8365366). 문자열 'Initial'은 첫 번째 커밋의 커밋 메시지로 표시됩니다. Windows를 사용하는 경우 Git Bash 명령 프롬프트에서 아래 명령을 입력하십시오.

# 8365366 is id of parent commit after which you want to preserve history
echo 'Initial' | git commit-tree 8365366^{tree}

위의 명령은 SHA를 인쇄합니다 (예 :) d10f7503bc1ec9d367da15b540887730db862023.

이제 다음을 입력하십시오.

# d10f750 is commit ID from previous command
git rebase --onto d10f750 8365366

먼저 커밋 8365366된 모든 파일을 더미 커밋에 넣습니다 d10f750. 그런 다음 모든 커밋 재생합니다. 8365366d10f750 합니다. 마지막으로 master분기 포인터가 마지막 커밋 재생으로 업데이트됩니다.

이 잘린 레포를 밀고 싶다면 git push -f .

명심할 사항이 거의 없습니다 (이 방법뿐만 아니라 다른 방법에도 적용됨). 태그가 전송되지 않습니다. 커밋 ID와 타임 스탬프는 유지되지만 GitHub는 이러한 커밋을 일시불 제목으로 표시합니다.Commits on XY date .

다행히 잘린 기록을 "보관"으로 유지할 수 있으며 나중에 정리 된 저장소와 다시 정리 된 저장소를 결합 할 수 있습니다. 이렇게하려면 이 안내서를 참조하십시오 .


-3

아래 언급 된 jar [download it] 및 명령을 사용하여 디렉토리, 파일 및 디렉토리 또는 파일과 관련된 전체 히스토리를 삭제할 수 있습니다.

bfg.jar 파일 : https://rtyley.github.io/bfg-repo-cleaner/

git clone --bare repo-url cd repo_dir java -jar bfg.jar --delete-folders folder_name git reflog expire --expire = now --all && git gc --prune = now-공격적인 git push --mirror repo_url


-10
  1. 자식 데이터 제거, rm .git
  2. 자식 초기화
  3. 자식 리모컨 추가
  4. 강제 밀기

6
그것은 모든 역사를 제거하기 위해 작동하지만 그가 요구 한 것은 아닙니다 : 2010 년 1 월부터 역사를 유지하십시오
Chris Maes

1
이 질문에 대한 정답이 아닐 수도 있지만 제 시나리오에서 도움이 되었기 때문에 감사의 말을 전하고 싶었습니다.
apnerve
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.