2008-10-15 30 views
23

Giả sử bạn có:git: squash/fixup trước đó cam kết

A-B-C 

Bây giờ xây dựng của bạn/thử nghiệm thất bại. Việc sửa chữa sẽ được sáp nhập trong A. hiện công việc dòng chảy của tôi là như thế này:

$ git commit -m "fixA" 

A-B-C-fixA 

$ git rebase -i A~1 

Và bí fixA trong A, dẫn đến:

A'-B-C 

Có một câu lệnh để làm một cái gì đó như:

A-B-C + (index with fix for A) 

$ git commit -supperdupper A 

Kết quả:

A'-B-C 
+1

Tại sao một yêu cầu kỳ lạ mà fixA phải được hợp nhất trong A? –

+9

bởi vì nó phải có trong A ở nơi đầu tiên. – elmarco

+4

Tôi cũng làm như vậy; Tôi không chắc tại sao mọi người nghĩ nó lạ. Nếu bạn đang cố gắng tổ chức các cam kết thành các phần nhỏ, được nhóm logic, thì sẽ tự nhiên khi chuẩn bị một số cam kết chưa được phát hành cùng một lúc. (Bạn có thể không biết nếu bạn đang thực sự thực hiện với A cho đến khi bạn hoàn thành C). – andy

Trả lời

19

Nếu bạn đang tìm kiếm giải pháp dễ dàng để sửa các cam kết trước đó, hãy đọc câu hỏi! Nó giải thích tất cả. Nhưng vì Elmarco đã yêu cầu một cách khéo léo, ở đây chúng tôi đi:

Kể từ Git 1.7.0, có một tùy chọn --autosquash cho rebase, điều bạn muốn. Ngoài ra còn có các tùy chọn --fixup--squash cho commit để giúp mọi thứ trở nên dễ dàng hơn. Với một số bí danh bạn có thể thậm chí có thể nhận được toàn bộ điều vào một lệnh duy nhất.

tôi muốn đề nghị nâng cấp lên Git mới nhất cho khiếp sợ tối đa:

git/Documentation/RelNotes $ grep -i -A1 autosquash\\\|fixup * 
1.7.0.txt: * "git rebase -i" learned new action "fixup" that squashes the change 
1.7.0.txt- but does not affect existing log message. 
-- 
1.7.0.txt: * "git rebase -i" also learned --autosquash option that is useful 
1.7.0.txt: together with the new "fixup" action. 
1.7.0.txt- 
-- 
1.7.3.txt: * "git rebase -i" peeks into rebase.autosquash configuration and acts as 
1.7.3.txt: if you gave --autosquash from the command line. 
1.7.3.txt- 
-- 
1.7.4.txt: * "git commit" learned --fixup and --squash options to help later invocation 
1.7.4.txt- of the interactive rebase. 
-- 
1.7.4.txt: * "git rebase --autosquash" can use SHA-1 object names to name which 
1.7.4.txt: commit to fix up (e.g. "fixup! e83c5163"). 
1.7.4.txt- 
+1

Ví dụ hay: http://technosorcery.net/blog/2010/02/07/fun-with-the-upcoming-1-7-release-of-git-rebase---interactive---autosquash/ –

+0

Điều này cũng là một blog có liên quan http://netsplit.com/2012/02/06/git-smash/ – elmarco

+0

Trong một lệnh duy nhất: http://stackoverflow.com/a/13671819/1456578 – Deiwin

0

Điều bạn đang làm là nguy hiểm nếu bạn đang chia sẻ chi nhánh mà bạn đang thực hiện thay đổi với người khác. Trong trường hợp của bạn, bạn đang viết lại cam kết A và rebasing B và C trên đầu trang của A mới, đó là một đối tượng hoàn toàn mới. Bất cứ ai đã kéo A cũ vào kho lưu trữ của họ có thể sẽ làm hỏng kho lưu trữ của họ vì lịch sử trong kho lưu trữ của bạn sẽ thay đổi "kỳ diệu". Git của họ sẽ không có cách nào để biết rằng lịch sử đã được viết lại.

Vì thực tế này, các nhà phát triển Git đã cố ý không thực hiện điều này dễ dàng để làm như bạn phải nhận thức được hậu quả của việc thực hiện một hoạt động như vậy.

+4

Tôi nhận thức được hậu quả, tôi không muốn chia sẻ những cam kết đó. – elmarco

+2

Trên thực tế, git * của nhà phát triển khác * sẽ biết rằng lịch sử đã thay đổi. Định dạng kho lưu trữ của Git đảm bảo rằng bạn không bao giờ có thể "hỏng" một kho lưu trữ theo cách này. Vấn đề là tinh tế hơn và git của người nhận có thể ném lên một loạt các xung đột hợp nhất không cần thiết. –

+0

Vấn đề được hiển thị ở đây - http://www.kernel.org/pub/software/scm/git/docs/user-manual.html#problems-with-rewriting-history –

2

Nếu bạn muốn dẹp hai cam kết cuối cùng thì bạn đã để gọi

