Git-svn dcommit sau khi hợp nhất trong git có nguy hiểm không?


133

Động lực của tôi để thử git-svn là sự hợp nhất và phân nhánh dễ dàng. Sau đó, tôi nhận thấy rằng người đàn ông git-svn (1) nói:

Chạy git-merge hoặc git-pull KHÔNG được khuyến nghị trên một nhánh mà bạn dự định dcommit từ đó. Subversion không đại diện cho sự hợp nhất trong bất kỳ thời trang hợp lý hoặc hữu ích; vì vậy người dùng sử dụng Subversion không thể thấy bất kỳ sự hợp nhất nào bạn đã thực hiện. Hơn nữa, nếu bạn hợp nhất hoặc kéo từ nhánh git là gương của nhánh SVN, dcommit có thể cam kết sai nhánh.

Điều này có nghĩa là tôi không thể tạo một nhánh cục bộ từ svn / trunk (hoặc một nhánh), hack đi, hợp nhất trở lại vào svn / trunk, sau đó là dcommit? Tôi hiểu rằng người dùng svn sẽ thấy cùng một mớ hỗn độn trong svn pre 1.5.x luôn tồn tại, nhưng có bất kỳ nhược điểm nào khác không? Câu cuối cùng đó cũng làm tôi lo lắng. Mọi người có thường xuyên làm những điều này?


14
Điều này là sai: Subversion không đại diện cho sự hợp nhất trong bất kỳ thời trang hợp lý hoặc hữu ích; vì vậy người dùng sử dụng Subversion không thể thấy bất kỳ sự hợp nhất nào bạn đã thực hiện. Hơn nữa, nếu bạn hợp nhất hoặc kéo từ một nhánh git là gương của nhánh SVN, dcommit có thể cam kết với nhánh sai. Subversion không chỉ đại diện cho thông tin theo dõi hợp nhất git, mà còn nhiều thông tin chi tiết hơn nữa. Đó là git-svn không thể soạn svn: mergeinfo hoặc tạo dcommit cho một nhánh thích hợp.
Alexander Kitaev

2
@AlexanderKitaev: các tài liệu git-svn đã thay đổi kể từ đó và ngoài ra còn có một --mergeinfo=<mergeinfo>tùy chọn có thể truyền thông tin hợp nhất cho SVN. Không chắc chắn làm thế nào nó nên được sử dụng mặc dù.
Mr_and_Mrs_D

Câu trả lời:


174

Trên thực tế, tôi tìm thấy một cách thậm chí tốt hơn với --no-fftùy chọn trên git merge. Tất cả các kỹ thuật squash tôi sử dụng trước đây không còn cần thiết.

Quy trình làm việc mới của tôi bây giờ như sau:

  • Tôi có một "bậc thầy" chi nhánh là chi nhánh duy nhất mà tôi dcommit từ và nhân bản kho SVN ( -sgiả sử bạn có một bố cục SVN tiêu chuẩn trong các kho lưu trữ trunk/, branches/tags/):

    git svn clone [-s] <svn-url>
    
  • Tôi làm việc trên một "công việc" chi nhánh địa phương ( -btạo ra "công việc" chi nhánh)

    git checkout -b work
    
  • cam kết cục bộ vào nhánh "công việc" ( -sđể đăng xuất thông điệp cam kết của bạn). Trong phần tiếp theo, tôi giả sử bạn đã thực hiện 3 lần xác nhận cục bộ

    ...
    (work)$> git commit -s -m "msg 1"
    ...
    (work)$> git commit -s -m "msg 2"
    ...
    (work)$> git commit -s -m "msg 3"
    

