스테이징 영역에서 커밋되지 않은 파일로 git reset --hard 실행 취소


110

작업을 복구하려고합니다. 나는 어리석은 짓 git reset --hard을했지만 그 전에는했지만 get add .하지 않았다 git commit. 도와주세요! 내 로그는 다음과 같습니다.

MacBookPro:api user$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)

#   modified:   .gitignore
...


MacBookPro:api user$ git reset --hard
HEAD is now at ff546fa added new strucuture for api

git reset --hard이 상황에서 실행 취소 할 수 있습니까?


@MarkLongair 굉장한 남자! 방금 내 일을 되찾았어요! 모든 출력 파일을 만들기 위해 Python 스크립트를 작성했습니다! 나는 대답으로 스크립트를 추가 할 것입니다
보이

4
'바보'가 아니라 '순진'... 나도 똑같이 했으니 까!
Rosdi Kasim 2014 년

여전히 수 바보 ;-)
던컨 맥그리거

이것의 일부를 되 돌리는 방법에 대한 훌륭한 기사 가 있습니다. 수작업이 필요합니다.
Jan Swart 2016 년

@MarkLongair```find .git / objects / -type f -printf '% TY- % Tm- % Td % TT % p \ n'| 정렬```는 나를 위해 일했습니다. 날짜도 나타나고 끝부터 Blob 검사를 시작합니다.
ZAky

답변:


175

git add .약간의 작업이긴하지만 색인에 추가 한 모든 파일을 복구 할 수 있어야합니다 (예 :를 사용하여 상황에서 ). 색인에 파일을 추가하기 위해 git은 파일을 객체 데이터베이스에 추가합니다. 즉, 가비지 수집이 아직 발생하지 않은 한 복구 할 수 있습니다. Jakub Narębski의 답변에 제공된 방법에 대한 예가 있습니다.

그러나 테스트 저장소에서 시도해 보았고 몇 가지 문제가있었습니다. --cached이어야하며 --cache실제로 .git/lost-found디렉터리를 생성하지 않았 음을 발견했습니다 . 그러나 다음 단계가 저에게 효과적이었습니다.

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)")

어떤 ref, 인덱스 또는 reflog를 통해 도달 할 수없는 개체 데이터베이스의 모든 개체를 출력해야합니다. 출력은 다음과 같습니다.

unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03

... 각 blob에 대해 다음을 수행 할 수 있습니다.

git show 907b308

파일의 내용을 출력합니다.


너무 많은 출력?

아래 sehe 의 의견 에 대한 응답으로 업데이트 :

해당 명령의 출력에 많은 커밋과 트리가 나열되어있는 경우 참조되지 않은 커밋에서 참조되는 모든 객체를 출력에서 ​​제거 할 수 있습니다. (일반적으로 리플 로그를 통해 이러한 커밋으로 돌아갈 수 있습니다. 인덱스에 추가되었지만 커밋을 통해 찾을 수없는 객체에만 관심이 있습니다.)

먼저 다음을 사용하여 명령의 출력을 저장합니다.

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > all

이제 도달 할 수없는 커밋의 개체 이름은 다음을 사용하여 찾을 수 있습니다.

egrep commit all | cut -d ' ' -f 3

따라서 다음을 사용하여 인덱스에 추가되었지만 커밋되지 않은 트리와 개체 만 찾을 수 있습니다.

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") \
  $(egrep commit all | cut -d ' ' -f 3)

이는 고려해야 할 대상의 수를 엄청나게 줄여줍니다.


업데이트 : 아래의 Philip Oakley 는 고려할 개체 수를 줄이는 또 다른 방법을 제안합니다. 이는 .NET에서 가장 최근에 수정 된 파일 만 고려하는 것 .git/objects입니다. 다음을 통해 찾을 수 있습니다.

find .git/objects/ -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort

( 여기서find 호출을 찾았 습니다 .) 목록의 끝은 다음과 같습니다.

2011-08-22 11:43:43.0234896770 .git/objects/b2/1700b09c0bc0fc848f67dd751a9e4ea5b4133b
2011-09-13 07:36:37.5868133260 .git/objects/de/629830603289ef159268f443da79968360913a

이 경우 다음과 같은 개체를 볼 수 있습니다.

