2012-05-25 24 views
10

Tôi hiểu rằng đây là một ý tưởng tồi đối với nhiều trường hợp. Tôi đang học Git và thử nghiệm. Không có mã nào bị tổn hại trong bài tập này.Làm cách nào để đổi tên tin nhắn cam kết trong Git?

Tôi đã tạo ra một cấu trúc như thế này:

* [cf0149e] (HEAD, branch_2) more editing 
* [8fcc106] some edit 
| 
| * [59e643e] (branch_2b) branch 2b 
|/
|/ 
| * [0f4c880] (branch_2_a) branch 2a 
|/
|/ 
* [a74eb2a] checkout 1 
* [9a8dd6a] added branch_2 line 
| 
| 
| * [bb903de] (branch_3) branch 3 
|/ 
| 
| * [674e08c] (branch_1) commit 1 
| * [7d3db01] added branch_1 line 
|/ 
* [328454f] (0.0.0) test 

Bây giờ tôi muốn đi qua đồ thị này và đổi tên các cam kết khác nhau để họ có ý nghĩa.

Ví dụ:

* | [a74eb2a] checkout 1 
    * | [9a8dd6a] added branch_2 line 

renamed to: 

    * | [a74eb2a] branch 2 commit 2 
    * | [9a8dd6a] branch 2 commit 1 

Lưu ý rằng:

[cf0149e] (HEAD, branch_2) more editing 
[59e643e] (branch_2b) branch 2b 
[0f4c880] (branch_2_a) branch 2a 

đều phân nhánh ra khỏi:

[a74eb2a] checkout 1 

tôi đã thử nghiệm với

git rebase -i 328454f 
.210

sau đó thay đổi "chọn" để "chỉnh sửa" trên các cam kết mà tôi muốn sửa đổi và sau đó chạy

git commit --amend -m "the new message" 

như quá trình rebase tiếp tục.

Vấn đề với cách tiếp cận này là, sau git rebase --continue cuối cùng tôi kết thúc với hai cam kết mới (bản sao của hai lần tôi muốn đổi tên) trên nhánh tôi đã xảy ra. Ví dụ, nếu tôi chạy rebase khi TRỤ là tại "branch_2" đồ thị có thể trông như thế này:

* [cf0149e] (HEAD, branch_2) more editing 
* [8fcc106] some edit 
* [3ff23f0] branch 2 commit 2 
* [2f287a1] branch 2 commit 1 
| 
| * [59e643e] (branch_2b) branch 2b 
| /
|/
| | * [0f4c880] (branch_2_a) branch 2a 
| |/
| |/ 
| * [a74eb2a] checkout 1 
| * [9a8dd6a] added branch_2 line 
|/ 
| 
| * [bb903de] (branch_3) branch 3 
|/ 
| 
| * [674e08c] (branch_1) commit 1 
| * [7d3db01] added branch_1 line 
|/ 
* [328454f] (0.0.0) test 

Nói cách khác bây giờ tôi có hai bộ cam kết đại diện cho chính xác tình trạng cùng một mã.

Tất cả những gì tôi muốn làm là thay đổi thông báo cam kết.

Tôi cũng muốn đổi tên thông báo ban đầu từ "thử nghiệm" thành nội dung như "Phiên bản ban đầu". Tôi không thể làm điều đó với git commit --amend -m "Initial version" vì tôi kết thúc ở chế độ không đầu nếu tôi thanh toán cam kết.

Tôi đang làm gì sai? Chắc chắn nó không thể là khó khăn.

CHỈNH SỬA:
Đây là phương pháp tôi vừa thử làm việc. Tất nhiên, nó viết lại lịch sử. Vì vậy, ngoài những trường hợp rất đặc biệt, đó là một ý tưởng tồi. Đây là các bước:

Thanh toán chi nhánh cần sửa đổi. Tạo các tệp bản vá:

git format-patch HEAD~x // Where x is how far back from HEAD you need to patch 

Chỉnh sửa tệp bản vá để thay đổi thông báo cam kết. Bây giờ hãy đặt lại đầu.

git reset --hard HEAD~x // Same x as before 

Áp dụng các bản vá lỗi:

git am 000* 

cam kết mới sẽ được tạo ra với của SHA1 mới. Nếu bất kỳ chi nhánh nào bây giờ cần phải tham khảo các cam kết mới với các thư đã sửa, bạn phải sử dụng git rebase để di chuyển chúng.

Để sử dụng ví dụ của riêng tôi, sau khi áp dụng các thủ tục vá Tôi đã kết thúc-up với điều này:

