This text is a work in progress—highly subject to change—and may not accurately describe any released version of the Apache™ Subversion® software. Bookmarking or otherwise referring others to this page is probably not such a smart idea. Please visit http://www.svnbook.com/ for stable versions of this book.

Xử Lý Xung Đột Về Cấu Trúc

Cho đến nay, chúng ta chỉ thảo luận về xung đột ở mức nội dung tập tin. Khi bạn và đồng nghiệp của bạn thực hiện việc thay đổi chồng lấn trên cùng một tập tin, Subversion yêu cầu bạn phải hợp nhất những thay đổi này trước khi bạn có thể chuyển giao. [9]

Nhưng điều gì sẽ xảy ra nếu đồng nghiệp của bạn di chuyển hoặc xóa một tập tin mà bạn vẫn đang làm việc? Có thể đó là do hiểu nhầm và một người nào đó nghĩ rằng tập tin cần phải được xóa bỏ, trong khi người khác vẫn cần phải chuyển giao những thay đổi vào tập tin. Hoặc là những đồng nghiệp của bạn đã thực hiện vài việc sắp xếp lại, đổi tên tập tin và di chuyển những thư mục. Nếu bạn vẫn đang làm việc trên những tập tin này, những điều chỉnh này có thể cần phải được áp vào những tập tin này ở vị trí mới. Những xung đột này thể hiện ở mức cấu trúc cây thư mục chứ không phải ở mức nội dung tập tin và được biết đến như là xung đột cấu trúc cây.

Tương tự như với xung đột văn bản, xung đột cấu trúc cây ngăn cản việc thực hiện chuyển giao, người sử dụng có cơ hội để xem xét trạng thái của bản sao làm việc cho những vấn đề tiềm ẩn có thể xuất hiện từ xung đột cấu trúc cây và giải quyết những xung đột này trước khi chuyển giao.

Một ví dụ xung đột cấu trúc cây

Giả sử một dự án phần mềm bạn đang tham gia hiện như thế này:

$ svn list -Rv svn://svn.example.com/trunk/
     13 harry                 Sep 06 10:34 ./
     13 harry              27 Sep 06 10:34 COPYING
     13 harry              41 Sep 06 10:32 Makefile
     13 harry              53 Sep 06 10:34 README
     13 harry                 Sep 06 10:32 code/
     13 harry              54 Sep 06 10:32 code/bar.c
     13 harry             130 Sep 06 10:32 code/foo.c
$

Sau đó, trong phiên bản 14, đồng nghiệp Harry của bạn đổi tên tập tin bar.c thành baz.c. Không may là bạn không biết về điều này. Bạn đang bận rộn với bản sao làm việc của bạn với một tập những thay đổi khác, một vài trong số đó cũng bao gồm việc chỉnh sửa trên tập tin bar.c:

$ svn diff
Index: code/foo.c
===================================================================
--- code/foo.c	(revision 13)
+++ code/foo.c	(working copy)
@@ -3,5 +3,5 @@
 int main(int argc, char *argv[])
 {
     printf("I don't like being moved around!\n%s", bar());
-    return 0;
+    return 1;
 }
Index: code/bar.c
===================================================================
--- code/bar.c	(revision 13)
+++ code/bar.c	(working copy)
@@ -1,4 +1,4 @@
 const char *bar(void)
 {
-    return "Me neither!\n";
+    return "Well, I do like being moved around!\n";
 }
$

Lần đầu tiên bạn nhận thấy rằng ai đó đã thay đổi bar.c khi việc cố gắng chuyển giao của bạn không thành công:

$ svn commit -m "Small fixes"
Sending        code/bar.c
Transmitting file data .
svn: E155011: Commit failed (details follow):
svn: E155011: File '/home/svn/project/code/bar.c' is out of date
svn: E160013: File not found: transaction '14-e', path '/code/bar.c'
$

Tại thời điểm này, bạn cần thực thi lệnh svn update. Bên cạnh việc cập nhật bản sao làm việc của bạn để bạn có thể thấy những thay đổi của Harry, việc này còn đánh dấu một xung đột cấu trúc cây và bạn có cơ hội để đánh giá và giải quyết nó.

$ svn update
Updating '.':
   C code/bar.c
A    code/baz.c
U    Makefile
Updated to revision 14.
Summary of conflicts:
  Tree conflicts: 1
$

Trong kết quả lệnh, lệnh svn update đánh dấu xung đột cấu trúc cây bằng cách dùng một ký tự hoa C trong cột kết xuất thứ tư. Lệnh svn status bộc lộ thêm chi tiết của xung đột:

$ svn status
M       code/foo.c
A  +  C code/bar.c
      >   local edit, incoming delete upon update
Summary of conflicts:
  Tree conflicts: 1
