Làm cách nào để yêu cầu git luôn chọn phiên bản cục bộ của tôi cho các hợp nhất bị xung đột trên một tệp cụ thể?


100

Giả sử tôi đang cộng tác với ai đó thông qua kho lưu trữ git và có một tệp cụ thể mà tôi không bao giờ muốn chấp nhận bất kỳ thay đổi bên ngoài nào.

Có cách nào để tôi thiết lập repo cục bộ của mình để không phàn nàn về việc hợp nhất bị xung đột mỗi khi tôi git pull không? Tôi muốn luôn chọn phiên bản cục bộ của mình khi hợp nhất tệp này.


1
Chỉ cần thêm một giải pháp đơn giản thông qua .gitattributes và một "trình điều khiển hợp nhất" rất cơ bản
VonC

12
TD; LR:echo 'path/to/file merge=ours' >> .gitattributes && git config --global merge.ours.driver true
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

@CiroSantilli: Hoạt động như một sự quyến rũ trên Linux. Trình điều khiển này là đủ đơn giản để được xây dựng thành Git ...
krlmlr

Bạn có muốn đẩy các thay đổi của mình vào tệp không? Hoặc nó là một tập tin cấu hình ví dụ, nơi mặc định được lưu trữ trong git.
Ian Ringrose

Nhận xét của @CiroSantilli 新疆 改造 中心 六四 事件 法轮功 là đúng, nhưng sẽ làm cho hành vi này xảy ra cho mọi repo trên hệ thống của bạn có --globalthẻ. Nếu bạn chỉ muốn hành vi này cho một repo duy nhất, bỏ qua những --globallá cờ:echo 'path/to/file merge=ours' >> .gitattributes && git config merge.ours.driver true
majorobot

Câu trả lời:


140

Đối với trường hợp cụ thể của tệp cấu hình, tôi đồng ý với câu trả lời của Ron :
cấu hình phải là "riêng tư" đối với không gian làm việc của bạn (do đó "bị bỏ qua", như trong "được khai báo trong .gitignoretệp").
Bạn có thể có một mẫu tệp cấu hình với các giá trị được mã hóa trong đó và một tập lệnh chuyển đổi config.templatetệp đó thành tệp cấu hình riêng tư (và bị bỏ qua).


Tuy nhiên, nhận xét cụ thể đó không trả lời câu hỏi rộng hơn, tổng quát hơn, tức là câu hỏi của bạn (!):

Làm cách nào để yêu cầu git luôn chọn phiên bản cục bộ của tôi cho các hợp nhất bị xung đột trên một tệp cụ thể? (đối với bất kỳ tệp hoặc nhóm tệp nào)

Loại hợp nhất này là "hợp nhất sao chép", trong đó bạn sẽ luôn sao chép phiên bản 'của chúng tôi' hoặc 'của họ' bất cứ khi nào có xung đột.

(như Brian Vandenberg lưu ý trong các nhận xét , ' ours' và ' theirs' ở đây được sử dụng để hợp nhất .
Chúng được đảo ngược cho một rebase : see " Why is the meaning of “ours” and “theirs” reversed with git-svn", sử dụng một rebase " git rebase, theo dõi 'local' và 'remote' " )

Đối với "một tệp" (một tệp nói chung, không nói đến tệp "cấu hình", vì nó là một ví dụ xấu), bạn sẽ đạt được điều đó với một tập lệnh tùy chỉnh được gọi thông qua hợp nhất.
Git sẽ gọi tập lệnh đó vì bạn sẽ xác định giá trị gitattributes , giá trị này xác định trình điều khiển hợp nhất tùy chỉnh .

Trong trường hợp này, "trình điều khiển hợp nhất tùy chỉnh" là một tập lệnh rất đơn giản về cơ bản sẽ không thay đổi phiên bản hiện tại, do đó cho phép bạn luôn chọn phiên bản cục bộ của mình.

IE., Theo ghi nhận của Ciro Santilli :

echo 'path/to/file merge=ours' >> .gitattributes
git config --global merge.ours.driver true

Hãy kiểm tra điều đó trong một kịch bản đơn giản, với msysgit 1.6.3 trên Windows, chỉ trong một phiên DOS:

cd f:\prog\git\test
mkdir copyMerge\dirWithConflicts
mkdir copyMerge\dirWithCopyMerge
cd copyMerge
git init
Initialized empty Git repository in F:/prog/git/test/copyMerge/.git/

Bây giờ, hãy tạo hai tệp, cả hai sẽ có xung đột, nhưng sẽ được hợp nhất khác nhau.

echo a > dirWithConflicts\a.txt
echo b > dirWithCopyMerge\b.txt
git add -A
git commit -m "first commit with 2 directories and 2 files"
[master (root-commit) 0adaf8e] first commit with 2 directories and 2 files

Chúng tôi sẽ giới thiệu một "xung đột" trong nội dung của cả hai tệp đó trong hai nhánh git khác nhau:

