Tuyên bố miễn trừ trách nhiệm: Tôi sử dụng Git, theo dõi sự phát triển của Git trong danh sách gửi thư của git và thậm chí đóng góp một chút cho Git (chủ yếu là gitweb). Tôi biết Mercurial từ tài liệu và một số từ thảo luận về kênh IRC #revctrl trên FreeNode.
Cảm ơn tất cả mọi người trên kênh IRC #mercurial đã cung cấp trợ giúp về Mercurial cho bài viết này
Tóm lược
Ở đây thật tuyệt khi có một số cú pháp cho bảng, giống như trong phần mở rộng PHPMarkdown / MultiMarkdown / Maruku của Markdown
- Cấu trúc kho lưu trữ: Mercurial không cho phép hợp nhất bạch tuộc (có nhiều hơn hai cha mẹ), cũng không gắn thẻ các đối tượng không cam kết.
- Thẻ: Mercurial sử dụng
.hgtags
tệp được phiên bản với các quy tắc đặc biệt cho các thẻ trên mỗi kho lưu trữ và cũng hỗ trợ các thẻ cục bộ trong .hg/localtags
; trong thẻ Git là các ref nằm trong refs/tags/
không gian tên và theo mặc định được tự động theo dõi khi tìm nạp và yêu cầu đẩy rõ ràng.
- Chi nhánh: Trong quy trình làm việc cơ bản của Mercurial dựa trên các đầu ẩn danh ; Git sử dụng các nhánh có tên nhẹ và có loại nhánh đặc biệt ( các nhánh theo dõi từ xa ) đi theo các nhánh trong kho lưu trữ từ xa.
- Đặt tên và phạm vi sửa đổi: Mercurial cung cấp số sửa đổi , cục bộ cho kho lưu trữ và căn cứ sửa đổi tương đối (tính từ đầu, tức là nhánh hiện tại) và phạm vi sửa đổi trên đánh số cục bộ này ; Git cung cấp một cách để tham khảo sửa đổi liên quan đến đầu nhánh và phạm vi sửa đổi là cấu trúc liên kết (dựa trên biểu đồ sửa đổi)
- Mercurial sử dụng theo dõi đổi tên , trong khi Git sử dụng phát hiện đổi tên để xử lý đổi tên tệp
- Mạng: Mercurial hỗ trợ các giao thức "thông minh" SSH và HTTP và giao thức HTTP tĩnh; Git hiện đại hỗ trợ các giao thức "thông minh" SSH, HTTP và GIT và giao thức "câm" HTTP (S). Cả hai đều có hỗ trợ cho các tệp bó để vận chuyển ngoại tuyến.
- Mercurial sử dụng các tiện ích mở rộng (plugin) và API đã thiết lập; Git có kịch bản và các định dạng được thiết lập.
Có một vài điều khác biệt Mercurial với Git, nhưng có những thứ khác làm cho chúng giống nhau. Cả hai dự án đều mượn ý tưởng của nhau. Ví dụ, hg bisect
lệnh trong Mercurial (trước đây là phần mở rộng bisect ) được lấy cảm hứng từ git bisect
lệnh trong Git, trong khi ý tưởng git bundle
được lấy cảm hứng từ hg bundle
.
Cấu trúc kho lưu trữ, lưu trữ sửa đổi
Trong Git, có bốn loại đối tượng trong cơ sở dữ liệu đối tượng của nó: các đối tượng blob chứa nội dung của tệp, đối tượng cây phân cấp lưu trữ cấu trúc thư mục, bao gồm tên tệp và các phần có liên quan của quyền tệp (quyền thực thi đối với tệp, là liên kết tượng trưng) , đối tượng cam kết chứa thông tin về quyền tác giả, con trỏ để chụp nhanh trạng thái của kho lưu trữ khi sửa đổi được biểu thị bằng một cam kết (thông qua một đối tượng cây của thư mục trên cùng của dự án) và tham chiếu đến các cam kết cha mẹ bằng 0 hoặc nhiều hơn và gắn thẻ các đối tượng khác tham chiếu các đối tượng khác và có thể được ký bằng PGP / GPG.
Git sử dụng hai cách lưu trữ đối tượng: định dạng lỏng , trong đó mỗi đối tượng được lưu trữ trong một tệp riêng biệt (các tệp đó được ghi một lần và không bao giờ sửa đổi) và định dạng đóng gói trong đó nhiều đối tượng được lưu trữ nén trong một tệp. Tính nguyên tử của các hoạt động được cung cấp bởi thực tế, tham chiếu đến một đối tượng mới được viết (về nguyên tử, sử dụng thủ thuật tạo + đổi tên) sau khi viết một đối tượng.
Kho lưu trữ Git yêu cầu bảo trì định kỳ bằng cách sử dụng git gc
(để giảm dung lượng ổ đĩa và cải thiện hiệu suất), mặc dù ngày nay Git tự động làm điều đó. (Phương pháp này cung cấp nén tốt hơn các kho lưu trữ.)
Mercurial (theo như tôi hiểu) lưu trữ lịch sử của một tệp trong filelog (cùng với tôi, với siêu dữ liệu bổ sung như theo dõi đổi tên và một số thông tin của người trợ giúp); nó sử dụng cấu trúc phẳng được gọi là tệp kê khai để lưu trữ cấu trúc thư mục và cấu trúc được gọi là changelog lưu trữ thông tin về các thay đổi (bản sửa đổi), bao gồm thông điệp cam kết và số không, một hoặc hai cha mẹ.
Mercurial sử dụng tạp chí giao dịch để cung cấp tính nguyên tử của các hoạt động và dựa vào các tệp bị cắt bớt để dọn dẹp sau khi hoạt động thất bại hoặc bị gián đoạn. Revlog chỉ là phụ lục.
Nhìn vào cấu trúc kho lưu trữ trong Git so với Mercurial, người ta có thể thấy Git giống cơ sở dữ liệu đối tượng hơn (hoặc hệ thống tệp có địa chỉ nội dung) và Mercurial giống như cơ sở dữ liệu quan hệ trường cố định truyền thống.
Sự khác biệt:
Trong Git, các đối tượng cây tạo thành một cấu trúc phân cấp ; trong tệp kê khai Mercurial là cấu trúc phẳng . Trong đối tượng Git blob lưu trữ một phiên bản nội dung của tệp; trong Mercurial filelog lưu trữ toàn bộ lịch sử của một tệp duy nhất (nếu chúng ta không tính đến ở đây bất kỳ biến chứng nào với việc đổi tên). Điều này có nghĩa là có các lĩnh vực hoạt động khác nhau trong đó Git sẽ nhanh hơn Mercurial, tất cả những thứ khác được coi là bằng nhau (như sáp nhập hoặc hiển thị lịch sử của dự án) và các khu vực mà Mercurial sẽ nhanh hơn Git (như áp dụng các bản vá hoặc hiển thị lịch sử của một tập tin duy nhất).Vấn đề này có thể không quan trọng đối với người dùng cuối.
Do cấu trúc bản ghi cố định của cấu trúc thay đổi của Mercurial , các cam kết trong Mercurial chỉ có thể có tối đa hai cha mẹ ; Cam kết trong Git có thể có nhiều hơn hai cha mẹ (nên được gọi là "hợp nhất bạch tuộc"). Mặc dù bạn có thể (về lý thuyết) thay thế hợp nhất bạch tuộc bằng một loạt hợp nhất hai cha mẹ, điều này có thể gây ra các biến chứng khi chuyển đổi giữa các kho lưu trữ của Mercurial và Git.
Theo như tôi biết thì Mercurial không tương đương với các thẻ chú thích (đối tượng thẻ) từ Git. Một trường hợp đặc biệt của các thẻ chú thích là các thẻ đã ký (có chữ ký PGP / GPG); tương đương trong Mercurial có thể được thực hiện bằng GpgExtension , phần mở rộng đang được phân phối cùng với Mercurial. Bạn không thể gắn thẻ đối tượng không cam kết trong Mercurial như bạn có thể trong Git, nhưng điều đó không quan trọng lắm, tôi nghĩ (một số kho git sử dụng blob được gắn thẻ để phân phối khóa PGP công cộng để sử dụng để xác minh thẻ đã ký).
Tài liệu tham khảo: chi nhánh và thẻ
Trong tài liệu tham khảo Git (các nhánh, các nhánh và thẻ theo dõi từ xa) nằm bên ngoài DAG của các xác nhận (nếu cần). Các tham chiếu trong refs/heads/
không gian tên ( các nhánh cục bộ ) trỏ đến các xác nhận và thường được cập nhật bởi "git commit"; họ chỉ vào đỉnh (đầu) của chi nhánh, đó là lý do tại sao tên đó. Các tham chiếu trong refs/remotes/<remotename>/
không gian tên ( các nhánh theo dõi từ xa ) trỏ đến cam kết, theo dõi các nhánh trong kho lưu trữ từ xa <remotename>
và được cập nhật bằng "git fetch" hoặc tương đương. Các tham chiếu trong refs/tags/
không gian tên ( thẻ ) thường chỉ đến các cam kết (thẻ nhẹ) hoặc các đối tượng thẻ (thẻ chú thích và thẻ đã ký) và không có nghĩa là thay đổi.
Thẻ
Trong Mercurial, bạn có thể đặt tên liên tục để sửa đổi bằng cách sử dụng thẻ ; các thẻ được lưu trữ tương tự như các mẫu bỏ qua. Điều đó có nghĩa là các thẻ hiển thị toàn cầu được lưu trữ trong .hgtags
tệp được kiểm soát sửa đổi trong kho lưu trữ của bạn. Điều đó có hai hậu quả: đầu tiên, Mercurial phải sử dụng các quy tắc đặc biệt cho tệp này để có được danh sách hiện tại của tất cả các thẻ và để cập nhật tệp đó (ví dụ: nó đọc bản sửa đổi được cam kết gần đây nhất của tệp, hiện chưa được kiểm tra phiên bản); thứ hai, bạn phải cam kết thay đổi tệp này để có thẻ mới hiển thị cho người dùng khác / kho lưu trữ khác (theo như tôi hiểu).
Mercurial cũng hỗ trợ các thẻ cục bộ , được lưu trữ trong hg/localtags
đó, những thẻ khác không hiển thị (và tất nhiên là không thể chuyển nhượng được)
Trong thẻ Git được cố định (không đổi) các tham chiếu có tên đến các đối tượng khác (thường là các đối tượng thẻ, lần lượt là điểm cam kết) được lưu trữ trong refs/tags/
không gian tên. Theo mặc định khi tìm nạp hoặc đẩy một tập hợp sửa đổi, git sẽ tự động tìm nạp hoặc đẩy các thẻ mà điểm để sửa đổi được tìm nạp hoặc đẩy. Tuy nhiên, bạn có thể kiểm soát ở một mức độ nào đó các thẻ được tìm nạp hoặc đẩy.
Git xử lý các thẻ nhẹ (chỉ trực tiếp vào các xác nhận) và các thẻ chú thích (trỏ đến các đối tượng thẻ, có chứa thông báo thẻ tùy chọn bao gồm chữ ký PGP, lần lượt để cam kết) hơi khác, ví dụ như mặc định nó chỉ xem xét các thẻ chú thích khi mô tả cam kết sử dụng "mô tả git".
Git không có tương đương nghiêm ngặt với các thẻ cục bộ trong Mercurial. Tuy nhiên, các thực tiễn tốt nhất của git khuyên bạn nên thiết lập kho lưu trữ công khai riêng biệt, trong đó bạn đẩy các thay đổi sẵn sàng và từ đó các bản sao khác được sao chép và tìm nạp. Điều này có nghĩa là các thẻ (và các nhánh) mà bạn không đẩy, là riêng tư đối với kho lưu trữ của bạn. Mặt khác, bạn cũng có thể sử dụng không gian tên khác hơn heads
, remotes
hoặc tags
, ví dụ local-tags
cho các thẻ cục bộ.
Ý kiến cá nhân: Theo ý kiến của tôi, các thẻ nên nằm bên ngoài biểu đồ sửa đổi, vì chúng ở bên ngoài nó (chúng là các con trỏ thành biểu đồ sửa đổi). Các thẻ nên không được phiên bản, nhưng có thể chuyển nhượng. Sự lựa chọn của Mercurial về việc sử dụng một cơ chế tương tự như cơ chế bỏ qua các tệp, có nghĩa là nó phải xử lý .hgtags
đặc biệt (tệp trong cây có thể chuyển nhượng được, nhưng thông thường là phiên bản) hoặc có các thẻ chỉ là cục bộ ( .hg/localtags
không phải là phiên bản, nhưng không thể truyền tải được).
Chi nhánh
Trong nhánh Git cục bộ (đầu nhánh hoặc đầu nhánh) là một tham chiếu được đặt tên cho một cam kết, nơi người ta có thể phát triển các cam kết mới. Nhánh cũng có thể có nghĩa là dòng phát triển tích cực, tức là tất cả các cam kết có thể truy cập từ đầu nhánh. Các nhánh cục bộ nằm trong refs/heads/
không gian tên, vì vậy, ví dụ tên đầy đủ của nhánh 'master' là 'refs / Heads / master'.
Chi nhánh hiện tại trong Git (có nghĩa là chi nhánh đã được kiểm tra và chi nhánh nơi cam kết mới sẽ đi) là chi nhánh được tham chiếu bởi các tham chiếu CHÍNH. Người ta có thể có ĐẦU chỉ trực tiếp vào một cam kết, thay vì là tham chiếu tượng trưng; tình huống này nằm trên một nhánh không tên ẩn danh được gọi là ĐẦU tách rời ("nhánh git" cho thấy bạn đang ở trên '(không có chi nhánh)').
Trong Mercurial có các nhánh ẩn danh (đầu chi nhánh) và người ta có thể sử dụng dấu trang (thông qua phần mở rộng dấu trang ). Các nhánh đánh dấu như vậy hoàn toàn cục bộ và những tên đó (lên đến phiên bản 1.6) không thể chuyển nhượng được bằng Mercurial. Bạn có thể sử dụng rsync hoặc scp để sao chép .hg/bookmarks
tệp vào kho lưu trữ từ xa. Bạn cũng có thể sử dụng hg id -r <bookmark> <url>
để lấy id sửa đổi của một mẹo hiện tại của dấu trang.
Vì 1.6 bookmark có thể được đẩy / kéo. Các BookmarksExtension trang có một phần về công tác Với Repositories từ xa . Có một sự khác biệt ở chỗ, tên bookmark của Mercurial là toàn cục , trong khi định nghĩa của 'remote' trong Git cũng mô tả ánh xạ tên nhánh từ tên trong kho lưu trữ từ xa sang tên của các nhánh theo dõi từ xa cục bộ; ví dụ refs/heads/*:refs/remotes/origin/*
ánh xạ có nghĩa là người ta có thể tìm thấy trạng thái của nhánh 'master' ('refs / Heads / master') trong kho lưu trữ từ xa trong nhánh theo dõi từ xa 'origin / master' ('refs / remote / origin / master').
Mercurial cũng được gọi là các nhánh được đặt tên , trong đó tên nhánh được nhúng trong một cam kết (trong tập thay đổi). Tên như vậy là toàn cầu (chuyển trên fetch). Các tên nhánh đó được ghi lại vĩnh viễn như một phần của siêu dữ liệu của bộ thay đổi. Với Mercurial hiện đại, bạn có thể đóng "nhánh được đặt tên" và dừng ghi tên nhánh. Trong cơ chế này, lời khuyên của các chi nhánh được tính toán khi đang bay.
Thay vào đó, "các nhánh được đặt tên" của Mercurial nên được gọi là nhãn cam kết , bởi vì đó là những gì chúng là. Có những tình huống trong đó "nhánh được đặt tên" có thể có nhiều mẹo (nhiều cam kết không có con) và cũng có thể bao gồm một số phần khác nhau của biểu đồ sửa đổi.
Không có tương đương với các "nhánh nhúng" Mercurial trong Git; hơn nữa triết lý của Git là trong khi người ta có thể nói rằng chi nhánh bao gồm một số cam kết, điều đó không có nghĩa là một cam kết thuộc về một số chi nhánh.
Lưu ý rằng tài liệu Mercurial vẫn đề xuất sử dụng các bản sao riêng biệt (kho lưu trữ riêng biệt) ít nhất là cho các nhánh tồn tại lâu dài (một nhánh trên mỗi luồng công việc của kho lưu trữ), còn gọi là phân nhánh bằng cách nhân bản .
Chi nhánh đẩy
Mercurial theo mặc định đẩy tất cả các đầu . Nếu bạn muốn đẩy một nhánh duy nhất ( một đầu ), bạn phải chỉ định sửa đổi mẹo của nhánh bạn muốn đẩy. Bạn có thể chỉ định mẹo chi nhánh theo số sửa đổi của nó (cục bộ đến kho lưu trữ), theo mã định danh sửa đổi, theo tên dấu trang (cục bộ đến kho lưu trữ, không được chuyển) hoặc theo tên nhánh được nhúng (tên nhánh).
Theo như tôi hiểu, nếu bạn đẩy một loạt các bản sửa đổi có chứa các cam kết được đánh dấu là nằm trên một "nhánh có tên" theo cách nói của Mercurial, bạn sẽ có "nhánh có tên" này trong kho lưu trữ mà bạn đẩy tới. Điều này có nghĩa là tên của các nhánh được nhúng như vậy ("các nhánh được đặt tên") là toàn cục (liên quan đến bản sao của kho / dự án đã cho).
Theo mặc định (tùy theo push.default
biến cấu hình) "git đẩy" hoặc "git đẩy < remote >" Git sẽ đẩy các nhánh phù hợp , tức là chỉ các nhánh cục bộ đã có sẵn trong kho lưu trữ từ xa mà bạn đẩy vào. Bạn có thể sử dụng --all
tùy chọn để git-đẩy ("git đẩy --all") để đẩy tất cả các nhánh , bạn có thể sử dụng "git đẩy < remote > < chi nhánh >" để đẩy một nhánh nhất định và bạn có thể sử dụng "git đẩy < từ xa > ĐẦU "để đẩy chi nhánh hiện tại .
Tất cả các giả định ở trên cho rằng Git không được cấu hình mà các nhánh sẽ đẩy qua remote.<remotename>.push
các biến cấu hình.
Chi nhánh trong lấy
Lưu ý: ở đây tôi sử dụng thuật ngữ Git trong đó "tìm nạp" có nghĩa là tải xuống các thay đổi từ kho lưu trữ từ xa mà không tích hợp các thay đổi đó với công việc cục bộ. Đây là những gì " git fetch
" và " hg pull
" làm.
Nếu tôi hiểu chính xác, theo mặc định, Mercurial tìm nạp tất cả các đầu từ kho lưu trữ từ xa, nhưng bạn có thể chỉ định nhánh để tìm nạp thông qua " hg pull --rev <rev> <url>
" hoặc " hg pull <url>#<rev>
" để lấy một nhánh . Bạn có thể chỉ định <rev> bằng cách sử dụng mã định danh sửa đổi, tên "nhánh được đặt tên" (nhánh được nhúng trong thay đổi) hoặc tên dấu trang. Tên bookmark tuy nhiên (ít nhất là hiện tại) không được chuyển. Tất cả các sửa đổi "chi nhánh có tên" mà bạn nhận được thuộc về được chuyển giao. "Hg pull" lưu trữ các mẹo của các nhánh mà nó tìm nạp dưới dạng các đầu ẩn danh, không tên.
Trong Git theo mặc định (đối với điều khiển từ xa 'origin' được tạo bởi "git clone" và cho điều khiển từ xa được tạo bằng "git remote add") " git fetch
" (hoặc " git fetch <remote>
") lấy tất cả các nhánh từ kho lưu trữ từ xa (từ refs/heads/
không gian tên) và lưu trữ chúng trong refs/remotes/
không gian tên. Điều này có nghĩa là ví dụ, nhánh có tên 'master' (tên đầy đủ: 'refs / Heads / master') trong remote 'origin' sẽ được lưu trữ (được lưu) dưới dạng 'origin / master' nhánh theo dõi từ xa (tên đầy đủ: 'refs / điều khiển từ xa / nguồn gốc / chủ ').
Bạn có thể tìm nạp một nhánh trong Git bằng cách sử dụng git fetch <remote> <branch>
- Git sẽ lưu trữ (các) nhánh được yêu cầu trong FETCH_HEAD, một cái gì đó tương tự như các đầu không được đặt tên của Mercurial.
Đó chỉ là những ví dụ về các trường hợp mặc định của cú pháp Git refspec mạnh mẽ : với các refspec bạn có thể chỉ định và / hoặc định cấu hình nhánh nào muốn tìm nạp và nơi lưu trữ chúng. Ví dụ: trường hợp "tìm nạp tất cả các nhánh" mặc định được biểu thị bằng '+ refs / Heads / *: refs / remotes / origin / *' wildcard refspec và "fetch single Branch" là viết tắt của 'refs / Heads / <Branch>:' . Refspec được sử dụng để ánh xạ tên của các nhánh (refs) trong kho lưu trữ từ xa thành tên refs cục bộ. Nhưng bạn không cần biết (nhiều) về các refspec để có thể hoạt động hiệu quả với Git (chủ yếu nhờ vào lệnh "git remote").
Ý kiến cá nhân: Cá nhân tôi nghĩ rằng "các nhánh được đặt tên" (với tên nhánh được nhúng trong siêu dữ liệu thay đổi) trong Mercurial là thiết kế sai với không gian tên toàn cầu, đặc biệt là đối với hệ thống kiểm soát phiên bản phân tán . Ví dụ: hãy xem trường hợp cả Alice và Bob có "nhánh được đặt tên" có tên là 'for-joe' trong kho của họ, các nhánh không có gì chung. Tuy nhiên, trong kho của Joe, hai nhánh đó sẽ bị ngược đãi thành một nhánh duy nhất. Vì vậy, bằng cách nào đó bạn đã đưa ra quy ước bảo vệ chống lại các cuộc đụng độ tên chi nhánh. Đây không phải là vấn đề với Git, trong đó chi nhánh 'for-joe' của Joe từ Alice sẽ là 'alice / for-joe', và từ Bob, nó sẽ là 'bob / for-joe'.
"Các nhánh đánh dấu" của Mercurial hiện thiếu cơ chế phân phối trong lõi.
Sự khác biệt:
Khu vực này là một trong những khác biệt chính giữa Mercurial và Git, như james woodyatt và Steve Losh đã nói trong câu trả lời của họ. Mercurial, theo mặc định, sử dụng các bộ mã nhẹ vô danh, theo thuật ngữ của nó được gọi là "người đứng đầu". Git sử dụng các nhánh có tên nhẹ, với ánh xạ tiêm để ánh xạ tên của các nhánh trong kho lưu trữ từ xa thành tên của các nhánh theo dõi từ xa. Git "buộc" bạn đặt tên cho các nhánh (tốt, ngoại trừ một nhánh không tên, tình huống được gọi là ĐẦU tách rời), nhưng tôi nghĩ rằng điều này hoạt động tốt hơn với các luồng công việc nặng nhánh như luồng công việc của nhánh chủ đề, nghĩa là nhiều nhánh trong mô hình kho lưu trữ đơn.
Đặt tên sửa đổi
Trong Git, có nhiều cách đặt tên phiên bản (được mô tả, ví dụ như trong trang git rev-parse ):
- Tên đối tượng SHA1 đầy đủ (chuỗi thập lục phân 40 byte) hoặc một chuỗi con như vậy là duy nhất trong kho lưu trữ
- Tên tham chiếu tượng trưng, ví dụ: 'master' (đề cập đến nhánh 'master') hoặc 'v1.5.0' (tham chiếu đến thẻ) hoặc 'origin / next' (đề cập đến nhánh theo dõi từ xa)
- Một hậu tố
^
cho tham số sửa đổi có nghĩa là cha mẹ đầu tiên của một đối tượng cam kết, ^n
có nghĩa là cha mẹ thứ n của một cam kết hợp nhất. Một hậu tố ~n
cho tham số sửa đổi có nghĩa là tổ tiên thứ n của một cam kết trong dòng cha mẹ đầu tiên thẳng. Những hậu tố này có thể được kết hợp, để tạo thành trình xác định sửa đổi theo đường dẫn từ một tham chiếu tượng trưng, ví dụ: 'pu ~ 3 ^ 2 ~ 3'
- Đầu ra của "git description", tức là một thẻ gần nhất, tùy ý theo sau là dấu gạch ngang và một số xác nhận, theo sau là dấu gạch ngang, 'g' và tên đối tượng viết tắt, ví dụ 'v1.6.5.1-75- g5bf8097 '.
Ngoài ra còn có các nhà đầu cơ sửa đổi liên quan đến reflog, không được đề cập ở đây. Trong Git mỗi đối tượng, có thể là cam kết, thẻ, cây hoặc blob có mã định danh SHA-1; có một cú pháp đặc biệt như ví dụ 'next: Documentation' hoặc 'next: README' để chỉ cây (thư mục) hoặc blob (nội dung tệp) tại phiên bản được chỉ định.
Mercurial cũng có nhiều cách đặt tên thay đổi (được mô tả trong hg manpage):
- Một số nguyên đơn giản được coi là một số sửa đổi. Người ta cần nhớ rằng số sửa đổi là cục bộ của kho lưu trữ đã cho ; trong kho khác họ có thể khác nhau.
- Các số nguyên âm được coi là độ lệch liên tiếp từ đầu, với -1 biểu thị đầu, -2 biểu thị sửa đổi trước đầu, v.v. Họ cũng là địa phương để lưu trữ.
- Một định danh sửa đổi duy nhất (chuỗi thập lục phân 40 chữ số) hoặc tiền tố duy nhất của nó.
- Tên thẻ (tên biểu tượng được liên kết với sửa đổi đã cho) hoặc tên dấu trang (có phần mở rộng: tên biểu tượng được liên kết với đầu đã cho, cục bộ vào kho lưu trữ) hoặc "nhánh có tên" (nhãn cam kết; sửa đổi được cung cấp bởi "nhánh có tên" là mẹo (cam kết không có con) của tất cả các cam kết với nhãn cam kết đã cho, với số sửa đổi lớn nhất nếu có nhiều hơn một mẹo như vậy)
- Tên dành riêng "mẹo" là một thẻ đặc biệt luôn xác định bản sửa đổi gần đây nhất.
- Tên dành riêng "null" cho biết sửa đổi null.
- Tên dành riêng "." chỉ ra thư mục cha mẹ làm việc.
Sự khác biệt
Như bạn có thể thấy so sánh các danh sách trên Mercurial cung cấp số sửa đổi, cục bộ cho kho lưu trữ, trong khi Git thì không. Mặt khác, Mercurial chỉ cung cấp các mức bù tương đối từ 'tip' (nhánh hiện tại), là cục bộ của kho lưu trữ (ít nhất là không có ParentrevspecExtension ), trong khi Git cho phép chỉ định bất kỳ cam kết nào theo bất kỳ mẹo nào.
Bản sửa đổi gần đây nhất được đặt tên là ĐẦU trong Git và "mẹo" trong Mercurial; không có sửa đổi null trong Git. Cả Mercurial và Git đều có thể có nhiều root (có thể có nhiều hơn một cam kết không có cha mẹ; đây thường là kết quả của các dự án riêng biệt trước đây tham gia).
Xem thêm: Nhiều loại bài viết chỉ định sửa đổi khác nhau trên Blog của Elijah (newren's).
Ý kiến cá nhân: Tôi nghĩ rằng số sửa đổi được đánh giá cao (ít nhất là cho sự phát triển phân tán và / hoặc lịch sử phi tuyến / chi nhánh). Đầu tiên, đối với một hệ thống kiểm soát phiên bản phân tán, chúng phải là cục bộ của kho lưu trữ hoặc yêu cầu xử lý một số kho lưu trữ theo cách đặc biệt như một cơ quan đánh số trung tâm. Thứ hai, các dự án lớn hơn, với lịch sử lâu hơn, có thể có số lần sửa đổi trong phạm vi 5 chữ số, vì vậy chúng chỉ mang lại lợi thế nhỏ so với số nhận dạng sửa đổi rút gọn thành 6-7 ký tự và ngụ ý đặt hàng nghiêm ngặt trong khi các phiên bản chỉ được đặt hàng một phần (ý tôi nói ở đây là bản sửa đổi n và n + 1 không cần phải là cha mẹ và con cái).
Phạm vi sửa đổi
Trong phạm vi sửa đổi Git là cấu trúc liên kết . A..B
Cú pháp thường thấy , đối với lịch sử tuyến tính có nghĩa là phạm vi sửa đổi bắt đầu từ A (nhưng không bao gồm A) và kết thúc tại B (nghĩa là phạm vi mở từ bên dưới ), là tốc ký ("đường cú pháp") ^A B
, đối với các lệnh đi qua lịch sử có nghĩa là tất cả cam kết có thể truy cập từ B, ngoại trừ những người có thể tiếp cận từ A. Điều này có nghĩa là hành vi của A..B
phạm vi là hoàn toàn có thể dự đoán được (và khá hữu ích) ngay cả khi A không phải là tổ tiên của B: A..B
có nghĩa là phạm vi sửa đổi từ tổ tiên chung của A và B (cơ sở hợp nhất ) để sửa đổi B.
Trong phạm vi sửa đổi Mercurial dựa trên phạm vi số sửa đổi . Phạm vi được chỉ định bằng A:B
cú pháp và trái với phạm vi Git hoạt động như một khoảng đóng . Ngoài ra phạm vi B: A là phạm vi A: B theo thứ tự ngược lại, đây không phải là trường hợp trong Git (nhưng xem ghi chú bên dưới về A...B
cú pháp). Nhưng sự đơn giản như vậy đi kèm với một mức giá: phạm vi sửa đổi A: B chỉ có ý nghĩa nếu A là tổ tiên của B hoặc ngược lại, tức là với lịch sử tuyến tính; mặt khác (tôi đoán rằng) phạm vi là không thể đoán trước và kết quả là cục bộ cho kho lưu trữ (vì số sửa đổi là cục bộ của kho lưu trữ).
Điều này được cố định với Mercurial 1.6, có phạm vi sửa đổi tô pô mới , trong đó 'A..B' (hoặc 'A :: B') được hiểu là tập hợp các thay đổi có cả hậu duệ của X và tổ tiên của Y. Đây là , Tôi đoán, tương đương với '--ancestry-path A..B' trong Git.
Git cũng có ký hiệu A...B
cho sự khác biệt đối xứng của các phiên bản; nó có nghĩa là A B --not $(git merge-base A B)
, có nghĩa là tất cả các cam kết có thể truy cập từ A hoặc B, nhưng loại trừ tất cả các cam kết có thể truy cập từ cả hai (có thể truy cập từ tổ tiên chung).
Đổi tên
Mercurial sử dụng theo dõi đổi tên để đối phó với đổi tên tập tin. Điều này có nghĩa là thông tin về thực tế là một tệp đã được đổi tên được lưu tại thời điểm cam kết; trong Mercurial, thông tin này được lưu ở dạng "diff diff" trong siêu dữ liệu filelog (revlog). Hậu quả của việc này là bạn phải sử dụng hg rename
/ hg mv
... hoặc bạn cần nhớ chạy hg addremove
để thực hiện phát hiện đổi tên dựa trên sự tương tự.
Git là duy nhất trong số các hệ thống kiểm soát phiên bản ở chỗ nó sử dụng phát hiện đổi tên để xử lý việc đổi tên tệp. Điều này có nghĩa là thực tế rằng tập tin đã được đổi tên được phát hiện tại thời điểm cần thiết: khi thực hiện hợp nhất hoặc khi hiển thị khác biệt (nếu được yêu cầu / định cấu hình). Điều này có lợi thế là đổi tên thuật toán phát hiện có thể được cải thiện và không bị đóng băng tại thời điểm cam kết.
Cả Git và Mercurial đều yêu cầu sử dụng --follow
tùy chọn để theo dõi đổi tên khi hiển thị lịch sử của một tệp. Cả hai đều có thể theo dõi tên khi hiển thị lịch sử dòng thông tin của tệp trong git blame
/ hg annotate
.
Trong Git, git blame
lệnh có thể theo dõi chuyển động mã, cũng di chuyển (hoặc sao chép) mã từ tệp này sang tệp khác, ngay cả khi chuyển động mã không phải là một phần của việc đổi tên tệp lành mạnh. Theo tôi biết tính năng này là duy nhất cho Git (tại thời điểm viết, tháng 10 năm 2009).
Giao thức mạng
Cả Mercurial và Git đều hỗ trợ tìm nạp và đẩy vào kho lưu trữ trên cùng một hệ thống tệp, trong đó URL kho lưu trữ chỉ là đường dẫn hệ thống tệp đến kho lưu trữ. Cả hai cũng có hỗ trợ để tìm nạp từ các tập tin bó .
Hỗ trợ Mercurial tìm nạp và đẩy qua SSH và qua các giao thức HTTP. Đối với SSH, người ta cần một tài khoản shell có thể truy cập trên máy đích và một bản sao hg được cài đặt / có sẵn. Để truy cập HTTP hg-serve
, tập lệnh hoặc Mercurial CGI đang chạy là bắt buộc và Mercurial cần được cài đặt trên máy chủ.
Git hỗ trợ hai loại giao thức được sử dụng để truy cập kho lưu trữ từ xa:
- Các giao thức "thông minh" , bao gồm quyền truy cập qua SSH và thông qua giao thức git: // tùy chỉnh (bởi
git-daemon
), yêu cầu cài đặt git trên máy chủ. Việc trao đổi trong các giao thức đó bao gồm khách hàng và máy chủ đàm phán về những đối tượng họ có chung, sau đó tạo và gửi một packfile. Modern Git bao gồm hỗ trợ cho giao thức HTTP "thông minh".
- Các giao thức "ngu ngốc" , bao gồm HTTP và FTP (chỉ để tìm nạp) và HTTPS (để đẩy qua WebDAV), không yêu cầu git được cài đặt trên máy chủ, nhưng chúng yêu cầu kho lưu trữ chứa thông tin bổ sung được tạo bởi
git update-server-info
(thường chạy từ hook ). Việc trao đổi bao gồm khách hàng đi theo chuỗi cam kết và tải xuống các đối tượng và gói dữ liệu lỏng lẻo khi cần. Nhược điểm là nó tải xuống nhiều hơn yêu cầu nghiêm ngặt (ví dụ trong trường hợp góc khi chỉ có một gói duy nhất, nó sẽ được tải xuống toàn bộ ngay cả khi chỉ tìm nạp một vài phiên bản) và nó có thể yêu cầu nhiều kết nối để kết thúc.
Mở rộng: kịch bản so với tiện ích mở rộng (plugin)
Mercurial được triển khai bằng Python , với một số mã lõi được viết bằng C để thực hiện. Nó cung cấp API để viết các tiện ích mở rộng (plugin) như một cách để thêm các tính năng bổ sung. Một số chức năng, như "nhánh đánh dấu" hoặc ký sửa đổi, được cung cấp trong các tiện ích mở rộng được phân phối với Mercurial và yêu cầu bật nó.
Git được thực hiện trong các tập lệnh C , Perl và shell . Git cung cấp nhiều lệnh cấp thấp ( hệ thống ống nước ) phù hợp để sử dụng trong các tập lệnh. Cách thông thường để giới thiệu tính năng mới là viết nó dưới dạng Perl hoặc shell script và khi giao diện người dùng ổn định viết lại bằng C để thực hiện, tính di động và trong trường hợp kịch bản shell tránh các trường hợp góc (quy trình này được gọi là tích hợp ).
Git dựa và được xây dựng xung quanh các định dạng [kho lưu trữ] và các giao thức [mạng]. Thay vì các ràng buộc ngôn ngữ, có các cách thực hiện Git (một phần hoặc toàn bộ) trong các ngôn ngữ khác (một số trong đó là tái hiện một phần và các trình bao bọc một phần xung quanh các lệnh git): JGit (Java, được sử dụng bởi EGit, Plugin Git Eclipse), Grit (Ruby) , Dulwich (Python), git # (C #).
TL; DR