Làm thế nào tôi có thể giảm thiểu cơn đau git khi mọi người đang làm việc trên chủ?


123

Nhóm tài liệu của chúng tôi gồm khoảng mười người gần đây đã chuyển từ SVN sang Git. Ở SVN, mọi người đều làm việc với chủ - một mô hình mà tôi luôn ghét, nhưng tôi không thể mang lại sự thay đổi đó. Là một phần của việc chuyển sang Git, chúng tôi đã đồng ý khắc phục điều đó, nhưng chúng tôi chưa thể làm điều đó (chờ đợi các thay đổi xây dựng sẽ cho phép các bản dựng từ các nhánh tùy ý). Trong khi đó, mọi người đang làm việc trên chủ. Có tôi biết điều này là khủng khiếp, tin tôi.

Bây giờ chúng ta đang thấy nhiều trục trặc hơn so với khi chúng ta sử dụng SVN, một số nguyên nhân gây ra bởi mô hình hai giai đoạn của Git (cục bộ và từ xa). Đôi khi mọi người cam kết nhưng không thúc đẩy, hoặc họ kéo và nhận xung đột với những thay đổi cục bộ đang chờ xử lý. Ngày hôm qua, ai đó đã ghi lại những thay đổi gần đây - bằng cách nào đó - với sự hợp nhất đã sai, điều mà tôi nghĩ là sự hợp nhất mà Git thực hiện khi bạn kéo và có những thay đổi nổi bật. (Anh ấy đã không thể cho tôi biết chính xác những gì anh ấy đã làm, và vì anh ấy đang sử dụng GUI nên tôi không thể kiểm tra lịch sử vỏ của anh ấy.)

Là người dùng Git thành thạo nhất (đọc: Tôi đã sử dụng nó trước đây, mặc dù không phải vì điều gì quá phức tạp), tôi là người thiết lập chính sách, dạy các công cụ và dọn dẹp mớ hỗn độn. Những thay đổi nào tôi có thể thực hiện đối với cách chúng tôi đang sử dụng các công cụ để làm cho một chủ hoạt động được chia sẻ, chủ động ít bị lỗi cho đến khi chúng tôi có thể chuyển sang phát triển trên các nhánh?

Nhóm đang sử dụng Rùa Git trên Windows. Chúng tôi đang sử dụng Rùa Git vì chúng tôi đã sử dụng Rùa SVN trước đây. ( Cá nhân tôi sử dụng dòng lệnh dưới Cygwin cho một số thao tác, nhưng nhóm đã làm rõ rằng họ cần GUI và chúng tôi sẽ sử dụng GUI này.) Câu trả lời nên hoạt động với công cụ này, không đề xuất thay thế.

Rùa Git có sẵn "Cam kết & Đẩy" dưới dạng một thao tác duy nhất và tôi đã bảo họ luôn luôn làm điều đó. Tuy nhiên, đó không phải là nguyên tử - có thể xảy ra rằng cam kết (mà sau tất cả là cục bộ) hoạt động tốt nhưng lực đẩy không (giả sử, do xung đột hoặc do sự cố mạng). Khi điều đó xảy ra, họ nhận được một lỗi mơ hồ; Tôi đã bảo họ kiểm tra nhật ký cam kết BitBucket nếu họ có bất kỳ nghi ngờ nào về một cam kết gần đây và, nếu họ không thấy điều đó, sẽ thúc đẩy. (Và để giải quyết xung đột nếu đó là vấn đề hoặc yêu cầu trợ giúp nếu họ không biết phải làm gì.)

Nhóm đã có thói quen tốt là "kéo sớm và thường xuyên". Tuy nhiên, dường như kéo có thể gây ra xung đột, mà tôi nghĩ là mới? Nếu không mới, thường xuyên hơn nhiều so với ở SVN. Tôi đã nghe nói rằng tôi có thể thay đổi cách Git thực hiện (rebase thay vì hợp nhất), nhưng tôi không hiểu rõ về sự đánh đổi ở đó (hoặc cách thực hiện trong môi trường của chúng ta).

Máy chủ là BitBucket (không phải Github). Tôi có toàn quyền kiểm soát đối với kho lưu trữ của chúng tôi nhưng không có gì trên máy chủ nói chung. Không ai trong số đó có thể thay đổi.

Các tệp nguồn là XML. Ngoài ra còn có các tệp đồ họa mà mọi người đều biết bạn không thể hợp nhất, nhưng chúng tôi cũng gần như không bao giờ có va chạm ở đó. Xung đột hợp nhất đến từ các tệp XML, không phải đồ họa.

Tôi có thể thay đổi gì để sử dụng Git của chúng tôi để giúp việc chia sẻ tổng thể diễn ra suôn sẻ hơn cho nhóm cho đến khi chúng tôi có thể chuyển sang sử dụng các nhánh tính năng với các yêu cầu kéo được kiểm tra, xác thực?


52
Đừng sử dụng rùa, hãy sử dụng Tiện ích mở rộng Git. Rùa cố gắng che giấu rằng Git không phải là SVN và phá hủy hầu hết sự vĩ đại của git. Tôi đã đi qua SVN-> Git transistion hai lần và Git Extension là một công cụ tuyệt vời để khiến mọi người nghĩ theo cách git.
Wilbert

91
Git không phải là SVN. Nếu bạn cố gắng sao chép SVN bằng Git, bạn sẽ nhận được tất cả các điểm đau của SVN với tất cả các điểm đau của Git, không có lợi ích nào cả, nó sẽ không bao giờ hoạt động. Vấn đề lớn nhất bạn gặp phải là một vấn đề xã hội, bạn đã có các thành viên trong nhóm không chịu học các khái niệm mới. Bạn không thể giải quyết điều đó bằng giải pháp kỹ thuật, bạn cần bắt đầu bằng cách mua các thành viên trong nhóm để tìm hiểu các khái niệm Git thay vì cố gắng thuyết phục họ rằng nó giống như SVN.
Lie Ryan

10
Tôi biết bạn nói không nên giới thiệu các ứng dụng khác, nhưng @Wilbert có quyền. TortoiseGit cố gắng che giấu mọi thứ, điều này thực sự khiến chúng đau đớn hơn trong trải nghiệm của tôi. Nếu muốn có UI, tôi đã tìm thấy quá trình chuyển đổi dễ dàng nhất (đọc: Tôi huấn luyện các nhóm phần mềm phi truyền thống về công cụ và DevOps) đã thông qua SourceTree của Atlassian (tất nhiên là được đào tạo phù hợp). Tôi cũng đã sử dụng GitFlow để giúp họ hiểu mô hình của Git (mặc dù, điều này không phù hợp với tất cả các đội).
JasCav