Bây giờ bạn muốn cam kết với máy chủ SVN

  • [Cuối cùng] bỏ các sửa đổi mà bạn không muốn thấy đã cam kết trên máy chủ SVN (thường bạn đã nhận xét một số mã trong tệp chính chỉ vì bạn muốn tăng tốc biên dịch và tập trung vào một tính năng nhất định)

    (work)$> git stash
    
  • khởi động lại nhánh chính với kho SVN (để cập nhật từ máy chủ SVN)

    (work)$> git checkout master
    (master)$> git svn rebase
    
  • quay trở lại nhánh làm việc và nổi loạn với chủ

    (master)$> git checkout work
    (work)$> git rebase master
    
  • Đảm bảo mọi thứ đều ổn khi sử dụng, ví dụ:

    (work)$> git log --graph --oneline --decorate
    
  • Bây giờ là lúc hợp nhất cả ba cam kết từ nhánh "công việc" thành "chính chủ" bằng --no-fftùy chọn tuyệt vời này

    (work)$> git checkout master
    (master)$> git merge --no-ff work
    
  • Bạn có thể nhận thấy trạng thái của các bản ghi:

    (master)$> git log --graph --oneline --decorate
    * 56a779b (work, master) Merge branch 'work'
    |\  
    | * af6f7ae msg 3
    | * 8750643 msg 2
    | * 08464ae msg 1
    |/  
    * 21e20fa (git-svn) last svn commit
    
  • Bây giờ bạn có thể muốn chỉnh sửa ( amend) cam kết cuối cùng cho các anh chàng SVN của bạn (nếu không họ sẽ chỉ thấy một cam kết duy nhất với thông báo "Hợp nhất chi nhánh 'công việc'"

    (master)$> git commit --amend
    
  • Cuối cùng cam kết trên máy chủ SVN

    (master)$> git svn dcommit
    
  • Quay trở lại làm việc và cuối cùng khôi phục các tập tin đã lưu của bạn:

    (master)$> git checkout work
    (work)$> git stash pop
    

4
Đây là chính xác dòng công việc mà git n00b này đang tìm kiếm. Tạo một nhánh cục bộ để sửa lỗi, làm bất cứ điều gì, sau đó gửi nó đến svn với một cam kết SINGLE. Sử dụng câu trả lời được đánh giá cao nhất ở đây đã làm rối tung những gì tôi đang làm - không thể git svn rebase mà không có lỗi.
João Bragança

19
Đây không phải là chính xác những gì các tài liệu git-svn trích dẫn trong op cảnh báo chống lại? Bằng cách chạy hợp nhất với --no-fftùy chọn, bạn rõ ràng đang tạo một cam kết hợp nhất (một cam kết với hai cha mẹ) thay vì chuyển tiếp nhanh. Để đảm bảo tất cả các cam kết trong nhánh theo dõi svn là các cam kết cha mẹ đơn lẻ, tất cả các kết hợp phải nhanh chóng chuyển tiếp ( --ff-onlycó thể giúp với điều này) hoặc, nếu thân cây đã thay đổi sau lưng của bạn --squash, phải không?
jemmons

4
Điều này hoạt động cho một nhánh một lần mà bạn xóa ngay lập tức, nhưng git svn dcommitviết lại cam kết git mà bạn đưa ra. Điều này có nghĩa là bạn mất đi cha mẹ khác của nó và bây giờ git repo của bạn không có hồ sơ mà bạn từng sáp nhập chi nhánh đó vào master. Điều này cũng có thể để thư mục làm việc của bạn trong một trạng thái không nhất quán.
rescdsk

9
sử dụng git merge --no-ff work -m "commit message"thay vì có thêmgit commit --amend bước
tekumara

3
đối với tôi, tôi thích sử dụng "git merge - chỉ hoạt động" vì tôi muốn duy trì tất cả các cam kết của mình và không chỉ là lần cuối cùng
Moataz Elmasry

49

Tạo các nhánh cục bộ là hoàn toàn có thể với git-svn. Miễn là bạn chỉ sử dụng các nhánh cục bộ cho chính mình và không cố gắng sử dụng git để hợp nhất giữa các nhánh svn ngược dòng, bạn sẽ ổn thôi.

Tôi có một nhánh "chính" mà tôi sử dụng để theo dõi máy chủ svn. Đây là chi nhánh duy nhất mà tôi dcommit từ. Nếu tôi đang làm một số công việc, tôi tạo một nhánh chủ đề và làm việc với nó. Khi tôi muốn cam kết, tôi làm như sau:

  1. Cam kết mọi thứ cho chi nhánh chủ đề
  2. git svn rebase (giải quyết mọi xung đột giữa công việc của bạn và svn)
  3. chủ thanh toán git
  4. git svn rebase (điều này làm cho bước tiếp theo hợp nhất nhanh chóng, xem ý kiến ​​của Aaron bên dưới)
  5. git merge topic_branch
  6. giải quyết mọi xung đột hợp nhất (không nên có bất kỳ xung đột nào tại thời điểm này)
  7. git svn dcommit

Tôi cũng có một tình huống khác là tôi cần duy trì một số thay đổi cục bộ (để gỡ lỗi) không bao giờ được đẩy lên svn. Vì thế, tôi có nhánh chính ở trên nhưng cũng có nhánh gọi là "công việc" nơi tôi thường làm việc. Chi nhánh chủ đề được phân nhánh làm việc. Khi tôi muốn cam kết công việc ở đó, tôi kiểm tra chủ và sử dụng cherry-pick để chọn các cam kết từ nhánh công việc mà tôi muốn cam kết với svn. Điều này là do tôi muốn tránh cam kết ba cam kết thay đổi cục bộ. Sau đó, tôi dcommit từ nhánh chính và rebase mọi thứ.

Điều đáng làm là chạy git svn dcommit -ntrước để đảm bảo rằng bạn sắp cam kết chính xác những gì bạn định cam kết. Khác với git, viết lại lịch sử trong svn thật khó!

Tôi cảm thấy rằng phải có một cách tốt hơn để hợp nhất sự thay đổi trên một nhánh chủ đề trong khi bỏ qua những cam kết thay đổi cục bộ đó hơn là sử dụng cherry-pick, vì vậy nếu có ai có ý tưởng nào thì họ sẽ được chào đón.


Nếu tôi hiểu điều này một cách chính xác, trong git, sáp nhập git với svn là OK, nhưng không phải svn với svn? Vì vậy, tôi không thể hợp nhất svn / chi nhánh của tôi với svn / trunk trong git?
Knut Eldhuset

1
Viết lại lịch sử trong SVN là khó nếu bạn muốn làm một cái gì đó tầm thường. Trong các trường hợp không tầm thường, thực tế là không thể.
Aristotle Pagaltzis

19
Câu trả lời của Greg Hewgill có rất nhiều phiếu bầu, nhưng tôi tin rằng nó sai. Việc hợp nhất từ ​​topic_branch thành master chỉ an toàn nếu đó chỉ là sự hợp nhất nhanh chóng. Nếu đó là một sự hợp nhất thực sự đòi hỏi một cam kết hợp nhất, thì cam kết hợp nhất sẽ bị mất khi bạn dcommit. Lần tới khi bạn cố gắng hợp nhất topic_branch thành chủ, git nghĩ rằng lần hợp nhất đầu tiên không bao giờ xảy ra, và tất cả địa ngục vỡ ra. Xem câu hỏi Tại sao git svn dcommit mất lịch sử cam kết hợp nhất cho các chi nhánh địa phương?
Aaron

1
@Greg Hewgill, bản chỉnh sửa không rõ ràng. Bạn đã viết rằng rebase "làm cho cam kết tiếp theo hợp nhất nhanh chóng." Tôi không biết điều đó có nghĩa là gì, bởi vì hợp nhất chuyển tiếp nhanh không liên quan đến một cam kết, nhưng có lẽ bạn có nghĩa là "làm cho hợp nhất tiếp theo thành một hợp nhất chuyển tiếp nhanh". Nhưng nếu đó là những gì bạn muốn nói thì nó không đúng. Việc hợp nhất từ ​​topic_branch thành master có phải là hợp nhất chuyển tiếp nhanh hay không phụ thuộc vào việc có bất kỳ cam kết nào đã được thực hiện trên master kể từ điểm nhánh hay không. Khi bạn rebase master, điều đó tạo ra các xác nhận mới trên master, do đó, việc hợp nhất tiếp theo không nhất thiết không phải là hợp nhất chuyển tiếp nhanh.
Aaron

1
@Aaron: Vâng tôi không biết tôi đang nghĩ gì ở đó. Tôi đã sửa nó một lần nữa, hy vọng điều đó có ý nghĩa. Một trong những rắc rối là có rất nhiều cách để thực hiện trong Git, mà phải mất khá nhiều lời giải thích để mô tả một chuỗi hành động cụ thể .
Greg Hewgill

33

Giải pháp đơn giản: Xóa chi nhánh 'công việc' sau khi hợp nhất

Câu trả lời ngắn: Bạn có thể sử dụng git theo cách bạn thích (xem bên dưới để biết quy trình làm việc đơn giản), bao gồm cả hợp nhất. Chỉ cần đảm bảo theo dõi từng ' công việc hợp nhất git ' với ' công việc git chi nhánh ' để xóa chi nhánh công việc tạm thời.

Giải thích cơ bản: Vấn đề hợp nhất / dcommit là bất cứ khi nào bạn 'git svn dcommit' một nhánh, lịch sử hợp nhất của nhánh đó là 'phẳng': git quên về tất cả các hoạt động hợp nhất đã đi vào nhánh này: Chỉ nội dung tệp được giữ nguyên, nhưng thực tế là nội dung này (một phần) đến từ một chi nhánh cụ thể khác bị mất. Xem: Tại sao git svn dcommit mất lịch sử hợp nhất cam kết cho các chi nhánh địa phương?

(Lưu ý: git-svn không thể làm được gì nhiều: svn đơn giản là không hiểu sự hợp nhất git mạnh hơn nhiều. Vì vậy, trong kho svn, thông tin hợp nhất này không thể được trình bày theo bất kỳ cách nào.)

Nhưng đây là toàn bộ vấn đề. Nếu bạn xóa nhánh 'công việc' sau khi nó được sáp nhập vào 'nhánh chính' thì kho git của bạn sạch 100% và trông giống hệt như kho lưu trữ svn của bạn.

Quy trình làm việc của tôi: Tất nhiên, lần đầu tiên tôi đã sao chép kho lưu trữ svn từ xa vào kho lưu trữ git cục bộ (việc này có thể mất một chút thời gian):

$> git svn clone <svn-repository-url> <local-directory>

Tất cả công việc sau đó xảy ra bên trong "thư mục cục bộ". Bất cứ khi nào tôi cần nhận cập nhật từ máy chủ (như 'svn update'), tôi sẽ:

$> git checkout master
$> git svn rebase

Tôi làm tất cả công việc phát triển của mình trong một 'công việc' riêng biệt được tạo ra như thế này:

$> git checkout -b work

Tất nhiên, bạn có thể tạo bao nhiêu nhánh cho công việc của mình theo ý muốn và hợp nhất và khởi động lại giữa chúng theo ý muốn (chỉ cần xóa chúng khi bạn hoàn thành chúng --- như được thảo luận dưới đây). Trong công việc bình thường của tôi, tôi cam kết rất thường xuyên:

$> git commit -am '-- finished a little piece of work'

Bước tiếp theo (git rebase -i) là tùy chọn --- nó chỉ làm sạch lịch sử trước khi lưu trữ nó trên svn: Khi tôi đạt được một viên đá ổn định mà tôi muốn chia sẻ với người khác, tôi viết lại lịch sử của 'tác phẩm' này phân nhánh và dọn sạch các thông điệp cam kết (các nhà phát triển khác không cần phải xem tất cả các bước nhỏ và lỗi mà tôi đã thực hiện trên đường --- chỉ là kết quả). Đối với điều này, tôi làm

$> git log

và sao chép hàm băm sha-1 của lần xác nhận cuối cùng tồn tại trong kho svn (như được chỉ định bởi git-svn-id). Sau đó tôi gọi

$> git rebase -i 74e4068360e34b2ccf0c5869703af458cde0cdcb

Chỉ cần dán băm sha-1 của cam kết svn cuối cùng của chúng tôi thay vì của tôi. Bạn có thể muốn đọc tài liệu với 'git help rebase' để biết chi tiết. Nói tóm lại: lệnh này trước tiên sẽ mở một trình soạn thảo trình bày các cam kết của bạn ---- chỉ cần thay đổi 'chọn' thành 'squash' cho tất cả các cam kết mà bạn muốn xóa sổ với các cam kết trước đó. Tất nhiên, dòng đầu tiên sẽ ở dạng 'chọn'. Bằng cách này, bạn có thể ngưng tụ nhiều cam kết nhỏ của mình thành một hoặc nhiều đơn vị có ý nghĩa. Lưu và thoát khỏi trình soạn thảo. Bạn sẽ nhận được một trình soạn thảo khác yêu cầu bạn viết lại các thông điệp nhật ký cam kết.

Tóm lại: Sau khi tôi hoàn thành 'hack mã', tôi xoa bóp nhánh 'công việc' của mình cho đến khi nó trông giống như cách tôi muốn trình bày nó với các lập trình viên khác (hoặc cách tôi muốn xem công việc trong vài tuần khi tôi duyệt lịch sử) .

Để đẩy các thay đổi vào kho svn, tôi làm:

$> git checkout master
$> git svn rebase

Bây giờ chúng tôi đã quay lại nhánh 'chủ' cũ được cập nhật với tất cả các thay đổi xảy ra trong thời gian trung bình trong kho svn (những thay đổi mới của bạn được ẩn trong nhánh 'công việc').

Nếu có những thay đổi có thể xung đột với những thay đổi 'công việc' mới của bạn, bạn phải giải quyết chúng cục bộ trước khi bạn có thể đẩy công việc mới của mình (xem chi tiết bên dưới). Sau đó, chúng tôi có thể đẩy các thay đổi của mình lên svn:

$> git checkout master
$> git merge work        # (1) merge your 'work' into 'master'
$> git branch -d work    # (2) remove the work branch immediately after merging
$> git svn dcommit       # (3) push your changes to the svn repository

Lưu ý 1: Lệnh 'git Branch -d work' khá an toàn: Nó chỉ cho phép bạn xóa các nhánh mà bạn không cần nữa (vì chúng đã được sáp nhập vào nhánh hiện tại của bạn). Nếu bạn thực hiện lệnh này do nhầm lẫn trước khi hợp nhất công việc của bạn với nhánh 'chính', bạn sẽ nhận được thông báo lỗi.

Lưu ý 2: Đảm bảo xóa chi nhánh của bạn bằng 'git Branch -d work' giữa việc hợp nhất và dcommit: Nếu bạn cố xóa chi nhánh sau dcommit, bạn sẽ nhận được thông báo lỗi: Khi bạn thực hiện 'git svn dcommit', hãy quên đi chi nhánh của bạn đã được hợp nhất với 'chủ nhân'. Bạn phải xóa nó bằng 'git chi nhánh -D work' mà không thực hiện kiểm tra an toàn.

Bây giờ, tôi lập tức tạo một nhánh 'công việc' mới để tránh việc vô tình hack vào nhánh 'chủ':

$> git checkout -b work
$> git branch            # show my branches:
  master
* work

Tích hợp 'công việc' của bạn với các thay đổi trên svn: Đây là những gì tôi làm khi 'git svn rebase' tiết lộ rằng những người khác đã thay đổi kho svn trong khi tôi đang làm việc trên nhánh 'công việc' của mình:

$> git checkout master
$> git svn rebase              # 'svn pull' changes
$> git checkout work           # go to my work
$> git checkout -b integration # make a copy of the branch
$> git merge master            # integrate my changes with theirs
$> ... check/fix/debug ...
$> ... rewrite history with rebase -i if needed

$> git checkout master         # try again to push my changes
$> git svn rebase              # hopefully no further changes to merge
$> git merge integration       # (1) merge your work with theirs
$> git branch -d work          # (2) remove branches that are merged
$> git branch -d integration   # (2) remove branches that are merged
$> git svn dcommit             # (3) push your changes to the svn repository

Có nhiều giải pháp mạnh mẽ hơn: Quy trình công việc được trình bày rất đơn giản: Nó chỉ sử dụng sức mạnh của git trong mỗi vòng 'update / hack / dcommit' --- nhưng để lại lịch sử dự án dài hạn giống như kho lưu trữ svn. Điều này là ổn nếu bạn chỉ muốn bắt đầu sử dụng git sáp nhập trong các bước đầu tiên nhỏ trong một dự án svn kế thừa.

Khi bạn trở nên quen thuộc hơn với việc hợp nhất git, hãy thoải mái khám phá các quy trình công việc khác: Nếu bạn biết bạn đang làm gì, bạn có thể kết hợp git với sáp nhập svn ( Sử dụng git-svn (hoặc tương tự) chỉ để giúp hợp nhất với svn? )


Điều này có vẻ phức tạp không cần thiết. Tôi đã nghe nói về những người làm git merge --squash work, tại sao không làm điều này? Tôi có thể thấy thực hiện các cam kết squash trong chi nhánh trước khi hợp nhất nếu bạn đang tạo nhiều hơn một 'chọn' (giả sử bạn có 8 lần xác nhận và bạn đang biến mỗi 4 cam kết thành 1 và hợp nhất 2 cam kết thành chủ). Ngoài ra, khi cập nhật chi nhánh 'công việc' của tôi, tôi làm rebase, nó đơn giản hơn việc tạo một chi nhánh khác cho chi nhánh của tôi và thực hiện hợp nhất ...
void.pulum

8

Câu trả lời hàng đầu của Greg Hewgill là không an toàn! Nếu bất kỳ cam kết mới nào xuất hiện trên trung kế giữa hai "git svn rebase", việc hợp nhất sẽ không được chuyển tiếp nhanh.

Nó có thể được đảm bảo bằng cách sử dụng cờ "--ff-only" cho git-merge, nhưng tôi thường không chạy "git svn rebase" trong nhánh, chỉ "git rebase master" trên nó (giả sử nó chỉ là cục bộ chi nhánh). Sau đó, một "git merge thebranch" được đảm bảo sẽ nhanh chóng chuyển tiếp.


6

Một cách an toàn để hợp nhất các nhánh svn trong git là sử dụng git merge --squash. Điều này sẽ tạo ra một cam kết duy nhất và dừng lại để bạn thêm một tin nhắn.

Giả sử bạn có một chi nhánh svn chủ đề, được gọi là svn-chi nhánh.

git svn fetch
git checkout remotes/trunk -b big-merge
git merge --squash svn-branch

tại thời điểm này, bạn có tất cả các thay đổi từ nhánh svn được nén thành một cam kết đang chờ trong chỉ mục

git commit

Như những người khác đã chỉ ra, tuy nhiên, điều này không làm mất độ chi tiết của các cam kết.
Kzqai

@Tchalvak: Đôi khi bạn chỉ muốn vậy thôi. Tôi thường cam kết trong một nhánh tính năng ala "lỗi cố định từ lần cam kết trước" và những ngõ cụt này tôi muốn che giấu vì danh tiếng tốt :-)
schoetbi

1
Vâng, mặc dù tôi coi đó là một cuộc đi bộ chặt chẽ, vì bất cứ khi nào bạn đè bẹp, bạn cũng mất khả năng để tách rời các cam kết cá nhân sau đó. Chỉ là một vấn đề của sự lựa chọn khác nhau của cam kết tiêu chuẩn.
Kzqai

1
@schoetbi vâng, đôi khi bạn cần dọn dẹp lịch sử lộn xộn trước khi xuất bản. Đây là nơi 'git rebase -i <trunk>' là một trợ giúp tuyệt vời: bạn có thể tham gia / sắp xếp lại / xóa và thậm chí tách (!) Cam kết, sửa lỗi tin nhắn một cách chọn lọc.
inger

5

Khởi động lại nhánh git cục bộ lên nhánh git chính sau đó dcommit và theo cách đó, có vẻ như bạn đã thực hiện tất cả các cam kết đó theo trình tự để mọi người có thể nhìn thấy nó một cách tuyến tính như họ đã quen. Vì vậy, giả sử bạn có một chi nhánh địa phương gọi là chủ đề bạn có thể làm

git rebase master topic

mà sau đó sẽ chơi các cam kết của bạn trên nhánh chính đã sẵn sàng để bạn tham gia dcommit

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.