Git 문제해결 가이드
flight rules가 뭐예요?
뭔가 잘못됐을 때 뭘 해야 할지에 대한 우주비행사(여기선 Git을 쓰는 개발자)를 위한 가이드
Flight Rules 는 어떤 문제가 발생한 이유와 그 단계의 매뉴얼에서 어렵사리 얻은 지식입니다. 기본적으로 각 시나리오의 매우 자세하고 구체적인 운영 절차랍니다. [...]
NASA는 수성(Mercury) 시대 때 지상팀에서 처음으로 "lessons learned"란 것을 모았는데 수천 개의 문제의 상황들, 부서진 해치 손잡이로 인한 엔진 고장부터 컴퓨터 문제 그리고 그 해답까지, 1960년대 초부터 우리의 실수들, 재앙들, 해결책 등이 목록화되어있습니다.
— Chris Hadfield, 인생을 위한 우주비행사의 가이드.
이 문서의 규칙
명확하게 하기 위해 이 문서의 모든 예제는 현재 브랜치를 표시하고 스테이지에 변경이 있는지를 나타내기 위해 커스텀 된 배시 프롬프트를 사용합니다. 브랜치는 괄호 안에 있고, 브랜치 다음의 *는 스테이지의 변경된 것을 나타냅니다.
리포지터리
로컬 리포지터리에서 시작하기
이미 존재하는 프로젝트 디렉터리를 Git 리포지터리로 최적화해 쓰려면 다음과 같이 입력합니다.
(my-folder) $ git init
리모트 리포지터리를 복제해오기
리모트 리포지터리를 클론하려면, 리포지터리 URL을 복사해와서 실행합니다.
$ git clone [url]
폴더 이름이 리모트 리포지터리 이름과 같이 저장됩니다.
복제할 리모트 서버의 연결을 확인하세요. (대부분 인터넷 연결을 확인하란 뜻입니다)
다른 리포지터리 이름으로 복제를 해오고 싶다면 다음 명령어를 실행합니다.
$ git clone [url] 새-폴더-이름
커밋 수정
최근 남긴 커밋 확인하기
git commit -a
로 막 커밋을 남긴 후에 커밋 메시지를 확인하고 싶다면 최근의 커밋을 현재 HEAD에서 볼 수 있습니다.
(main)$ git show
또는
$ git log -n1 -p
만약 특정 커밋의 파일을 보고 싶다면, 이렇게 할 수도 있습니다. (commitID 는 확인하고자 하는 그 커밋입니다)
$ git show <commitid>:filename
커밋 메시지를 잘못 썼을 때
만약 메시지를 잘못 썼고 아직 푸시를 안 했다면, 커밋 메시지 바꾸기를 따라 해보세요.
$ git commit --amend --only
이 방법은 편집 가능한 기본 텍스트 에디터가 열릴 텐데요, 다른 방법으론 한 줄에 쓸 수도 있습니다.
$ git commit --amend --only -m 'xxxxxxx'
만약 푸시를 이미 했다면, 커밋을 수정해서 강제 푸시를 할 수 있긴 한데 별로 추천하는 방법은 아닙니다.
커밋을 원하지 않는 이름과 이메일 설정으로 해버렸을 때
하나의 커밋이라면 이렇게 수정합니다.
$ git commit --amend --no-edit --author "이름 <이메일>"
대안으로 git config --global author.(이름|이메일)
에서 설정을 다시 맞추고 다음 명령을 실행하는 방법도 있습니다.
$ git commit --amend --reset-author --no-edit
만약 전체 이력 변경이 필요하다면, git filter-branch
의 설명 페이지를 보세요.
지난 커밋에서 파일 하나를 지우고 싶을 때
지난 커밋에서 파일 변경내역을 지우려면, 이렇게 해보세요.
$ git checkout HEAD^ myfile
$ git add myfile
$ git commit --amend --no-edit
그 파일이 새롭게 커밋으로 추가됐고 그 파일만 지우고 (git에서만) 싶은 경우엔,
$ git rm --cached myfile
$ git commit --amend --no-edit
이 방법은 열린 패치가 있고 불필요한 파일을 커밋 했거나 리모트에 강제 푸시로 패치를 업데이트 해야 할 때 특히 유용합니다. --no-edit
옵션은 기존 커밋 메시지를 유지하는 데 사용됩니다.
마지막 커밋 삭제하기
푸시된 커밋을 지우고 싶다면 이걸 따라 하면 되는데, 이력을 돌이킬 수 없게 되고 리포지터리에서 이미 풀을 받아 간 다른 사람의 이력도 엉망이 됩니다. 간단히 말하자면, 잘 모르겠으면 절대 하지 마세요.
$ git reset HEAD^ --hard
$ git push --force-with-lease [remote] [branch]
아직 푸시하지 않았다면, 리셋으로 마지막 커밋 전 상태로 돌아가세요. (변경 점은 스테이지에 남겨둡니다)
(my-branch*)$ git reset --soft HEAD@{1}
이 방법은 푸시를 안 했을 때만 동작합니다. 푸시를 했으면, 안전한 방법은 git revert SHAofBadCommit
한 가지밖에 없습니다.
이 방법은 모든 지난 커밋 변경 점으로 되돌아간 새 커밋을 만듭니다. 또는, 만약 푸시한 브랜치가 리베이스에 안전하다면 (만약 다른 사람이 풀 받지 않는다면), git push --force-with-lease
명령어를 쓸 수 있습니다.
더 알고 싶다면, 이 섹션을 참고해주세요.
임의적인 커밋 지우기
이전과 동일한 경고입니다. 가능한 이 방 법은 쓰지 마세요.
$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT
$ git push --force-with-lease [remote] [branch]
아니면 대화형 리베이스를 쓰고 지우고 싶은 커밋 라인을 지워도 돼요.
수정된 커밋을 푸시했는데, 에러 메시지가 나타납니다
To https://github.com/yourusername/repo.git
! [rejected] mybranch -> mybranch (non-fast-forward)
error: failed to push some refs to 'https://github.com/tanay1337/webmaker.org.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
알아두기
리베이스(아래를 보세요)나 어멘드는 기존 커밋을 새 걸로 바꿉니다. 그래서 이미 먼저 수정된 커밋이 푸시됐다면 강제 푸시를 해야 합니다. 이 방법을 쓸 땐 조심하세요! 항상 작업 되는 브랜치가 맞나 확인하세요!
(my-branch)$ git push origin mybranch --force-with-lease
일반적으로 강제 푸시를 쓰지 마세요.
새 커밋을 만들어서 푸시하는 게 수정된 커밋을 강제로 푸시하는 것보다 훨씬 낫습니다. 그런 수정된 커밋은 그 브랜치나 다른 자식 브랜치를 쓰는 다른 개발자의 소스 이력과 충돌의 원인이 될 겁니다.
--force-with-lease
는 여전히 실패할 텐데, 누군가가 같은 브랜치를 쓴다면 변경 점을 덮어쓰는 푸시를 할 수도 있어요.
절대로 아무도 같은 브랜치를 안 쓰거나, 절대로 브랜치에 업데이트를 해야 할 때 --force
(-f
) 옵션을 쓸 수 있지만, 일반적으론 피하는 게 좋아요.
하드 리셋을 해버렸는데 되돌리고 싶습니다
만약 하드 리셋을 했다고 해도 커밋을 돌릴 순 있습니다. Git은 며칠간은 로그를 가지고 있거든요.
알아두기
이건 커밋을 남겼거나 스테이시같이 백업을 했을 때만 유효합니다.
git reset --hard
은 커밋되지 않은 수정사항을 다 지울겁 니다, 그러니 조심해서 써야 합니다. (안전한 방법으론git reset --keep
이 있어요)
(main)$ git reflog
지난 커밋과 리셋을 위한 커밋을 볼 수 있습니다. 돌아가고 싶은 커밋의 SHA 코드를 골라서, 리셋하세요.
(main)$ git reset --hard SHA1234
계속할 수 있습니다.
머지를 실수로 커밋, 푸시해버렸습니다
만 약 실수로 머지할 준비가 안 된 피쳐 브랜치를 메인 브랜치에 머지했어도 되돌릴 순 있습니다. 하지만 문제는 있습니다: 머지 커밋은 한 개 이상의 부모(보통은 두 개)를 가지게 됩니다.
사용하려면 다음 명령을 실행합니다.
(feature-branch)$ git revert -m 1 <commit>
여기서 -m 1 옵션은 부모 번호 1(머지가 만들어진 브랜치)을 되돌릴 상위 항목으로 선택하라는 의미입니다.
알아두기
부모 번호는 커밋 식별자가 아니고, 오히려 머지된 커밋이
Merge: 8e2ce2d 86ac2e7
이라는 라인을 가지고 있습니다. 부모 번호는 이 라인에서 원하는 부모의 1 기반 인덱스이고, 첫 번째 식별자는 1, 다음은 2 이렇 게 이어집니다.
스테이지
지난 커밋에 스테이지 변경 점을 추가하고 싶습니다
(my-branch*)$ git commit --amend