git rebase --interactive <3rd last commit> 

Sau đó bạn cần phải chọn cam kết cuối cùng và bí quyết cam kết từ lần cuối đến lần cuối. Bạn không thể đè bẹp cam kết trên cùng của lịch sử.

1

I created some aliases để giúp việc sử dụng các lệnh git commit --fixupgit commit --squash được thêm vào git 1.7 dễ dàng hơn. Thêm những để ~/.gitconfig của bạn:

[alias] 
    fixup = !sh -c 'REV=$(git rev-parse $1) && git commit --fixup [email protected] && git rebase -i --autosquash $REV^' - 
    squash = !sh -c 'REV=$(git rev-parse $1) && git commit --squash [email protected] && git rebase -i --autosquash $REV^' - 

Cách sử dụng:

$ git commit -am 'bad commit' 
$ git commit -am 'good commit' 

$ git add .   # Stage changes to correct the bad commit 
$ git fixup HEAD^ # HEAD^ can be replaced by the SHA of the bad commit 

Nhược điểm cam kết có thể có một số cam kết trở lại.

3

luồng công việc git hiện tại của tôi là để --fixup/--squash chuyên sâu, mà tôi đã viết một lệnh git-fixup mới để xử lý hầu hết các bit gây phiền nhiễu tự động:

  • git fixup cho thấy các tập tin sửa đổi nhóm dưới mà mới nhất cam kết rằng liên lạc các cùng file
  • git fixup -a cam kết tất cả những thay đổi đó như --fixup thay đổi với "cha mẹ" tương ứng của họ cam kết
  • git fixup -r làm một tự động git rebase --autosquash cho tất cả các fixup cam

Rất nhiều thay đổi là như vậy mà chỉ là ba lệnh trên là đủ để hoàn thành công việc, không sao chép dán của của cam-id hoặc đọc qua các git log để tìm đúng mục tiêu --fixup.

Nguồn: https://github.com/ohmu/git-crust

0

Tôi nghĩ rằng gốc rễ của vấn đề là git (và điều khiển phiên bản thường) buộc bạn phải suy nghĩ về chuỗi các thay đổi, nhưng một changeset hoặc tính năng ngành hoặc bất cứ điều gì bạn gọi một nhóm liên kết các thay đổi có liên quan nói chung không theo thứ tự logic. Thứ tự trong đó mã được viết là ngẫu nhiên và không nhất thiết liên quan đến thứ tự mà nó được đọc.

Tôi không có giải pháp cho điều đó, nhưng tôi đã viết Perl script để giúp tự động hóa quá trình viết lại lịch sử. Nó tương tự như kịch bản Python của @MikaEloranta mà tôi chưa từng thấy khi tôi viết nó.

commit --fixuprebase --autosquash rất tuyệt, nhưng chúng không đủ. Khi tôi có một chuỗi các cam kết A-B-C và tôi viết thêm một số thay đổi trong cây đang hoạt động trong một hoặc nhiều cam kết hiện có, tôi phải xem lịch sử theo cách thủ công, quyết định những thay đổi nào trong đó cam kết, tạo giai đoạn và tạo các cam kết fixup!. Nhưng git đã có quyền truy cập vào đủ thông tin để có thể làm tất cả những gì cho tôi.

Đối với mỗi đoạn trong git diff, tập lệnh sử dụng git blame để tìm cam kết chạm lần cuối vào các dòng có liên quan và gọi để thực hiện điều tương tự tôi đã thực hiện thủ công trước đây.

Nếu tập lệnh không thể phân giải một đoạn thành một cam kết đơn lẻ, rõ ràng, nó sẽ báo cáo nó như là một hunk thất bại và bạn sẽ phải quay trở lại phương pháp thủ công cho điều đó. Nếu bạn thay đổi một dòng hai lần trong hai cam kết riêng biệt, tập lệnh sẽ giải quyết một thay đổi trên dòng đó đến lần gần đây nhất của các cam kết đó, có thể không phải lúc nào cũng là độ phân giải chính xác. IMHO trong một chi nhánh "hình thức bình thường" chi nhánh bạn không nên thay đổi một dòng hai lần trong hai cam kết khác nhau, mỗi cam kết nên trình bày phiên bản cuối cùng của dòng mà nó chạm, để giúp người đánh giá (s). Tuy nhiên, nó có thể xảy ra trong một nhánh bugfix, để lấy ví dụ dòng foo(bar()); có thể được chạm vào bằng cam kết A (đổi tên foo thành fox) và cam kết B (đổi tên bar thành baz).

Nếu bạn thấy tập lệnh hữu ích, vui lòng cải thiện và lặp lại trên đó và có thể một ngày nào đó chúng tôi sẽ có được tính năng như vậy trong git thích hợp. Tôi rất muốn nhìn thấy một công cụ có thể hiểu được một cuộc xung đột hợp nhất nên được giải quyết như thế nào khi nó được giới thiệu bởi một rebase tương tác.

Các vấn đề liên quan