28
Tôi hơi ngạc nhiên khi tất cả mọi người đều đang làm việc với chủ, đó là nguyên lý trung tâm của Tích hợp liên tục . Miễn là bạn có một bộ kiểm tra mạnh mẽ và mọi người đều biết khi bản dựng bị hỏng, làm việc từ chủ có thể thuận lợi cho việc hợp tác nhóm. Phân nhánh tính năng (mà hầu như tất cả các quy trình công việc khác dựa vào một mức độ nào đó) có thể bị phá hủy như nhau mà không có sự bảo vệ tại chỗ. Bạn có thể có một số vấn đề gốc sâu hơn tại chơi ở đây.
DanK

14
@DanK, tôi cũng nghĩ op đã xác định sai gốc rễ của vấn đề. Nếu bạn có người ghi đè các thay đổi trên chủ và bạn chuyển sang một chi nhánh, bạn sẽ có những người thay đổi ghi chú trên chi nhánh. Nếu bạn chuyển đến các chi nhánh riêng lẻ, bạn sẽ có những người gặp vấn đề khi hợp nhất trong các chi nhánh của họ (hoặc những người phát triển trên chi nhánh của họ mà không hợp nhất trong nhiều tháng).
dùng3067860

Câu trả lời:


11

Cho đến nay SourceTree là IDE tốt nhất để tìm hiểu các khái niệm, bởi vì nó hiển thị tất cả các hộp thoại và tùy chọn có liên quan bạn có trên mỗi giai đoạn, các tùy chọn mặc định thường ổn, không gây rối với rebase, v.v. Chỉ cần làm theo quy trình thông thường:

  • Kéo từ chủ, chỉ để chắc chắn rằng bạn đang cập nhật
  • Sửa đổi tập tin của bạn
  • Cam kết thay đổi của bạn (đó chỉ là cục bộ)
  • Kéo lại từ chủ (điều này sẽ khiến xung đột xuất hiện)
  • Chỉnh sửa tất cả các tệp cho đến khi các xung đột được giải quyết, có nghĩa là tệp ở trạng thái propper mà bạn muốn cam kết (không có <<<<< ĐẦU và >>>> tin nhắn chính trong tệp thô)
  • Cam kết thay đổi hợp nhất
  • Đẩy

Nếu mọi người làm theo công thức này, họ sẽ ổn thôi.

Mỗi khi ai đó thực hiện thay đổi lớn hơn hoặc trung tâm, hãy thông báo cho những người dùng khác cam kết tại địa phương và rút từ chủ, để họ không gặp quá nhiều xung đột sau đó và người đầu tiên vẫn ở đó để giải quyết xung đột với họ.

Đầu tư nhiều thời gian để khiến mọi người hiểu về dòng chảy, nếu không họ có thể loanh quanh một lúc và sau đó cảm thấy thoải mái với nó trong khi thực sự vặn nhánh chính, ví dụ "sử dụng tệp của tôi thay vì từ xa" để giải quyết xung đột sẽ xảy ra ra tất cả những thay đổi được thực hiện bởi những người khác.

Git là một hệ thống khó học, đặc biệt nếu bạn lớn lên với Svn, hãy kiên nhẫn và cho họ thời gian để học nó đúng cách, với người dùng mới, đôi khi bạn có thể dành một ngày để dọn dẹp một số mớ hỗn độn, đó là điều bình thường. ;)


9
nitpick: SourceTree không phải là Môi trường phát triển tích hợp ...
Mathieu Guindon

Bây giờ tôi đã có ai đó (ngoài tôi) lái thử quy trình công việc này (với Rùa Git, ý tôi là) để rũ bỏ mọi bất ngờ / vấn đề. Giả sử không có, tôi dự định sẽ đưa nó ra cho đội trong một vài ngày.
Monica Cellio

Tôi biết rằng câu trả lời được bình chọn cao này bao gồm rất nhiều lãnh thổ giống như câu trả lời này, nhưng phải đến khi tôi thấy công thức được đưa ra từng bước trong câu trả lời này, tôi mới thực sự hiểu cách áp dụng nó, vì vậy tôi Tôi chấp nhận điều này (đối với công thức, không phải IDE :-)). Chúng tôi đã theo dõi quá trình này trong một vài ngày mà không có vấn đề gì thêm. Chúng tôi cũng sẽ tập trung nhiều hơn vào việc khám phá và hiểu "cách git".
Monica Cellio

99

Có ba điều chính cần nhớ khi bạn làm việc ở cùng chi nhánh với người khác:

  • Không bao giờ sử dụng --forcetrừ khi bạn thực sự biết những gì bạn đang làm.
  • Hoặc là commithoặc stashcông việc của bạn trong tiến trình trước mỗi pull.
  • Nó thường đi dễ dàng hơn nếu bạn pullngay trước khi a push.

Ngoài ra, tôi sẽ chỉ ra rằng với điều khiển phiên bản phân tán, việc repo "chính thức" của bạn có sử dụng chi nhánh hay không là không quan trọng. Điều đó không ảnh hưởng gì đến những gì người dùng cá nhân làm trong repos địa phương của họ. Tôi đã từng sử dụng git để có được các chi nhánh địa phương khi công ty của tôi sử dụng một VCS trung tâm hoàn toàn khác. Nếu họ tạo các nhánh cục bộ cho các tính năng của mình và thực hiện các lỗi kết hợp với cục bộ của mình master, việc khắc phục sẽ dễ dàng hơn rất nhiều mà không cần đi vào reflog hoặc một số phép thuật khác.


51
Luôn pulltrước pushlà lời khuyên tuyệt vời, nhưng tôi muốn đi thêm một bước nữa và đề nghị bạn nên xem xét liệu bạn có thể pull --rebasekhi bạn làm.
anaximander

20
@anaximander, tôi khuyên mọi người nên sử dụng --rebase hoặc không ai ...
keuleJ

12
@TemporalWolf Đó là những gì họ nói với tôi về chiếc bánh quá ...
BlackV ăn được

