development

잘못된 병합을 수정하고 올바른 병합을 고정 병합으로 재생하는 방법은 무엇입니까?

big-blog 2020. 2. 22. 11:41
반응형

잘못된 병합을 수정하고 올바른 병합을 고정 병합으로 재생하는 방법은 무엇입니까?


나는 실수로 원하지 않는 파일 ( 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명령 에 제공해야합니다. 이름.)


소개 : 5 가지 솔루션 제공

원래 포스터는 다음과 같이 말합니다.

실수로 원하지 않는 파일을 여러 커밋 전에 내 저장소에 커밋했습니다 ... 리포지토리 기록에서 파일을 완전히 삭제하고 싶습니다.

filename.orig처음에는 저장소에 추가되지 않은 변경 히스토리를 다시 작성할 수 있습니까?

git에서 파일 히스토리를 완전히 제거하는 방법에는 여러 가지가 있습니다.

  1. 커밋을 수정합니다.
  2. 하드 리셋 (아마도 리베이스).
  3. 비 대화식 리베이스.
  4. 대화식 리베이스.
  5. 가지 필터링.

원래 포스터의 경우 커밋을 수정하는 것은 실제로 몇 가지 추가 커밋을했기 때문에 실제로는 선택 사항이 아니지만 완전성을 위해 그것을 원하는 방법을 설명합니다. 이전 커밋을 수정합니다.

이러한 모든 솔루션에는 히스토리 / 커밋을 변경 / 다시 쓰기 와 관련이 있으므로 커밋 사본이 오래된 사람은 히스토리를 새 히스토리와 다시 동기화하기 위해 추가 작업을 수행해야합니다.


해결 방법 1 : 커밋 수정

이전 커밋에서 실수로 파일을 추가하는 등의 변경을하고 해당 변경 내역이 더 이상 존재하지 않게하려면 이전 커밋을 수정하여 파일에서 파일을 제거하면됩니다.

git rm <file>
git commit --amend --no-edit

해결 방법 2 : 하드 리셋 (아마도 리베이스 추가)

솔루션 # 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}

해결 방법 3 : 비 대화식 리베이스

히스토리에서 커밋을 완전히 제거하려는 경우 작동합니다.

# 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}

솔루션 4 : 대화식 리베이스

이 솔루션을 사용하면 솔루션 # 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}

솔루션 5 : 브랜치 필터링

마지막으로,이 솔루션은 기록에서 파일 존재의 모든 흔적을 완전히 지우고 싶을 때 가장 효과적이며 다른 솔루션 중 어느 것도 작업에 달려 있지 않습니다.

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

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.

추가 자료

  1. Pro Git § 6.4 Git 도구 – 기록 기록 .
  2. git-filter-branch (1) 매뉴얼 페이지 .
  3. git-commit (1) 매뉴얼 페이지 .
  4. git-reset (1) 매뉴얼 페이지 .
  5. git-rebase (1) 매뉴얼 페이지 .
  6. BFG Repo Cleaner ( 제작자 자신의 답변 참조 ).

이후에 커밋하지 않은 경우 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변경 될 때마다 HEADfilename.orig를 삭제하고 변경 사항을 다시 작성하십시오. 사용하면 --ignore-unmatch어떤 이유로 인해 filename.orig가 변경 사항에서 누락 된 경우 명령이 실패하지 않습니다. git-filter-branch 매뉴얼 페이지 의 예제 섹션에서 권장되는 방법입니다 .

Windows 사용자를위한 참고 사항 : 파일 경로 슬래시를 사용해야합니다.


이것이 가장 좋은 방법입니다 :
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_gitand ../tmpdir디렉토리를 삭제할 수 있습니다 (몇 주 또는 몇 달 후에는 경우에 따라)


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

내가 찾은 가장 간단한 방법 leontalbotAnoopjohn게시게시물 인 ( 의견으로) 제안되었습니다 . 나는 그 자체의 공간 가치가 있다고 생각합니다.

(나는 그것을 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 filter-branch갈 길입니다.

안타깝게도 filename.orig태그, 참조 로그 항목, 리모컨 등으로 여전히 참조 할 수 있기 때문에 리포지토리에서 완전히 제거해도 충분하지 않습니다 .

이러한 참조도 모두 제거한 다음 가비지 수집기를 호출하는 것이 좋습니다. 웹 사이트 git forget-blob스크립트를 사용 하여이 모든 작업을 한 번에 수행 할 수 있습니다.

git forget-blob filename.orig


정리하려는 최신 커밋 인 경우 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 filter-branch설계된 것입니다.


다음을 사용할 수도 있습니다.

git reset HEAD file/path

참고 URL : https://stackoverflow.com/questions/307828/how-do-you-fix-a-bad-merge-and-replay-your-good-commits-onto-a-fixed-merge



반응형