특정 파일에서 충돌하는 병합을 위해 항상 내 로컬 버전을 선택하도록 git에 지시하는 방법은 무엇입니까?


100

내가 git 저장소를 통해 다른 사람과 공동 작업하고 있는데 외부 변경 사항을 절대 받아들이고 싶지 않은 특정 파일이 있다고 가정 해 보겠습니다.

git pull 할 때마다 충돌하는 병합에 대해 불평하지 않도록 로컬 저장소를 설정할 수있는 방법이 있습니까? 이 파일을 병합 할 때 항상 내 로컬 버전을 선택하고 싶습니다.


1
.gitattributes 및 매우 기본적인 "병합 드라이버"를 통해 간단한 솔루션을 추가했습니다.
VonC

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

@CiroSantilli : Linux에서 매력처럼 작동합니다. 이 드라이버는 ... 힘내에 내장되는 간단한 충분하다
krlmlr

변경 사항을 파일에 푸시 하시겠습니까? 또는 예를 들어 기본값이 git에 저장되는 구성 파일입니까?
Ian Ringrose 2016 년

@CiroSantilli 新疆 改造 中心 六四 事件 法轮功의 설명은 정확하지만 --global태그 가있는 시스템의 모든 repo에 대해이 동작이 발생합니다 . 단일 --globalecho 'path/to/file merge=ours' >> .gitattributes && git config merge.ours.driver true
리포지토리에

답변:


140

구성 파일의 특정 인스턴스에서 Ron의 대답에 동의합니다 .
구성은 작업 공간에 "개인"이어야합니다 (따라서 " .gitignore파일 에서 선언"에서와 같이 "무시 "). 토큰 화 된 값
이있는 구성 파일 템플릿 과 해당 파일을 개인 (및 무시 된) 구성 파일로 변환하는 스크립트 가있을 수 있습니다 .config.template


그러나 그 구체적인 언급은보다 광범위한 일반적인 질문, 즉 귀하의 질문 (!)에 대한 답변이 아닙니다.

특정 파일에서 충돌하는 병합을 위해 항상 내 로컬 버전을 선택하도록 git에게 알리려면 어떻게해야합니까? (모든 파일 또는 파일 그룹)

이러한 종류의 병합은 "복사 병합"으로, 충돌이있을 때마다 항상 파일의 '우리의'또는 '자신의'버전을 복사합니다.