15
@anaximander "sau đó bạn không giải quyết được xung đột và bạn đang làm sai. Trong trường hợp này, họ không thể tin tưởng được với rebase". Vì vậy, bạn đang nói rằng bạn thậm chí không bao giờ một lần, làm rối tung một cuộc xung đột hợp nhất? Phải làm việc tốt trên các cơ sở mã đủ đơn giản để bạn có thể thực hiện khái quát hóa đó. Đây là cuộc tấn công của Linus, mà cá nhân tôi thấy khá dễ chịu hơn bất kỳ cách tiếp cận đen trắng nào.
Voo

10
"Không bao giờ sử dụng --forcetrừ khi bạn thực sự biết những gì bạn đang làm." Tôi sẽ đi xa hơn. Không cho phép viết lại lịch sử trong kho "chính" từ mọi người trừ những cá nhân đáng tin cậy nhất. Cho dù bạn có thể làm điều đó ít nhất một phần tùy thuộc vào lưu trữ của bạn, nhưng BitBucket không có tùy chọn.
jpmc26

68

Có thể mất một ngày để mọi người học git?

Máy tính sử dụng các chuyên gia thực sự cần được học một công cụ mới và mặc dù có thể mắc nhiều lỗi trong bất kỳ VCS nào, họ nên sử dụng công cụ này vì nó được thiết kế để sử dụng.

Cách tốt nhất để giới thiệu điều này là khiến mọi người làm việc trên nhánh riêng của họ khi họ thực hiện thay đổi (càng ngắn càng tốt) và rebase sau đó hợp nhất trở lại thành chủ khi hoàn thành. Điều này không quá xa với cách làm việc hiện tại và giới thiệu một quy trình làm việc đơn giản mà họ có thể làm quen cho đến khi họ cảm thấy đủ tự tin để thực hiện các hoạt động phức tạp hơn.

Tôi không sử dụng windows nhưng nếu Rùa về cơ bản là ẩn git khỏi chúng và giả vờ rằng đó là SVN thì có lẽ Rùa là công cụ sai.


37
"nếu Rùa về cơ bản là che giấu git khỏi chúng và giả vờ rằng đó là SVN thì có lẽ Rùa là công cụ sai." điều này. Tôi biết OP đã nói không thay thế công cụ này nhưng nếu nó che khuất cách thức hoạt động của git theo bất kỳ cách nào thì điều đó sẽ gây bất lợi cho sự phát triển cá nhân của nhà phát triển và hiệu quả hoạt động của bạn. Nhóm của bạn sẽ tiếp tục sử dụng sai VCS của bạn nếu họ không hiểu nó.
2rs2ts

3
Một tài nguyên học tập git hữu ích khác là Học Git Branching . Nó cho thấy một cây trực quan và ngoài ra có một hộp cát để bạn có thể giả lập một loạt các lệnh và xem loại kết quả của cây.
TemporalWolf

4
Phải mất nhiều thời gian hơn một ngày để mọi người trong nhóm phát triển học git (và họ không bị mờ hoặc chậm chạp), vì vậy tôi cho rằng điều đó cũng đúng với nhóm tài liệu. Tôi sẽ xem các trang web được đề cập ở đây trong các bình luận (có lẽ chúng nên ở trong câu trả lời này hoặc câu trả lời khác?).
Monica Cellio

3
Bạn sẽ không học được git cho đến khi bạn mắc phải tất cả các sai lầm và có nỗi đau của sự hợp nhất và nổi loạn mâu thuẫn, họ chỉ cần học dòng chảy ngắn bên trên của việc tạo một nhánh, từ chối chi nhánh đó để bao gồm bất kỳ thay đổi nào từ chủ và sáp nhập chi nhánh của họ trở lại thành chủ. Bất kỳ học tập nào khác họ có thể làm khi họ cố gắng giải quyết bất kỳ nỗi đau nào họ gặp phải trong dòng chảy này (sẽ có một số). Ít nhất nhóm tài liệu không có mối quan tâm về việc phá vỡ cơ sở mã.
MarkJL

1
@ 2rs2ts Rùa Git là một git gui đặc biệt. Tôi cài đặt nó trên tất cả các hộp cửa sổ của mình và tôi rất quen thuộc với dòng lệnh git. Mergetool của nó là một trong những tốt nhất tôi từng sử dụng. Tôi đã giới thiệu rất nhiều người dùng mới làm quen với git bằng Rùa Git. Vấn đề lớn nhất của nó là nó phơi bày một số tùy chọn git nâng cao chỉ bằng một hộp kiểm đơn giản. Vì vậy, một tùy chọn như - lực đẩy có thể được thực hiện bằng cách chỉ cần chọn một hộp trong gui đẩy. Đây có thể là những gì đã được thực hiện mà mất công việc. Tôi không sử dụng Rùa nhiều nhưng có một vài điều thực sự đơn giản hơn.
gnash117

26

Đôi khi, những gì bạn đang làm phải thay đổi.

Vấn đề lớn nhất là tất cả mọi người đang làm việc trên tổng thể. Đây không phải là điển hình cho việc phát triển mã và cũng có thể là mô hình sai trong trường hợp của bạn. Nếu bạn có thể thay đổi điều đó, bằng cách yêu cầu / yêu cầu thay đổi được thực hiện trên các nhánh riêng biệt, bạn sẽ ở trạng thái tốt hơn nhiều. Với các chi nhánh, bạn có thể đạt được những điều sau đây:

  • Thực thi mà không đẩy trực tiếp masterđược cho phép.
  • Thực thi thông qua Bitbucket rằng các yêu cầu kéo được tạo và có ít nhất một phê duyệt trước khi sáp nhập . Điều này đảm bảo ai đó đang xem xét các thay đổi và cũng làm cho việc hợp nhất trở nên ít đau đớn hơn, vì UI sẽ hiển thị xung đột với phiên bản mã từ xa, chứ không phải bất cứ điều gì người dùng có trên máy tính để bàn. Điều này ngăn chặn kịch bản cam kết thành công nhưng không thành công.
  • Thực hiện "bản dựng" chống lại repo của bạn trước khi sáp nhập. Tôi nhận ra đó là một tài liệu repo, nhưng có thể có kiểm tra chính tả, quét hợp pháp hoặc thậm chí dịch tự động (xuất các thứ STRING_DEF sang tệp csv) có thể được xây dựng từ bản dựng này. Hoặc có thể không, phụ thuộc vào công việc của bạn.
  • Cho phép mọi người làm việc trên nhiều thứ khác nhau đồng thời dễ dàng hơn. Vâng, điều này cũng có thể được thực hiện với stash, nhưng nó hơi rắc rối và một cái gì đó cho tôi biết bạn cũng không sử dụng chúng.

