SOには 、さまざまなブランチのさまざまなファイルの追跡に関する多くの スレッドがあります。git はデプロイメントも容易にするため、デプロイメント固有のシークレット ファイルを社内ブランチに追加しようとすると、それらのスレッドで議論されている問題が発生します。考えられる解決策の 1 つは、 を使用することsubmodule
です。私はそれを避けることを好みます。したがって、このメソッドを拡張することで、 にリストされているシークレット ファイルの変更を考慮せずにプライベート ブランチをマージできるスクリプトが作成されました.branchownfiles
。
うまく機能しているようですが、git でのファイルの削除について質問があります。このファイルが に関連するコミットに参加したことがなくても、からへのgit merge
マージ時に削除されたファイルについて不平を言います:「ファイルは HEAD で削除され、プライベートで変更されました」private
master
master
これはあなたが見ることができる手順です:
i@my % git init
Initialized empty Git repository in /here/there/.git/
i@my % echo 'hello' > 'start.txt'
i@my % git add start.txt
i@my % git commit -m 'started'
[master (root-commit) 32d41f5] started
1 file changed, 1 insertion(+)
create mode 100644 start.txt
i@my % git checkout -b private
Switched to a new branch 'private'
i@my % echo 'this is secret' > a_secret.txt
i@my % echo a_secret.txt > .branchownfiles
i@my % echo .branchownfiles >> .branchownfiles
i@my % git add a_secret.txt
i@my % git add .branchownfiles
i@my % git commit -m 'a secret file added'
[private c2e174f] a secret file added
2 files changed, 3 insertions(+)
create mode 100644 .branchownfiles
create mode 100644 a_secret.txt
i@my % git checkout master
Switched to branch 'master'
i@my % git-merge-with-care.sh private
Automatic merge went well; stopped before committing as requested
rm '.branchownfiles'
rm 'a_secret.txt'
Success
# this is basically a `git merge` followed by two `git rm` for two files.
# so i suppose .branchownfiles and a_secret.txt never should leave anything in `master`
# but my assumption is not true..
i@my % git commit
[master 916c14b] Merge branch 'private'
i@my % git checkout private
Switched to branch 'private'
i@my % echo 'changed in private, should change in public too' > start.txt
i@my % echo 'secret changed too' > a_secret.txt
i@my % git add -u
i@my % git commit -m 'secret changed'
[private dc8938a] secret changed
2 files changed, 2 insertions(+), 2 deletions(-)
i@my % git checkout master
Switched to branch 'master'
i@my % git-merge-with-care.sh private
# now my question arise.
CONFLICT (modify/delete): a_secret.txt deleted in HEAD and modified in private. Version private of a_secret.txt left in tree.
# git merge complains about a_secret.txt an knows it deleted in HEAD. Why?!
Automatic merge failed; fix conflicts and then commit the result.
Trying to fix conflicts for branch limited files
a_secret.txt: [b]
Remerge with ours strategy
CONFLICT (modify/delete): a_secret.txt deleted in HEAD and modified in private. Version private of a_secret.txt left in tree.
Automatic merge failed; fix conflicts and then commit the result.
a_secret.txt: needs merge
rm 'a_secret.txt'
Success
私のスクリプトはマージ前にこれらのファイルを削除するため、引き続き機能します。a_secret.txt
しかし、このブランチのコミットに一度もなかったのに、なぜ git がリコールするのか知りたいですか? これらの削除されたファイルが以前にここにあったことを何らかの方法で git が思い出せないという効果を達成する方法はありgit merge --no-commit
ますか? git rm
つまりgit merge
、インデックスディレクトリにいくつかのファイルを追加する機能があります。
私のアプローチは安全ですか?いくつかのファイルがあり、後でこれらのファイルが以前にここにあったことを git が思い出した場合git rm
、git はこれらのファイルに関する情報をまだ持っています。どのようなデータで、どこにあるのですか? a_secret.txt
彼女のアクセスがmaster
ブランチに制限されているコンテンツを誰かが見つけることができますか?
そして、これは私のものgit-merge-with-care.sh
です:
#!/bin/env zsh
ls -d .git 2>/dev/null 1>&2
if [ $? != 0 ]; then
echo "error: run this from root of project"
exit 1
fi
if [ $# != 1 ]; then
echo "usage: $0 <private branch>"
exit 1
fi
current_branch=$(git branch -q | sed -n -e 's/^* //p')
from_branch=$1 #private branch
into_branch=master #public branch
if [ $current_branch != $into_branch ]; then
echo "error: merge while out of $into_branch branch"
exit 1
fi
new_files=$(git diff -z --name-only --diff-filter=A $into_branch $from_branch)
changed_files=$(git diff -z --name-only --diff-filter='M|T' $into_branch $from_branch)
branch_files=$(git show $from_branch:.branchownfiles)
git merge --no-commit --no-ff $from_branch
# fix conflict with strategy
if [ $? != 0 ]; then
echo "Trying to fix conflicts for branch limited files"
failing=false
git diff -z --name-only --diff-filter=U | while IFS= read -r -d $'\0' f; do
{ grep -x -q "$f" <<<"$branch_files" && echo "$f: [b]"; } || { [ $f != ".branchownfiles" ] && echo "$f: [!]" && failing=true; }
done
git merge --abort
if $failing; then
echo "Merge aborted because of merge conflicts"
exit 2
else
echo "Remerge with ours strategy"
git merge --no-commit $from_branch -s recursive -X ours --no-ff
if [ $? != 0 ]; then
# conflits for deleted files in merger but added again from mergee
conflicting_files=$(git diff -z --name-only --diff-filter=U)
# echo "$conflicting_files" | sort
# echo "$new_files" | sort
if grep -q "$(echo \"$conflicting_files\" | sort -z)" <<<"$(echo \"$new_files\" | sort -z)"; then
xargs -0 <<<"$conflicting_files" -I'{}' git rm --ignore-unmatch '{}'
[[ x`git diff --diff-filter=U` == 'x' ]] || exit 5
fi
fi
fi
fi
# remove files added from private branch but are restricted to private branch
xargs -0 -I'{}' <<<$new_files sh -c "grep -x -q '{}' <<<\"$branch_files\" && [ -e '{}' ] && git rm -rf '{}'"
# bring back public files overwritten bye private branch data
xargs -0 -I'{}' <<<"$changed_files" sh -c "grep -x -q '{}' <<<\"$branch_files\" && git checkout HEAD '{}'"
echo "Success"
exit 0