Git và Mercurial - So sánh và tương phản


520

Trong một thời gian bây giờ tôi đã sử dụng lật đổ cho các dự án cá nhân của tôi.

Càng ngày tôi càng nghe thấy những điều tuyệt vời về Git và Mercurial, và DVCS nói chung.

Tôi muốn cung cấp cho toàn bộ điều DVCS, nhưng tôi không quá quen thuộc với một trong hai lựa chọn.

Một số khác biệt giữa Mercurial và Git là gì?

Lưu ý: Tôi không cố gắng tìm ra cái nào là "tốt nhất" hoặc thậm chí cái nào tôi nên bắt đầu. Tôi chủ yếu tìm kiếm các lĩnh vực chính nơi chúng giống nhau và nơi chúng khác nhau, bởi vì tôi quan tâm để biết chúng khác nhau như thế nào về mặt thực hiện và triết lý.



Câu trả lời:


451

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 .hgtagstệ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 bisectlệnh trong Mercurial (trước đây là phần mở rộng bisect ) được lấy cảm hứng từ git bisectlệ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 .hgtagstệ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, remoteshoặc tags, ví dụ local-tagscho 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/localtagskhô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/bookmarkstệ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.defaultbiế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 --alltù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 woodyattSteve 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, ^ncó nghĩa là cha mẹ thứ n của một cam kết hợp nhất. Một hậu tố ~ncho 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..BCú 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..Bphạ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..Bcó 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:Bcú 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...Bcú 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...Bcho 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 --followtù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 blamelệ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 , Perlshell . 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


32
Những gì có thể được thêm vào là hg rất cố gắng để ngăn chặn việc viết lại lịch sử (nó chỉ có thể được thực hiện với các phần mở rộng: mq, histedit, rebase), trong khi git thực hiện nó bên ngoài (và nó trông giống như một phần của cộng đồng thậm chí khuyến khích nó).
tonfa

80
Tôi nghĩ rằng "viết lại lịch sử" là âm thanh tiêu cực không cần thiết. Điều tôi khuyến khích trong git là mọi người xem xét lịch sử mà họ xuất bản. Những người khác cần phải tiêu thụ lịch sử đó. Không ai (thậm chí cả bạn) quan tâm đến tất cả các cam kết "ôi, quên một tập tin" của bạn. Cũng không ai quan tâm đến chuỗi các sự hợp nhất trong nước bạn đã trải qua trong khi bạn đang theo dõi một chi nhánh ngược dòng trong khi làm việc trên một tính năng mới. Loại công cụ đó làm cho lịch sử (và các công cụ liên quan) khó hiểu hơn nhiều và không cung cấp giá trị.
Dustin

5
@Jakub: các nhánh được đặt tên là một cái gì đó không tồn tại trong git. Nó chỉ đơn giản là một trường trong mô tả cset (và đó là một phần của lịch sử, vì vậy nó là bất biến trừ khi bạn thay đổi băm, v.v.). Một cái gì đó giống như các nhánh git là dấu trang ("đầu được đặt tên") nhưng hiện tại chúng không thể chuyển từ xa (bạn không nhập dấu trang từ xa khi kéo). stevelosh.com/blog/entry/2009/8/30/ cho giải thích rất rõ.
tonfa

28
"Mercurial ban đầu chỉ hỗ trợ một nhánh trên mỗi luồng công việc của kho lưu trữ và nó hiển thị." À, không. Mercurial ban đầu không hỗ trợ các chi nhánh được đặt tên , nhưng bạn luôn có thể có nhiều chi nhánh ẩn danh như trái tim bạn mong muốn trong một repo duy nhất. Ngược lại với git, làm cho nhánh vô danh trở thành một nỗi đau rất lớn . Bạn sẽ phải nghĩ ra một cái tên cho mỗi chi nhánh nhỏ nếu bạn muốn hoàn thành mọi việc (và tránh thu gom rác công việc của bạn).
Steve Losh

17
@SteveLosh: bạn dường như nghĩ rằng có rất nhiều chi nhánh ẩn danh trong Mercurial là một điều tốt, nhưng với tôi nó có vẻ kinh khủng. Làm thế nào để bạn nói với họ tất cả ngoài? Và bạn dường như nghĩ rằng việc đặt tên chi nhánh trong Git là một số khó khăn rất lớn, nhưng nếu bạn có mục đích tạo ra chi nhánh thì bạn có một cái tên đã sẵn sàng. Nếu bạn không có mục đích, thì đừng chi nhánh. Tôi không thấy Mercurial cung cấp bất kỳ lợi ích nào ở đây. Tôi chỉ thấy đau đớn và hoang mang.
iconoclast