Nếu bạn không thể sử dụng phân nhánh, bạn có thể cân nhắc viết một merge-and-pushtập lệnh có thể tự động hóa một số điểm đau. Có lẽ nó sẽ kiểm tra xem người dùng không ở phía sau chủ, thực hiện tìm nạp và kéo, sau đó thử hợp nhất ( có thể với--no-commit --no-ff ), v.v.


3
Chúng tôi sẽ chuyển sang phân nhánh, vì tất cả các lý do bạn đã đề cập (nhưng đặc biệt nhất là các PR được kiểm soát và khả năng bắt buộc các xung đột được giải quyết trên chi nhánh trước khi hợp nhất). Bạn có thể nói thêm về cách tấn công một kịch bản hợp nhất và đẩy không?
Monica Cellio

6
Tôi không đồng ý với lời khuyên này. Các nhánh tính năng chạy dài có thể tàn phá hơn nhiều so với làm việc từ chủ (mà nếu bạn không có một quy trình làm việc tốt thì đó là điều sẽ xảy ra). Martin Fowler có một bài viết tuyệt vời về chủ đề này. Vào cuối ngày, nhóm OP có vấn đề cộng tác quy trình công việc không phải là vấn đề Git .. Tôi sẽ lập luận rằng nhiều chi nhánh sẽ đơn giản kết hợp vấn đề này.
DanK

6
Các nhánh tính năng chạy dài không phải là những gì tôi đã ủng hộ (cũng không được đề cập). Tôi đồng ý rằng họ không tốt cho sự phát triển "thường xuyên" và sẽ không tốt hơn ở đây. Các nhánh "ravioli" thông thường với các bộ thay đổi nhỏ có thể được xem xét / kiểm tra trước khi hợp nhất là rất hữu ích và sẽ không kém phần hữu ích ở đây chỉ vì đó là tài liệu repo cho tất cả các lý do được nêu trong câu trả lời.
Dan1701

3
Chắc chắn, tôi hiểu những gì bạn đang nói và tôi không đồng ý về mặt lý thuyết nhưng với các vấn đề hợp tác hiện đang được mô tả ở đây, ngay cả với ý định tốt nhất, tôi nghĩ các chi nhánh của OP sẽ vô tình biến thành các chi nhánh hoạt động lâu dài. Vào cuối ngày, làm việc trên một dòng chính so với nhánh tính năng không phải là vấn đề gốc ở đây. Vấn đề là sự thiếu hiểu biết chung về các phần mở rộng của một VCS phân tán và thiếu sự hợp tác / gắn kết giữa các nhà phát triển. Tính năng phân nhánh của chính nó sẽ không khắc phục điều đó nhưng IMO làm trầm trọng thêm nó.
DanK

2
Chúng ta đang tiến gần đến việc cần chuyển điều này sang trò chuyện, nhưng, nếu bạn luôn ở trong một nhánh tính năng thì bạn sẽ không hoàn thành công việc của mình. Chúng phải được sáp nhập vào chi nhánh vận chuyển (hoặc xuất bản, trong trường hợp này). Điều này cho phép giới thiệu các kiểm tra tự động và biện pháp bảo vệ xung quanh các vấn đề mà nhóm của họ đang gặp phải. Một nhánh tính năng hoàn toàn khác với làm việc trên master trong hầu hết các công cụ (ít nhất là Bitbucket) được thiết lập để cho phép các yêu cầu kéo với sự chấp thuận cần thiết và các bản dựng hợp nhất như một phần của mô hình phân nhánh, không phải là điều xảy ra khi chỉ hoạt động trên master.
Dan1701

12

Nhấn mạnh rằng bạn có thể làm lại hợp nhất

Điều này có thể rõ ràng với bạn nhưng người dùng SVN trước đây có thể không biết họ có thể cố gắng giải quyết hợp nhất nhiều lần. Điều này có thể cắt giảm số lượng cờ trợ giúp mà bạn nhận được.

Trong SVN khi làm việc, trunkbạn sẽ có những thay đổi không được cam kết. Sau đó, bạn sẽ làm một svn update. Tại thời điểm đó, những thay đổi của bạn sẽ trộn lẫn với những người khác thay đổi mãi mãi. Không có cách nào để hoàn tác nó (afaik), vì vậy bạn không có lựa chọn nào khác ngoài việc kiểm tra thủ công mọi thứ và hy vọng repo ở trạng thái tốt. Khi thực sự bạn sẽ thoải mái hơn nhiều chỉ cần làm lại việc hợp nhất.

Mọi người sẽ có cùng tâm lý ngay cả khi chúng tôi chuyển sang git. Dẫn đến rất nhiều lỗi vô ý.

May mắn thay với git có một cách quay trở lại, đặc biệt bởi vì bạn có thể thực hiện các cam kết địa phương. (Tôi mô tả sau về cách điều này được thể hiện trong dòng lệnh)

Mặc dù cách này được thực hiện sẽ khác nhau dựa trên dụng cụ. Tôi thấy việc làm lại một cái kéo không phải là thứ gì đó được phơi bày trong nhiều GUI dưới dạng một nút duy nhất nhưng có lẽ là có thể. Tôi thích bạn sử dụng cygwin. Đồng nghiệp của tôi sử dụng sourcetree. Vì bạn sử dụng BitBucket, nên sử dụng nó làm GUI của bạn vì nó được quản lý bởi cùng một công ty: Atlassian. Tôi nghi ngờ có một số tích hợp chặt chẽ hơn.

Về kéo

