# git history 명령

> Clean Markdown view of GeekNews topic #31438. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=31438](https://news.hada.io/topic?id=31438)
- GeekNews Markdown: [https://news.hada.io/topic/31438.md](https://news.hada.io/topic/31438.md)
- Type: GN+
- Author: [xguru](https://news.hada.io/@xguru)
- Published: 2026-07-15T00:39:27+09:00
- Updated: 2026-07-15T00:39:27+09:00
- Original source: [lalitm.com](https://lalitm.com/post/git-history/)
- Points: 1
- Comments: 1

## Topic Body

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

---

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

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

### 원자적 처리와 충돌 제한
- 세 하위 명령은 모두 **원자적으로 처리**돼 작업 트리를 반쯤 깨진 상태로 남기지 않음
- 충돌을 일으킬 수 있는 작업 자체를 거부하는 방식으로 원자성을 보장함
- 이는 [충돌을 1급 객체로 처리](https://docs.jj-vcs.dev/latest/conflicts/)해 리베이스 도중 충돌 상태를 유지하고 나중에 해결할 수 있는 `jj`보다 기능적으로 제한적임
- [`git history` 제한 사항](https://git-scm.com/docs/git-history#_limitations)은 이력 재작성이 상태 보존 작업으로 바뀌지 않도록 의도적으로 적용된 것임
  - Git이 향후 **1급 충돌**을 지원하면 이 제한을 해제할 수 있음

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

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

### jj와의 기능 차이
- `git history`는 `jj`가 제공하는 전체 작업 모델을 대체하지 않음
  - `jj`는 [작업 로그와 간단한 실행 취소](https://docs.jj-vcs.dev/latest/operation-log/)를 제공함
  - [작업 복사본을 커밋으로 모델링](https://docs.jj-vcs.dev/latest/working-copy/)함
  - [리베이스 과정에서 충돌 상태를 유지](https://docs.jj-vcs.dev/latest/conflicts/)할 수 있음
- 반면 `git history`는 기존 Git 안에서 과거 커밋 수정, 메시지 변경, 커밋 분할과 파생 브랜치 갱신을 지원해 **`jj`의 일부 장점**을 현재 작업 흐름에 도입할 수 있음
- 향후 제한을 완화할 가능성이 열려 있어 이후 Git 릴리스에서 추가로 개선될 여지가 있음

## Comments



### Comment 61800

- Author: neo
- Created: 2026-07-15T00:39:28+09:00
- Points: 1

###### [Hacker News 의견들](https://news.ycombinator.com/item?id=48901010) 
- Git이 불편했지만 무료로 공개된 [Pro Git 책](<https://git-scm.com/book/en/v2>)의 첫 세 장을 읽고 나아졌음. Git 내부 동작에 관한 훌륭한 **정신 모형**을 제공하며, 좋든 나쁘든 Git의 사용자 인터페이스는 내부 구조를 직접 반영하므로 이를 이해하자 모든 것이 맞아떨어졌다
  - 사용자 인터페이스가 내부 구조를 직접 반영한다는 데 동의하지 않음. 가령 `git checkout`은 브랜치를 전환할 때는 아직 Git에 들어가지 않은 변경을 잃지 않지만, 파일의 다른 버전을 복원할 때는 변경을 되돌리도록 설계됐는데 이것이 어떻게 내부 구조를 직접 반영하는가?  
    이와 별개로, 내부에서 원하는 작업과 명령을 알아도 정확한 명령줄 구문을 찾기 어렵게 만드는 **CLI의 비일관성**도 Git을 사용하기 어렵게 함
  - Git은 알려진 것만큼 혼란스럽지 않으며, 몇 시간만 들여 이해하면 됨. 소프트웨어 엔지니어의 필수 도구인데도 이를 배우지 않는 것은 안타까우며, 전문가라면 `git commit -am “Changes”`만으로는 부족하다
  - 나도 Git 명령이 혼란스러웠지만, Linus가 Git의 **내부 자료 구조**를 설명하는 YouTube 영상을 보고 아주 단순하다는 걸 알게 됐음. 도구의 기능이 자료 구조를 어떻게 조작하는지 쉽게 대응시킬 수 있었고, 오히려 Git이 제공하려는 상위 수준 추상화가 상황을 더 혼란스럽게 만든다
  - 그렇지 않음. Git 명령의 절반은 저수준이고 나머지 절반은 고수준이므로, **내부 구조와 1:1 대응**하지 않는다
  - **Mercurial의 사용자 경험**이 단연 최고였고, 따로 문서를 읽지 않아도 배울 수 있었음. Git을 쓸 때도 Mercurial처럼 사용하다가 문제가 생기면 로컬 저장소를 지우는 방식으로 경력 내내 버텨왔다

- `git rebase --abort`가 있으며, 리베이스 전에 태그를 만들었다가 `git reset --hard $set_tag`로 돌아갈 수도 있음. 이전 상태가 사라지는 게 아니므로 **대화형 리베이스**를 무서워할 이유가 없다
  - 바로 그 용도로 `temp`나 `before-rebase`라는 브랜치를 많이 만들어 둠. 태그처럼 쓰지만 브랜치는 옮기기가 더 간편하므로 큰 `rebase -i` 전에 보통 `git branch before-rebase/some-feature`를 실행한다  
    실제로 `git reset before-rebase`가 필요했던 적은 거의 없지만, `git log -p before-rebase`를 리베이스 후 브랜치와 비교해 **충돌 해결 과정의 의도치 않은 변경**을 확인하는 경우는 많았다
  - 별도의 안전 태그를 만들 필요조차 없으며, **reflog**가 자동으로 그 역할을 해준다
  - `git reflog`를 사용하는가?
  - `jj undo`보다 더 간단할 수는 없다
  - 맞음. `git rebase`를 입력한 뒤 절반 정도는 바로 `--abort`를 입력한다

- 스스로 코더나 프로그래머라고 생각하지 않지만, Git을 배우는 것은 내 사고방식에 **정리의 초능력**을 준 것 같았음. 웹사이트, 디자인 프로젝트, 전자공학, 작곡, 개인 지식 저장소, 원격 관리 스크립트, 설정 관리, 코드 조각 등 수많은 용도로 활용하며, 완벽하진 않아도 함께 일하는 모두에게 배우라고 권한다
  - 나도 정리 능력이 크게 향상됐지만, 이는 Git 자체보다 **버전 관리 시스템** 전반의 효과에 가까움. 버전 관리 시스템은 만든 것을 정리하고 보존하며 언제든 원하는 만큼 복사본을 꺼낼 수 있어, 걱정 없이 일하게 해준다  
    David Allen의 『Getting Things Done』에는 생각을 모으고 정리할 **신뢰할 수 있는 시스템**이 필요하다는 내용이 있음. 나는 작업·프로젝트·목록에는 OmniFocus를 쓰지만, 컴퓨터에서 만든 소프트웨어와 일부 데이터를 정리하고 보존하는 일은 Git을 신뢰한다
  - 작곡에는 Git을 어떻게 사용하는가?

- 이 명령들을 이론적으로 매우 좋아하고 `jj`의 동등한 명령도 늘 사용하지만, `git history`가 수정한 커밋에 **서명하는 방법**을 찾지 못했음. 매뉴얼과 Git 소스 코드를 살펴봐도 기존 GPG 서명 커밋을 `git history reword HEAD~`로 수정하면 서명이 모두 사라진다  
  커밋 서명을 유지하고 싶어서 결국 전통적인 `git rebase -i`로 돌아갔다
  - 이는 큰 누락처럼 보이며 표준 커밋 처리 과정도 우회하는 듯함. 매뉴얼에서도 `git history`를 **실험적 기능**이라고 하니 버그로 신고할 만하다

- 새 명령 없이도 10년 넘게 로컬 Git 커밋 기록을 큰 어려움 없이 대폭 수정해 왔음. Git과 리베이스가 어렵게 느껴지는 큰 이유는 정확한 명령줄 옵션과 동작 방식을 직접 파악해야 하는 **사용자 인터페이스 문제**라고 확신한다  
  Windows에서는 **TortoiseGit의 리베이스 대화상자**가 강력하면서도 사용하기 쉬웠음. 커밋 로그에서 기준 커밋을 우클릭해 `"Rebase onto this"`를 선택하면 되고, [https://tortoisegit.org/docs/tortoisegit/tgit-dug-rebase.htm...](<https://tortoisegit.org/docs/tortoisegit/tgit-dug-rebase.html>)처럼 커밋의 순서 변경·분할·결합·편집이 명확하며 중간에 취소하면 원래 상태로 돌아간다  
  다만 셸 확장 방식이라 클릭이 너무 많아 탭이 있는 독립 앱이면 더 좋겠지만, 이는 TortoiseSVN을 계승한 설계 원칙으로 보임. Git Extensions도 잠시 써봤으나 자주 쓰던 기능을 찾지 못했거나 이미 TortoiseGit에 익숙해져 정착하지 못했다. 로그 화면은 [https://tortoisegit.org/docs/tortoisegit/tgit-dug-showlog.ht...](<https://tortoisegit.org/docs/tortoisegit/tgit-dug-showlog.html>)에서 볼 수 있다
  - Windows에서 Linux로 옮긴 뒤 아쉬운 것 중 하나는 모든 프로그램이 파일·폴더의 우클릭 메뉴, TortoiseGit의 상태 아이콘 같은 오버레이, `My Computer` 같은 특수 객체를 추가할 수 있었던 **통합 셸**임. XP에서 정점을 찍었고 Vista부터 Microsoft가 확장하기 어려운 방향으로 바꾼 것으로 이해한다  
    다만 VC++6의 어떤 메뉴가 일반 목록 대신 폴더처럼 생긴 셸 객체를 만들어 파일 열기 대화상자에서 표시했던 이유는 아직도 이해되지 않음. 프로젝트에 구성 요소를 추가하는 마법사였는데, 대화상자가 열린 동안 그 가짜 폴더를 `My Computer`에서 찾곤 했다
  - 기존 작업 흐름이 조금 더 번거로웠고 새 명령이 더 편리한 것은 맞음. `fixup`은 이미 `git commit --fixup`이나 그 이전의 `git rebase -i`로 가능했고, `reword`도 `git rebase -i`로 할 수 있었다  
    각 커밋에 수행할 작업을 정하려고 파일을 편집하는 과정이 불편하다는 피드백이 충분히 쌓여, 기존 기능을 감싼 **상위 수준 명령**이 추가된 듯하다
  - Git 사용자 인터페이스 중에서는 **Magit**에 근접한 도구를 본 적이 없음. 깊이 있는 기능과 단순화된 상태 유지형 인터페이스를 함께 제공한다  
    명령줄 도구는 다음 명령을 실행하면 이전 출력에 접근하기 어려워진다. `git log`에서 본 커밋을 체리픽하려 해도 해시가 터미널에 남아 있지 않으면 기억만으로 입력할 수 없지만, 상태 유지형 인터페이스는 여러 상호작용을 병렬로 유지하고 이전 정보를 보존해 준다  
    Git은 현재 브랜치, 직전 브랜치, 수정된 파일, 푸시하지 않은 커밋 등 기억해야 할 **상태 정보**가 매우 많음. 셸 플러그인의 브랜치 표시와 자동 완성, 여러 탭·창 분할, 탭 간 복사, 자동 완성 검색 같은 주변 도구를 익히지 못하면 Git 사용이 매우 불쾌해질 수 있다

- `jj`의 실질적인 약점은 **Git 연동**뿐이라고 봄. 파일 집합, 리비전 집합, 템플릿을 위한 도메인 특화 언어를 정형화해 설정과 CLI 전반에서 일관되게 쓰며, 내부 구조를 직접 노출하지 않고 강력한 개념을 인터페이스로 제공한다  
  하지만 너무 유연해 Git 상호운용성이 관례와 규율에 크게 의존하고, 거의 모든 `jj` 상태를 Git으로 표현할 수 있어 원격 저장소를 망치기 쉬움. 이를 복구하려면 Git을 깊이 이해해야 한다  
  새 단독 개발 프로젝트라면 `jj`를 시도할 만함. 유리한 조건에서 보면 Subversion과 비교한 초기 Git 같은 느낌이며, 먼저 `jj` 자체의 방식에 익숙해지면 이후 험난한 Git 연동에도 조금 더 잘 대비할 수 있다. 관련 맥락은 [https://news.ycombinator.com/item?id=48903054](<https://news.ycombinator.com/item?id=48903054>)에 있음
  - 원칙적으로 `jj`에는 Git이 아닌 백엔드도 있지만, 현재 운영 환경에 적합하다고 명시된 것은 **Git 백엔드**뿐임. Git 연동 없는 `jj`는 아직 현실보다 개념에 가깝다  
    `jj`와 그 버전 관리 정신 모형을 좋아해 계속 쓰겠지만, 가끔 작업을 끝내려고 Git으로 내려가야 하므로 새로운 형상 관리 시스템보다는 **Git 위의 편리한 래퍼**처럼 느껴짐. Git이 `jj` 작업 흐름을 지원하는 명령을 추가하면 `jj`를 따로 설치할 이유를 찾기 어려울 것이다

- `git history fixup`이 모든 후손 브랜치를 자동 리베이스하는 방식은 내 작업 흐름과 맞지 않음. 기능 브랜치의 각 버전을 `myfeature.1`, `myfeature.2`처럼 보존해 새 버전에서 동작이 달라졌을 때 예전 버전을 찾는데, `git reflog`로는 이를 대체할 수 없다  
  예를 들어 문제가 `myfeature.58`과 `myfeature.59` 사이에서 생겼고 기능마다 커밋이 15개라면 `git range-diff myfeature.58~15..myfeature.58 myfeature.59~15..myfeature.59`로 기반 커밋이 달라도 두 버전의 차이를 커밋별로 볼 수 있음. 따라서 특정 커밋을 포함한 **모든 브랜치의 자동 리베이스**를 원하지 않으며, 오래된 버전에 태그를 쓰는 방식도 잘 맞지 않았다

- 최신 Git은 자주 쓰는 `git rebase --interactive` 작업 세 가지를 마찰이 적은 별도 명령으로 구현했으며, 충돌이 없을 때만 동작하는 듯하다
  - `git history`가 정말 유용한지는 의문임. 나는 **대화형 리베이스**로 커밋을 시각적으로 옮기는 편을 선호하며, 현재 로컬 브랜치에서 사라진 고아 커밋 해시를 실수로 `git history` 대상으로 지정하고 싶지 않다

- Git 기록을 완벽하게 다듬는 데 왜 그렇게 공을 들이는지 이해하지 못하겠음. 어차피 개별 커밋을 다시 읽지 않으니 병합 전에 **모두 스쿼시**하면 된다
  - 강하게 반대함. PR을 검토할 때 최소한 커밋 기록을 읽어 변경의 흐름을 파악하며, `"oops"`와 `"fix"`로 가득하면 즉시 반려한다  
    커밋은 작업의 이야기를 전달하므로, 검토자가 의도를 이해하려고 더 고생하지 않도록 **읽기 좋은 커밋 기록**을 제공해야 한다
  - 나는 개별 커밋을 자주 읽음. 세심하게 관리된 저장소에서는 버그나 회귀를 추적하고 작성자의 **변경 의도**를 이해하는 데 매우 유용하다
  - 코드가 왜 이렇게 됐는지 궁금할 때 해당 코드를 도입한 커밋의 맥락을 보면 큰 도움이 됨. 서로 다른 변경을 한꺼번에 담은 거대한 커밋이라면 그 가치가 훨씬 떨어진다
  - 큰 팀에서 일하지 않는다는 사실이 드러나는 생각임. 휴가에서 돌아오면 가장 먼저 Git 기록을 읽어 그동안 무엇이 바뀌었는지 확인하고, CI가 깨졌을 때도 유용해서 사실상 매일 어떤 형태로든 기록을 살펴본다  
    코드베이스와 기능이 아주 작다면 모두 스쿼시해도 되겠지만, 나는 실제 변경 흐름을 확인하고 커밋을 선택적으로 **체리픽하거나 되돌릴 수 있는 방식**을 선호한다
  - 논리적 변경 하나당 커밋 하나로 잘 정리된 기록은 **회귀를 이분 탐색**할 때 대단히 유용하다

- 현재 `D`를 체크아웃한 상태에서 `B`를 `B'`로 바꿀 때, `git rebase --update-refs`는 리베이스 범위 밖의 `E`를 그대로 두고 `B' → C' → D'`만 만들며 기존 `B → E`를 유지하는 것으로 이해했음  
  반면 `git history fixup`은 `E`까지 `E'`로 갱신하는가? 그렇다면 수십 년간 손에 밴 `git rebase`로도 **모든 후손 브랜치 갱신**을 수행할 방법이 있는지 궁금하다
  - 커밋 해시는 부모 커밋의 해시도 반영하므로 `E`를 그대로 둘 수 없음. `--update-refs`를 직접 써보지는 않았지만 문서대로라면 `E'`까지 생기는 세 번째 그래프가 결과일 것이므로, 원하는 동작은 이미 `git rebase`에서 가능하다
  - [https://blog.hot-coffee.dev/en/blog/git_update_refs/](<https://blog.hot-coffee.dev/en/blog/git_update_refs/>)를 참고하면 된다