* [7761415] (HEAD, branch_2) branch 2 commit 4 
* [286e1b5] branch 2 commit 3 
* [53d638c] branch 2 commit 2 
* [52f82f7] branch 2 commit 1 
| * [bb903de] (branch_3) branch 3 
|/ 
| * [59e643e] (branch_2b) branch 2b 
| | * [0f4c880] (branch_2_a) branch 2a 
| |/ 
| * [a74eb2a] checkout 1 
| * [9a8dd6a] added branch_2 line 
|/ 
| * [674e08c] (branch_1) commit 1 
| * [7d3db01] added branch_1 line 
|/ 
* [328454f] (0.0.0) test 

Vì vậy, tôi có branch_2 tôi cam kết độc đáo dán nhãn. Bây giờ tôi muốn chuyển branch_2a để nó chi nhánh ra khỏi [53d638c] branch 2 commit 2

Thanh toán branch_2a

git checkout branch_2a 

rebase nó

git rebase 53d638c 

Bây giờ tôi có:

* [fb4d1c5] (HEAD, branch_2a) branch 2a 
| * [7761415] (branch_2) branch 2 commit 4 
| * [286e1b5] branch 2 commit 3 
|/ 
* [53d638c] branch 2 commit 2 
* [52f82f7] branch 2 commit 1 
| * [bb903de] (branch_3) branch 3 
|/ 
| * [59e643e] (branch_2b) branch 2b 
| * [a74eb2a] checkout 1 
| * [9a8dd6a] added branch_2 line 
|/ 
| * [674e08c] (branch_1) commit 1 
| * [7d3db01] added branch_1 line 
|/ 
* [328454f] (0.0.0) test 

Cùng thủ tục với kết quả chi nhánh_2b trong này:

* [ca9ff6c] (HEAD, branch_2b) branch 2b 
| * [fb4d1c5] (branch_2a) branch 2a 
|/ 
| * [7761415] (branch_2) branch 2 commit 4 
| * [286e1b5] branch 2 commit 3 
|/ 
* [53d638c] branch 2 commit 2 
* [52f82f7] branch 2 commit 1 
| * [bb903de] (branch_3) branch 3 
|/ 
| * [674e08c] (branch_1) commit 1 
| * [7d3db01] added branch_1 line 
|/ 
* [328454f] (0.0.0) test 

Đó chính xác là những gì tôi đang tìm kiếm. Không quá lộn xộn. Một lần nữa, không phải cái gì bạn muốn làm bên ngoài các trường hợp rất đặc biệt. Trong trường hợp của tôi, tôi chỉ đang chơi để học Git, vì vậy ở trên không thực sự ảnh hưởng đến một kho lưu trữ mã thực. Thật tuyệt khi biết rằng bạn có thể làm điều này nếu bạn phải làm vậy.

Bây giờ để đổi tên cam kết đầu tiên.

+0

Bạn không thể. Git được cho là không hướng máy chủ. Nếu bạn đẩy nó vào một nơi khác, đúng vậy. –

+3

Làm thế nào bạn có được những biểu đồ mát mẻ của các chi nhánh của bạn và tất cả? Đó có phải là một lệnh git hay bạn đã vẽ nó ra bằng cách sử dụng ASCII chính mình? –

Trả lời

16

