git으로 일하다보면 커밋 히스토리를 수정해야할 때가 있다.

깃에서는 커밋한 내용 뿐만 아니라 커밋 순서, 커밋 메세지, 커밋 파일 수정이 가능하다. 단, 다른 사람과 코드를 공유하기 전에 해야 한다!


git의 장점

  • staging area로 커밋할 파일 고르는 일을 커밋하는 순간으로 미룰 수 있다.
  • stash 명령으로 하던 일을 미루고 다른 브랜치로 이동할 수 있다.
  • 이미 커밋한 내용을 수정할 수 있다.
  • 커밋 순서, 커밋 메세지, 커밋 파일 수정이 가능하다.

(1) 마지막 커밋을 수정하기

1) 커밋 메세지 수정하기

  • git commit --amend
  • 자동으로 텍스트편집기(vim)을 실행시켜서 마지막 커밋 메세지를 열어주므로, 쉽게 수정하고 저장하면 된다.

  • 커밋하고 난 후 새로만든 파일 또는 수정파일을 가장 최근 커밋에 집어넣는 법
    • 파일을 수정한 후 git add로 staging area에 넣기
    • 또는 git rm으로 추적하는 파일(Tracked Files) 삭제하기
    • git commit --amend를 통해 staging area의 파일들을 커밋한다.


2) 커밋메세지를 여러 개 수정하는 법 - rebase

  • 예전 커밋을 수정하려면 rebase를 사용한다.
  • 마지막 커밋메세지 3개를 수정하고 싶을 때, git rebase -i HEAD~3을 입력
  • HEAD~3 : 수정하려는 커밋의 부모인 4번째 이전 커밋까지.
  • 이미 중앙 서버에 push한 커밋을 결국 같은 내용을 2번 push하므로 수정하면 안된다.
  • Rebase를 하면 목록에 있는 모든 커밋의 SHA-1 값이 변경되므로 중복 푸시를 하게 된다.
  • 오래된 커밋메세지부터 최신 커밋메세지까지 뜨고, 그 안에서 pick을 지우고 수정하고 싶은 커밋메세지 앞에 edit을 쓴 후 저장하면 된다.
<git rebase -i HEAD~2> 수정 결과: 
	Stopped at 81d7b22... add stash2.txt
	You can amend the commit now, with

		git commit --amend

	Once you are satisfied with your changes, run

		git rebase --continue
  • 알려주는 대로 git commit --amend를 치면 수정하고자 했던 커밋 메세지를 수정할 수 있도록 텍스트 편집기가 열려서 수정할 수 있다.(여기서 수정하는 것!)
  • 수정하고 나서 나머지 것들도 아래의 명령어로 계속 진행하여 바꿔나갈 수 있다.
<git rebase --continue> 
Successfully rebased and updated refs/heads/master.
: 앞에서 1개만 수정한다고 명령했으므로 아래와 같이 결과가 나온다.


3) 커밋 순서바꾸기

  • git rebase -i HEAD~n으로 할 수 있는 다른 기능
  • 대화형 편집기에서 커밋 전체를 삭제하거나 순서를 조정한 후 저장하면 log에서 바뀐 것을 확인할 수 있다.
  1 pick 8ea9f5b 파일 다시 추가
  2 pick beeb261 add .gitignore
  3 pick 7f38535 add stash2.txt
  
  1 pick 7f38535 add stash2.txt
  2 pick 8ea9f5b 파일 다시 추가
 : 위에서 아래처럼 수정하고 저장하면 커밋 순서, 삭제 등을 할 수 있다. 


4) 커밋 합치기

  • 대화형 Rebase(vim)를 활용할 수 있다.
<합칠 커밋 앞에 squash로 바꿔주기>
  1 pick 8ea9f5b 파일 다시 추가
  2 squash beeb261 add .gitignore
  3 squash 7f38535 add stash2.txt
  
<squah한 후 알림창>
  # This is a combination of 3 commits.
  2 # This is the 1st commit message:
  3 파일 다시 추가
  4
  5 # This is the commit message #2:
  6
  7 add .gitignore
  8
  9 # This is the commit message #3:
 10
 11 add stash2.txt
 
<git log> - 3개의 커밋이 하나로 합쳐졌다.
 commit 05e65584897be79c3a56ac9264d64a1c5c43a3a4
Author: juliahwang-imac <[email protected]>
Date:   Tue May 16 22:19:26 2017 +0900

    파일 다시 추가

    add .gitignore

    add stash2.txt


5) 커밋 분리하기

  • 기존 커밋을 해제하(되돌리)고 stage를 여러 개로 분리한 후 그것을 원하는 횟수만큼 다시 커밋할 수 있다.
  • git rebase -i에서 해당 커밋을 edit 으로 변경한다.
  • git reset HEAD^로 해당 커밋을 해제하고 내용을 다시 2개로 나누어서 커밋한다.
  • 수정파일은 unstaged 상태가 되는데 파일을 add한 후 커밋을 충분히 나누어서 한다.
  • git rebase --continue를 통해 남은 rebase 커밋메세지 수정을 완료해준다.



수정할 커밋이 너무 많을 때 - filter-branch

  • rebase 는 --continue 옵션으로 하나하나 수정하므로 많은 양의 커밋을 수정할 때는 번거로운 작업이다.

filter-branch

  • 히스토리 전체에서 필요한 것만을 골라내는데 사용하는 도구.
  • 모든 커밋의 이메일 주소를 변경하거나 어떤 파일을 삭제할 경우 사용할 수 있다.
  • 역시 타인과 공유중이라면 사용하지 말아야한다


1) 모든 커밋에서 파일 제거하기

  • 잘못된 파일을 커밋했거나 암호 등 보안정보를 커밋한 경우 돌아가서 삭제해야 한다.
    • fliter-branch --tree-filter 'rm -f password.txt' HEAD
  • 편집기의 백업파일을 잘못 커밋한 경우
    • filter-branch --tree-filter rm -f *~ HEAD
    • 모든 파일, 커밋을 정리하고 브랜치 포인터를 복원해준다.
  • 프로젝트에 체크아웃하고 각 커밋에 해당 명령을 실행시킨다.
  • 그리고 그 결과를 다시 커밋한다.


2) 모든 브랜치에 적용할 수 있는 명령을 내리고 싶을 경우

-`filter-branch --all` 
  • 이러한 작업은 테스팅 브랜치에 먼저 해보는 게 좋다.


3) 하위 디렉토리를 루트 디렉토리로 만들고 싶을 경우

  • 다른 VCS(버전컨트롤시스템)에서 코드를 임포트하고 사용할 때 받은 하위 디렉토리를 모든 커밋에 대한 루트 디렉토리로 만들 때 사용한다.

  • git filter-branch --subdirectory-filter trunk(하위디렉토리명) HEAD

    • trunk를 루트디렉토리로 만들고, 관련없는 커밋은 자동으로 삭제한다.


4) 모든 커밋의 이메일 주소 수정하기

  • 프로젝트를 오픈소스로 공개할 때 개인이메일 주소로 변경해야한다.
  • or git config로 이메일 주소 넣는 것을 잊었다.
  • git filter-branch --commit-filter + if문
  • 해당 커밋만 골라서 수정가능하다.
  • 조건에 만족하는 커밋만 SHA-1값이 바뀌는 것이 아니다.
  • 커밋은 부모의 SHA-1값을 받아오므로 모든 커밋의 SHA-1 값이 바뀐다
  • 따라서 push하기 전에 해줘야 한다!

Julia Hwang

디발자를 꿈꾸는 웹개발자의 블로그입니다.