Tôi nghĩ rằng bạn đã đúng rằng sự hợp nhất trong đó pulllà những gì làm mọi người rối tung lên. A pullthực sự git fetchlấy các thay đổi từ máy chủ, theo sau là git merge origin/<branchname>* kết hợp các thay đổi từ xa vào nhánh cục bộ của bạn. ( https://git-scm.com/docs/git-pull )

Kết quả cuối cùng là tất cả các lệnh hợp nhất tiêu chuẩn làm việc với kéo. Nếu sự hợp nhất đó có xung đột, bạn có thể hủy bỏ git merge --abort. Mà sẽ đưa bạn trở lại trước khi hợp nhất của bạn. Sau đó, bạn có thể thử lại với một trong hai git pullhoặc git merge origin/<branchname>.

Nếu bạn có thể bằng cách nào đó tìm hiểu cách thực hiện những điều trên bằng cách sử dụng công cụ GUI của đồng nghiệp, tôi nghĩ rằng điều đó sẽ giải quyết hầu hết các vấn đề của bạn. Xin lỗi tôi không thể cụ thể hơn.

* Tôi hiểu rằng nguồn gốc không phải luôn luôn như vậy ở đây.

Sử dụng git reflogđể chẩn đoán vấn đề

Tôi cũng như bạn, phải chẩn đoán các vấn đề chủ yếu được tạo ra do lạm dụng các công cụ GUI. Tôi thấy rằng git reflogđôi khi có thể hữu ích vì đó là một hành động khá nhất quán trên kho lưu trữ. Mặc dù rất khó đọc.

Một sự thay thế

Vì tình hình của bạn là tạm thời , bạn có thể quay lại SVN cho đến khi bạn có quy trình tại chỗ để triển khai. Tôi sẽ do dự để làm điều này vì nhiều nơi sẽ tiếp tục nói rằng 'Chúng tôi đã thử git một lần nhưng nó không hoạt động ...' và không bao giờ thực sự lấy lại.

Một số vấn đề chuyển tiếp phổ biến khác

  • Mọi người thường xóa và đóng lại repo của họ, tin chắc rằng repo của họ ở trạng thái không sử dụng được. Thông thường điều này được gây ra bởi mất dấu vết của sự khác biệt địa phương và từ xa. Cả công cụ GUI và CLI đều thất bại trong việc hiển thị tốt điều này. Trong CLI tôi tìm thấy git log --decoratecách dễ nhất để tổng quan về sự khác biệt. Nhưng nếu mọi thứ trở nên quá nhiều lông trên chủ (ví dụ), bạn có thểgit reset --hard origin/master

2
Về điểm cuối cùng của bạn: Đối với một tổng quan thực sự nhanh chóng, cấu trúc, tôi thấy git log --oneline --decorate --graphlý tưởng. Vì vậy, tôi đã xác định một bí danh shell cho sự kết hợp chính xác đó.
cmaster

1
+1 cho câu trả lời của bạn, tôi chỉ thấy đề xuất thay thế là xấu, vì lý do bạn đề cập. Bạn sẽ bị đau ngay cả khi bạn quay trở lại SVN và sau đó trong tương lai hãy đến git. Mọi người trong nhóm sẽ chỉ học công cụ mới, khác biệt, đau đớn nếu họ không có lựa chọn nào khác. Chỉ sau khi sử dụng và làm những sai lầm ngu ngốc, họ sẽ bắt đầu đánh giá cao những gì git có thể làm.
CodeMonkey

8

Một cơ chế có thể, mà rất nhiều nhóm nguồn mở đã áp dụng, là sử dụng mô hình forking - https://www.atlassian.com/git/tutorials/compared-workflows (hãy chắc chắn phát âm rõ khi thảo luận về quy trình làm việc git ) .

Trong mỗi nhà phát triển hoặc tiểu đội có riêng của họ ngã ba của kho mà họ kiểm tra từ BitBucket không cung cấp một cơ chế cho việc này , thiết lập một nguồn gốc "lội ngược dòng", thêm vào điều khiển từ xa mặc định - họ sẽ phải nhớ để "lấy thượng nguồn "Và" hợp nhất từ ​​xa / ngược dòng / chính "một cách thường xuyên.

Nó có thể sẽ giải quyết các vấn đề về cơ chế xây dựng của bạn vì các công cụ xây dựng có thể sẽ được trỏ đến chủ trong một dự án khác, tức là ngã ba.

Sau đó, bạn có thể loại bỏ khỏi hầu hết mọi người khả năng đẩy trực tiếp vào dự án chính và biến nhóm đó thành một nhóm nhỏ hơn với vai trò xem xét và phê duyệt. Xem https://www.atlassian.com/git/tutorials/making-a-pull-request

Nơi để đọc về việc đảm bảo rằng mọi kiểm tra mong muốn đều được thực hiện trước khi đẩy là trong phần sách git trên hook - https://git-scm.com/book/gr/v2/Customizing-Git-Git-Hooks - bạn có thể sử dụng móc trước cam kết và đẩy trước để thực hiện những việc như chạy một số thử nghiệm trên cam kết được đề xuất để đảm bảo rằng công việc hợp lệ, v.v. - vấn đề duy nhất với móc phía máy khách là nhà phát triển có thể vô hiệu hóa chúng hoặc không bật được họ

Cả hai tìm nạp / hợp nhất & móc ngược dòng đều có sẵn trong TortoiseGit.


Một ý tưởng không tồi, thực sự. Có vẻ như nhóm này sẽ được hưởng lợi từ Merge Master cho đến khi họ cảm thấy thoải mái hơn. +1
Greg Burghardt

2
BitBucket có tính năng đồng bộ hóa ngã ba, tự động chuyển nhanh các nhánh khi có thể. Thật thuận tiện để rẽ nhánh ngày hôm nay và lấy từ nguồn gốc vào tuần tới mà không bao giờ lo lắng về thượng nguồn.
piedar

3

Điều này sẽ nghe có vẻ phản trực giác, nhưng hãy nghe tôi nói:

Khuyến khích họ bắt đầu thử nghiệm với git

Một trong những điều thú vị về git là thật dễ dàng để thực hiện bất kỳ hoạt động địa phương nào hoàn toàn an toàn. Khi tôi lần đầu tiên bắt đầu sử dụng git, một trong những điều tôi thấy mình đang làm là nén toàn bộ thư mục để sao lưu trong trường hợp tôi làm hỏng cái gì đó. Sau đó tôi phát hiện ra rằng đây là một loại bùn khổng lồ và gần như không bao giờ thực sự cần thiết để bảo vệ công việc của bạn, nhưng nó có ưu điểm là rất an toàn và rất đơn giản, ngay cả khi bạn không biết bạn đang làm gì và làm thế nào lệnh bạn muốn thử sẽ bật ra. Điều duy nhất bạn phải tránh khi bạn làm điều này là push. Nếu bạn không thúc đẩy bất cứ điều gì, đây là cách an toàn 100% để thử bất cứ điều gì bạn muốn.

Sợ thử đồ là một trong những cản trở lớn nhất đối với việc học git. Nó mang lại cho bạn rất nhiều quyền kiểm soát đối với mọi thứ mà nó gây khó chịu. Thực tế là bạn có thể tuân thủ một vài thao tác rất an toàn cho hầu hết việc sử dụng hàng ngày của bạn, nhưng việc tìm ra những lệnh nào cần thực hiện một số khám phá.

Bằng cách mang lại cho họ cảm giác an toàn , họ sẽ sẵn sàng hơn rất nhiều để cố gắng tìm ra cách tự làm mọi thứ. Và họ sẽ được trao quyền nhiều hơn nữa để tìm ra một luồng công việc cá nhân trên máy cục bộ phù hợp với họ. Và nếu không phải ai cũng làm điều tương tự tại địa phương , thì tốt, miễn là họ tuân thủ các tiêu chuẩn với những gì họ thúc đẩy . Nếu phải nén toàn bộ repo trước khi thực hiện một thao tác để khiến họ cảm thấy như vậy, thì tốt thôi; họ có thể chọn những cách tốt hơn để làm mọi thứ khi họ đi và khi họ thử đồ. Bất cứ điều gì để có được chính mình để bắt đầu thử công cụ và xem những gì nó làm.

Điều này không có nghĩa là đào tạo là vô giá trị. Ngược lại, đào tạo có thể giúp giới thiệu cho bạn các tính năng và mô hình và chuẩn mực. Nhưng nó không phải là một sự thay thế cho việc ngồi xuống và thực sự làm công việc trong công việc hàng ngày của bạn. Cả git và SVN đều không phải là những thứ mà bạn có thể chỉ cần đến một lớp học và sau đó bạn biết mọi thứ về nó. Bạn phải sử dụng chúng để giải quyết vấn đề của mình để làm quen với chúng và những tính năng nào phù hợp với những vấn đề nào.

Ngừng làm họ nản lòng khi học những thứ trong và ngoài của git

Tôi đã đề cập đến việc không thúc đẩy bất cứ điều gì, điều thực sự đi ngược lại với một trong những điều bạn đã dạy họ: luôn luôn "Cam kết & Đẩy". Tôi tin rằng bạn nên ngừng bảo họ làm điều này và bảo họ bắt đầu làm ngược lại. Git về cơ bản có 5 "địa điểm" trong đó các thay đổi của bạn có thể là:

  • Trên đĩa, không cam kết
  • Dàn dựng nhưng không cam kết
  • Trong một cam kết địa phương
  • Trong một stash địa phương
  • Kho lưu trữ từ xa (Chỉ các cam kết và thẻ được đẩy và kéo giữa các kho khác nhau)

Thay vì khuyến khích họ kéo và đẩy mọi thứ trong một bước duy nhất, hãy khuyến khích họ tận dụng 5 địa điểm khác nhau này. Khuyến khích họ:

  • Lấy các thay đổi trước khi họ cam kết bất cứ điều gì.
  • Đưa ra quyết định làm thế nào để xử lý các thay đổi được tìm nạp. Các tùy chọn là:

    • Cam kết thay đổi cục bộ của họ, sau đó khởi động lại chúng trên đầu trang của các thay đổi được tìm nạp.
    • Cam kết thay đổi cục bộ của họ và sau đó thực hiện hợp nhất với các thay đổi được tìm nạp.
    • Bỏ bớt các thay đổi của họ, hợp nhất và sau đó hủy bỏ và giải quyết bất kỳ xung đột nào.

      Có những thứ khác, nhưng tôi sẽ không nhận được ở đây. Lưu ý rằng kéo theo nghĩa đen chỉ là tìm nạp và hợp nhất. Nó không giống họ; chính họ (Chuyển các --rebasethay đổi kéo từ tìm nạp + hợp nhất sang tìm nạp + rebase.)

  • Giai đoạn thay đổi của họ và sau đó xem xét chúng.
  • Cam kết thay đổi theo giai đoạn của họ và sau đó xem lại cam kết.
  • Đẩy riêng.

Điều này sẽ khuyến khích họ kiểm tra công việc của họ trước khi nó được công khai cho mọi người, điều đó có nghĩa là họ sẽ bắt lỗi sớm hơn. Họ sẽ thấy cam kết và nghĩ, "Đợi đã, đó không phải là điều tôi muốn" và không giống như ở SVN, họ có thể quay lại và thử lại trước khi họ đẩy.

Khi họ đã quen với ý tưởng tìm hiểu các thay đổi của họ ở đâu , sau đó họ có thể bắt đầu quyết định khi nào nên bỏ qua các bước và kết hợp các thao tác nhất định (khi nào nên kéo vì bạn đã biết bạn muốn tìm nạp + hợp nhất hoặc khi nào nên nhấp vào tùy chọn Cam kết & Đẩy) .

Đây thực sự là một trong những lợi ích to lớn của git so với SVN và git được thiết kế với mô hình sử dụng này trong tâm trí. Ngược lại, SVN giả định một kho lưu trữ trung tâm, vì vậy sẽ không có gì đáng ngạc nhiên nếu công cụ cho git không được tối ưu hóa cho cùng một quy trình. Trong SVN, nếu cam kết của bạn sai, thì cách truy đòi thực sự duy nhất của bạn là một cam kết mới để hoàn tác sai lầm.

Làm điều này thực sự sẽ tự nhiên dẫn đến chiến lược tiếp theo:

Khuyến khích họ sử dụng các chi nhánh địa phương

Các chi nhánh địa phương thực sự giảm bớt rất nhiều điểm đau khi làm việc trên các tệp được chia sẻ. Tôi có thể thực hiện tất cả các thay đổi tôi muốn trong chi nhánh của mình và nó sẽ không bao giờ ảnh hưởng đến bất cứ ai vì tôi không thúc đẩy chúng. Sau đó, khi thời gian đến, tôi có thể sử dụng tất cả các chiến lược hợp nhất và rebase giống nhau, chỉ dễ dàng hơn:

  • Tôi có thể nổi loạn chi nhánh địa phương của mình, điều này làm cho việc hợp nhất nó thành tầm thường.
  • Tôi có thể sử dụng một sự hợp nhất đơn giản (tạo một cam kết mới) trong tổng thể để đưa các thay đổi của chi nhánh địa phương của tôi vào đó.
  • Tôi có thể squash hợp nhất toàn bộ chi nhánh địa phương của mình thành một cam kết duy nhất trên chủ nếu tôi nghĩ rằng chi nhánh của tôi quá nhiều thứ để cứu vãn.

Sử dụng các chi nhánh địa phương cũng là một khởi đầu tốt để tìm ra một chiến lược phân nhánh có hệ thống. Nó giúp người dùng của bạn hiểu rõ hơn về nhu cầu phân nhánh của họ, vì vậy bạn có thể chọn chiến lược dựa trên nhu cầu và mức độ hiểu biết / kỹ năng hiện tại của nhóm và không chỉ bỏ qua Gitflow vì mọi người đã nghe về nó.

Tóm lược

Tóm lại, git không phải là SVN và không thể được đối xử như vậy. Bạn cần phải:

  • Loại bỏ nỗi sợ hãi bằng cách khuyến khích thử nghiệm an toàn.
  • Giúp họ hiểu git khác nhau như thế nào để họ có thể thấy điều đó thay đổi quy trình làm việc bình thường của họ.
  • Giúp họ hiểu các tính năng có sẵn để giúp họ giải quyết vấn đề của họ dễ dàng hơn.

Tất cả điều này sẽ giúp bạn dần dần chấp nhận sử dụng git tốt hơn, cho đến khi bạn đạt đến điểm mà bạn có thể bắt đầu thực hiện một bộ tiêu chuẩn.

Tính năng cụ thể

Trước mắt, những ý tưởng sau đây có thể giúp ích.

Nổi loạn

Bạn đã đề cập đến rebase và rằng bạn không thực sự hiểu nó trong câu hỏi của bạn. Vì vậy, đây là lời khuyên của tôi: hãy thử những gì tôi vừa mô tả. Thực hiện một số thay đổi cục bộ trong khi người khác đẩy một số thay đổi. Cam kết thay đổi của bạn tại địa phương . Zip thư mục kho lưu trữ của bạn như là một bản sao lưu. Lấy các thay đổi của người khác. Bây giờ hãy thử chạy một lệnh rebase và xem điều gì xảy ra với các cam kết của bạn! Bạn có thể đọc các bài đăng trên blog vô tận hoặc được đào tạo về rebase và cách bạn nên hoặc không nên sử dụng nó, nhưng không ai trong số đó là sự thay thế để thấy nó hoạt động. Vì vậy, hãy thử nó.

merge.ff=only

Điều này sẽ là một vấn đề sở thích cá nhân, nhưng tôi sẽ đề xuất nó ít nhất là tạm thời vì bạn đã đề cập rằng bạn đã gặp rắc rối với việc xử lý xung đột. Tôi khuyên bạn nên cài đặt merge.ffthànhonly :

git config --global merge.ff only

"ff" là viết tắt của "chuyển tiếp nhanh." Hợp nhất chuyển tiếp nhanh là khi git không cần kết hợp các thay đổi từ các cam kết khác nhau. Nó chỉ di chuyển con trỏ của nhánh lên một cam kết mới dọc theo một đường thẳng trong biểu đồ.

Những gì nó làm trong thực tế là ngăn git tự động cố gắng tạo các cam kết hợp nhất. Vì vậy, nếu tôi cam kết một cái gì đó cục bộ và sau đó kéo các thay đổi của người khác, thay vì cố gắng tạo một cam kết hợp nhất (và có khả năng buộc người dùng phải giải quyết các xung đột), việc hợp nhất sẽ thất bại. Trong thực tế, git sẽ chỉ thực hiện a fetch. Khi bạn không có cam kết cục bộ, việc hợp nhất tiến hành bình thường.

Điều này cung cấp cho người dùng cơ hội để xem xét các cam kết khác nhau trước khi cố gắng hợp nhất chúng và buộc họ đưa ra quyết định về cách xử lý tốt nhất khi kết hợp chúng. Tôi có thể rebase, tiếp tục với việc hợp nhất (sử dụng git merge --no-ffđể bỏ qua cấu hình) hoặc thậm chí tôi có thể tắt việc hợp nhất các thay đổi của mình ngay bây giờ và xử lý nó sau. Tôi nghĩ rằng cú va chạm tốc độ nhỏ này sẽ giúp nhóm của bạn tránh đưa ra những quyết định sai lầm về việc sáp nhập. Bạn có thể để nhóm của mình tắt nó đi khi họ xử lý tốt hơn việc hợp nhất.


2

Tôi đã trải qua cùng một SVN -> kinh nghiệm git tại công ty của tôi và từ kinh nghiệm của tôi, biện pháp khắc phục duy nhất là thời gian. Hãy để mọi người quen với các công cụ, để họ mắc lỗi, chỉ cho họ cách khắc phục chúng. Vận tốc của bạn sẽ bị ảnh hưởng trong một thời gian và mọi người sẽ mất việc, và mọi người sẽ có một chút mệt mỏi, nhưng đó là bản chất của việc thay đổi một cái gì đó cơ bản như VCS của bạn.

Điều đó nói rằng, tôi đồng ý với tất cả những người có quan điểm cho rằng TortoiseGit là một trở ngại, thay vì giúp đỡ, nên rất sớm trong giai đoạn chuyển tiếp. TortoiseGit ... không phải là một GUI tuyệt vời vào thời điểm tốt nhất và bằng cách che khuất cách git thực sự hoạt động với tên đơn giản, nó cũng ngăn đồng nghiệp của bạn hiểu được các khái niệm git cốt lõi như cam kết hai pha.

Chúng tôi đã đưa ra quyết định (khá quyết liệt) để buộc các nhà phát triển sử dụng dòng lệnh (git bash hoặc posh-git ) trong một tuần, và điều đó đã làm việc kỳ diệu để hiểu cách git thực sự hoạt động và nó khác với SVN như thế nào. Nghe có vẻ quyết liệt, nhưng tôi khuyên bạn nên thử đơn giản vì nó tạo ra sự hiểu biết về mô hình git - và một khi họ đã hạ nó xuống, đồng nghiệp của bạn có thể bắt đầu sử dụng bất kỳ mặt tiền GUI nào trên git mà họ thích.

Lưu ý cuối cùng: sẽ có một số đồng nghiệp của bạn tìm hiểu cách thức hoạt động của git gần như ngay lập tức và sẽ có một số người sẽ không bao giờ làm như vậy. Nhóm thứ hai, bạn chỉ cần dạy các câu thần chú để làm cho mã của họ lấy từ máy cục bộ của họ đến máy chủ để mọi người có thể nhìn thấy nó.


1

Chà, gần đây tôi đã điều chỉnh quy trình làm việc sau đây để không bao giờ f * ck lên nhánh chính:

1) Mọi người đều sử dụng nhánh riêng của mình, đây là bản sao từ nhánh chính.