Bạn thực sự không thể thay đổi cam kết. Nếu bạn thực hiện thay đổi đơn bit nhỏ nhất ở bất kỳ đâu, từ chính tả của tên trong dòng email đến giây chính xác của dấu thời gian cam kết, bạn sẽ nhận được một cam kết mới khác với SHA1 mới, khác nhau (SHA1 là "true" tên ", như nó đã được, của mỗi" đối tượng "trong cơ sở dữ liệu git, với cam kết là một trong bốn loại đối tượng).

Một trong những phần không thể thay đổi của cam kết là "cam kết gốc", tạo chuỗi ngược về từ cam kết gần đây nhất đến cam kết cũ nhất.

Do đó, những gì git rebase -i làm là tạo ra một mới cam kết chuỗi, với từng cam kết trong chuỗi có nội dung tương tự/hiệu ứng như bản gốc cam cộng hoặc trừ bất kỳ thay đổi bạn thực hiện trong sự tương tác. Khi tất cả được thực hiện, nó sẽ loại bỏ nhãn (lưu ý dính, như nó) từ cuối chuỗi cam kết cũ và dán nó vào cuối chuỗi cam kết mới. Nó bắt đầu bằng cách tạo một bản sao của cam kết cũ nhất được sửa đổi/rebased. Điều này có cha mẹ được rebased (có thể là cùng một cha mẹ cam kết như trong chuỗi ban đầu, hoặc một phụ huynh khác: một trong hai cách nó là OK vì nó là một cam kết mới). Sau đó, nó tạo một bản sao của cái tiếp theo trong chuỗi cũ, nhưng chỉ vào chuỗi mới. Nó lặp đi lặp lại tất cả các cách để kết thúc chuỗi cũ.

Đây là lý do tại sao tất cả các chi nhánh khác của bạn hiện không phụ thuộc vào chi nhánh được rebased của bạn. Họ là vì họ sử dụng ID cam kết cũ. Nếu bạn muốn họ phân nhánh chi nhánh mới được rebased của bạn, bạn phải đi vào từng nhánh và rebase chúng.

Có lệnh mạnh mẽ của Thụy Sĩ-Quân đội-cưa, git filter-branch, bạn có thể sử dụng để thực hiện một loạt rất lớn "làm lại nhiều cam kết, làm cho tất cả các cam kết mới có (chủ yếu) nội dung giống như bản gốc ", loại git rebase trên steroid như nó đã được, mà bạn có thể sử dụng cho mục đích này. (Chạy nó với --all để ảnh hưởng đến tất cả các chi nhánh.) Tất nhiên, vì nó thực tế làm lại tất cả các cam kết, bạn sẽ có một repo về cơ bản không liên quan đến repo gốc.

Thật khó để viết lại cam kết ban đầu (không phải không thể) vì nó không có phụ huynh, vì vậy thường xuyên rebase sẽ không thực hiện. (filter-branch có thể.) Bởi vì những thay đổi này ID SHA1, và bất cứ ai đã nhân bản repo của bạn đang sử dụng/tùy thuộc vào những người, nó thường là một tính năng mà bạn không thể đi chơi với cam kết như thế này. Khi bạn biết không ai khác phụ thuộc vào một số cam kết cụ thể, thì bạn có thể rebase tất cả những gì bạn thích, nhưng bạn sẽ không quay trở lại cam kết ban đầu vì nó sẽ nằm trong phần được chia sẻ của repo. Nó khá hiếm (mặc dù tất nhiên không phải là "không bao giờ") rằng toàn bộ điều tất cả các cách trở lại "cam kết ban đầu" là tất cả các công cụ riêng của bạn.


Kể từ khi tôi viết bài này, git rebase đã học được cách sao chép một gốc cam như cam kết ban đầu. Sử dụng cú pháp git rebase thông thường, bạn sẽ phải đặt tên cho thư mục gốc là cha, và dĩ nhiên có không có cha mẹ (đó là điều làm cho nó thành "gốc" trong biểu đồ). Vì vậy, rebase sử dụng --root làm đối số để đề cập đến trường hợp này.

Có thể có nhiều gốc; sử dụng git checkout --orphan, ví dụ, theo sau là git commit, để tạo một cam kết gốc mới. Có một chút bất thường khi có, mặc dù nguồn git chính nó được giữ trong một repo git với nhiều rễ.

+0

OK, có ý nghĩa. Tôi đã dành cả ngày để thử nghiệm và thất bại. Thực ra, tôi không biết, tôi học được rất nhiều. Tôi đoán một chìa khóa cất cánh từ điều này là "chọn một cách khôn ngoan" khi nói đến cam kết tin nhắn. Trong khi tôi hiểu tại sao điều này xảy ra tôi không thể không nghĩ rằng nó sẽ rất hữu ích để cung cấp một phương tiện để thực hiện các chỉnh sửa đó - với cảnh báo gấp bốn lần. Nếu bạn mắc lỗi trong thông báo cam kết của mình, bạn sẽ bị hosed. Thử nghiệm của tôi là một trường hợp cực đoan, nơi tôi vừa ném tin nhắn xung quanh và sau đó quyết định "này, bây giờ chúng ta hãy hiểu ý nghĩa của họ", điều đó, tất nhiên, nó rất khó. –

+3

Đúng! Đó là lý do tại sao quá trình thông thường của tôi với git là làm việc trên một nhánh riêng (hoặc nhiều nhánh riêng) và chắc chắn rằng chúng ở riêng, sau đó khi mọi thứ đã sẵn sàng, kiểm tra lại và nếu cần thiết hãy viết lại tất cả (các) commit đó được phát hành. Một "rebase -i" cuối cùng của tất cả mọi thứ, như nó được. – torek

+0

@torek có vẻ như quá mức đối với hầu hết các dự án để kiểm tra lại tất cả các cam kết ... Thay vào đó, chỉ cần không cam kết cho đến khi sẵn sàng, và nếu bạn phạm sai lầm, chỉ cần thực hiện cam kết mới. dường như đơn giản hơn nhiều đối với hầu hết các trường hợp sử dụng – Paul

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