特定のファイルで競合するマージに対して常にローカルバージョンを選択するようにgitに指示するにはどうすればよいですか?


100

私がgitリポジトリを介して誰かと共同作業をしていて、外部の変更を受け入れたくない特定のファイルがあるとします。

git pullするたびにマージの競合について不平を言わないようにローカルリポジトリを設定する方法はありますか?このファイルをマージするときは、常にローカルバージョンを選択したいと思います。


1
.gitattributesと非常に基本的な「マージドライバー」を介した単純なソリューションを追加しました
VonC 09年

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

@CiroSantilli:Linuxの魅力のように動作します。このドライバーは、Gitに組み込むのに十分簡単です...
krlmlr '15

変更をファイルにプッシュしますか?それとも、たとえばデフォルトがgitに保存されている設定ファイルですか?
Ian Ringrose 2016

@CiroSantilli新疆改造中心六四事件法轮功のコメントは正しいですが、--globalタグのあるシステム上のすべてのリポジトリに対してこの動作が発生します。あなたは、単一のレポのために、この動作をしたい場合は、除外--globalフラグ:echo 'path/to/file merge=ours' >> .gitattributes && git config merge.ours.driver true
majorobot

回答:


140

設定ファイルの特定のインスタンスでは、私は同意するだろうロンの答え
configが(「で宣言されたのように、それゆえ「無視」ワークスペースへの「プライベート」である必要があります.gitignoreファイル」)。
あなたは、設定ファイルがあり、テンプレートトークン化された値のことで、その変換スクリプトconfig.templateプライベート(および無視)設定ファイルにファイルを。


ただし、その特定の発言は、より一般的な質問、つまりあなたの質問(!)には答えません。

特定のファイルで競合するマージに対して常にローカルバージョンを選択するようにgitに指示するにはどうすればよいですか?(任意のファイルまたはファイルのグループ)

この種類のマージは「コピーマージ」であり、競合がある場合は常に、ファイルの「ours」または「theirs」バージョンを常にコピーします。

(としてブライアン・ヴァンデンバーグのノートのコメントでours」と「theirs」ここにマージするために使用されている
彼らがされている逆転のためにリベース:「を参照してくださいWhy is the meaning of “ours” and “theirs” reversed with git-svn」、リベースを使用した、「git rebase『ローカル』を追跡して、 『リモート』」 )

「ファイル」(一般的には「config」ファイルではなく、悪い例であるためファイル)の場合、マージによって呼び出されるカスタムスクリプトでそれを実現します。
あなたが定義する必要がありますので、Gitはそのスクリプトを呼び出しますgitattributesのを定義し、カスタムマージドライバを

「カスタムマージドライバー」は、この場合、基本的に現在のバージョンを変更せずに維持する非常に単純なスクリプトなので、常にローカルバージョンを選択できます。

。IE、としては指摘によりチロSantilli

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/

次に、2つのファイルを作成してみましょう。どちらも競合しますが、マージ方法は異なります。

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

2つの異なる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

次に、「myBranch」に「hisBranch」をマージしてみましょう。

  • 競合するマージの手動解決
  • 除く外のため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私たちのUnixの友人のために。私はもちろん両方を行います:私はVirtualBoxセッションでUbuntuセッションを持っています)

以下のようにコメントlrkwz、と「で説明マージ戦略の」セクションのカスタマイズのGit - Gitの属性は、シェルコマンドでシェルスクリプトを置き換えることができます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

(それは、1人の、単純なマージドライバーだった;)(その場合、使用中でもシンプルな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ドライバーを介してpomをマージする」を参照してください。
ただし、他の質問で述べたように、これは競合(両方のブランチでの同時変更)の場合にのみ機能します。


1
詳しい回答ありがとうございます!バージョン管理の構成ファイルには意味がないことは理解していますが、単純な動機付けの例を探していました。確かに、私に興味を持ったのは、幅広い質問です。これまでにgitマージドライバーについて聞いたことがありませんでした。
saffsd 2009年

6
cp -f $3 $2おそらくすなわち、引用符で囲む必要がありますcp -f "$3" "$2"
アーク

1
@VonC、詳細な回答ありがとうございます!私が抱えている問題は、.git / configファイルでドライバーを設定する人々に依存していることです。ドライバー情報をプロジェクト自体に追加したいので、自動化され、セットアップ作業が少なくなります。ポインタはありますか?
ファンデルガド

2
@ulmangt:親ディレクトリをPATH(UnixまたはWindows PATH)に追加する方法が見つかれば、そのスクリプトをgitリポジトリにも非常に多く保存できます。このスクリプトはUnix bashシェルまたはMingWin bash MsysGit Windowsシェルを介して解釈されるため、移植可能です。
VonC、2012

5
@VonCありがとう。もう一つの問題。特定の状況下では(マージされるローカルブランチに変更が加えられていない場合)、マージドライバーが呼び出されず、ローカルファイルが変更される(カスタムマージドライバーを使用することになっている)ようです。マージ中に変更されないようにするため)。gitに常にマージドライバーを使用させる方法はありますか?
ulmangt

1

@ ciro-santilliがコメントしているように.gitattributes、設定で使用するための簡単な方法:

path/to/file merge=ours

この戦略を有効にするには:

git config --global merge.ours.driver true

(私はこれをより見やすくするための回答として追加していますが、自分のユーザークレジットを上回らないようにするためにコミュニティWikiにしています。Qの下で彼のコメントに賛成して、彼に賞賛を与えてください!)


(さておき:誰かが回答をコメントとして提供し、回答を追加しない場合、CW以外の回答を書き込んでクレジットを取得することは完全に問題ありません。彼らがまだアクティブなメンバーである場合、pingを実行して回答を追加できます。望みますが、技術的にはすでにチャンスがありました:-))。
1

0

上書きしたくない設定ファイルが複数あります。ただし、.gitignoreおよび.gitattributesは、この状況では機能しませんでした。私たちのソリューションは、設定ファイルを設定ブランチに保存することでした。次に、gitマージ中にファイルを変更できるようにしますが、マージの直後に「git checkoutブランチ-」を使用します。マージのたびにconfigsブランチから設定ファイルをコピーします。 詳細なスタックオーバーフローの回答はこちら

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.