git checkout -b myBranch
Switched to a new branch 'myBranch'
echo myLineForA >> dirWithConflicts\a.txt
echo myLineForB >> dirWithCopyMerge\b.txt
git add -A
git commit -m "add modification in myBranch"
[myBranch 97eac61] add modification in myBranch

git checkout master
Switched to branch 'master'
git checkout -b hisBranch
Switched to a new branch 'hisBranch'
echo hisLineForA >> dirWithConflicts\a.txt
echo hisLineForB >> dirWithCopyMerge\b.txt
git add -A
git commit -m "add modification in hisBranch"
[hisBranch 658c31c] add modification in hisBranch

Bây giờ, hãy thử hợp nhất "hisBranch" với "myBranch", với:

  • giải quyết thủ công cho các hợp nhất xung đột
  • ngoại trừ cho dirWithCopyMerge\b.txtnơi tôi luôn muốn giữ tôi phiên bản của b.txt.

Vì quá trình hợp nhất xảy ra trong ' MyBranch', chúng tôi sẽ chuyển về nó và thêm các lệnh ' gitattributes' sẽ tùy chỉnh hành vi hợp nhất.

git checkout myBranch
Switched to branch 'myBranch'
echo b.txt merge=keepMine > dirWithCopyMerge\.gitattributes
git config merge.keepMine.name "always keep mine during merge"
git config merge.keepMine.driver "keepMine.sh %O %A %B"
git add -A
git commit -m "prepare myBranch with .gitattributes merge strategy"
[myBranch ec202aa] prepare myBranch with .gitattributes merge strategy

Chúng tôi có một .gitattributestệp được xác định trong dirWithCopyMergethư mục (chỉ được xác định trong nhánh nơi hợp nhất sẽ xảy ra myBranch:), và chúng tôi có một .git\configtệp hiện chứa trình điều khiển hợp nhất.

[merge "keepMine"]
        name = always keep mine during merge
        driver = keepMine.sh %O %A %B

Nếu bạn chưa xác định keepMine.sh và vẫn khởi chạy hợp nhất, đây là những gì bạn nhận được.

git merge hisBranch
sh: keepMine.sh: command not found
fatal: Failed to execute internal merge
git st
# On branch myBranch
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   dirWithConflicts/a.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

type dirWithConflicts\a.txt
a
<<<<<<< HEAD:dirWithConflicts/a.txt
myLineForA
=======
hisLineForA
>>>>>>> hisBranch:dirWithConflicts/a.txt

Điều đó là tốt:

  • a.txt sẵn sàng được hợp nhất và có xung đột trong đó
  • b.txtvẫn chưa bị ảnh hưởng, vì trình điều khiển hợp nhất phải xử lý nó (do chỉ thị trong .gitattributestệp trong thư mục của nó).

Xác định một keepMine.shbất kỳ nơi nào trong của bạn %PATH%(hoặc $PATHcho người bạn Unix của chúng tôi. Tất nhiên tôi làm cả hai: Tôi có phiên Ubuntu trong phiên VirtualBox)

Như nhận xét của lrkwz và được mô tả trong phần " Chiến lược hợp nhất " của Tùy chỉnh Git - Thuộc tính Git , bạn có thể thay thế shell script bằng lệnh shell true.

git config merge.keepMine.driver true

Nhưng trong trường hợp chung, bạn có thể xác định một tệp script:

keepMine.sh

# I want to keep MY version when there is a conflict
# Nothing to do: %A (the second parameter) already contains my version
# Just indicate the merge has been successfully "resolved" with the exit status
exit 0

(đó là một tài xế hợp nhất đơn giản;) (Thậm chí đơn giản trong trường hợp đó, việc sử dụng true)
(Nếu bạn muốn giữ phiên bản khác, chỉ cần thêm trước khi exit 0dòng:
cp -f $3 $2.
Vậy là xong Bạn nhập tài xế aways sẽ giữ phiên bản đến từ bên kia. nhánh, ghi đè mọi thay đổi cục bộ)

Bây giờ, hãy thử lại hợp nhất từ ​​đầu:

git reset --hard
HEAD is now at ec202aa prepare myBranch with .gitattributes merge strategy

git merge hisBranch
Auto-merging dirWithConflicts/a.txt
CONFLICT (content): Merge conflict in dirWithConflicts/a.txt
Auto-merging dirWithCopyMerge/b.txt
Automatic merge failed; fix conflicts and then commit the result.

Hợp nhất không thành công ... chỉ cho a.txt .
Chỉnh sửa a.txt và để dòng từ 'hisBranch', sau đó:

git add -A
git commit -m "resolve a.txt by accepting hisBranch version"
[myBranch 77bc81f] resolve a.txt by accepting hisBranch version

Hãy kiểm tra xem b.txt đã được giữ nguyên trong quá trình hợp nhất này chưa

type dirWithCopyMerge\b.txt
b
myLineForB

Cam kết cuối cùng đại diện cho sự hợp nhất đầy đủ :

git show -v 77bc81f5e
commit 77bc81f5ed585f90fc1ca5e2e1ddef24a6913a1d
Merge: ec202aa 658c31c
git merge hisBranch
Already up-to-date.