57

Tôi nghĩ bạn có thể cảm nhận được những hệ thống đó giống hoặc khác nhau bằng cách ghép hai video đó:

Linus Torvalds trên Git ( http://www.youtube.com/watch?v=4XpnKHJAok8 )
Bryan O'Sullivan trên Mercurial ( http://www.youtube.com/watch?v=JExtkqzEoHY )

Cả hai đều rất giống nhau về thiết kế nhưng rất khác nhau trong việc thực hiện.

Tôi sử dụng Mercurial. Theo như tôi hiểu về Git, một điều quan trọng khác của git là nó theo dõi nội dung của các tệp thay vì chính các tệp. Linus nói rằng nếu bạn di chuyển một chức năng từ tệp này sang tệp khác, Git sẽ cho bạn biết lịch sử của chức năng đó trong suốt quá trình di chuyển.

Họ cũng nói rằng git chậm hơn HTTP nhưng nó có giao thức mạng và máy chủ riêng.

Git hoạt động tốt hơn với tư cách là khách hàng dày của SVN so với Mercurial. Bạn có thể kéo và đẩy vào máy chủ SVN. Chức năng này vẫn đang được phát triển trong Mercurial

Cả Mercurial và Git đều có sẵn các giải pháp lưu trữ web rất hay (BitBucket và GitHub), nhưng Google Code chỉ hỗ trợ Mercurial. Nhân tiện, họ có một so sánh rất chi tiết về Mercurial và Git mà họ đã làm để quyết định nên hỗ trợ cái nào ( http://code.google.com.vn/p/support/wiki/DVCSAnalysis ). Nó có rất nhiều thông tin tốt.


8
Tôi khuyên bạn nên đọc tất cả các ý kiến ​​trên trang mã google đó. Thông tin cảm thấy hơi thiên vị và không phù hợp với kinh nghiệm của tôi. Tôi như hg, và sử dụng nó rộng rãi trong vòng một năm hoặc lâu hơn. Tôi sử dụng git gần như độc quyền bây giờ. Có những điều tôi cần phải thực hiện mà git làm cho dễ dàng và hg làm cho gần như không thể (mặc dù một số người thích gọi nó bằng "biến chứng".) Git cơ bản dễ như cơ sở hg.
Dustin

11
Dustin, có thể liệt kê một số trường hợp "git easy, hg không quá nhiều"?
Gregg Lind

1
@knittl không nó không. Chủ yếu là vì họ sẽ gặp khó khăn khi triển khai nó vì git thiếu giao thức http thông minh (hầu hết các giao diện người dùng của Google đều dựa trên http).
tonfa

2
@tonfa: Giao thức HTTP thông minh cho Git hiện đang được phát triển (như trong: có các bản vá trong danh sách gửi thư git và chúng nằm trong 'pu' = nhánh cập nhật được đề xuất trong kho git.git).
Jakub Narębski

4
Cho đến bây giờ Google Code cũng hỗ trợ Git.
Andrej Kirejeŭ

30

Tôi đã viết một mục blog về các mô hình phân nhánh của Mercurial trước đây và bao gồm các so sánh với mô hình phân nhánh của git. Có thể bạn sẽ thấy thú vị: http://stevelosh.com/blog/entry/2009/8/30/a-guide-to-branching-in-mercurial/


@Steve Losh: Tôi muốn bình luận về mục blog này (về chi nhánh không tên hay còn gọi là CHÍNH, và về git-fetch tìm nạp tất cả các chi nhánh, không phải một), nhưng tôi đã gặp lỗi 500 máy chủ.
Jakub Narębski

1
@Jakub Narębski Tôi cá rằng vấn đề là ký tự không phải ASCII trong tên của bạn. Tôi khá chắc chắn rằng tôi đã gặp vấn đề tương tự trên một trang web khác và hóa ra là cuộn cảm ràng buộc Python Askimet trên Unicode. Tôi sẽ xem qua một chút.
Steve Losh

@Steve Losh: Cảm ơn bạn về một thông tin, sau khi "unidecoding" tên của tôi, tôi đã có thể đăng bình luận. Mô tả rất hay về sự phân nhánh trong Mercurial (nhưng tôi vẫn nghĩ nó kém hơn ;-))
Jakub Narębski

@SteveLosh Tôi khuyến khích bạn mở rộng câu trả lời này để đánh giá đầy đủ hơn về đồng bóng. Ngay bây giờ, câu trả lời hàng đầu không may là phần lớn quảng cáo cho git vì tác giả của nó đã không sử dụng rộng rãi và không hiểu làm thế nào để sử dụng nó một cách hiệu quả. Sẽ là tốt đẹp cho một câu trả lời khác để cung cấp quan điểm đồng bóng, để nói.
Warren Dew

25

Tôi sử dụng cả hai khá thường xuyên. Sự khác biệt chính về chức năng là ở cách các nhánh tên Git và Mercurial trong kho. Với Mercurial, tên chi nhánh được nhân bản và kéo theo cùng với bộ thay đổi của chúng. Khi bạn thêm các thay đổi vào một nhánh mới trong Mercurial và đẩy sang một kho lưu trữ khác, tên nhánh được đẩy cùng một lúc. Vì vậy, các tên nhánh ít nhiều mang tính toàn cầu trong Mercurial và bạn phải sử dụng tiện ích mở rộng Bookmark để có các tên nhẹ chỉ cục bộ (nếu bạn muốn chúng; theo mặc định, Mercurial sử dụng các mã nhẹ vô danh, theo thuật ngữ của nó là gọi là "thủ trưởng"). Trong Git, tên nhánh và ánh xạ tiêm truyền của chúng tới các nhánh từ xa được lưu trữ cục bộ và bạn phải quản lý chúng một cách rõ ràng, điều đó có nghĩa là biết cách thực hiện điều đó.

Như những người khác sẽ lưu ý ở đây, có rất nhiều và rất nhiều sự khác biệt nhỏ. Điều với các chi nhánh là sự khác biệt lớn.


2
Xem thêm bài đăng này để được giải thích tốt về bốn loại chi nhánh trong Mercurial: stevelosh.com/blog/entry/2009/8/30/ Kẻ
Martin Geisler

19

Hãy xem Git vs. Mercurial: Hãy thư giãn bài đăng trên blog của Patrick Thomson, nơi anh viết:
Git là MacGyver , Mercurial là James Bond

Lưu ý rằng bài đăng trên blog này là từ ngày 7 tháng 8 năm 2008 và cả SCM đều được cải thiện nhiều kể từ đó.


11

Mercurial gần như được viết hoàn toàn bằng trăn. Lõi của Git được viết bằng C (và phải nhanh hơn so với Mercurial) và các công cụ được viết bằng sh, perl, tcl và sử dụng các dụng cụ GNU tiêu chuẩn. Do đó, nó cần phải mang tất cả các tiện ích và trình thông dịch này đến hệ thống không chứa chúng (ví dụ: Windows).

Cả hai hỗ trợ đều hoạt động với SVN, mặc dù hỗ trợ svn của AFAIK bị hỏng đối với git trên Windows (có thể tôi chỉ là người không may mắn / khập khiễng, ai biết được). Ngoài ra còn có các phần mở rộng cho phép tương tác giữa git và Mercurial.

Mercurial có tích hợp Visual Studio đẹp . Lần trước tôi đã kiểm tra, plugin cho Git đã hoạt động nhưng cực kỳ chậm.

Chúng là các bộ lệnh cơ bản rất giống nhau (init, clone, add, status, commit, push, pull, v.v.). Vì vậy, quy trình làm việc cơ bản sẽ giống nhau. Ngoài ra, có ứng dụng khách giống rùaSVN cho cả hai.

Phần mở rộng cho Mercurial có thể được viết bằng python (không có gì bất ngờ!) Và đối với git, chúng có thể được viết dưới bất kỳ hình thức thực thi nào (nhị phân thực thi, shell script, v.v.). Một số phần mở rộng là điên mạnh mẽ, như git bisect.


9
Lõi Mercurial được viết bằng C quá FYI (nhưng có lẽ đó là lõi nhỏ hơn git).
tonfa

1
Tôi sử dụng git-svn trên Windows mà không gặp rắc rối nào. Đó là sử dụng Cygwin (cách duy nhất đúng để sử dụng git trên Windows nếu bạn hỏi tôi). Không thể nói cho msysgit.
Dan Mould

@Dan Moulding: Có, tôi đã gặp sự cố với msysgit. Có lẽ cần phải thử cổng cygwin (tôi đã có một số kinh nghiệm kém về việc sử dụng cygwin trước đó, vì vậy tôi đã tránh nó). Cảm ơn về lời khuyên!
Eld_george

Cá nhân tôi không thích sự xâm nhập của Cygwin vào sổ đăng ký để lưu trữ dữ liệu người dùng. Đó là Pita để làm cho nó chạy hết khóa USB và giữ cho bản sao ổ đĩa c: \ được đồng bộ hóa khi tôi muốn chạy nhanh hơn khóa USB của mình. : - /
Chris K

1
Tôi sử dụng plugin Git cho Visual Studio đã đề cập ở trên và hiệu suất của phiên bản hiện tại là tốt. Nó trình bày các công cụ dòng lệnh để thực hiện công việc, vì vậy tôi không nghĩ rằng nó sẽ làm giảm đáng kể hiệu suất trong các dự án lớn.
Stuart Ellis

11

Nếu bạn cần hỗ trợ Windows tốt, bạn có thể thích Mercurial. TortoiseHg (plugin Windows explorer) quản lý để cung cấp một giao diện đồ họa đơn giản để sử dụng cho một công cụ khá phức tạp. Như trạng thái ở đây, bạn cũng sẽ có một plugin Visual Studio . Tuy nhiên, lần trước tôi đã thử, giao diện SVN không hoạt động tốt trên Windows.

Nếu bạn không quan tâm đến giao diện dòng lệnh, tôi sẽ khuyên dùng Git. Không phải vì lý do kỹ thuật mà vì một chiến lược. Tỷ lệ chấp nhận git cao hơn nhiều . Chỉ cần xem có bao nhiêu dự án nguồn mở nổi tiếng đang chuyển từ cvs / svn sang Mercurial và có bao nhiêu dự án đang chuyển sang Git. Xem có bao nhiêu nhà cung cấp dịch vụ lưu trữ mã / dự án bạn có thể tìm thấy với hỗ trợ git so với lưu trữ Mercurial.


Ngoài ra còn có TortoiseGit, nếu bạn không thích sử dụng dòng lệnh. (Nhưng nó yêu cầu msysgit phải được cài đặt.)
Ben James

2
Công ty chúng tôi cuối cùng đã chọn git sự hỗ trợ tuyệt vời của nó trên Windows - hãy xem Git Tiện ích mở rộng . Tôi thiên vị bởi vì bây giờ tôi là một người đóng góp, nhưng tôi đã không bắt đầu sử dụng nó.
Jacob Stanley

11

Sau khi đọc tất cả rằng Mercurial dễ dàng hơn (mà tôi vẫn tin là, sau tất cả cộng đồng mạng là ý kiến), khi tôi bắt đầu làm việc với Git và Mercurial, tôi cảm thấy Git tương đối đơn giản hơn để tôi thích nghi (Tôi bắt đầu với Mercurial với TortoiseHg) khi làm việc từ dòng lệnh, chủ yếu là do các lệnh git được đặt tên phù hợp theo tôi và có số lượng ít hơn. Mercurial có cách đặt tên khác nhau cho mỗi lệnh thực hiện một công việc riêng biệt, trong khi các lệnh Git có thể đa năng theo tình huống (ví dụ:checkout). Mặc dù Git đã khó hơn trước đây, nhưng bây giờ sự khác biệt là không đáng kể. YMMV .. Với một máy khách GUI tốt như TortoiseHg, đúng là làm việc với Mercurial dễ dàng hơn nhiều và tôi không phải nhớ các lệnh hơi khó hiểu. Tôi sẽ không đi sâu vào chi tiết mỗi lệnh cho cùng một hành động khác nhau, nhưng đây là hai danh sách toàn diện: 1 từ trang web riêng của Mercurialthứ 2 từ wikivs .

╔═════════════════════════════╦════════════════════════════════════════════════════════════════════════════════════════════════╗
║           Git               ║                Mercurial                                                                       ║
╠═════════════════════════════╬════════════════════════════════════════════════════════════════════════════════════════════════╣
║ git pull                    ║ hg pull -u                                                                                     ║
║ git fetch                   ║ hg pull                                                                                        ║
║ git reset --hard            ║ hg up -C                                                                                       ║
║ git revert <commit>         ║ hg backout <cset>                                                                              ║
║ git add <new_file>          ║ hg add <new_file> (Only equivalent when <new_file> is not tracked.)                            ║
║ git add <file>              ║ Not necessary in Mercurial.                                                                    ║
║ git add -i                  ║ hg record                                                                                      ║
║ git commit -a               ║ hg commit                                                                                      ║
║ git commit --amend          ║ hg commit --amend                                                                              ║
║ git blame                   ║ hg blame or hg annotate                                                                        ║
║ git blame -C                ║ (closest equivalent): hg grep --all                                                            ║
║ git bisect                  ║ hg bisect                                                                                      ║
║ git rebase --interactive    ║ hg histedit <base cset> (Requires the HisteditExtension.)                                      ║
║ git stash                   ║ hg shelve (Requires the ShelveExtension or the AtticExtension.)                                ║
║ git merge                   ║ hg merge                                                                                       ║
║ git cherry-pick <commit>    ║ hg graft <cset>                                                                                ║
║ git rebase <upstream>       ║ hg rebase -d <cset> (Requires the RebaseExtension.)                                            ║
║ git format-patch <commits>  ║ hg email -r <csets> (Requires the PatchbombExtension.)                                         ║
║   and git send-mail         ║                                                                                                ║
║ git am <mbox>               ║ hg mimport -m <mbox> (Requires the MboxExtension and the MqExtension. Imports patches to mq.)  ║
║ git checkout HEAD           ║ hg update                                                                                      ║
║ git log -n                  ║ hg log --limit n                                                                               ║
║ git push                    ║ hg push                                                                                        ║
╚═════════════════════════════╩════════════════════════════════════════════════════════════════════════════════════════════════╝

Git lưu bản ghi của mọi phiên bản của các tệp đã cam kết trong nội bộ, trong khi Hg chỉ lưu các thay đổi có thể có dấu chân nhỏ hơn. Git giúp thay đổi lịch sử dễ dàng hơn so với Hg, nhưng sau đó lại là tính năng ghét-nó-hoặc-yêu-nó. Tôi thích Hg cho trước và Git cho sau.

Điều tôi nhớ ở Hg là tính năng mô đun con của Git. Hg có subrepose nhưng đó không chính xác là mô đun con Git.

Hệ sinh thái xung quanh cả hai cũng có thể ảnh hưởng đến sự lựa chọn của một người: Git phải phổ biến hơn (nhưng đó là tầm thường), Git có GitHub trong khi Mercurial có BitBucket , Mercurial có TortoiseHg mà tôi chưa thấy tương đương với Git.

Mỗi cái đều có ưu điểm và nhược điểm, với một trong hai thứ bạn sẽ không thua.


8

Kiểm tra bài viết của Scott Chacon một lúc trước.

Tôi nghĩ rằng git có tiếng là "phức tạp hơn", mặc dù theo kinh nghiệm của tôi thì nó không phức tạp hơn mức cần thiết. IMO, mô hình git là cách dễ hiểu (tags chứa cam kết (và con trỏ đến số không hoặc nhiều cam mẹ) chứa cây chứa các đốm màu và cây khác ... thực hiện).

Đó không chỉ là kinh nghiệm của tôi mà git không khó hiểu hơn so với đồng bóng. Tôi khuyên bạn nên đọc lại bài đăng trên blog này từ Scott Chacon về vấn đề này.


1
Mô hình Mercurial thực sự gần như giống hệt nhau: thay đổi điểm để biểu hiện điểm sửa đổi tập tin / blob ... được thực hiện. Nếu bạn đang so sánh định dạng trên đĩa, có lẽ bạn đã không tính đến tệp gói khó giải thích hơn định dạng revlog đơn giản từ hg.
tonfa

Chà, mô hình đơn giản hóa đó đã bỏ qua việc gắn thẻ, điều này thực sự khó hiểu hơn trong thực tế trong hg (mặc dù tôi cho rằng thẻ git hơi khó hiểu vì nó không tạo đối tượng thẻ theo mặc định). Định dạng trên đĩa đặc biệt đắt đối với cả hai dự án có lịch sử nhiều tên tập tin.
Dustin

1
Tôi không nghĩ rằng mô hình bỏ qua việc gắn thẻ: gắn thẻ là chuyện nhỏ trong Mercurial - như bạn biết, đó chỉ là một tệp đặt tên cho băm SHA-1. Không có phỏng đoán về cách các thẻ chảy xung quanh trong hệ thống: chúng di chuyển cùng với các lần đẩy và kéo. Và nếu có xung đột thẻ, thì cũng thật tầm thường để giải quyết nó: bạn giải quyết nó như bất kỳ xung đột nào khác. Rốt cuộc, nó chỉ là một dòng trong một tệp văn bản. Tôi nghĩ rằng sự đơn giản của mô hình này là một tính năng rất tốt đẹp.
Martin Geisler

Dustin: Vâng, người dùng thường bối rối bởi thực tế là bạn không thể thấy thẻ 1.0 .hgtagskhi bạn kiểm tra phiên bản 1.0. Tuy nhiên, bạn không cần phải nhìn vào bên trong .hgtagsvà bạn sẽ thấy hg tagsvẫn liệt kê tất cả các thẻ. Hơn nữa, hành vi này là hậu quả đơn giản của việc lưu trữ thẻ trong tệp được kiểm soát phiên bản - một lần nữa mô hình rất dễ nắm bắt và rất dễ đoán .
Martin Geisler

1
Martin Geisler Tôi cho rằng các quy tắc cho các thẻ trong Mercurial, là bắt buộc vì nó sử dụng tệp được kiểm soát phiên bản để vận chuyển, với lớp trên các quy tắc đặc biệt để làm cho các thẻ không có phiên bản, là bất cứ điều gì nhưng dễ nắm bắt.
Jakub Narębski

5

Tôi đã sử dụng Git hơn một năm trong công việc hiện tại của mình và trước đó, đã sử dụng Mercurial trong hơn một năm tại công việc trước đây của tôi. Tôi sẽ cung cấp một đánh giá từ quan điểm của người dùng.

Đầu tiên, cả hai đều là hệ thống kiểm soát phiên bản phân tán. Các hệ thống kiểm soát phiên bản phân tán đòi hỏi một sự thay đổi trong tư duy từ các hệ thống kiểm soát phiên bản truyền thống, nhưng thực sự hoạt động tốt hơn nhiều theo nhiều cách một khi người ta hiểu chúng. Vì lý do này, tôi coi cả Git và Mercurial vượt trội hơn nhiều so với Subversion, Perforce, v.v ... Sự khác biệt giữa hệ thống kiểm soát phiên bản phân tán và hệ thống kiểm soát phiên bản truyền thống lớn hơn nhiều so với sự khác biệt giữa Git và Mercurial.

Tuy nhiên, cũng có những khác biệt đáng kể giữa Git và Mercurial khiến mỗi loại phù hợp hơn với tập hợp con của các trường hợp sử dụng.

Mercurial đơn giản hơn để học. Tôi đã đến mức mà tôi hiếm khi phải tham khảo tài liệu hoặc ghi chú sau một vài tuần sử dụng Mercurial; Tôi vẫn phải tham khảo các ghi chú của mình thường xuyên với Git, ngay cả sau khi sử dụng nó trong một năm. Git phức tạp hơn đáng kể.

Điều này một phần vì Mercurial chỉ đơn giản là sạch hơn. Bạn hiếm khi phải phân nhánh thủ công trong Mercurial; Mercurial sẽ tự động tạo một nhánh ẩn danh cho bạn nếu và khi bạn cần. Danh pháp Mercurial trực quan hơn; bạn không phải lo lắng về sự khác biệt giữa "tìm nạp" và "kéo" như bạn làm với Git. Mercurial là một lỗi ít hơn một chút. Có các vấn đề về độ nhạy trường hợp tên tệp được sử dụng để gây ra sự cố khi đẩy các dự án trên các nền tảng bằng cả Git và Mercurial; điều này đã được sửa trong Mercurial một thời gian trước trong khi chúng chưa được sửa trong Git lần trước tôi đã kiểm tra. Bạn có thể nói với Mercurial về việc đổi tên tập tin; với Git, nếu nó không tự động phát hiện việc đổi tên - một đề xuất rất hay bị bỏ lỡ trong trải nghiệm của tôi - việc đổi tên hoàn toàn không thể được theo dõi.

Tuy nhiên, lý do khác cho sự phức tạp thêm của Git là phần lớn cần thiết để hỗ trợ các tính năng và sức mạnh bổ sung. Đúng, việc xử lý phân nhánh trong Git sẽ phức tạp hơn - nhưng mặt khác, một khi bạn có các nhánh, sẽ không quá khó để làm những việc với những nhánh gần như không thể trong Mercurial. Nổi loạn các nhánh là một trong những điều sau: bạn có thể di chuyển nhánh của mình sao cho gốc của nó, thay vì là trạng thái của thân cây khi bạn phân nhánh, là trạng thái của thân cây bây giờ; điều này đơn giản hóa rất nhiều lịch sử phiên bản khi có nhiều người làm việc trên cùng một cơ sở mã, vì mỗi lần đẩy vào thân cây có thể được thực hiện để xuất hiện tuần tự, thay vì đan xen. Tương tự, việc thu gọn nhiều cam kết trên chi nhánh của bạn thành một cam kết dễ dàng hơn nhiều

Cuối cùng, tôi nghĩ rằng sự lựa chọn giữa Mercurial và Git nên phụ thuộc vào mức độ lớn của các dự án kiểm soát phiên bản của bạn, được đo lường theo số lượng người làm việc trên chúng cùng một lúc. Ví dụ, nếu bạn có một nhóm hơn chục người làm việc trên một ứng dụng web nguyên khối, thì các công cụ quản lý chi nhánh mạnh hơn của Git sẽ giúp nó phù hợp hơn với dự án của bạn. Mặt khác, nếu nhóm của bạn đang phát triển một hệ thống phân tán không đồng nhất, chỉ có một hoặc hai nhà phát triển làm việc trên bất kỳ một thành phần nào cùng một lúc, sử dụng kho lưu trữ Mercurial cho mỗi dự án thành phần sẽ cho phép phát triển diễn ra suôn sẻ hơn với ít hơn quản lý kho lưu trữ trên cao.

Điểm mấu chốt: nếu bạn có một nhóm lớn phát triển một ứng dụng khổng lồ, hãy sử dụng Git; nếu các ứng dụng riêng lẻ của bạn nhỏ, với bất kỳ tỷ lệ nào đến từ số lượng thay vì kích thước của các ứng dụng đó, hãy sử dụng Mercurial.


4

Một sự khác biệt hoàn toàn không liên quan đến chính DVCS:

Git dường như rất phổ biến với các nhà phát triển C. Git là kho lưu trữ thực tế cho Linux Kernel và đây có thể là lý do tại sao nó rất phổ biến với các nhà phát triển C. Điều này đặc biệt đúng đối với những người có sự sang trọng chỉ làm việc trong thế giới Linux / Unix.

Các nhà phát triển Java dường như ủng hộ Mercurial hơn Git. Có thể có hai lý do cho điều đó: Một là một số dự án Java rất lớn được lưu trữ trên Mercurial, bao gồm cả chính JDK. Một điều nữa là cấu trúc và tài liệu sạch của Mercurial hấp dẫn những người đến từ trại Java trong khi những người như vậy thấy lệnh Gt không nhất quán đặt tên và thiếu tài liệu. Tôi không nói điều đó thực sự đúng, tôi đang nói mọi người đã quen với một thứ gì đó từ môi trường sống thông thường của họ và sau đó họ có xu hướng chọn DVCS từ đó.

Các nhà phát triển Python hầu như chỉ ủng hộ Mercurial, tôi cho rằng. Thực tế không có lý do hợp lý nào cho điều đó ngoài thực tế là Mercurial dựa trên Python. (Tôi cũng sử dụng Mercurial và tôi thực sự không hiểu tại sao mọi người lại ồn ào về ngôn ngữ triển khai của DVCS. Tôi không hiểu một từ của Python và nếu nó không được liệt kê ở đâu đó được dựa trên Python thì tôi sẽ không biết).

Tôi không nghĩ bạn có thể nói rằng một DVCS phù hợp với ngôn ngữ hơn ngôn ngữ khác, vì vậy bạn không nên chọn từ đó. Nhưng trong thực tế, mọi người chọn (một phần) dựa trên DVCS mà họ tiếp xúc nhiều nhất với tư cách là một phần của cộng đồng của họ.

(không, tôi không có số liệu thống kê sử dụng để sao lưu các khiếu nại của mình ở trên .. tất cả đều dựa trên sự chủ quan của riêng tôi)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.