$

Lưu ý việc tập tin bar.c được tự động lên lịch cho việc tái-bổ-sung trong bản sao làm việc của bạn, giúp đơn giản hoá nhiều việc trong trường hợp bạn muốn giữ lại tập tin.

Bởi vì việc di chuyển trong Subversion được thực hiện như là một thao tác sao chép được nối tiếp bởi một thao tác xoá, hai thao tác này không dễ để có liên hệ qua lại trong quá trình cập nhật, tất cả điều mà Subversion có thể cảnh báo bạn đó là thao tác xoá sắp xảy ra trên tập tin được chỉnh sửa cục bộ. Thao tác xoá này có thể là một phần của việc di chuyển hoặc nó có thể là một thao tác xoá chính hiệu. Xác định chính xác thay đổi ngữ nghĩa nào được thực hiện trên kho lưu trữ là việc quan trọng—bạn muốn biết thay đổi của bạn khớp với quỹ đạo tổng thể của dự án như thế nào. Hãy đọc ghi chú nhật ký, nói chuyện với người cộng tác của bạn, nghiên cứu sự khác biệt trên từng dòng—làm tất cả những gì mà bạn cần làm—để xác định hành động phù hợp nhất của bạn.

Trong trường hợp này, ghi chú nhật ký chuyển giao của Harry thông báo cho bạn biết những điều bạn cần biết.

$ svn log -r14 ^/trunk
------------------------------------------------------------------------
r14 | harry | 2011-09-06 10:38:17 -0400 (Tue, 06 Sep 2011) | 1 line
Changed paths:
   M /Makefile
   D /code/bar.c
   A /code/baz.c (from /code/bar.c:13)

Rename bar.c to baz.c, and adjust Makefile accordingly.
------------------------------------------------------------------------
$

Lệnh svn info hiển thị URL của những mục có liên quan trong xung đột. Phần URL bên trái hiển thị nguồn gốc của xung đột ở phía cục bộ, trong khi phần URL bên phải hiển thị nguồn gốc của xung đột của phần mới lấy về. Những URL này cho bạn biết bạn nên bắt đầu tìm kiếm trong lịch sử của kho lưu trữ thay đổi mà gây ra xung đột với thay đổi cục bộ của bạn.

$ svn info code/bar.c
Path: code/bar.c
Name: bar.c
URL: http://svn.example.com/svn/repo/trunk/code/bar.c
…
Tree conflict: local edit, incoming delete upon update
  Source  left: (file) ^/trunk/code/bar.c@4
  Source right: (none) ^/trunk/code/bar.c@5

$

Tập tin bar.c bây giờ được cho là nạn nhân của xung đột cấu trúc cây. Nó không thể được chuyển giao cho tới khi xung đột được giải quyết:

$ svn commit -m "Small fixes" 
svn: E155015: Commit failed (details follow):
svn: E155015: Aborting commit: '/home/svn/project/code/bar.c' remains in confl
ict
$

Để giải quyết xung đột này, bạn cần phải hoặc là đồng ý hoặc là không đồng ý với những di chuyển mà Harry đã thực hiện.

Nếu bạn đồng ý với việc di chuyển, tập tin bar.c của bạn lúc này trở nên vô dụng. Bạn sẽ muốn xóa nó và đánh dấu xung đột cấu trúc cây như đã giải quyết. Nhưng chờ đã: bạn đã thực hiện thay đổi trên tập tin này! Trước khi xoá bar.c, bạn cần phải quyết định rằng những thay đổi bạn đã thực hiện trên tập tin này có cần được sử dụng ở đâu đó khác không, ví dụ áp vào tập tin baz.c là nơi mà toàn bộ mã của tập tin bar.c sẽ được chuyển vào. Giả sử rằng thay đổi của bạn cần phải theo luồng. Subversion không đủ thông minh để thực hiện việc này giúp bạn. [10], vì vậy bạn cần phải thực hiện việc chuyển đổi trên những thay đổi của bạn một cách thủ công.

Trong ví dụ của chúng ta, bạn có thể tái tạo lại thay đổi của bạn một cách thủ công trên tập tin bar.c rất dễ dàng—nó chỉ là thay đổi trên-một-dòng. Tuy nhiên, không phải lúc nào cũng là trường hợp này, vì vậy chúng tôi sẽ chỉ ra cách tiếp cận có thể mở rộng hơn. Trước tiên chúng ta dùng lệnh svn diff để tạo một tập tin vá. Sau đó chúng ta điều chỉnh phần nhãn đầu (header) của tập tin này để trỏ tới tên mới của tập tin đã đổi tên. Cuối cùng, chúng ta áp-lại tập tin vá đã điều chỉnh vào bản sao làm việc của chúng ta.