git show b21700b09c0bc0fc848f67dd751a9e4ea5b4133b
git show de629830603289ef159268f443da79968360913a

( /객체 이름을 얻으려면 경로 끝에 있는를 제거해야합니다 .)


내 답변을 게시하기 전에 정확히 이러한 단계를 포함하는 것을 고려했습니다. 그러나 내 로컬 리포지토리 중 하나를 사용하여이 작업을 시도했을 때 수백 개의 도달 할 수없는 항목이 있었고 제출 된 날짜별로 정렬하는 적절한 접근 방식을 찾을 수 없었습니다. 이제이 좋지 않을까
sehe

3
마지막 커밋 이후의 가장 최근 객체에 대한 객체 타임 스탬프를 볼 수 없습니까? 아니면 내가 뭔가를 놓치고 있습니다.
Philip Oakley

1
@Philip Oakley : 제안 해 주셔서 감사합니다. 객체 데이터베이스에서 가장 최근에 수정 된 객체를 찾기위한 제안을 추가했습니다.
Mark Longair 2011 년

2
야, 대단해! 방금 @ $$를 절약했습니다. 그리고 나는 약간의 자식 과학을 배웠습니다. 더 나은 인덱스에 추가 ... 무엇을보고
bowsersenior

2
여기서 누구의 라임 라이트를 훔치지 마십시오. 하지만 그냥 똑같은 문제에 부딪 쳤고 모든 일을 잃어버린 줄 알았 기 때문에 메모처럼. IDE를 사용하는 경우 IDE 에는 일반적으로 원 클릭 복구 솔루션이 있습니다. PHPstorm의 경우 디렉토리를 마우스 오른쪽 버튼으로 클릭하고 로컬 기록 표시를 선택한 다음 마지막 유효한 상태로 되돌려 야했습니다.
Shalom Sam

58

나는 방금 git reset --hard한 커밋을 잃었습니다. 하지만 커밋 해시를 알고 있었으므로 git cherry-pick COMMIT_HASH복원 할 수있었습니다 .

커밋을 잃은 지 몇 분 안에이 작업을 수행 했으므로 일부 사용자에게는 효과가있을 수 있습니다.


5
감사합니다 감사합니다 감사
합니다이

21
아마 언급 할 가치가 있습니다 git reflog. 예를 들어 git reset --hard-> git reflog(HEAD @ {1} 해시를 보면) 그리고 마지막으로git cherry-pick COMMIT_HASH
Javier López

2
이 글을 읽는 사람들은 이것이 하나의 커밋 해시에서만 작동한다는 점에 유의하십시오. 하나 이상의 커밋이 있으면 한 번에 문제를 해결할 수 없습니다!
Ain Tohvri 2014-06-05

4
실제로 "앞으로"재설정 할 수 있습니다. 따라서 재설정이 여러 커밋을 전달한 경우 다른 'git reset --hard <commit>'을 수행하면 전체 커밋 체인이 해당 지점까지 복원됩니다. (그들은 아직 GC'ed하지 않는 주어진)
akimsko

6
과 손실 복구 커밋에 대한 한 줄 git reset --hard(당신은이 명령어 다음에 무엇을하지 않은 가정)입니다git reset --hard @{1}
Ajedi32

13

Mark Longair 덕분에 제 물건을 되찾았습니다!

먼저 모든 해시를 파일에 저장했습니다.

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > allhashes

다음으로 나는 그것들을 모두 목록에 넣고 ( '연결할 수없는 blob'을 제거하는) 데이터를 모두 새 파일에 넣습니다 ... 파일을 선택하고 필요한 이름을 다시 바꿔야합니다 ...하지만 몇 개만 필요했습니다. files .. 누군가에게 도움이되기를 바랍니다 ...

commits = ["c2520e04839c05505ef17f985a49ffd42809f",
    "41901be74651829d97f29934f190055ae4e93",
    "50f078c937f07b508a1a73d3566a822927a57",
    "51077d43a3ed6333c8a3616412c9b3b0fb6d4",
    "56e290dc0aaa20e64702357b340d397213cb",
    "5b731d988cfb24500842ec5df84d3e1950c87",
    "9c438e09cf759bf84e109a2f0c18520",
    ...
    ]

from subprocess import call
filename = "file"
i = 1
for c in commits:
    f = open(filename + str(i),"wb")
    call(["git", "show", c],stdout=f)
    i+=1

1
예! 이것이 바로 내가 필요한 것입니다. 모든 파일을 다시 만드는 Python 스크립트도 좋아합니다. 파일을 덤핑하는 :) 나를 위해 승리 그래서 다른 답변은 가비지 컬렉션 내 데이터의 손실에 대해 나를 긴장하게
에릭 올슨에게