(Dòng bắt đầu bằng Merge chứng tỏ điều đó)


Hãy xem xét bạn có thể xác định, kết hợp và / hoặc ghi đè trình điều khiển hợp nhất, vì Git sẽ:

  • kiểm tra <dir>/.gitattributes(nằm trong cùng thư mục với đường dẫn được đề cập): sẽ chiếm ưu thế so với đường dẫn khác .gitattributestrong thư mục
  • Sau đó, nó kiểm tra .gitattributes(nằm trong thư mục mẹ), sẽ chỉ thiết lập các chỉ thị nếu chưa được thiết lập
  • Cuối cùng nó kiểm tra $GIT_DIR/info/attributes. Tệp này được sử dụng để ghi đè cài đặt trong cây. Nó sẽ ghi đè các <dir>/.gitattributeschỉ thị.

Bằng cách "kết hợp", ý tôi là "tổng hợp" nhiều trình điều khiển hợp nhất.
Nick Green cố gắng, trong các ý kiến , để thực sự kết hợp trình điều khiển hợp nhất: xem " qua python git lái xe Merge pom của ".
Tuy nhiên, như đã đề cập trong câu hỏi khác của anh ấy , nó chỉ hoạt động trong trường hợp xung đột (sửa đổi đồng thời ở cả hai nhánh).


1
Cảm ơn bạn đã trả lời chi tiết! Tôi hiểu rằng nó không có ý nghĩa gì đối với các tệp cấu hình kiểm soát phiên bản, nhưng tôi đã theo đuổi một ví dụ thúc đẩy thẳng thắn. Thật vậy, đó là câu hỏi rộng hơn mà tôi quan tâm. Tôi chưa bao giờ nghe nói về trình điều khiển hợp nhất git trước đây, vì vậy cảm ơn bạn đã khai sáng cho tôi.
saffsd

6
cp -f $3 $2lẽ nên được trích dẫn, tức là cp -f "$3" "$2".
Arc

1
@VonC cảm ơn vì câu trả lời chi tiết! Vấn đề tôi gặp phải là nó phụ thuộc vào việc mọi người thiết lập trình điều khiển trong tệp .git / config của họ. Tôi muốn thêm thông tin trình điều khiển vào chính dự án, vì vậy nó sẽ tự động và ít công việc thiết lập phải thực hiện hơn. Bất kỳ gợi ý?
Juan Delgado

2
@ulmangt: bạn cũng có thể lưu tập lệnh đó trong git repo, ... miễn là bạn tìm cách thêm thư mục mẹ của nó vào PATH(Unix hoặc Windows PATH). Vì script đó sẽ được diễn giải thông qua shell bash Unix hoặc thông qua shell MsysGit Windows của MingWin bash, nó sẽ có thể di động được.
VonC

5
@VonC Cảm ơn. Một vấn đề nữa. Trong một số trường hợp nhất định (nếu không có bất kỳ thay đổi nào đối với nhánh cục bộ được hợp nhất vào), có vẻ như trình điều khiển hợp nhất thậm chí không bao giờ được gọi, điều này dẫn đến các tệp cục bộ bị sửa đổi (được cho là đang sử dụng trình điều khiển hợp nhất tùy chỉnh để ngăn chúng thay đổi trong quá trình hợp nhất). Có cách nào để buộc git luôn sử dụng trình điều khiển hợp nhất không?
ulmangt

1

Như @ ciro-santilli đã nhận xét, cách đơn giản để thực hiện nó là sử dụng .gitattributesvới cài đặt nó:

path/to/file merge=ours

và kích hoạt chiến lược này với:

git config --global merge.ours.driver true

(Tôi thêm câu trả lời này như một câu trả lời để hiển thị rõ hơn nhưng biến nó thành Wiki Cộng đồng để không cố gắng giành được nhiều hơn tín dụng của người dùng cho bản thân. Vui lòng ủng hộ nhận xét của anh ấy dưới phần Q ở đây để tặng anh ấy kudo!)


(Ngoài ra: nếu ai đó đưa ra câu trả lời dưới dạng nhận xét và không thêm câu trả lời, bạn hoàn toàn có thể viết câu trả lời không phải CW và nhận tín dụng. Nếu họ vẫn là thành viên tích cực, bạn có thể ping họ để thêm câu trả lời nếu bạn muốn, nhưng về mặt kỹ thuật họ đã có cơ hội của họ :-)).
halfer

0

Chúng tôi có nhiều tệp cấu hình mà chúng tôi không bao giờ muốn ghi đè. Tuy nhiên .gitignore và .gitattributes không hoạt động trong trường hợp của chúng tôi. Giải pháp của chúng tôi là lưu trữ các tệp cấu hình trong một nhánh cấu hình. Sau đó, cho phép các tệp được thay đổi trong quá trình hợp nhất git, nhưng ngay sau khi hợp nhất, hãy sử dụng "git checkout branch -." để sao chép các tệp cấu hình của chúng tôi từ nhánh cấu hình sau mỗi lần hợp nhất. Câu trả lời chi tiết về stackoverflow tại đây

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.