$ svn diff code/bar.c > PATCHFILE
$ cat PATCHFILE
Index: code/bar.c
===================================================================
--- code/bar.c	(revision 14)
+++ code/bar.c	(working copy)
@@ -1,4 +1,4 @@
 const char *bar(void)
 {
-    return "Me neither!\n";
+    return "Well, I do like being moved around!\n";
 }
$ ### Edit PATCHFILE to refer to code/baz.c instead of code/bar.c
$ cat PATCHFILE
Index: code/baz.c
===================================================================
--- code/baz.c	(revision 14)
+++ code/baz.c	(working copy)
@@ -1,4 +1,4 @@
 const char *bar(void)
 {
-    return "Me neither!\n";
+    return "Well, I do like being moved around!\n";
 }
$ svn patch PATCHFILE
U         code/baz.c
$

Như vậy là những thay đổi bạn thực hiện ban đầu trên bar.c đã được sao chép vào baz.c, bạn có thể xoá tập tin bar.c và giải quyết xung đột, hướng lôgic giải quyết để chấp nhận những gì đang có trong bản sao làm việc như là kết quả mong muốn.

$ svn delete --force code/bar.c
D         code/bar.c
$ svn resolve --accept=working code/bar.c
Resolved conflicted state of 'code/bar.c'
$ svn status
M       code/foo.c
M       code/baz.c
$ svn diff
Index: code/foo.c
===================================================================
--- code/foo.c  (revision 14)
+++ code/foo.c  (working copy)
@@ -3,5 +3,5 @@
 int main(int argc, char *argv[])
 {
     printf("I don't like being moved around!\n%s", bar());
-    return 0;
+    return 1;
 }
Index: code/baz.c
===================================================================
--- code/baz.c  (revision 14)
+++ code/baz.c  (working copy)
@@ -1,4 +1,4 @@
 const char *bar(void)
 {
-    return "Me neither!\n";
+    return "Well, I do like being moved around!\n";
 }
$

Nhưng sẽ như thế nào nếu bạn không đồng ý với việc di chuyển? Trong trường hợp này, bạn có thể xoá tập tin baz.c sau khi đã chắc chắn rằng bất kỳ những thay đổi nào được thực hiện trên nó sau khi nó được đổi tên đã được lưu giữ lại hoặc là nó không đáng để lưu giữ. (Lưu ý không quên hoàn lại những thay đổi Harry đã thực hiện trên Makefile.) Bởi vì bar.c đã được lên lịch để lại thêm vào kho nên không còn việc gì cần phải làm nữa và xung đột được đánh dấu như đã được giải quyết:

$ svn delete --force code/baz.c
D         code/baz.c
$ svn resolve --accept=working code/bar.c
Resolved conflicted state of 'code/bar.c'
$ svn status
M       code/foo.c
A  +    code/bar.c
D       code/baz.c
M       Makefile
$ svn diff
Index: code/foo.c
===================================================================
--- code/foo.c	(revision 14)
+++ code/foo.c	(working copy)
@@ -3,5 +3,5 @@
 int main(int argc, char *argv[])
 {
     printf("I don't like being moved around!\n%s", bar());
-    return 0;
+    return 1;
 }
Index: code/bar.c
===================================================================
--- code/bar.c	(revision 14)
+++ code/bar.c	(working copy)
@@ -1,4 +1,4 @@
 const char *bar(void)
 {
-    return "Me neither!\n";
+    return "Well, I do like being moved around!\n";
 }
Index: code/baz.c
===================================================================
--- code/baz.c	(revision 14)
+++ code/baz.c	(working copy)
@@ -1,4 +0,0 @@
-const char *bar(void)
-{
-    return "Me neither!\n";
-}
Index: Makefile
===================================================================
--- Makefile	(revision 14)
+++ Makefile	(working copy)
@@ -1,2 +1,2 @@
 foo: 
-	$(CC) -o $@ code/foo.c code/baz.c
+	$(CC) -o $@ code/foo.c code/bar.c

Bây giờ bạn đã giải quyết xung đột cấu trúc cây đầu tiên! Bạn có thể chuyển giao những thay đổi của bạn và báo cho Harry về tất cả những việc phát sinh mà anh ấy đã gây ra cho bạn.



[9] Nếu thật sự cần thiết, bạn có thể đánh dấu những tập tin chứa dấu hiện xung đột như là đã được giải quyết xung đột và chuyển giao những tập tin này. Nhưng điều này hiếm khi được thực hiện trên thực tế.

[10] Trong vài trường hợp, Subversion 1.5 và 1.6 sẽ thực sự xử lý việc này cho bạn, nhưng chức năng có phần trúng-hoặc-trượt này đã bị loại bỏ trong Subversion 1.7