1
이 답변을 기반 으로이 스크립트를 작성했습니다. 상자 밖으로 작동 : github.com/pendashteh/git-recover-index
Alexar

1
다음과 같이 자동화하는 것이 더 쉽습니다 mkdir lost; git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") | grep -Po '\s\S{40}$' | xargs -i echo "git show {} > lost/{}.blob" | sh. 파일은 다음 lost/*.blob
날짜

6

댓글의 @ Ajedi32의 솔루션은 정확히이 상황에서 저에게 효과적이었습니다.

git reset --hard @{1}

이 모든 솔루션은 git gc가 없다는 것에 의존하고 일부는 원인이 될 수 있으므로 아무것도 시도하기 전에 .git 디렉토리의 내용을 압축하여 스냅 샷이없는 경우 다시 돌아갈 수 있도록합니다. 당신을 위해 일하지 않습니다.


커밋되지 않은 파일이 많이 있습니다. 그런 다음 1 개의 파일을 커밋하고를 수행하여 git reset --hard알 수없는 방식으로 내 저장소를 엉망으로 만들었습니다 . @Duncan, @ {1}는 무엇을합니까? 그리고 어떤 주석을 언급하고 있습니까? 재설정 중 git reset입니까?
alpha_989

나는 @ {1} 마지막에 상대 참조이라고 생각하지만, 하나의 커밋,하지만 난 그냥 나를 위해 일한 것을보고, 전문가는 아니지만
던컨 맥그리거에게

3

동일한 문제가 발생했지만 색인에 변경 사항을 추가하지 않았습니다. 따라서 위의 모든 명령은 원하는 변경 사항을 다시 가져 오지 못했습니다.

위의 모든 정교한 답변 끝에 이것은 순진한 힌트이지만 내가 한 것처럼 먼저 생각하지 않은 사람을 구할 수 있습니다.

절망에 빠진 저는 열려있는 모든 탭에서 한 번씩 내 편집기 (LightTable)에서 CTRL-Z를 누르려고했습니다. 운 좋게도 해당 탭의 파일을 git reset --hard. HTH.


1
정확히 동일한 상황, 인덱스 추가를 놓치고 하드 리셋을 수행했으며 위의 모든 솔루션이 작동하지 않았습니다. 이것은 현명한 움직임이었습니다 .Ctrl + Z를 사용하여 하루를 저장했습니다. 내 편집자 : SublimeText. 내 옆에서 하나!
Vivek

1

이것은 아마도 git 전문가들에게는 분명 할 것입니다. 그러나 나는 광란의 검색에서 이것이 제기되는 것을 보지 못했기 때문에 그것을 올리고 싶었습니다.

일부 파일을 스테이징 git reset --hard하고, 약간 놀란 후 내 상태에 모든 파일이 스테이징되고 모든 삭제가 스테이징되지 않은 것으로 표시되는 것을 발견했습니다.

이 시점에서 삭제를 준비하지 않는 한 이러한 단계적 변경을 커밋 할 수 있습니다. 그 후에는 용기를 내면됩니다.git reset --hard 한 번 더 변경 사항으로 돌아갈 수 있습니다.

다시 말하지만, 이것은 아마도 대부분의 사람들에게 새로운 것은 아니지만, 그것이 저를 도왔고 이것을 제안하는 것을 찾지 못했기 때문에 다른 사람에게 도움이 될 수 있기를 바랍니다.


1

맙소사, 나는이 질문과 대답을 만날 때까지 머리카락을 뽑았습니다. 질문에 대한 정확하고 간결한 답변은 위의 두 개의 댓글을 함께 가져와야 만 가능하므로 여기에 모두 한곳에 있습니다.

  1. chilicuil에서 언급했듯이 돌아 가려는 git reflog커밋 해시를 확인하기 위해 실행 하십시오.

  2. akimsko에서 언급했듯이 커밋을 하나만 잃지 않는 한 체리 선택을 원하지 않을 것이므로 다음을 실행해야합니다. git reset --hard <hash-commit-you-want>

