Google Copybara: Di chuyển mã giữa các kho lưu trữ
(github.com/google)- Copybara là công cụ được sử dụng nội bộ tại Google để chuyển đổi và di chuyển mã nguồn giữa nhiều kho lưu trữ, được dùng trong các trường hợp đồng bộ kho lưu trữ confidential và kho lưu trữ public
- Có thể chọn một kho lưu trữ làm kho lưu trữ có thẩm quyền để duy trì một nguồn chân lý duy nhất, nhưng vẫn có thể nhận đóng góp từ bất kỳ kho nào và tạo bản phát hành từ bất kỳ kho nào
- Trường hợp sử dụng chính là di chuyển mã lặp lại, hỗ trợ luồng lấy một phần mã từ kho confidential sang kho public hoặc đưa thay đổi từ kho public vào kho authoritative
- Copybara sử dụng cách tiếp cận stateless, lưu trạng thái không phải trên máy chủ riêng mà trong nhãn của thông điệp commit ở kho đích, nên nhiều người dùng hoặc dịch vụ có thể nhận được cùng kết quả với cùng cấu hình và kho lưu trữ
- Loại kho lưu trữ hiện được hỗ trợ là Git; khả năng đọc Mercurial là tính năng thử nghiệm, và kiến trúc có thể mở rộng để thêm origin và destination tùy biến
Vấn đề mà Copybara giải quyết
- Copybara là công cụ để di chuyển và chuyển đổi mã nguồn giữa các kho lưu trữ
- Có những trường hợp mã nguồn cần tồn tại ở nhiều kho lưu trữ, và Copybara cho phép chuyển đổi cũng như di chuyển mã giữa các kho đó
- Trường hợp tiêu biểu là dự án cần duy trì đồng bộ giữa kho lưu trữ confidential và kho lưu trữ public
- Cách dùng phổ biến nhất là lặp lại việc chuyển mã từ kho này sang kho khác
- Công cụ này cũng có thể dùng để chuyển mã một lần sang kho mới
Kho lưu trữ có thẩm quyền và luồng đóng góp
- Copybara yêu cầu chọn một trong các kho lưu trữ làm authoritative repository
- Đây là điều kiện để luôn tồn tại một source of truth duy nhất
- Có thể đóng góp từ bất kỳ kho lưu trữ nào
- Cũng có thể tạo bản phát hành từ bất kỳ kho lưu trữ nào
- Khi thay đổi phát sinh ở kho không có thẩm quyền, Copybara có thể chuyển đổi thay đổi đó và đưa nó đến vị trí phù hợp trong kho có thẩm quyền
- Ví dụ là thay đổi do contributor trong kho public tạo ra
- Xung đột hợp nhất được xử lý giống như cách xử lý các thay đổi cũ trong kho có thẩm quyền
Ví dụ sử dụng
- Các ví dụ dùng Copybara bao gồm
- Đưa một phần mã từ kho confidential sang kho public
- Đưa mã từ kho public vào kho confidential
- Đưa thay đổi từ kho non-authoritative vào authoritative repository
- Cấu hình ví dụ định nghĩa origin và destination bằng
core.workflow- origin dùng
git.github_originvới nhánhmastercủahttps://github.com/google/copybara.git - destination dùng
git.destinationvớifile:///tmp/foo destination_filesnhắm tớithird_party/copybara/**và loại trừREADME_INTERNAL.txtcore.replacevàcore.moveđược dùng để thay thế đường dẫn tệp BUILD và di chuyển thư mục
- origin dùng
- Ví dụ chạy là tạo một kho Git bare rồi chạy
copybara copy.bara.sky
Cách lưu trạng thái và hỗ trợ kho lưu trữ
- Một trong những đặc điểm chính của Copybara là kiến trúc stateless
- Nói chính xác hơn, trạng thái được lưu trong kho lưu trữ đích
- Vị trí lưu là nhãn trong thông điệp commit
- Với cách này, nhiều người dùng hoặc dịch vụ có thể dùng cùng một cấu hình và cùng tập hợp kho lưu trữ để nhận được cùng kết quả
- Loại kho lưu trữ hiện được hỗ trợ là Git
- Có thể đọc từ kho Mercurial nhưng đây vẫn là tính năng thử nghiệm
- Nhờ kiến trúc mở rộng được, có thể thêm origin và destination dạng bespoke cho gần như mọi trường hợp sử dụng
- Hỗ trợ chính thức cho các loại kho lưu trữ khác sẽ được bổ sung trong tương lai
Cài đặt và build
- Cách dễ nhất để bắt đầu là dùng snapshot release hằng tuần có sẵn binary dựng sẵn
- Bản phát hành được tạo tự động
- Không có kiểm thử thủ công, không đảm bảo tương thích phiên bản hay độ chính xác
- Có thể chọn bản phát hành tại
https://github.com/google/copybara/releases
- Nếu muốn dùng phiên bản chưa phát hành, cần build từ HEAD
- Cần cài JDK 11
- Cần cài Bazel
- Sao chép mã nguồn bằng
git clone https://github.com/google/copybara.git - Build bằng
bazel build //java/com/google/copybara - File uberjar thực thi được tạo bằng
bazel build //java/com/google/copybara:copybara_deploy.jar - Có thể chạy test bằng
bazel test //...
- Một số test yêu cầu cài các công cụ nền như Mercurial và Quilt
- Nếu Pull Request không liên quan đến các mô-đun đó thì có thể bỏ qua các test tương ứng
- CI sẽ chạy toàn bộ test
- Trên Arch Linux, có thể dùng gói
aur/copybara-git
Dùng Copybara dựng sẵn trong Bazel
- Có thể dùng snapshot release hằng tuần trong Bazel
- Copybara được cung cấp với class file version 65.0, nên phải chạy trên Java Runtime 21 trở lên
- Cần thêm
run --java_runtime_version=remotejdk_21vào.bazelrc
- Cần thêm
- Tải artifact phát hành bằng
http_jar- Trong
WORKSPACE, dùngload("@bazel_tools//tools/build_defs/repo:http.bzl", "http_jar") - Trong
MODULE.bazel, dùnghttp_jar = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_jar")
- Trong
- Trong
WORKSPACEhoặcMODULE.bazel, điền chỗ[version]để chỉ địnhcopybara_deploy.jar - Trong tệp BUILD, khai báo
java_binaryvà dùngcom.google.copybara.Mainlàm main class - Ví dụ chạy là
bazel run //tools:copybara -- migrate copy.bara.sky
Build mã nguồn như kho Bazel bên ngoài
- Có cung cấp macro tiện lợi cho các dependency của Copybara
- Thêm
http_archivevàoWORKSPACEvà điền các giá trị{{ sha256sum }}và{{ commit }} - Sau đó load và gọi các macro sau
copybara_repositories()copybara_maven_repositories()copybara_go_repositories()
- Trong workspace, có thể build và chạy bằng
bazel run @com_github_google_copybara//java/com/google/copybara -- <args...>
Sử dụng Docker
- Cách build và chạy Copybara bằng Docker hiện vẫn là thử nghiệm
- Build bằng
docker build --rm -t copybara . - Chạy từ thư mục gốc của mã đích theo dạng
docker run -it -v "$(pwd)":/usr/src/app copybara help - Có thể dùng biến môi trường thay cho tham số khi chạy container
COPYBARA_SUBCOMMAND=migrate: thay đổi lệnh chạy, mặc định làmigrateCOPYBARA_CONFIG=copy.bara.sky: chỉ định đường dẫn tệp cấu hình, mặc định làcopy.bara.skyở thư mục gốcCOPYBARA_WORKFLOW=default: chỉ định workflow cần chạy, mặc định làdefaultCOPYBARA_SOURCEREF='': chỉ định sourceref, mặc định không cóCOPYBARA_OPTIONS='': chỉ định các tùy chọn Copybara, mặc định không có
- Có thể chia sẻ cấu hình Git và thông tin xác thực SSH với container Docker
- Ví dụ là mount
~/.gitconfig,~/.ssh,SSH_AUTH_SOCKvào container
- Ví dụ là mount
Tài liệu và liên hệ
- Tài liệu hiện vẫn đang được hoàn thiện
- Các tài liệu hiện có gồm
- Có thể gửi câu hỏi qua mailing list
- Nếu muốn xem lỗi test Bazel mà không cần
cattệp log, có thể thêmtest --test_output=streamedvào~/.bazelrc
1 bình luận
Ý kiến trên Hacker News
Thật thú vị là bài này xuất hiện đúng lúc ngay sau khi tôi mã nguồn mở bản vá hỗ trợ Perforce mà tôi làm để dùng ở studio phát triển game
Xét việc mục đích chính của Copybara là phân phối mã nội bộ của Google, và số mã đó nằm trong Piper có liên quan đến Perforce, thì việc không có hỗ trợ Perforce mà chỉ có hỗ trợ Git đáng kể là điều khá bất ngờ
Trước khi tạo PR, tôi xem lịch sử Git thì thấy rất nhiều dấu Gerrit Change-ID, nên cũng lo là có một hệ thống review mã Gerrit nào đó mà tôi không thể truy cập và PR của tôi sẽ không được đưa upstream
Việc không có Gerrit/Rietveld cho Perforce cũng đáng tiếc, nhưng không thể đòi hỏi mọi thứ được
https://github.com/google/copybara/pull/347
https://abseil.io/resources/swe-book/html/ch19.html
https://read.engineerscodex.com/p/how-google-takes-the-pain-...
Tôi vẫn chưa tìm được công cụ bên ngoài nào tốt ngang Critique, và thật ngạc nhiên khi công cụ review PR còn khá yếu của GitHub lại được đa số chấp nhận. Nếu ai biết công cụ nào ở mức tương tự thì tôi rất muốn nghe
Khi rời Google vài năm trước, tôi đã tìm khá kỹ, và https://codeapprove.com/ là thứ gần nhất, nhưng vẫn còn thiếu khá nhiều
Các dự án mã nguồn mở như gVisor hay Bazel sống trong monorepo nội bộ cũng thường vận hành gần như vậy, dù có khác đôi chút tùy dự án
Một công cụ thú vị khác trong lĩnh vực này là Rust dùng công cụ tên Josh để đồng bộ commit
https://josh-project.dev
Cũng có một bài blog từ phía Rust
https://blog.rust-lang.org/inside-rust/2026/06/04/how-josh-h...
Meta trước đây từng có một công cụ mã nguồn mở tên fbshipit, nhưng theo kho công khai thì họ không còn dùng nữa
https://github.com/facebookarchive/fbshipit
Còn công cụ nào khác trong mảng này không?
Sau đó nó đã được hợp nhất vào Git chính thức
https://manpages.debian.org/testing/git-man/git-subtree.1.en...
https://docs.github.com/en/get-started/using-git/about-git-s...
Điều này có tiện khi nhiều kho muốn chia sẻ một ít mã, nhưng lại không đáng để tách thành thư viện, thêm tham chiếu, phát hành phiên bản, rồi cập nhật các kho phụ thuộc không?
Ví dụ, liệu có thể dùng nó để đơn giản đồng bộ một thư mục chứa mô hình miền dùng chung từ một kho chính sang các kho khác không?
Đây là kiểu nhu cầu gần với triết lý Go rằng “thà sao chép một chút còn hơn mang theo cả đống phụ thuộc”
Một số dự án được phát triển trong monorepo rồi dùng Copybara để xuất ra bên ngoài
Nhóm của chúng tôi cũng dùng nó nội bộ để quản lý phiên bản cho một bộ quy tắc Starlark
Nếu để kho công khai làm dependency của kho riêng nội bộ thì xét về mặt phát triển sẽ khá phiền. Khi các dependency như vậy bắt đầu mọc thành cây thì thực sự rất đau đầu
Tôi đã dùng khá lâu rồi, chủ yếu khi một công cụ được tạo ra trong một dự án lớn phát triển đủ lớn để phát hành độc lập
Nó đủ mạnh để xử lý toàn bộ quy trình phân phối hai chiều gồm xuất mã rồi nhập lại, nhưng việc đó quá phiền phức nên tôi tránh
Tôi chủ yếu dùng nó cho các lần xuất một chiều đơn giản: tách một thư mục khỏi kho lưu trữ gốc nhưng vẫn giữ lịch sử. Sau đó việc phát triển được chuyển sang kho lưu trữ mới
Tôi hài lòng vì
Git blamevẫn hoạt động ngay cả khi cấu trúc dự án mới hoàn toàn khácHai chiều thì trở nên lộn xộn, vì các phép biến đổi như ánh xạ lại đường dẫn, loại trừ tệp, xóa header thì theo một chiều khá dễ nhưng không phải lúc nào cũng có thể đảo ngược sạch sẽ
Khi cả hai bên đều tiếp tục phân nhánh, việc theo dõi baseline của Copybara bắt đầu cho ra kết quả khó hiểu. Các commit có cùng ý nghĩa nhưng sau biến đổi lại tạo ra SHA khác nhau
Điều đáng biết là việc “giữ” lịch sử không phải là di chuyển nguyên bản, mà là cherry-pick các commit đã được viết lại. Nội dung tệp và thông tin tác giả được mang sang nên
Git blamevẫn hoạt động, nhưng SHA thì được tạo mớiCopybara lưu SHA gốc trong trailer của thông điệp commit là
GitOrigin-RevId, nên rất hữu ích khi sau này cần đối chiếu commit giữa các kho lưu trữHơn 52 năm tiến bộ cơ à :-)
Tháng 7 năm 2026: Google copybara cho phép chuyển mã giữa hai kho lưu trữ production
Tháng 3 năm 1974: IBM COPY cho phép chuyển mã giữa hai tập dữ liệu phân vùng production. OS/MVT và OS/VS2 TSO Data Utilities COPY, FORMAT, LIST, MERGE User's Guide and Reference https://www.computinghistory.org.uk/downloads/8987
Ở Dagster, chúng tôi đã dùng Copybara để dựng một mô hình hub-spoke cho phép kho lưu trữ công khai tồn tại bên trong một monorepo nội bộ lớn hơn, và nó có hoạt động, nhưng cần khá nhiều cách xử lý vòng vo
https://dagster.io/blog/monorepos-the-hub-and-spoke-model-an...
Nếu chỉ cần đồng bộ kho lưu trữ mà không có loại trừ hay biến đổi, có lẽ tôi sẽ không dùng nó. Có thể hiện tại vẫn ổn, nhưng sẽ rắc rối nếu nó bị đưa vào diện lưu trữ hoặc ngừng phát triển như kaniko hay nhiều sản phẩm/công cụ khác của Google
GitLab có một cách rất đơn giản để mirror từ GitLab sang GitHub hoặc nhà cung cấp/máy chủ Git khác
Ví dụ như chuyển
bzlbên ngoài sang dạng tương thích với BlazeBUILDnội bộ, hoặc chuyển đổi giữa import bên ngoài và import kiểuthird_partynội bộNếu chỉ mirror thuần túy thì Copybara là quá nặng
Hay đấy. Khoảng 5 năm trước tôi cũng đã tự làm thứ gì đó tương tự bằng các kho Git lồng nhau và script để đạt mục đích quản lý đồng thời kho riêng tư và công khai
Shell script của tôi tất nhiên không ở quy mô Google
git subtree, nhưng hóa ra nó làm được nhiều việc hơn hẳnChẳng hạn như nó còn có tính năng đổi email tác giả commit trong lúc đồng bộ nữa