16P by GN⁺ | ★ favorite | 댓글 3개
  • 페이스북이 만든 hg absorb의 git용 포팅 버전
    • hg absorb는 최근 몇 년간 버전 관리 시스템에서 가장 멋진 워크플로우 개선이라고 할 수 있음
    • 작업 디렉토리에 커밋되지 않은 변경 사항이 draft changeset 위에 있을 때, hg absorb를 실행하면 커밋되지 않은 수정 사항이 자동으로 적절한 draft ancestor changeset에 흡수됨
    • 커밋을 생성하거나 수동으로 history 수정 규칙을 만들지 않고도 hg histedit + "roll" 작업을 수행할 수 있음
    • 명령어는 수정된 라인을 보고, 해당 라인을 수정한 changeset을 찾아, 커밋되지 않은 변경 사항을 포함하도록 해당 changeset을 수정함
    • 충돌 없이 변경 사항을 만들 수 없으면 커밋되지 않은 상태로 남음
    • 이 워크플로우는 리뷰 피드백을 적용하는 것과 같은 작업에 매우 유용함. 파일 변경 사항을 만들고, hg absorb를 실행하면 변경 사항과 커밋 간의 매핑이 자동으로 정리됨. 마법 같은 기능임

git absorb 사용 시나리오

  • 몇 개의 커밋이 있는 feature 브랜치가 있음
  • 팀원이 브랜치를 리뷰하고 몇 가지 버그를 지적함
  • 버그 수정 사항이 있지만, atomic 커밋을 믿기 때문에 fixes라는 불투명한 커밋에 모두 넣고 싶지 않음
  • git commit --fixup을 위해 커밋 SHA를 수동으로 찾거나 수동 대화형 rebase를 실행하는 대신 다음을 수행:
    1. git add $FILES_YOU_FIXED
    2. git absorb --and-rebase
  • git absorb는 어떤 커밋을 안전하게 수정할 수 있는지, 어떤 staged 변경 사항이 각 커밋에 속하는지 자동으로 식별함
  • 그런 다음 이러한 변경 사항에 대한 fixup! 커밋을 작성함
  • --and-rebase 플래그를 사용하면 이러한 fixup 커밋이 해당 커밋에 자동으로 통합됨
  • 신뢰하지 않는 경우 출력을 수동으로 확인한 다음, git의 내장 autosquash 기능을 사용하여 fixup을 feature 브랜치에 통합할 수 있음

설치

  • 최신 태그 릴리스에서 아티팩트를 다운로드하여 설치 가능
  • Windows, MacOS, Linux용 아티팩트 제공

사용법

  1. 흡수할 변경 사항을 git add로 추가
  2. git absorb 실행
  3. 결과에 만족하면 git rebase -i --autosquash 실행
  4. 만족하지 않으면 git reset --soft로 이전 상태로 복구

How it works (roughly)

  • git absorb는 두 패치 P1과 P2가 교환 가능한지 확인함
  • 기본적으로 마지막 10개의 커밋을 고려함
  • 인덱스의 각 덩어리에 대해, 해당 덩어리가 마지막 커밋과 교환 가능한지 확인함
  • 교환 불가능한 커밋을 찾으면, 해당 덩어리를 fixup 커밋으로 변환함

Configuration

Stack size

  • 기본적으로 마지막 10개의 커밋을 고려함
  • 더 많은 커밋을 고려하려면 .gitconfigmaxStack 값을 설정

One fixup per fixable commit

  • 기본적으로 각 흡수 가능한 덩어리에 대해 별도의 fixup 커밋을 생성함
  • -F 플래그를 사용하여 동일한 커밋에 흡수되는 모든 덩어리에 대해 하나의 fixup 커밋만 생성 가능

Auto-stage all changes if nothing staged

  • 기본적으로 스테이지된 파일만 고려함
  • 스테이지된 변경 사항이 없을 때 모든 변경 사항을 자동으로 스테이지하려면 autoStageIfNothingStaged 설정