Hãy đặt tên cho nhánh chính là "master" và nhánh của riêng tôi là "my_master".

Tôi mới làm chi nhánh của mình từ chủ, nên nó giống hệt nhau. Tôi bắt đầu làm việc trên một tính năng mới trên chi nhánh của riêng mình và khi hoàn thành, tôi làm như sau.

Currenly trên chi nhánh của tôi, vừa hoàn thành mã hóa

git add . && git commit -m "Message" && git push

Quay trở lại nhánh chính

git checkout master

Kéo nếu nó không cập nhật

git pull

Quay trở lại chi nhánh của tôi

git checkout my_master

Hợp nhất chủ mới nhất vào chi nhánh của riêng tôi

git merge master

Khắc phục xung đột và sáp nhập

Kiểm tra lại mọi thứ

Khi mọi thứ được hợp nhất và cố định trên nhánh của riêng tôi, hãy đẩy nó

git push

Quay trở lại nhánh chính

git checkout master

Hợp nhất với chi nhánh của tôi

git merge my_master

Không thể có xung đột khi chúng được giải quyết trên nhánh của bạn với sự hợp nhất trước đó

Đẩy chủ

git push

Nếu mọi người làm theo điều này, nhánh chính sẽ sạch sẽ.


0

Vì vậy, chúng tôi có một nhóm chuyển từ TFS sang git và giữ lại những lối suy nghĩ cũ. Các quy tắc hoạt động chung ít nhiều giống nhau.

