나는 실수로 원하지 않는 파일 ( filename.orig
병합을 해결하는 동안)을 커밋하기 전에 저장소에 저장했지만 지금까지는 알지 못했습니다. 리포지토리 기록에서 파일을 완전히 삭제하고 싶습니다.
filename.orig
처음에는 저장소에 추가되지 않은 변경 히스토리를 다시 작성할 수 있습니까?
나는 실수로 원하지 않는 파일 ( filename.orig
병합을 해결하는 동안)을 커밋하기 전에 저장소에 저장했지만 지금까지는 알지 못했습니다. 리포지토리 기록에서 파일을 완전히 삭제하고 싶습니다.
filename.orig
처음에는 저장소에 추가되지 않은 변경 히스토리를 다시 작성할 수 있습니까?
답변:
귀하의 상황이 질문에 설명 된 것이 아닌 경우이 레시피를 사용하지 마십시오. 이 레시피는 잘못된 병합을 수정하고 올바른 커밋을 고정 병합에서 재생하기위한 것입니다.
filter-branch
당신이 원하는 것을 할 것이지만 , 그것은 매우 복잡한 명령이며 아마도 이것을 사용하여 선택할 것입니다 git rebase
. 아마 개인적인 취향 일 것입니다. 솔루션은 한 번에 한 단계 씩 동등한 논리 연산을 수행하는 filter-branch
반면 약간 더 복잡한 단일 명령 rebase
으로 수행 할 수 있습니다.
다음 레시피를 시도하십시오.
# create and check out a temporary branch at the location of the bad merge
git checkout -b tmpfix <sha1-of-merge>
# remove the incorrectly added file
git rm somefile.orig
# commit the amended merge
git commit --amend
# go back to the master branch
git checkout master
# replant the master branch onto the corrected merge
git rebase tmpfix
# delete the temporary branch
git branch -d tmpfix
(실제로 임시 분기가 필요하지는 않지만 '분리 된 HEAD'로이를 수행 할 수 있지만 임시 분기를 사용하는 대신 git commit --amend
단계에 의해 생성 된 커밋 ID 를 기록하여 git rebase
명령 에 제공해야합니다. 이름.)
git rebase -i
빠르고 여전히 쉽게 등 수? $ git rebase -i <sh1-of-merge> 올바른 것을 "edit"로 표시 $ git rm somefile.orig $ git commit --a $ $ git rebase --continue 그러나 어떤 이유로 나는 여전히 마지막 파일을 가지고 있습니다 내가 한 시간. 아마도 뭔가 빠졌을 것입니다.
git rebase -i
특히 여러 개의 rebase-y 작업을 수행 할 때 매우 유용하지만 실제로 누군가의 어깨를 가리 키지 않을 때 정확하게 설명하기가 어려우며 편집자가 어떤 일을하고 있는지 알 수 있습니다. vim을 사용하지만 "ggjcesquash <Esc> jddjp : wq"및 "현재 두 번째 줄 다음으로 맨 위 줄을 이동하고 네 번째 줄의 첫 번째 단어를 'edit'로 변경하여 저장하고 종료 "는 실제 단계보다 더 복잡해 보입니다. 일반적으로 일부 작업 --amend
과 --continue
작업 으로 끝납니다 .
원래 포스터는 다음과 같이 말합니다.
실수로 원하지 않는 파일을 여러 커밋 전에 내 저장소에 커밋했습니다 ... 리포지토리 기록에서 파일을 완전히 삭제하고 싶습니다.
filename.orig
처음에는 저장소에 추가되지 않은 변경 히스토리를 다시 작성할 수 있습니까?
git에서 파일 히스토리를 완전히 제거하는 방법에는 여러 가지가 있습니다.
원래 포스터의 경우 커밋을 수정하는 것은 실제로 몇 가지 추가 커밋을했기 때문에 실제로는 선택 사항이 아니지만 완전성을 위해 그것을 원하는 방법을 설명합니다. 이전 커밋을 수정합니다.
이러한 모든 솔루션에는 히스토리 / 커밋을 변경 / 다시 쓰기 가 포함되어 있으므로 커밋 사본이 오래된 사람은 히스토리를 새 히스토리와 다시 동기화하기 위해 추가 작업을 수행해야합니다.
이전 커밋에서 실수로 파일을 추가하는 등의 변경을 한 경우 해당 변경 내역이 더 이상 존재하지 않게하려면 이전 커밋을 수정하여 파일에서 파일을 제거하면됩니다.
git rm <file>
git commit --amend --no-edit
솔루션 # 1과 마찬가지로 이전 커밋을 제거하려면 부모로 간단하게 재설정하는 옵션도 있습니다.
git reset --hard HEAD^
이 명령은 분기를 이전의 첫 번째 부모 커밋으로 강제 재설정합니다 .
그러나 원본 포스터와 마찬가지로 변경을 취소하려는 커밋 후에 여러 커밋을 한 경우에도 하드 리셋을 사용하여 수정할 수 있지만 리베이스를 사용하는 것도 포함됩니다. 히스토리에서 커밋을 다시 수정하는 데 사용할 수있는 단계는 다음과 같습니다.
# Create a new branch at the commit you want to amend
git checkout -b temp <commit>
# Amend the commit
git rm <file>
git commit --amend --no-edit
# Rebase your previous branch onto this new commit, starting from the old-commit
git rebase --preserve-merges --onto temp <old-commit> master
# Verify your changes
git diff master@{1}
히스토리에서 커밋을 완전히 제거하려는 경우 작동합니다.
# Create a new branch at the parent-commit of the commit that you want to remove
git branch temp <parent-commit>
# Rebase onto the parent-commit, starting from the commit-to-remove
git rebase --preserve-merges --onto temp <commit-to-remove> master
# Or use `-p` insteda of the longer `--preserve-merges`
git rebase -p --onto temp <commit-to-remove> master
# Verify your changes
git diff master@{1}
이 솔루션을 사용하면 솔루션 # 2 및 # 3과 동일한 작업을 수행 할 수 있습니다. 즉, 즉시 이전 커밋보다 기록에서 다시 커밋을 수정하거나 제거 할 수 있습니다. 인터랙티브 리베이스는 성능상의 이유로 수백 개의 커밋을 리베이스하는 데 적합하지 않으므로 이러한 상황에서 비 인터랙티브 리베이스 또는 필터 분기 솔루션 (아래 참조)을 사용합니다.
대화식 리베이스를 시작하려면 다음을 사용하십시오.
git rebase --interactive <commit-to-amend-or-remove>~
# Or `-i` instead of the longer `--interactive`
git rebase -i <commit-to-amend-or-remove>~
이것은 git이 커밋 히스토리를 수정하거나 제거하려는 커밋의 부모로 되감습니다. 그런 다음 편집기 git이 사용하도록 설정된 모든 순서대로 되감기 커밋 목록을 역순으로 표시합니다 (기본적으로 Vim입니다).
pick 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
pick 7668f34 Modify Bash config to use Homebrew recommended PATH
pick 475593a Add global .gitignore file for OS X
pick 1b7f496 Add alias for Dr Java to Bash config (OS X)
수정 또는 제거하려는 커밋이이 목록의 맨 위에 있습니다. 제거하려면 목록에서 해당 행을 삭제하십시오. 그렇지 않으면 다음 과 같이 첫 번째 줄에서 "pick"을 "edit"로 바꾸십시오 .
edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
그런 다음을 입력하십시오 git rebase --continue
. 커밋을 완전히 제거하기로 선택한 경우 확인 이외의 모든 작업을 수행해야합니다 (확인 이외의 경우이 솔루션의 마지막 단계 참조). 반면에 커밋을 수정하려면 git이 커밋을 다시 적용한 다음 rebase를 일시 중지합니다.
Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
이 시점에서 파일을 제거하고 커밋을 수정 한 다음 리베이스를 계속할 수 있습니다.
git rm <file>
git commit --amend --no-edit
git rebase --continue
그게 다야. 마지막 단계로 커밋을 수정했는지 아니면 완전히 제거했는지에 관계없이 브랜치가 rebase 이전의 상태와 다른 것으로 인해 분기에 다른 예기치 않은 변경 사항이 없는지 확인하는 것이 좋습니다.
git diff master@{1}
마지막으로,이 솔루션은 기록에서 파일 존재의 모든 흔적을 완전히 지우고 싶을 때 가장 효과적이며 다른 솔루션 중 어느 것도 작업에 달려 있지 않습니다.
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>'
<file>
루트 커밋부터 시작하여 모든 커밋에서 제거 됩니다. 대신 커밋 범위를 다시 작성하려면 이 답변 에서 지적한대로
HEAD~5..HEAD
추가 인수로 전달할 수 있습니다filter-branch
.
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>' HEAD~5..HEAD
다시 말하지만, filter-branch
필터링 작업을 수행하기 전에 분기를 이전 상태로 비교하여 다른 예기치 않은 변경 사항이 없는지 확인하는 것이 좋습니다.
git diff master@{1}
BFG Repo Cleaner 도구가보다 빠르게 실행 된다는 말을 들었 git filter-branch
으므로 옵션으로 확인하고 싶을 수도 있습니다. 필터 브랜치 문서 에서 공식적 으로 실행 가능한 대안으로 언급되었습니다 .
git-filter-branch를 사용하면 Git 히스토리를 복잡한 쉘 스크립트로 다시 작성할 수 있지만 큰 파일이나 암호와 같은 원치 않는 데이터를 단순히 제거하는 경우 이러한 유연성이 필요하지 않습니다 . 이러한 작업의 경우 git-filter-branch에 대한 JVM 기반 대안 인 BFG Repo-Cleaner 를 고려할 수 있습니다 . 일반적으로 이러한 사용 사례의 경우 10-50 배 이상 빠르며 특성이 매우 다릅니다.
파일의 특정 버전은 정확히 한 번만 정리됩니다 . BFG는 git-filter-branch와 달리 파일이 기록 내에서 언제 어디서 커밋되었는지에 따라 다르게 처리 할 수있는 기회를 제공하지 않습니다. 이 제약 조건은 BFG의 핵심 성능 이점을 제공하며 불량 데이터를 정리하는 작업에 적합합니다. 불량 데이터의 위치를 신경 쓰지 않고 원하는 데이터 만 보관할 수 있습니다.
기본적으로 BFG는 멀티 코어 시스템을 최대한 활용하여 커밋 파일 트리를 병렬로 정리합니다. 자식 필터 - 지점의 세척 과정의 커밋은 순차적으로 (즉, 단일 스레드 방식으로), 그것은 비록 입니다 각각에 대해 실행 스크립트가 커밋 가능한, 자신의 parallellism을 포함하는 필터를 작성합니다.
명령 옵션은 단지 예를 들어, 데이터 - 불필요한을 제거하는 작업에 더 많은 자식 필터 지점보다 더 제한하고, 최선을 다하고 있습니다
--strip-blobs-bigger-than 1M
.
filter-branch
해시를 다시 계산 합니까 ? 팀이 큰 파일을 필터링해야하는 리포지토리와 함께 작업하는 경우 모든 사람이 동일한 리포지토리 상태를 갖도록하려면 어떻게해야합니까?
이후에 커밋하지 않은 경우 git rm
파일과 git commit --amend
.
당신이 가지고 있다면
git filter-branch \
--index-filter 'git rm --cached --ignore-unmatch path/to/file/filename.orig' merge-point..HEAD
에서 merge-point
로 변경 될 때마다 HEAD
filename.orig를 삭제하고 변경 사항을 다시 작성하십시오. 사용하면 --ignore-unmatch
어떤 이유로 인해 filename.orig가 변경 사항에서 누락 된 경우 명령이 실패하지 않습니다. git-filter-branch 매뉴얼 페이지 의 예제 섹션에서 권장되는 방법입니다 .
Windows 사용자를위한 참고 사항 : 파일 경로 는 슬래시를 사용해야합니다.
git-filter-branch
는 첫 번째 문서 를 제공하는 것 같습니다.
"
하지 말아야합니다. 그렇지 않으면 '
도움이되지 않는 "잘못된 개정"오류가 표시됩니다.
이것이 가장 좋은 방법입니다 :
http://github.com/guides/completely-remove-a-file-from-all-revisions
파일 사본을 먼저 백업하십시오.
편집하다
검토 중에 Neon 의 수정 사항이 거부되었습니다.
아래의 네온 게시물을 참조하십시오. 유용한 정보가 포함되어있을 수 있습니다!
예를 들어 *.gz
실수로 자식 저장소에 커밋 된 모든 파일 을 제거하려면 :
$ du -sh .git ==> e.g. 100M
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch *.gz' HEAD
$ git push origin master --force
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now
$ git gc --aggressive --prune=now
그래도 여전히 효과가 없었습니까? (현재 git 버전 1.7.6.1에 있습니다)
$ du -sh .git ==> e.g. 100M
마스터 지점이 하나 밖에 없기 때문에 왜 그런지 확실하지 않습니다. 어쨌든, 마침내 새로운 빈 저장소와 빈 git 저장소로 밀어 넣어서 git repo를 실제로 정리했습니다.
$ git init --bare /path/to/newcleanrepo.git
$ git push /path/to/newcleanrepo.git master
$ du -sh /path/to/newcleanrepo.git ==> e.g. 5M
(예!)
그런 다음 해당 디렉토리를 새 디렉토리에 복제하고 .git 폴더로 이동합니다. 예 :
$ mv .git ../large_dot_git
$ git clone /path/to/newcleanrepo.git ../tmpdir
$ mv ../tmpdir/.git .
$ du -sh .git ==> e.g. 5M
(예! 드디어 청소!)
모든 것이 정상인지 확인한 후 ../large_dot_git
and ../tmpdir
디렉토리를 삭제할 수 있습니다 (몇 주 또는 몇 달 후에는 경우에 따라)
--prune-empty
filter-branch 명령에 추가 하는 것이 좋습니다 .
Git 히스토리를 다시 작성하려면 영향을받는 커밋 ID를 모두 변경해야하므로 프로젝트를 수행하는 모든 사람은 이전 리포지토리 사본을 삭제하고 히스토리를 정리 한 후 새로운 복제본을 작성해야합니다. 당신의 불필요한 파일이 실제로 문제를 일으키는 것이 아니라, 경우에만 - 당신이 좋은 이유를 필요로하는 더는 불편을 더 많은 사람들이 그것을 수행하는 당신 프로젝트에서 작업하는 당신이 원한다면, 당신은뿐만 아니라 망할 놈의 기록을 정리 할 수 에!
가능한 한 쉽게 만들려면 Git 히스토리에서 파일을 제거하기 위해 특별히 설계된 더 빠르고 간단한 대안 인 BFG Repo-Cleaner를 사용하는 것이 좋습니다 git-filter-branch
. 여기에서 인생을 더 편하게 만드는 한 가지 방법은 실제로 모든 심판, 기본적으로 모든 태그, 지점 등을 처리하지만 10-50 배입니다. 빠릅니다.
: 당신은 신중하게 여기 단계를 수행해야 http://rtyley.github.com/bfg-repo-cleaner/#usage -하지만, 코어 비트는 바로 이것이다 : 다운로드 BFG 항아리 (자바 6 이상 필요)이 명령을 실행 :
$ java -jar bfg.jar --delete-files filename.orig my-repo.git
전체 리포지토리 기록이 스캔되고 filename.orig
( 최근 커밋에없는 ) 파일이 제거됩니다. git-filter-branch
동일한 작업을 수행하는 것보다 훨씬 쉽습니다 !
전체 공개 : 저는 BFG Repo-Cleaner의 저자입니다.
You should probably clone your repository first.
Remove your file from all branches history:
git filter-branch --tree-filter 'rm -f filename.orig' -- --all
Remove your file just from the current branch:
git filter-branch --tree-filter 'rm -f filename.orig' -- --HEAD
Lastly you should run to remove empty commits:
git filter-branch -f --prune-empty -- --all
Charles Bailey의 솔루션에 추가하기 위해 git rebase -i를 사용하여 이전 커밋에서 원하지 않는 파일을 제거하고 매력처럼 작동했습니다. 단계:
# Pick your commit with 'e'
$ git rebase -i
# Perform as many removes as necessary
$ git rm project/code/file.txt
# amend the commit
$ git commit --amend
# continue with rebase
$ git rebase --continue
내가 찾은 가장 간단한 방법 leontalbot
은 Anoopjohn 이 게시 한 게시물 인 ( 의견으로) 제안되었습니다 . 나는 그 자체의 공간 가치가 있다고 생각합니다.
(나는 그것을 bash 스크립트로 변환했다)
#!/bin/bash
if [[ $1 == "" ]]; then
echo "Usage: $0 FILE_OR_DIR [remote]";
echo "FILE_OR_DIR: the file or directory you want to remove from history"
echo "if 'remote' argument is set, it will also push to remote repository."
exit;
fi
FOLDERNAME_OR_FILENAME=$1;
#The important part starts here: ------------------------
git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch $FOLDERNAME_OR_FILENAME" -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
if [[ $2 == "remote" ]]; then
git push --all --force
fi
echo "Done."
모든 크레딧으로 이동 Annopjohn
하고에 leontalbot
그것을 지적.
노트
스크립트에는 유효성 검사가 포함되어 있지 않으므로 실수하지 말고 문제가 발생할 경우 백업을해야합니다. 그것은 나를 위해 일했지만 당신의 상황에서 작동하지 않을 수 있습니다. 주의해서 사용하십시오 (무슨 일인지 알고 싶다면 링크를 따라 가십시오).
정리하려는 최신 커밋 인 경우 git 버전 2.14.3 (Apple Git-98)으로 시도했습니다.
touch empty
git init
git add empty
git commit -m init
# 92K .git
du -hs .git
dd if=/dev/random of=./random bs=1m count=5
git add random
git commit -m mistake
# 5.1M .git
du -hs .git
git reset --hard HEAD^
git reflog expire --expire=now --all
git gc --prune=now
# 92K .git
du -hs .git
git reflog expire --expire=now --all; git gc --prune=now
매우 나쁜 일입니다. 디스크 공간이 부족하지 않은 경우 git 쓰레기가 몇 주 후에 이러한 커밋을 수집하도록하십시오
이것은 git filter-branch
설계된 것입니다.
다음을 사용할 수도 있습니다.
git reset HEAD file/path