Fixup target always SHA

  • 기본적으로 fixup 커밋 메시지는 대상 커밋의 요약을 가리킴
  • 항상 대상의 SHA를 가리키도록 설정 가능

TODO

  • 강제 플래그 구현
  • 원격 기본 브랜치 확인 구현
  • 개별 안전 검사 비활성화를 위한 작은 강제 플래그 추가
  • 모든 오류 출력이 사용자에게 유용하도록 보장
  • 성공 시 더 많은 로그 출력
  • 더 많은 테스트 추가
  • 스택 및 교환 세부 사항 문서화
  • 더 많은 교환 사례 추가
  • 동시에 모든 덩어리를 메모리에 로드하지 않도록 구현
  • 동시 수정으로부터 보호하기 위한 인덱스 잠금 구현

GN⁺의 정리

  • git absorb는 Facebook의 hg absorb에서 포팅된 도구로, 커밋을 자동으로 수정하여 개발 워크플로우를 개선함
  • 리뷰 피드백을 적용할 때 매우 유용하며, 수동으로 커밋을 찾거나 수정할 필요 없이 자동으로 처리해줌
  • 이와 유사한 기능을 제공하는 다른 도구로는 git-autofixup이 있음. 커밋 메시지 기반으로 fixup 커밋을 자동 생성해주는 도구임
  • 장점은 코드 리뷰 피드백 반영이 쉬워지고, 커밋 히스토리가 더 깔끔해진다는 점. 단점은 자동화에 너무 의존하다 보면 예상치 못한 결과가 발생할 수 있다는 점
GeekNews Weekly에 포함된 글입니다. 에디터 코멘트 보기

댓글과 토론

Hacker News 의견
  • git absorb를 사용하는 사람들은 이 도구가 매우 유용하다고 생각함

    • 여러 커밋이 포함된 PR이 CI에서 실패할 때, git absorb는 자동으로 올바른 커밋을 찾아줌
    • 수동으로 커밋을 찾는 번거로움을 덜어줌
    • 가짜 긍정은 거의 없고, 가짜 부정이 발생하면 수동으로 처리하면 됨
    • PR이 하나의 커밋이어야 한다는 의견도 있지만, 항상 그런 것은 아님
    • GitHub에서 논리적으로 작은 커밋을 선호함
  • git commit --fixup을 위한 별칭을 사용하여 만족함

    • 현재 브랜치의 커밋을 보여주고 fzf를 통해 선택할 수 있음
    • 선택한 커밋을 기반으로 fixup 커밋을 생성함
  • git absorb를 사용해봤지만, 부모 커밋을 잘못 선택하는 경우가 많았음

    • 수동으로 커밋을 찾는 것이 더 나았음
    • 커밋 기록을 완전히 제어하는 것을 선호함
    • 이 도구는 너무 많은 마법 같음
  • git --fixupgit rebase --autosquash를 사용해본 적은 없지만 유용해 보임

    • git-absorb는 한 단계 더 나아가는 것 같음
    • README에서 특정 상황에서 정확히 무엇을 할지 명확하지 않음
  • 커밋과 기록을 수정하는 것에 대해 회의적임

    • 실수를 수정하고 새로운 커밋을 만들고 계속 진행하는 것이 좋음
  • magit을 사용하여 쉽게 fixup 커밋을 만들 수 있음

    • Emacs를 사용하지 않더라도 magit을 사용하기 위해 유지할 가치가 있음
    • vscode 사용자에게는 Edamagit이 좋음
  • git commit --fixupgit rebase --autosquash에 대해 알게 됨

    • 인터랙티브 git rebase는 가장 좋아하는 Git 도구임
    • 완벽한 논리적 원자 커밋을 만드는 데 도움이 됨
    • 그러나 이러한 기록 편집이 때때로 역효과를 낼 수 있음
  • git rebase -i는 다양한 요구를 충족시킴

    • squash, fixup, reword, delete 커밋을 인터랙티브하게 할 수 있음
    • git을 사용하는 모든 사람이 배워야 함
  • 초청결한 커밋 기록에 집착하는 것을 이해하지 못함

    • 커밋 기록을 그렇게 자주 자세히 확인하는지 의문임
  • 마지막 10개의 커밋 중 충돌 없는 수정 가능한 커밋을 선택하는 것이 이해되지 않음

    • 종종 충돌을 해결하면서 fixup^를 자주 사용함
    • 자동으로 타겟 커밋을 선택하는 것에 대해 신뢰하지 않음
  • 초청결한 커밋 기록에 집착하는 것을 이해하지 못함
    • 커밋 기록을 그렇게 자주 자세히 확인하는지 의문임

