Claude Code ghi gì vào commit của bạn?
Claude Code thêm trailer Co-authored-by vào commit; session cloud còn chèn link claude.ai. Xem chính xác nội dung trong git history trước khi push public.
Claude Code ghi gì vào commit
Claude Code thêm một trailer Co-authored-by: ở cuối commit message mà nó tạo, đồng thời thêm một dòng ghi công vào phần mô tả pull request mà nó mở. Các session chạy trên cloud hoặc thông qua Remote Control cũng thêm một liên kết đến session trên claude.ai. Tất cả đều là văn bản thuần được lưu trong git history. Sau khi push lên public repository, nội dung đó sẽ ở chế độ công khai và vẫn còn đó cho đến khi có người viết lại history.
Cách diễn đạt chính xác đã thay đổi giữa các bản release, vì vậy không nên tin vào bản sao trailer được in trong bất kỳ hướng dẫn nào, kể cả hướng dẫn này. Hãy đọc các commit của chính bạn. Các lệnh git bên dưới là phần ổn định của chủ đề này: cơ chế xử lý trailer của git đã hoạt động theo cùng một cách trong nhiều năm và sẽ tiếp tục như vậy ngay cả khi bản release tiếp theo thay đổi nội dung văn bản.
Một git trailer thực sự là gì
Trailer là một dòng có dạng như Token: value trong block cuối của commit message. Git không cung cấp danh sách token cố định. Signed-off-by:, Reviewed-by:, Fixes: và Co-authored-by: là các quy ước dựa trên cùng một cơ chế. Forge, tức website lưu trữ repository như GitHub hoặc GitLab, đọc các trailer này để quyết định nội dung hiển thị trên trang commit.
Git quy định chặt chẽ vị trí của block này. Tài liệu về git interpret-trailers nêu rằng nhóm này phải được ngăn cách với phần trước bằng một hoặc nhiều dòng trống, phải nằm ở cuối message hoặc ngay trước một dòng bắt đầu bằng ---, đồng thời phải chứa toàn bộ trailer hoặc "chứa ít nhất một trailer do Git tạo hoặc do người dùng cấu hình và có ít nhất 25% số dòng là trailer".
Quy tắc cuối rất quan trọng trong trường hợp này. Một URL đứng riêng hoặc một dòng văn bản trong block cuối không phải là trailer. Nếu có đủ dòng không phải trailer, toàn bộ block sẽ không được phân tích thành trailer. Vì vậy, một commit có thể trông như chứa dòng Co-authored-by:, nhưng mọi công cụ đọc trailer đúng cách lại không thấy gì.
Cách đọc các trailer đã có trong history là gì?
Bắt đầu bằng raw message của commit mới nhất.
git log -1 --format=%B%B in subject và body chính xác như được lưu, không wrap và không format lại. Đây là dữ liệu gốc cần đối chiếu. Mọi nội dung forge hiển thị đều là bản render từ dữ liệu đó.
Bây giờ yêu cầu git cho biết những dòng nào được tính là trailer.
git log -1 --format=%B | git interpret-trailers --parse--parse là dạng viết tắt của --only-trailers --only-input --unfold, vì vậy output chỉ chứa trailer block. Kết quả đúng thường có một dòng cho mỗi trailer. Nếu output rỗng trong khi bạn nhìn rõ một dòng Co-authored-by:, nghĩa là block đó không đáp ứng các quy tắc vị trí ở trên.
Để quét toàn bộ history, hãy tìm trailer theo key.
git log --format='%h %(trailers:key=Co-authored-by,valueonly)'Các phiên bản git cũ không hỗ trợ option key= trên %(trailers). Có thể tìm trong nội dung message; cách này hoạt động trên mọi phiên bản.
git log -i --grep='^Co-authored-by:' --format='%h %an %s'--grep khớp với commit message và -i giúp tìm không phân biệt chữ hoa chữ thường. Điều này cần thiết vì cách viết hoa của trailer này không nhất quán giữa các tool. Trước khi push, hãy giới hạn phạm vi kiểm tra vào những gì bạn chưa gửi.
git log origin/main..HEAD --format=%BCác commit đó vẫn chỉ có trên local, nên bạn vẫn có thể thay đổi chúng với chi phí thấp.
Co-authored-by: ảnh hưởng thế nào đến attribution trên GitHub?
GitHub đọc trailer này và hiển thị thêm một tác giả trên trang commit. GitHub chỉ liên kết tác giả đó với một profile khi địa chỉ email thuộc về một account. Tài liệu của GitHub nói rằng commit xuất hiện trên contributions graph khi được “tạo bằng địa chỉ email được liên kết với account của bạn trên GitHub”, vì vậy một địa chỉ không thuộc về account nào sẽ không thể liên kết với profile. Với một cặp cộng tác viên là người thật, đây chính là mục đích: địa chỉ của đồng nghiệp được liên kết với account của họ, và commit được tính cho cả hai người. Với một địa chỉ không thuộc về account nào, trailer này chỉ thay đổi nội dung hiển thị trên trang commit và không thay đổi danh sách contributor của repository.
Bản thân Git hoàn toàn bỏ qua trailer này. git shortlog -sn và git log --author đọc author header, trong đó có tên và email của bạn, nên local count sẽ không bao giờ hiển thị co-author. Attribution ở đây là một tính năng của forge được xây dựng trên một quy ước văn bản thuần túy. Đây chính là ranh giới giữa bản thân Git và forge nơi bạn host repository.
Liên kết session là một vấn đề khác
Trailer ghi tên một đồng tác giả. URL của session là con trỏ đến một transcript. Tài liệu tham chiếu về settings của Claude Code ghi nhận attribution.sessionUrl là key dùng để loại bỏ “liên kết session claude.ai khỏi các commit trên cloud và Remote Control”. Điều này cũng cho biết liên kết đó xuất phát từ đâu: các session trên web và các session được điều khiển qua Remote Control.
Liên kết này không phải là credential. Việc ai đó có thể mở session hay không phụ thuộc vào quyền truy cập tài khoản, không phải vào việc URL có được giữ bí mật hay không. Lý do không đưa liên kết này vào repository public đơn giản hơn. Đây là văn bản public tồn tại lâu dài, chứa internal session id và trỏ đến một transcript đang hoạt động vốn không được tạo cho người đọc bên ngoài. Với repository private, lập luận ngược lại có giá trị, vì reviewer có thể mở liên kết và xem thay đổi đã được thực hiện như thế nào. Phần Claude Code lưu session và resume session như thế nào giải thích các transcript này được lưu ở đâu và tồn tại trong bao lâu.
Các setting kiểm soát thông tin ghi nhận trong commit của Claude Code
Đã đối chiếu với tài liệu tham chiếu về setting của Claude Code vào ngày 1 September 2026. Các key này được ghi trong mục “Git and attribution”:
attribution: “Tùy chỉnh thông tin ghi nhận mà Claude Code thêm vào commit và pull request”attribution.commit: “Thay đổi hoặc ẩn trailer mà Claude Code thêm vào commit”attribution.pr: “Thay đổi hoặc ẩn dòng thông tin ghi nhận trong phần mô tả pull request”attribution.sessionUrl: “Bỏ liên kết session claude.ai khỏi commit trên cloud và Remote Control”includeGitInstructions: “Xóa các instruction tích hợp cho commit và PR khỏi system prompt”includeCoAuthoredBy: được đánh dấu là deprecated, kèm ghi chú “dùngattributionđể ẩn hoặc thay đổi thông tin ghi nhận trong commit và PR”
Hãy lấy giá trị mà mỗi key chấp nhận từ chính mục của key đó trên tài liệu tham chiếu về setting, thay vì lấy từ một guide. Tên key ổn định. Các giá trị được chấp nhận và giá trị mặc định mới là phần có thể thay đổi. Một settings file có key viết đúng nhưng value viết sai sẽ fail im lặng.
Vị trí bạn ghi setting sẽ quyết định ai nhận được setting đó. ~/.claude/settings.json áp dụng cho mọi project bạn mở. Một .claude/settings.json được commit ở thư mục gốc của repository sẽ áp dụng cho mọi người clone repository đó. .claude/settings.local.json chỉ thuộc về bạn trong project đó. Claude Code thêm file này vào global git excludes ngay lần đầu ghi file, nên file không xuất hiện trong các commit của bạn. Thứ tự ưu tiên là managed settings, command line, project local, shared project, rồi user. Vì vậy, file local của teammate sẽ được ưu tiên hơn file bạn đã commit. Hãy xem setting được commit là giá trị mặc định, không phải đảm bảo tuyệt đối.
Sau đó hãy xác minh, vì việc bạn tin rằng setting đã được áp dụng không phải là bằng chứng. Để Claude Code tạo commit tiếp theo theo cách bình thường, rồi đọc lại kết quả.
git log -1 --format=%B
git log -1 --format=%B | git interpret-trailers --parseNếu trailer vẫn được in ra, thay đổi chưa được áp dụng vào session. Trước khi kết luận key bị lỗi, hãy kiểm tra file bạn đã chỉnh sửa và kiểm tra lại thứ tự ưu tiên ở trên.
Các cơ chế kiểm soát không phụ thuộc vào một setting
Một setting chỉ cấu hình cho một công cụ. Rule của repository phải chịu được trường hợp contributor dùng công cụ khác hoặc không dùng agent nào. Git cung cấp một nơi để đặt rule đó: hook commit-msg chạy với đường dẫn đến file message làm đối số đầu tiên. Hook có thể sửa file này hoặc từ chối commit ngay lập tức.
#!/bin/sh
# .githooks/commit-msg
grep -qi '^co-authored-by: claude' "$1" || exit 0
grep -vi '^co-authored-by: claude' "$1" > "$1.new" && mv "$1.new" "$1"chmod +x .githooks/commit-msg
git config core.hooksPath .githooksgrep đầu tiên thoát sớm khi không có gì cần xử lý, nên commit thông thường gần như không phát sinh chi phí. grep thứ hai ghi lại message sau khi xóa dòng khớp. Hãy khớp đúng một token cụ thể thay vì mọi trailer, vì bộ lọc được viết theo kiểu “xóa block cuối” cũng sẽ xóa dòng Signed-off-by: mà project yêu cầu.
.git/hooks không thuộc repository, nên hook đặt ở đó sẽ không được chuyển cho người khác. core.hooksPath trỏ Git đến một directory mà bạn có thể commit. Mỗi người vẫn phải tự chạy dòng git config đó. Git sẽ không tự đặt cấu hình này cho họ. Đây là chủ ý: nếu repository có thể tự cài executable khi clone, nó có thể chạy code trên máy của bạn.
Để từ chối commit thay vì rewrite nó, hãy in message ra standard error rồi exit 1 từ hook. Từ chối là lựa chọn trung thực trong repository dùng chung, vì âm thầm sửa commit message sẽ che giấu rule thay vì giúp người dùng hiểu rule đó. Đây là một layer khác với hook riêng của agent. Hook của agent chạy khi gọi tool, trước khi Git được dùng. cách Claude Code hook khớp một tool và chặn tool đó trình bày phần này.
Cả hai cơ chế trên đều không xử lý được pull request từ fork, vì hook nằm trên máy mà bạn không kiểm soát. Một check trong CI (continuous integration) là layer duy nhất nhìn thấy mọi commit trước khi merge.
if git log --format=%B "origin/${BASE_BRANCH:-main}..HEAD" | grep -qi '^co-authored-by: claude'; then
echo "Attribution trailer found. Rewrite the branch before merging." >&2
exit 1
fiHãy ghi lại rule bên cạnh việc enforce rule đó. Một file AGENTS.md đặt cạnh CONTRIBUTING.md dễ đọc cho con người sẽ truyền đạt cùng một nội dung cho agent và người dùng. CI check là cơ chế biến rule đó thành yêu cầu bắt buộc.
Xóa trailer trước khi push
Với commit bạn vừa tạo:
git log -1 --format=%B | grep -vi '^co-authored-by: claude' | git commit --amend -F --F - đọc message mới từ standard input. Git tự xóa các dòng trống ở đầu và cuối message được cung cấp theo cách này, nên dòng trống còn lại sau khi xóa trailer cũng tự biến mất. Dùng git log -1 --format=%B để đọc lại kết quả trước khi tiếp tục.
Với nhiều commit trên một branch, hãy chạy interactive rebase dựa trên branch mà bạn sẽ merge vào, rồi đánh dấu từng message cần thay đổi bằng reword.
git rebase -i origin/mainMọi commit từ commit được chỉnh sửa sớm nhất trở đi đều nhận hash mới, vì hash của commit bao gồm message và parent của nó. Việc này không tốn nhiều chi phí khi các commit còn local, nhưng sẽ tốn kém sau khi chúng không còn local.
Xóa trailer sau khi đã push
Trên một branch chỉ mình bạn sử dụng, hãy rewrite branch đó như ở trên rồi push đè lên remote.
git push --force-with-lease--force-with-lease sẽ từ chối push nếu remote đã thay đổi kể từ lần fetch gần nhất của bạn. Vì vậy, nó không thể âm thầm loại bỏ một commit mà người khác đã thêm. --force thông thường không có kiểm tra này.
Với một trailer xuất hiện trong lịch sử dài, git filter-repo sẽ rewrite mọi message trong một lần chạy. Hãy chạy clone từ bên trong working copy hiện có, để URL được lấy từ remote mà bạn đã cấu hình.
pip install git-filter-repo
git clone "$(git remote get-url origin)" ../project-rewrite
cd ../project-rewrite
git filter-repo --message-callback '
return b"\n".join(l for l in message.split(b"\n") if not l.lower().startswith(b"co-authored-by: claude"))
'Callback nhận từng message dưới dạng bytes và trả về message cần lưu. Vì vậy, mọi string trong đó đều có tiền tố b. filter-repo sẽ không chạy trên repository không phải fresh clone, trừ khi bạn truyền --force. Khi hoàn tất, nó sẽ xóa remote origin để bạn không thể vô tình push ngược bản rewrite. Hãy chủ động thêm remote lại, force-push, rồi yêu cầu mọi người clone lại, vì mọi hash họ đang giữ đều không còn đúng.
Cần hiểu rõ rewrite không thể làm gì. Nó chỉ thay đổi bản sao repository của bạn. Nó không tác động đến fork, clone hiện có, mirror, các trang pull request đã trích dẫn message, hoặc code search index đã crawl repository của bạn vào tuần trước. Với secret bị lộ, cách xử lý là rotate secret; rewrite chỉ là bước dọn dẹp. Disclosure trailer không phải secret, vì vậy hãy cân nhắc việc rewrite toàn bộ history so với chi phí của nó: mọi pull request đang mở đều phải được rebuild.
Điều reviewer ở phía bên kia mong đợi
Các maintainer không thống nhất về việc này. Đây cũng là lý do thực sự bạn phải kiểm tra trước khi xóa bất kỳ nội dung nào. Một số project muốn giữ thông tin disclosure và sẽ yêu cầu bạn thêm lại, vì reviewer đọc patch do máy tạo ra với cách nhìn khác. Một số project cấm việc này, thường vì tranh luận về người có quyền tác giả hợp pháp đối với thay đổi. Các project dùng DCO (developer certificate of origin) yêu cầu dòng Signed-off-by:. Dòng này là tuyên bố về quyền gửi code của bạn, và một bộ lọc message bất cẩn có thể xóa cả dòng đó cùng với thông tin attribution.
Trước tiên, hãy đọc CONTRIBUTING.md. Nếu project không nói rõ, hãy chọn một quy tắc cho repository và ghi lại. Một trailer chỉ xuất hiện trong một nửa số commit còn tệ hơn cả hai lựa chọn trên: nó khiến một history trông như thuộc về hai project khác nhau.
Nếu bạn chạy agent trên server thay vì laptop, câu hỏi tương tự sẽ xuất hiện ở một lớp thấp hơn. Chạy Claude Code trên VPS bằng account riêng của nó quyết định agent có thể truy cập những repository nào, còn agent coding gửi gì ra ngoài máy đề cập đến lưu lượng không bao giờ xuất hiện trong commit message.
FAQ
Một trailer Co-authored-by cho Claude có được tính vào thống kê contributor của repository không?
Không. GitHub gắn một commit với profile thông qua địa chỉ email được liên kết với tài khoản GitHub, rồi tính contribution dựa trên thông tin đó. Địa chỉ không thuộc tài khoản nào thì không thể được liên kết, nên trailer chỉ thay đổi trang commit và không thay đổi danh sách contributor. Bản thân Git không đọc trailer cho việc này. git shortlog -sn đếm header author, nên co-author không xuất hiện ở đó.
Làm cách nào để tìm mọi commit đã có trailer này?
git log -i --grep='^Co-authored-by:' --format='%h %an %s' liệt kê các commit đó trên toàn bộ lịch sử và không phân biệt chữ hoa, chữ thường. Trên phiên bản git gần đây, git log --format='%h %(trailers:key=Co-authored-by,valueonly)' đọc giá trị bằng trailer parser của chính git thay vì so khớp văn bản thô. Để chỉ xem những gì bạn chưa push, hãy thêm một range: git log origin/main..HEAD --format=%B.
Tôi có thể xóa trailer khỏi các commit đã push không?
Có, bằng cách viết lại lịch sử, nhưng chi phí là đáng kể. Trên branch chỉ mình bạn sử dụng, hãy amend hoặc rebase, rồi chạy git push --force-with-lease. Trên branch dùng chung, mọi commit từ commit được chỉnh sửa đầu tiên trở đi sẽ có hash mới, nên mọi clone và mọi pull request đang mở đều phải được cập nhật lại. Việc viết lại lịch sử cũng không bao giờ cập nhật các fork, mirror hoặc clone mà ai đó đã tạo hôm qua.
Link session claude.ai trong commit message có phải là rủi ro bảo mật không?
Đó không phải là credential, và quyền truy cập session được quyết định bởi account chứ không phải bởi việc URL có khó đoán hay không. Vấn đề là đây là văn bản public cố định, trỏ đến một transcript được viết như working notes. Tài liệu tham chiếu về thiết lập Claude Code ghi nhận attribution.sessionUrl là key dùng để loại link đó khỏi các commit trên cloud và Remote Control, còn một hook commit-msg sẽ loại link khỏi mọi trường hợp mà thiết lập này không áp dụng.
Có nên tắt attribution hoàn toàn không?
Điều đó phụ thuộc vào repository, không phụ thuộc vào sở thích. Với một project public, hãy tuân theo CONTRIBUTING.md, vì maintainer muốn giữ thông tin disclosure sẽ yêu cầu bật lại. Với repository của công ty, giữ trailer thường hữu ích vì nó cho reviewer biết, ngay cả sau một năm, tại sao một thay đổi lại có dạng như vậy. Hãy quyết định một lần cho toàn bộ repository và enforce trong CI để lịch sử luôn nhất quán.