커밋 메시지
고친 사람 github-actions[bot]
커밋 메시지는 이번 변경을 왜 했는지 이력에 남겨 둡니다. 무엇이 바뀌었는지는 코드를 견줘 보면 드러납니다. 왜 바꿨는지는 고친 사람의 머릿속에만 있습니다. 커밋 메시지가 그 빈칸을 맡습니다.
쉽고 빠른 이해
커밋 메시지는 변경 한 묶음을 이력에 넣을 때 같이 적는 설명입니다. 요청끼리 엇갈리던 문제를 고친 변경이라면 「요청마다 번호를 붙여 가장 최근 요청의 응답만 받는다. 엇갈림을 누그러뜨리려고 넣었던 타임아웃은 이제 필요 없어 지운다」가 그 설명입니다.
이게 없으면 몇 달 뒤에 곤란해집니다. 코드는 지금 모습만 보여 줍니다. 왜 그 모습이 됐는지는 안 보여 줍니다.
어떻게 도는가:
- 바꾼 파일을 한 묶음으로 고릅니다
- 그 묶음을 이력에 넣을 때 제목 한 줄과 설명을 적습니다
- 나중에 이력을 뒤지는 사람이 제목으로 커밋을 고릅니다. 고른 커밋의 설명을 읽습니다
대가가 있습니다. 남들과 이미 나눈 이력은 메시지만 고치기도 어렵습니다. 형식을 검사하는 도구는 모양만 봅니다. 이유가 적혔는지는 못 봅니다.
상세
이삿짐 상자에 「주방 · 깨지는 것」이라고 적어 붙여 두면 열어 보지 않고도 무엇이 들었는지 압니다. 커밋 메시지는 한 걸음을 더 갑니다. 왜 그것을 담았는지까지 적습니다.
커밋은 바뀐 파일을 한 묶음으로 확정해 이력에 넣는 일입니다. 버전관리 도구는 커밋을 차례로 쌓아 프로젝트의 이력을 만듭니다. 커밋 메시지는 그 묶음마다 하나씩 딸리는 설명 글입니다.
요청끼리 엇갈리던 문제를 고친 커밋을 예로 듭니다. 바뀐 코드를 견주면 두 가지가 보입니다. 하나는 요청마다 붙은 번호입니다. 가장 최근 요청이 아닌 응답은 이 번호로 가려 버립니다. 다른 하나는 지워진 타임아웃 코드입니다.
그런데 타임아웃을 왜 지워도 되는지는 코드에 없습니다. 「엇갈림을 누그러뜨리려고 넣었던 타임아웃이라 이제 필요 없다」는 문장은 메시지에만 있습니다.
그래서 메시지가 맡는 몫은 「무엇」보다 「왜」입니다. 「무엇이」는 바뀐 코드가 이미 말합니다. 코드를 읽으면 바로 보이는 것을 한 번 더 적어도 얻는 것이 없습니다.
메시지는 사람이 씁니다. 읽는 쪽도 나중의 사람입니다. 수천 개의 커밋 가운데 어느 것이 문제를 들였는지 찾을 때 그 사람은 메시지를 훑습니다. 어느 변경을 되돌릴지 고를 때도 메시지를 봅니다.
메시지는 커밋마다 하나씩 붙습니다. 그래서 쓸지 말지를 고르는 일은 없습니다. 고르는 것은 무엇을 적느냐입니다.
배경
코드는 지금 모습만 남깁니다. 이 줄을 왜 이렇게 바꿨는지는 고친 사람의 머릿속에만 있습니다. 그 사람도 몇 달 지나면 잊습니다. 팀을 떠나면 물어볼 데도 없습니다.
코드 안의 주석으로는 이 빈칸이 안 메워집니다. 주석은 지금 코드의 한 줄에 붙습니다. 그 줄이 다시 바뀌면 주석도 고쳐지거나 지워집니다. 필요한 것은 변경 한 번에 붙어서 그 변경과 함께 남는 설명이었습니다.
그래서 버전관리 도구는 커밋을 만들 때 설명 글을 함께 받습니다. 변경과 설명이 한 단위로 이력에 쌓입니다. 이 설명을 커밋 메시지라고 부릅니다. 도구에 따라 로그 메시지라고도 부릅니다.
형태
이 절은 커밋 메시지 한 벌이 어떤 토막으로 나뉘는지 봅니다. 토막은 위에서부터 제목 한 줄 · 본문 · 꼬리말 셋입니다. 이 가운데 본문과 꼬리말은 없어도 됩니다. 토막 사이마다 빈 줄이 하나 섭니다.
block-beta columns 1 t["제목 한 줄"] b1["빈 줄"] body["본문 · 문단이 여럿이어도 된다"] b2["빈 줄"] f["꼬리말"]
토막마다 담는 것이 다릅니다.
| 토막 | 무엇을 담나 | 생김새 |
|---|---|---|
| 제목 | 이번 변경을 한 줄로 줄인 요약 | 짧은 한 줄. git log --oneline 처럼 한 줄로 훑을 때는 이 줄만 보일 수 있다 |
| 빈 줄 | 아무것도 안 담는다 | 토막 사이마다 하나. 제목과 본문, 본문과 꼬리말을 가르는 경계 |
| 본문 | 제목에 못 넣은 사정. 무엇이 문제였나 · 왜 이 방법인가 | 자유 문단. 여러 문단이어도 된다. 없어도 된다 |
| 꼬리말 | 도구도 읽는 정보. 누가 검토했나 · 어느 이슈와 이어지나 | 메시지 맨 끝 덩어리. 가장 단순한 꼴은 키: 값 한 줄. 없어도 된다 |
빈 줄이 경계 노릇을 합니다. 제목은 첫 빈 줄 앞까지입니다. 빈 줄을 빠뜨리고 둘째 줄부터 본문을 이어 쓰면 본문 첫 문단까지 제목으로 읽힙니다.
꼬리말은 트레일러(trailer)라고도 부릅니다. 가장 단순한 꼴은 키: 값 한 줄입니다. 꼬리말 덩어리 앞에는 빈 줄이 하나 섭니다.
키는 영문자 · 숫자 · 하이픈으로 짓습니다. 낱말 사이는 하이픈으로 잇습니다. Signed-off-by 와 Reviewed-by 가 그렇게 지은 키입니다.
제목 앞에 종류를 붙이는 규약
모양을 더 좁게 정하는 규약도 있습니다. Conventional Commits 가 그 하나입니다. 커밋 메시지 위에 얹는 약속입니다. 제목의 꼴은 이렇습니다.
<type>[optional scope]: <description>
| 칸 | 무엇을 정하나 | 안 쓰면 |
|---|---|---|
type |
변경의 종류. 기능을 더했으면 feat, 버그를 고쳤으면 fix 를 써야 합니다. 그 밖의 낱말도 쓸 수 있습니다. docs 같은 것입니다 |
못 뺍니다 |
scope |
코드베이스의 어느 부분을 고쳤는지. 괄호 안에 명사로 적습니다. fix(parser): |
빼도 됩니다 |
! |
호환을 깨는 변경이라는 표시. 콜론 바로 앞에 붙입니다 | 꼬리말로 대신 알릴 수 있습니다 |
description |
콜론과 공백 바로 뒤에 오는 짧은 요약 | 못 뺍니다 |
호환을 깨는 변경은 제목의 ! 나 꼬리말 둘 중 하나로 알려야 합니다. 꼬리말로 알릴 때는 대문자 BREAKING CHANGE: 뒤에 설명을 붙입니다.
이 규약의 꼬리말은 토큰과 값을 콜론과 공백으로 가릅니다. 공백과 # 로 가르는 꼴도 씁니다. 값에는 공백과 줄바꿈이 들 수 있습니다. 그래서 한 꼬리말이 여러 줄이 되기도 합니다.
모양이 정해지면 도구가 메시지를 읽을 수 있습니다. 이 규약은 그런 도구를 짓기 쉽게 하려고 만든 것입니다. 메시지가 기능 추가 · 버그 수정 · 호환을 깨는 변경 셋을 가려 주기 때문입니다.
시맨틱 버저닝(Semantic Versioning)은 바뀐 것의 종류를 보고 버전 번호의 어느 토막을 올릴지 정하는 약속입니다. 메시지가 그 셋을 가려 주므로 이 규약은 시맨틱 버저닝과 맞물립니다.
예시
Git
Git 에서는 첫 빈 줄 앞까지의 글이 커밋 제목입니다. 제목은 50자를 넘지 않는 한 줄로 쓰기를 권합니다. 꼭 지켜야 하는 것은 아닙니다. 그 아래에 빈 줄을 하나 둡니다. 자세한 설명은 빈 줄 다음에 씁니다.
Git 의 여러 명령이 이 제목을 꺼내 씁니다. git format-patch 는 커밋 하나를 메일 한 통으로 바꿉니다. 제목은 메일의 Subject 줄로 갑니다. 나머지는 메일 본문이 됩니다.
기본으로 Subject 앞에 [PATCH] 가 붙습니다. 출력 파일 이름도 메시지 첫 줄에서 따옵니다.
From 8f72bad1baf19a53459661343e21d6491c3908d3 Mon Sep 17 00:00:00 2001
From: Tony Luck <tony.luck@intel.com>
Date: Tue, 13 Jul 2010 11:42:54 -0700
Subject: [PATCH] =?UTF-8?q?[IA64]=20Put=20ia64=20config=20files=20on=20the=20?=
=?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20diet?=
arch/arm config files were slimmed down using a python script
(See commit c2330e286f68f1c408b4aa6515ba49d57f05beae comment)
Do the same for ia64 so we can have sleek & trim looking
...
Subject 줄은 메일에 실으려고 인코딩된 꼴입니다. 풀면 [IA64] Put ia64 config files on the Uwe Kleine-König diet 입니다. 메일 본문이 커밋 메시지의 본문입니다.
본문 첫 문단이 까닭을 댑니다. arch/arm 쪽 설정 파일을 파이썬 스크립트로 줄인 적이 있다는 것입니다. 괄호 안의 긴 16진수는 그 커밋을 가리키는 식별값입니다. 둘째 문단은 ia64 에도 같은 일을 한다고 적습니다.
리눅스 커널
리눅스 커널은 변경을 패치 메일로 받습니다. 패치는 바뀐 줄을 담은 변경분입니다.
메일에 적은 설명은 Git 의 커밋 메시지로 옮겨집니다. 변경 로그는 무엇이 언제 왜 바뀌었는지 쌓아 둔 기록입니다. 메일의 본문은 이 영구 변경 로그에 남습니다. 그래서 커널은 본문에 담을 것을 차례로 요구합니다.
| 차례 | 담을 것 |
|---|---|
| 1 | 문제. 한 줄짜리 버그 수정이든 5000줄짜리 새 기능이든 이 일을 하게 만든 문제가 있습니다. 검토하는 사람이 고칠 만한 문제라고 믿게 씁니다 |
| 2 | 사용자에게 보이는 영향. 코드 리뷰 중에 찾은 문제여도 사용자에게 끼칠 수 있는 영향을 적습니다 |
| 3 | 수치. 성능 · 메모리 · 스택 · 바이너리 크기가 나아졌다고 하면 받쳐 줄 숫자를 적습니다. 드러나지 않는 비용도 적습니다 |
| 4 | 무엇을 했나. 문제를 세운 뒤에 기술적으로 자세히 적습니다 |
패치 하나는 문제 하나만 풉니다. 설명이 길어지기 시작하면 패치를 쪼개야 한다는 신호일 수 있습니다. 문장은 명령형으로 씁니다. 「[This patch] makes xyzzy do frotz」가 아니라 「make xyzzy do frotz」입니다. 코드베이스에게 동작을 바꾸라고 지시하듯 씁니다.
제목에는 패치가 무엇을 바꾸는지와 왜 필요할 수 있는지를 함께 담습니다. 길이는 70~75자 안입니다. 두세 달 뒤 git log --oneline 으로 수천 개의 패치를 훑는 사람이 보는 것은 이 줄뿐일 수 있습니다. 커널 문서가 예로 든 제목 둘입니다.
Subject: [PATCH 2/5] ext2: improve scalability of bitmap searching
Subject: [PATCH v2 01/27] x86: fix eflags tracking
다른 커밋을 가리킬 때는 식별값만 쓰지 않습니다. 그 커밋의 한 줄 요약을 함께 적습니다.
Commit e21d2170f36602ae2708 ("video: remove unnecessary
platform_set_drvdata()") removed the unnecessary
platform_set_drvdata(), but left the variable "dev" unused,
delete it.
이 문단이 「왜」를 적은 예입니다. 앞선 커밋이 쓸모없는 함수 호출을 지웠습니다. 그러면서 변수 dev 를 안 쓰는 채로 남겼습니다. 그래서 그 변수를 지운다는 것입니다.
꼬리말은 둘이 자주 나옵니다.
Fixes: 54a4f0239f2e ("KVM: MMU: make kvm_mmu_zap_page() return the number of pages it actually freed")
Signed-off-by: Random J Developer <random@developer.example.org>
Fixes: 는 이 패치가 어느 커밋의 버그를 고치는지 가리킵니다. 식별값은 앞 12자 이상을 씁니다. 그 뒤에 한 줄 요약을 붙입니다.
이 줄은 여러 줄로 쪼개지 않습니다. 75열에서 줄을 바꾸라는 규칙에서도 빠집니다. 스크립트가 읽기 쉽게 하려는 것입니다.
Signed-off-by: 는 이 패치를 직접 썼거나 오픈소스 패치로 넘길 권리가 있다고 밝히는 줄입니다. 알려진 신원으로 적습니다. 익명은 받지 않습니다. git commit -s 를 쓰면 저절로 붙습니다.
메일에서 변경 로그에 안 남길 말은 --- 한 줄 아래에 적습니다. 패치를 다루는 도구는 이 줄을 보고 변경 로그가 어디서 끝나는지 압니다.
Conventional Commits
앞 절 「상세」에서 든 요청 엇갈림 커밋의 원문입니다. 이 규약이 직접 실은 메시지입니다.
fix: prevent racing of requests
Introduce a request id and a reference to latest request. Dismiss
incoming responses other than from latest request.
Remove timeouts which were used to mitigate the racing issue but are
obsolete now.
Reviewed-by: Z
Refs: #123
제목은 fix 로 시작하므로 버그 수정입니다. 본문 첫 문단은 무엇을 했는지 적습니다. 둘째 문단이 타임아웃을 지운 까닭입니다. 꼬리말 둘은 검토한 사람과 이어진 이슈 번호입니다.
호환을 깨는 변경은 이렇게 생겼습니다.
feat: allow provided config object to extend other configs
BREAKING CHANGE: `extends` key in config file is now used for extending other config files
본문 없이 제목 한 줄로 끝나는 메시지도 있습니다. docs: correct spelling of CHANGELOG 가 그렇습니다.
Subversion
Subversion 도 커밋마다 메시지를 받습니다. 이 도구는 로그 메시지라고 부릅니다. --message(-m)나 --file(-F)로 메시지를 안 주면 편집기를 띄워 쓰게 합니다.
$ svn commit -m "added howto section."
Sending a
Transmitting file data .
Committed revision 3.
커밋은 리비전 번호를 받습니다. 위에서는 3 입니다. svn log 는 리비전마다 번호 · 작성자 · 시각 · 메시지 줄 수를 한 줄에 보입니다. 메시지는 그 아래에 나옵니다.
$ svn log
------------------------------------------------------------------------
r20 | harry | 2003-01-17 22:56:19 -0600 (Fri, 17 Jan 2003) | 1 line
Tweak.
------------------------------------------------------------------------
이 절이 보이는 것은 Git 밖에도 같은 것이 있다는 점입니다. 이름이 로그 메시지로 다르고 커밋을 리비전 번호로 부르는 것이 다릅니다. 메시지가 커밋마다 하나씩 붙어 이력에 남는 것은 같습니다.
r20 의 메시지는 「Tweak.」 한 낱말입니다. 번호 · 작성자 · 시각은 도구가 남겨 줍니다. 왜 그렇게 고쳤는지만 안 남았습니다.
GitHub
메시지를 사람 말고 서비스가 읽기도 합니다. GitHub 은 커밋 메시지 안의 닫기 키워드를 읽습니다. close · closes · closed · fix · fixes · fixed · resolve · resolves · resolved 가 그 키워드입니다.
Closes #10
이 줄이 든 커밋이 기본 브랜치에 병합되면 같은 저장소의 10번 이슈가 닫힙니다. 그 커밋을 담은 풀 리퀘스트는 연결된 풀 리퀘스트로는 표시되지 않습니다.
운영
이 절은 커밋 메시지를 굴릴 때 아픈 곳 셋을 봅니다. 대상은 Git · Subversion · Mercurial 의 명령 · 설정 키 · 훅입니다. 훅은 저장소가 어떤 일을 하기 바로 전이나 뒤에 실행해 주는 스크립트입니다.
아픈 곳 셋은 이렇습니다.
- 남과 나눈 메시지는 세 도구 모두에서 고치기 어렵습니다. 막는 까닭은 도구마다 다릅니다
- 메시지에 틀을 거는 설정과 훅이 있습니다. 훅은 건너뛸 수 있습니다
- 형식 검사는 모양만 잽니다
남과 나눈 메시지를 고칠 때
Git 은 커밋을 내용으로 부릅니다. 커밋 객체에는 그 시점의 파일 트리, 부모 커밋(있으면), 작성자와 커미터(커밋을 만든 사람) 정보, 빈 줄, 그리고 메시지가 들어갑니다. 부모 커밋은 이 커밋보다 앞서 쌓인 커밋을 가리킵니다. 첫 커밋에는 부모가 없고, 부모가 둘 이상인 커밋도 있습니다.
Git 은 이 내용에 머리 정보를 더해 SHA-1(Secure Hash Algorithm 1) 해시를 계산합니다. 40자짜리 그 값이 커밋의 이름입니다. 앞 절 예시에서 커밋을 가리키던 긴 16진수 식별값이 이 해시입니다.
메시지는 커밋 내용의 일부입니다. 그래서 메시지 한 줄을 고치면 해시가 바뀝니다. git commit --amend 로 마지막 메시지를 고치면 새 커밋이 생겨 마지막 커밋 노릇을 합니다.
리베이스는 커밋들을 차례로 다시 쓰는 명령입니다. 대화형 리베이스의 reword 로 더 오래된 메시지를 고치면 그 커밋과 뒤의 모든 커밋이 새로 쓰입니다.
flowchart TD
subgraph before["고치기 전"]
C1["커밋 C"] -->|부모| B1["커밋 B"]
B1 -->|부모| A1["커밋 A"]
end
subgraph after["B 의 메시지만 고친 뒤"]
C2["커밋 C · 새 해시"] -->|부모| B2["커밋 B · 새 해시"]
B2 -->|부모| A2["커밋 A · 그대로"]
end
손댄 것은 B 의 메시지뿐입니다. 그런데 C 까지 새것이 됐습니다. C 는 부모인 B 의 해시를 내용에 품고 있기 때문입니다.
이미 원격 저장소로 push 한 커밋을 이렇게 다시 쓰면 같은 변경이 다른 해시로 두 벌 생깁니다. 다른 개발자는 어느 쪽을 따라야 할지 헷갈립니다. 그래서 마땅한 까닭이 없으면 push 한 작업은 끝난 것으로 봅니다.
다른 두 도구는 막는 까닭이 다릅니다.
| 도구 | 메시지가 사는 곳 | 고치는 명령 | 무엇이 막나 |
|---|---|---|---|
| Git | 커밋 내용. 해시에 들어간다 | git commit --amend · 리베이스의 reword |
해시가 바뀌어 뒤 커밋까지 새로 쓰인다 |
| Subversion | 리비전 속성 svn:log. 버전이 매겨지지 않는 속성이다 |
svn propset --revprop -r 25 svn:log "…" |
기본은 거부. pre-revprop-change 훅이 있어야 열린다 |
| Mercurial | changeset(이 도구의 커밋) | hg commit --amend |
public 단계가 된 changeset 과 자식이 달린 changeset 은 거부 |
Subversion 의 pre-revprop-change 는 보통 커밋 밖에서 리비전 속성을 바꾸려 할 때 바로 앞에 돕니다. 다른 훅과 달리 기본 상태가 거부입니다. 훅이 없거나 실행할 수 없거나 0 이 아닌 값으로 끝나면 속성은 안 바뀝니다. 이 훅은 저장소 관리자가 만들어 둡니다.
Mercurial 은 changeset 마다 단계를 둡니다. 기본으로 새 changeset 은 draft 단계에서 시작합니다. 다른 저장소로 push 하면 public 단계로 옮겨 갑니다. public 이 되면 rebase 같은 확장도 그 changeset 을 다루기를 거부합니다. 같은 변경이 두 벌 생기는 것을 막으려는 것입니다.
메시지에 틀을 거는 설정과 훅
Git 은 메시지 모양을 미리 깔거나 검사하는 설정 · 훅 · 명령을 둡니다.
| 설정 · 훅 · 명령 | 무엇을 하나 |
|---|---|
commit.template |
새 커밋 메시지의 틀로 쓸 파일 경로를 정합니다 |
commit.cleanup |
git commit --cleanup 의 기본값을 바꿉니다. 기본값에서는 주석 문자(core.commentChar, 기본 #)로 시작하는 줄이 메시지에 안 남습니다. whitespace 로 두면 남습니다. 그때는 틀에 든 안내 줄을 손으로 지워야 합니다 |
prepare-commit-msg 훅 |
기본 메시지를 만든 직후, 편집기를 띄우기 전에 돕니다. 메시지 파일 이름과 메시지가 어디서 왔는지(message · template · merge · squash · commit)를 받습니다. 0 이 아닌 값으로 끝나면 커밋이 멈춥니다 |
commit-msg 훅 |
제안된 메시지 파일 이름 하나를 받습니다. 파일을 고쳐 프로젝트 형식에 맞추거나 커밋을 거절할 수 있습니다. 0 이 아닌 값으로 끝나면 멈춥니다. --no-verify 로 건너뛸 수 있습니다 |
git commit -s |
Signed-off-by: 꼬리말을 붙입니다 |
git interpret-trailers |
메시지 끝의 꼬리말을 붙이거나 읽어 냅니다 |
Git 이 함께 주는 기본 commit-msg 훅은 켜 두면 같은 Signed-off-by 꼬리말이 두 번 들었는지 봅니다. 들었으면 커밋을 멈춥니다.
꼬리말로 읽히는 데도 조건이 있습니다. 메시지 맨 끝 덩어리여야 합니다. 그 앞에 빈 줄이 있어야 합니다.
덩어리가 전부 꼬리말이면 됩니다. 아니면 두 조건을 다 채워야 합니다. Git 이 만들거나 설정한 꼬리말이 하나 이상 있어야 합니다. 꼬리말 줄이 덩어리의 25% 이상이어야 합니다.
flowchart TD
A["메시지 맨 끝 덩어리인가"] -->|아니다| X["꼬리말 덩어리가 아니다"]
A -->|그렇다| B["덩어리 앞에 빈 줄이 있나"]
B -->|없다| X
B -->|있다| C["덩어리가 전부 꼬리말인가"]
C -->|그렇다| Y["꼬리말 덩어리로 읽힌다"]
C -->|아니다| D["Git 이 만들거나 설정한 꼬리말이 하나 이상이고 꼬리말 줄이 25% 이상인가"]
D -->|그렇다| Y
D -->|아니다| X
형식 검사가 재는 것
commitlint 같은 검사 도구는 메시지를 규칙 목록에 대 봅니다. 규칙 이름은 대상과 조건을 이어 짓습니다. 조건은 비었나 · 길이 · 대소문자 · 끝 마침표 · 빈 줄로 시작하나 · 정해 둔 낱말에 드나 같은 것입니다.
대상은 header · type · scope · subject · body · footer 처럼 메시지의 토막인 것이 많습니다. 토막이 아닌 것도 대상이 됩니다. signed-off-by 와 trailer-exists 는 메시지 전체를 봅니다. references-empty 는 이슈 참조를 봅니다.
| 규칙 | 보는 것 |
|---|---|
type-enum |
type 이 정해 둔 목록에 드나 |
subject-empty |
제목 줄에서 종류 · 범위를 뗀 설명 칸이 비었나 |
header-max-length |
제목 줄 전체가 정한 길이 안인가 |
body-leading-blank |
본문이 빈 줄로 시작하나 |
signed-off-by |
메시지에 정한 서명 줄이 있나 |
trailer-exists |
정한 꼬리말이 있나 |
목록 어디에도 본문이 무엇을 담았는지 따지는 규칙은 없습니다. 문제가 적혔나, 이유가 적혔나는 규칙 이름에 안 나옵니다. 검사가 재는 것은 모양입니다. 이유가 적혔는지는 사람이 읽어야 압니다.
관련 항목
커밋 메시지가 붙는 이력의 단위
커밋 · 커밋 객체 · 리비전 · 스냅숏 · 부모 커밋 · 해시 · SHA-1 · 태그
커밋 메시지를 적고 고치는 명령
git commit · git log · git format-patch · 리베이스 · 스쿼시 · 되돌리기 · 블레임
커밋 메시지의 모양을 정하는 규약
Conventional Commits · 트레일러 · 커밋 템플릿 · 커밋 훅 · commitlint · 변경 로그 · 시맨틱 버저닝
커밋 메시지를 읽어 굴러가는 협업 절차
코드 리뷰 · 풀 리퀘스트 · 패치 · 브랜치 전략 · 커밋에서 배포까지 · 릴리스 노트
커밋 메시지를 담아 두는 도구와 서비스
Git · Subversion · Mercurial · 버전관리 · GitHub · GitLab · 저장소
이력을 되짚을 때 커밋 메시지에 기대는 작업
다른 이름: commit message · 커밋메시지 · commit log message