이 댓글 찔리네요. fixup / autosquash 를 조금 찍먹 해보았는데, 나만 신경쓰는건가 싶어서 조금 현타가 오긴 하네요...

Lobste.rs 의견들
  • jj가 등장하면서 Git 사용성에 관한 논의가 다시 활발해졌고, 최근 Git에도 git history가 추가됐지만 매일 업무에서 쓰는 git absorb 는 아직 충분히 알려지지 않은 듯함
    먼저 풀 리퀘스트에 넣을 대략적인 커밋 묶음을 만든 뒤, 개발하면서 수정 사항을 알맞은 커밋에 계속 흡수시키는 방식으로 작업함

    • 변경 이력을 활용해 수정 사항과 커밋을 짝짓는 매칭 성능까지 빨라지면 아주 좋겠음
  • Magitgit absorb 연동을 지원하며, 유용한 도구로 잘 쓰고 있음

    • 잠깐, Magit 어디에서 쓸 수 있나?
  • 예전에 lobste.rs 스레드에서 누군가 알려준 뒤로 git absorb 를 쓰기 시작했는데 정말 훌륭함
    리뷰가 오래 걸리는 고객사 프로젝트에서 상당히 깊은 PR 스택을 관리하고 있지만, 최상단에서 변경한 뒤 이력 속 적절한 커밋에 흡수하고 이 블로그 글git push-stack 별칭으로 모든 PR을 갱신하면 돼서 전혀 부담스럽지 않음

  • Graphite는 스택을 인식하는 버전을 제공한다고 함: https://www.graphite.com/docs/command-reference#gt-absorb

  • git absorb는 좋지만 수정 커밋을 만든 뒤 리베이스까지 자동으로 해주면 더 좋겠음
    추가 단계가 크게 번거롭지는 않아 수년간 안정적으로 사용하고 있음

    • 원하는 기능은 아마 -r 플래그일 것 같음
  • 리뷰 피드백을 반영하려고 굳이 이력을 다시 쓰는 이유가 무엇인지 모르겠고, 오히려 혼란스러워 보임

    • 처음에 잘못 작성한 커밋 자체를 올바르게 만들기 위해서임
      리뷰어가 나중에 코드를 처음부터 커밋별로 검토하더라도, 뒤에서 완전히 바뀐 내용을 앞선 커밋에서 따로 볼 필요가 없어짐
      다만 변경 단위와 그 안의 커밋을 어떻게 구성하는지에 따라 달라짐
    • GitHub를 쓰지 않는 여러 조직의 스택형 PR·차이점 검토 작업 흐름에 해당하며, 이런 방식은 원자적이고 git bisect로 추적 가능한 커밋에 초점을 맞춤
    • 변경 사항을 작성해 푸시했는데 린터에서 걸렸다면, 그 수정만 별도 커밋으로 남기기보다 원래 변경에 포함하는 편이 자연스러움
      수정 후 git absorb를 실행하면 깔끔하게 처리됨
      대규모 CI 모음을 돌리다가 미처 생각하지 못한 경계 사례로 문제가 생겼을 때도 사용함
      경계 사례를 고치고 테스트를 추가하면 git absorb가 수정 내용을 fixup! 커밋에 넣어주고, 여기에 테스트까지 보완해 모두 원래 커밋으로 합칠 수 있음
      작업량이 많을 때는 하루에 50~100번씩 사용함