egit Eclipse 사용자를위한 참고 사항 : egit을 사용하여 Eclipse 내에서 이러한 단계를 수행하는 방법을 찾을 수 없습니다. Eclipse를 닫고 터미널 창에서 위의 명령을 실행 한 다음 Eclipse를 다시 열면 잘 작동했습니다.


0

위의 솔루션은 작동 할 수 있지만 git-s 복잡한 실행 취소 를 수행하는 대신이 문제를 복구하는 더 간단한 방법이 있습니다. 대부분의 git-resets는 적은 수의 파일에서 발생하며 이미 VIM을 사용하는 경우 가장 시간 효율적인 솔루션 일 수 있습니다. 주의 할 ViM's점은 변경 사항을 무제한으로 실행 취소 할 수있는 기능을 제공하기 때문에 어느 쪽이든 사용해야하는 영구 실행 취소 를 이미 사용해야 한다는 것입니다.

단계는 다음과 같습니다.

  1. vim :에서 명령을 입력하십시오 set undodir. 당신이 경우 persistent undo당신에 켜져 .vimrc, 그것은 유사한 결과를 보여줍니다 undodir=~/.vim_runtime/temp_dirs/undodir.

  2. 리포지토리 git log에서 마지막 커밋을 한 마지막 날짜 / 시간을 확인하십시오.

  3. 당신이 당신의 쉘 탐색에 undodir사용 cd ~/.vim_runtime/temp_dirs/undodir.

  4. 이 디렉토리에서이 명령을 사용하여 마지막 커밋 이후 변경 한 모든 파일을 찾습니다.

    find . -newermt "2018-03-20 11:24:44" \! -newermt "2018-03-23" \( -type f -regextype posix-extended -regex '.*' \) \-not -path "*/env/*" -not -path "*/.git/*"

    여기서 "2018-03-20 11:24:44"는 마지막 커밋의 날짜와 시간입니다. 날짜 git reset --hard가 '2018-03-22'이면 '2018-03-22'를 사용하고 '2018-03-23'을 사용하세요. 이것은 하한이 포함되고 상한이 배타적 인 find의 기이 한 특성 때문입니다. https://unix.stackexchange.com/a/70404/242983

  5. 다음으로 각 파일로 이동하여 vim에서 열고 "20m 이전"을 수행하십시오. "h early"를 사용하여 "earlier"에 대한 자세한 내용을 찾을 수 있습니다. 여기에서 earlier 20m20 분 전에 파일을 수행했다고 가정하고 20 분 전에 파일 상태로 git hard --reset돌아갑니다. find명령 에서 추출 된 모든 파일에 대해이 작업을 반복합니다 . 나는 누군가가 이러한 것들을 결합 할 스크립트를 작성할 수 있다고 확신합니다.


0

IntelliJ를 사용하고 있으며 각 파일을 간단히 살펴보고 다음을 수행 할 수 있습니다.

Edit -> reload from disk

운 좋게도 git status작업 변경 사항을 지우기 직전에 작업을 완료 했기 때문에 무엇을 다시로드해야하는지 정확히 알았습니다.


0

파일 계층 구조를 기억하고 Phil Oakley 수정과 함께 Mark Longair 기술을 사용하면 극적인 결과를 얻을 수 있습니다.

기본적으로 최소한 파일을 저장소에 추가했지만 커밋하지 않은 경우 대화식으로를 사용하여 복원 git show하고 로그를 검사하고 셸 리디렉션을 사용하여 각 파일을 생성 할 수 있습니다 (관심 경로 기억).

HTH!


0

최근에 a git diff를 검토 한 경우 아직 변경 사항을 준비하지 않은 경우에도 이와 같은 사고에서 복구 할 수있는 또 다른 방법이 있습니다.의 출력이 git diff콘솔 버퍼에 여전히 있으면 위로 스크롤하여 복사하여 붙여 넣을 수 있습니다. 파일로 diff하고 patch도구를 사용 하여 diff를 트리에 적용하십시오 patch -p0 < file.. 이 접근 방식은 저를 몇 번이나 절약했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.