git history 명령

9 hours ago 4
  • Git 2.54와 2.55에 걸쳐 도입된 실험적 git history 명령은 기존 Git 작업 흐름을 유지하면서 과거 커밋 수정과 파생 브랜치 재구성을 자동화함
  • fixup 은 스테이징한 변경을 과거 커밋에 합친 뒤, 해당 커밋에서 파생된 모든 로컬 브랜치를 새로운 커밋 해시에 맞춰 자동 리베이스함
  • reword 는 과거 커밋 메시지를 바꾸고, split 은 커밋의 변경 내역을 헝크 단위로 나눠 두 커밋으로 분리한 뒤 후속 커밋과 브랜치를 다시 구성함
  • 세 하위 명령은 충돌 가능성이 있는 작업을 거부하는 원자적 처리로 작업 트리가 중간에 깨지는 상황을 막지만, 병합 커밋이 있는 이력에서는 동작하지 않음
  • jj의 1급 충돌 처리, 작업 로그 기반 실행 취소, 작업 복사본의 커밋 모델링까지 제공하지는 않지만, 별도 설치 없이 Git 코어 배포판에서 여러 jj식 이력 편집 기능을 사용할 수 있음

기존 Git 이력 편집이 불편한 이유

  • 여러 변경을 병렬로 다루면 브랜치와 커밋을 계속 오가야 하며, rebase -i 도중 작업 트리가 불완전한 상태에 빠질 수 있음
  • Git 대안인 jj는 이런 문제의 해결책으로 자주 거론되지만, 1년 반 동안 3개월마다 며칠씩 시도해도 결국 기존 Git 작업 흐름으로 돌아오게 됨
  • git history는 Git 코어 배포판에 포함된 실험적 명령이어서 별도 도구를 설치하지 않고 시험할 수 있음
    • Git 2.54는 4월에 reword와 split을 도입함
    • Git 2.55는 6월에 fixup을 추가함
  • 하위 명령은 fixup, reword, split 세 가지로 구성됨

fixup: 과거 커밋과 파생 브랜치 동시 수정

  • git history fixup은 과거 커밋의 잘못된 부분을 수정하고, 그 커밋에서 파생된 브랜치를 자동으로 리베이스함
  • 일반적인 수정 절차처럼 git add로 변경을 스테이징한 뒤 git history fixup <commit> 을 실행하면 스테이징된 변경이 대상 커밋에 합쳐짐
  • git commit --fixup과 autosquash 리베이스를 합친 것과 비슷하지만, 대상 커밋을 포함하는 다른 브랜치까지 갱신한다는 차이가 있음
  • git rebase --update-refs가 현재 리베이스 범위 안의 참조만 이동하는 데 비해, git history는 대상 커밋에서 파생된 모든 로컬 브랜치를 찾아 다시 작성함
    • 필요하면 갱신 범위를 현재 브랜치로만 제한할 수 있음
    • 병합 커밋이 있는 이력에서는 동작하지 않아 일부 Git 사용 방식에는 적용하기 어려움
  • B에 수정 사항을 합치면 새로운 해시를 가진 B*가 생성되고, 그 위의 C와 D도 각각 C*, D*로 다시 만들어짐
    • feat-1과 feat-2의 브랜치 끝도 새 커밋을 따라 이동함

원자적 처리와 충돌 제한

  • 세 하위 명령은 모두 원자적으로 처리돼 작업 트리를 반쯤 깨진 상태로 남기지 않음
  • 충돌을 일으킬 수 있는 작업 자체를 거부하는 방식으로 원자성을 보장함
  • 이는 충돌을 1급 객체로 처리해 리베이스 도중 충돌 상태를 유지하고 나중에 해결할 수 있는 jj보다 기능적으로 제한적임
  • git history 제한 사항은 이력 재작성이 상태 보존 작업으로 바뀌지 않도록 의도적으로 적용된 것임
    • Git이 향후 1급 충돌을 지원하면 이 제한을 해제할 수 있음

reword: 작업 트리 없이 커밋 메시지 변경

  • git history reword는 과거 커밋 메시지를 바꾸고, 그 위의 커밋과 브랜치를 자동으로 다시 구성함
  • git history reword <commit> 을 실행하면 편집기에 기존 메시지가 열리며, 수정 후 저장하면 후속 커밋 스택이 새 해시에 맞춰 재구축됨
  • 반복 개발 중 설계가 바뀌었을 때 이전 커밋 메시지를 현재 구조에 맞게 고치는 데 유용함
  • 트리 내용 대신 커밋 메시지만 바꾼다는 점을 제외하면 fixup과 같은 방식으로 동작함
  • reword는 인덱스나 작업 트리를 건드리지 않고 커밋 그래프만 변경
    • 체크아웃하지 않은 브랜치의 커밋도 현재 작업을 방해하지 않고 수정할 수 있음
    • B의 메시지를 바꾸면 해시가 달라지므로 후속 C는 C*로 다시 만들어지고 feat-1도 새 커밋을 따라감

split: 하나의 커밋을 둘로 분리

  • git history split은 한 커밋의 변경을 대화식으로 선택해 두 커밋으로 나눔
  • git history split <commit> 을 실행하면 대상 커밋의 변경 내역을 헝크 단위로 선택하는 프롬프트가 열림
    • 선택해 유지한 헝크는 첫 번째 커밋을 구성함
    • 나머지 헝크는 두 번째 커밋에 들어감
  • git add -p와 비슷하지만 git rebase를 조합하는 복잡한 절차가 필요 없음
  • 서로 관련 없는 두 변경이 들어 있던 B를 분할하면 B1과 B2가 생성되고, 후속 C는 두 커밋 위에 C*로 재구축
  • 세 명령 중 사용 범위가 가장 특화돼 있지만, 커밋을 실제로 분리해야 할 때 유용함

jj와의 기능 차이

Read Entire Article