Vâng, điều này có nghĩa là tất cả mọi người làm việc trên chủ. Đây không phải là xấu; và một nhóm được sử dụng cho TFS hoặc SVN sẽ thấy điều này tự nhiên nhất.

Các thủ tục chung để làm điều này không đau nhất có thể:

  1. làm git stash && git pull --rebase && git stash popmỗi sáng
  2. cam kết sớm và thường xuyên (không cần phải thúc đẩy ngay lập tức; ít nhất chúng ta có thể bắt đầu tận dụng lợi thế này của git sớm)
  3. để đẩy làm vòng lặp sau:

    git add git commit git pull --rebase fix any merges compile git push loop until you don't get the can't fast forward error message.


Nếu bạn làm điều này, bạn cũng có thể ở lại với SVN. Tương tự như bạn có thể ở lại với xe ngựa trong thời của ô tô. Tất nhiên, bạn có thể lái xe của mình với tốc độ như bạn có thể làm với xe ngựa. Nhưng, tất cả những gì bạn đạt được với điều này là cản trở bản thân và khiến những người có khả năng lái xe giận bạn. Học lái xe của bạn. Hiện nay.
cmaster

@cmaster: Đối với chúng tôi, lợi thế số 1 của git là mất máy chủ không làm mất toàn bộ lịch sử kiểm soát nguồn. (Điều đó đã xảy ra với chúng tôi - chúng tôi đã có bản sao lưu nhưng ổ đĩa băng bắt đầu ăn băng khi chúng tôi cố gắng khôi phục.)
Joshua