(로 브라이언 반덴버그의 노트 코멘트에 , ' ours'과 '은 theirs'여기에 병합에 사용됩니다 .
그들이 반전 A에 대한 REBASE 다음을 참조하십시오 " Why is the meaning of “ours” and “theirs” reversed with git-svn"는 REBASE를 사용하는 " git rebase, 추적을 유지하는 '원격' '지역'및 ' )

"파일"(일반적으로 "구성"파일을 말하는 것이 아니라 나쁜 예이기 때문에 파일)의 경우 병합을 통해 호출되는 사용자 지정 스크립트를 사용하면됩니다.
당신이 정의하는 것 때문에 힘내는 해당 스크립트를 호출합니다 gitattributes의 정의, 사용자 정의 병합 드라이버를 .

이 경우 "사용자 지정 병합 드라이버"는 기본적으로 현재 버전을 변경하지 않고 유지하므로 항상 로컬 버전을 선택할 수있는 매우 간단한 스크립트입니다.

. IE는,로는 지적 에 의해 치로 틸리 :

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

Windows에서 msysgit 1.6.3을 사용하여 단순한 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/

이제 충돌이 발생하지만 서로 다른 방식으로 병합되는 두 개의 파일을 만들어 보겠습니다.

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

두 개의 다른 git 브랜치에있는 두 파일의 내용에 "충돌"을 도입 할 것입니다.

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

이제 "hisBranch"를 "myBranch"에 병합 해 보겠습니다.

  • 충돌하는 병합에 대한 수동 해결
  • dirWithCopyMerge\b.txt항상 버전 을 유지하려는 경우제외하고b.txt .

병합은 ' MyBranch' 에서 발생하므로 다시 전환 gitattributes하고 병합 동작을 사용자 지정하는 ' '지시문을 추가합니다 .

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

우리는이 .gitattributes에 정의 파일 dirWithCopyMerge디렉토리 (만 병합 의지가 발생한 지점의 정의 : myBranch), 그리고 우리는이 .git\config지금 병합 드라이버가 포함 파일을.

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

아직 keepMine.sh를 정의하지 않고 어쨌든 병합을 시작하면 다음과 같은 결과를 얻을 수 있습니다.

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

괜찮습니다.

  • a.txt 병합 할 준비가되어 있으며 충돌이 있습니다.
  • b.txt병합 드라이버가 처리해야하기 때문에 여전히 변경되지 않습니다 ( .gitattributes디렉토리 의 파일에 있는 지시문으로 인해 ).

정의 keepMine.sh당신의 어디를 %PATH%(또는 $PATH우리의 유닉스 친구 나는 물론 모두를 수행합니다. 나는 버추얼 세션에서 우분투 세션이)

으로 주석 에 의해 lrkwz , 그리고 "에 설명 된 병합 전략 의"섹션 사용자 정의 힘내 - 힘내 속성 , 당신은 쉘 명령으로 쉘 스크립트를 대체 할 수 있습니다 true.

git config merge.keepMine.driver true

그러나 일반적인 경우에는 스크립트 파일을 정의 할 수 있습니다.

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

(즉, 하나의 간단한 병합 드라이버였다) (이 경우, 사용도 간단 true)
(방금 전에 추가, 다른 버전을 유지하고 싶었다면 exit 0라인 :
cp -f $3 $2.
그게 다예요 당신은 드라이버가가는 요리는 다른에서 나오는 버전을 유지하는 것입니다 병합합니다. 분기, 로컬 변경 무시)

이제 처음부터 병합을 다시 시도해 보겠습니다.

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.

병합이 실패합니다 ... a.txt에 대해서만 .
a.txt를 편집하고 'hisBranch'의 줄을 그대로 둔 다음 :

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

이 병합 중에 b.txt가 보존되었는지 확인하겠습니다.

type dirWithCopyMerge\b.txt
b
myLineForB

마지막 커밋은 전체 병합을 나타냅니다 .

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

(Merge로 시작하는 줄이이를 증명합니다)


Git은 다음과 같이 병합 드라이버를 정의, 결합 및 / 또는 덮어 쓸 수 있습니다.

  • 검사 <dir>/.gitattributes(문제의 경로와 동일한 디렉토리에 있음) : 다른 경로보다 우선합니다..gitattributes 디렉토리 디렉토리보다
  • 그런 다음 .gitattributes(부모 디렉토리에 있음) 검사하고 아직 설정하지 않은 경우 지시문 만 설정합니다.
  • 마지막으로 $GIT_DIR/info/attributes. 이 파일은 트리 내 설정을 재정의하는 데 사용됩니다. <dir>/.gitattributes지시문 을 덮어 씁니다 .

"결합"이란 다중 병합 드라이버를 "집계"한다는 의미입니다.
Nick Green 은 주석 에서 실제로 병합 드라이버를 결합 하려고 시도합니다 . " python git driver를 통한 Merge pom 's "를 참조하십시오 .
그러나 그의 다른 질문 에서 언급했듯이 충돌이 발생한 경우에만 작동합니다 (두 분기에서 동시에 수정).


1
자세한 답변 감사합니다! 나는 버전 제어 구성 파일이 의미가 없다는 것을 이해하지만, 직접적인 동기 부여 예제를 따랐습니다. 실제로 저에게 관심이있는 것은 더 광범위한 질문입니다. 나는 전에 git merge 드라이버에 대해 들어 본 적이 없으므로 깨달아 주셔서 감사합니다.
saffsd

6
cp -f $3 $2아마도 즉, 인용한다 cp -f "$3" "$2".
Arc :

1
@VonC 자세한 답변에 감사드립니다! 내가 가진 문제는 사람들이 .git / config 파일에 드라이버를 설정하는 것에 달려 있다는 것입니다. 프로젝트 자체에 드라이버 정보를 추가하고 싶습니다. 자동으로 수행해야하는 설정 작업이 줄어 듭니다. 포인터가 있습니까?
Juan Delgado

2
@ulmangt : 부모 디렉터리를 PATH(Unix 또는 Windows PATH) 에 추가하는 방법을 찾는 한 git repo에도 해당 스크립트를 저장할 수 있습니다 . 해당 스크립트는 Unix bash 셸 또는 MingWin bash MsysGit Windows 셸을 통해 해석되므로 이식 가능합니다.
VonC 2012

5
@VonC 감사합니다. 한 가지 더. 특정 상황에서 (병합되는 로컬 브랜치에 대한 변경 사항이없는 경우) 병합 드라이버가 호출되지 않는 것으로 나타나 로컬 파일이 수정 (사용자 지정 병합 드라이버를 사용하는 것으로 가정 됨) 병합 중에 변경되는 것을 방지하기 위해). git이 항상 병합 드라이버를 사용 하도록 강제 하는 방법이 있습니까?
ulmangt 2012

1

@ ciro-santilli가 언급했듯이 .gitattributes설정과 함께 사용하는 간단한 방법은 다음 과 같습니다.

path/to/file merge=ours

다음을 사용하여이 전략을 활성화하십시오.

git config --global merge.ours.driver true

(나는 이것을 더 눈에 띄게 만들기 위해 답변으로 추가하고 있지만 사용자의 크레딧을 초과하지 않기 위해 커뮤니티 위키로 만들고 있습니다. 여기 Q에서 그의 댓글에 찬사를 보내주세요!)


(참고 : 누군가 댓글로 답변을하고 답변을 추가하지 않으면 CW가 아닌 답변을 작성하고 크레딧을받는 것은 완벽합니다. 아직 활동중인 회원 인 경우 핑을 통해 답변을 추가 할 수 있습니다. 당신은 원하지만 그들은 기술적으로 이미 기회가 있습니다 :-)).
halfer

0

덮어 쓰지 않으려는 구성 파일이 여러 개 있습니다. 그러나 .gitignore 및 .gitattributes는 우리 상황에서 작동하지 않았습니다. 우리의 해결책은 configs 브랜치에 config 파일을 저장하는 것이 었습니다. 그런 다음 git 병합 중에 파일이 변경되도록 허용하되 병합 직후 "git checkout branch-."를 사용하십시오. 병합 후 구성 브랜치에서 구성 파일을 복사합니다. 여기에 자세한 스택 오버플로 답변

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.