@cmaster: Chúng tôi đã bắt đầu giới thiệu một số tính năng git hữu ích khác kể từ đó, nhưng thay đổi phân nhánh có thể sẽ không được sử dụng.
Joshua

@cmaster Sự khác biệt giữa lái xe chậm và cưỡi ngựa là lái xe chuẩn bị cho bạn lái nhanh hơn. Cưỡi ngựa không. Không phải ai nhảy vào xe cũng cần nhấn ga để đi 60 dặm / giờ trong vài lần đầu tiên họ ở trong đó.
jpmc26

@ jpmc26 Khi tôi học bài học lái xe đầu tiên của mình, tôi đã được yêu cầu lái xe 30 km / h chắc chắn, và tôi tin rằng bài học đó cũng bao gồm một khoảng cách ngắn ở 50 km / h. Đó chắc chắn là nhiều hơn một chiếc xe ngựa thông thường. Và điều tương tự cũng xảy ra git: Bạn thường học cách rẽ nhánh và hợp nhất từ ​​ngày đầu tiên. Đó là một phần không thể thiếu trong việc sử dụng git. Tránh điều đó, và bạn đang lạm dụng công cụ theo cách tương tự như bạn đang lạm dụng xe hơi khi đi không quá 15 km / h.
cmaster

-3

Nếu tất cả mọi người đang làm việc trên chủ, không có gì bạn có thể làm. Mọi thứ chắc chắn sẽ bị rối tung.

Bạn nên sử dụng tổng thể cho các sản phẩm hoàn thành được gửi cho khách hàng. Bạn nên sử dụng sự phát triển để phát triển liên tục và bạn không nên cho phép bất cứ ai thúc đẩy phát triển. Tiêu chuẩn là mọi người đều phân nhánh từ dev, thực hiện các thay đổi của họ, đẩy họ từ địa phương đến chi nhánh của họ trên máy chủ và đưa ra yêu cầu đẩy. Sau đó, một người nào đó xem xét sự thay đổi và hợp nhất nó vào sự phát triển.

Để tránh xung đột, mọi người hợp nhất phát triển thành chi nhánh của riêng mình trước khi đẩy và giải quyết xung đột ở giai đoạn đó (vì vậy nó chỉ ảnh hưởng đến một nhà phát triển cục bộ). Nếu sáp nhập vào phát triển sẽ gây ra xung đột, thì nó không được hợp nhất - nhà phát triển hợp nhất lại phát triển vào chi nhánh của họ và đẩy lại máy chủ một lần nữa, và sau đó nó được xem xét lại.

Bạn có thể sử dụng sourcetree chẳng hạn để làm cho công việc này mà không có bất kỳ đau đớn.


4
Đó chỉ là thay thế "chủ" bằng "phát triển", với nguy cơ mọi người không chuyển sang ngành phát triển sau khi thanh toán mặc định. Tôi thích GitLab Flow , một phương tiện hạnh phúc giữa GitHlow nặng và GitHub thưa thớt.
Cees Timmerman

@CeesTimmerman Nếu bạn không thích Gitflow, bạn cũng có thể quan tâm đến Oneflow .
jpmc26
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.