当社の開発者は、Windows と Unix ベースの OS を混在して使用しています。そのため、Unix マシンで作成されたシンボリック リンクは、Windows 開発者にとって問題となります。Windows (マイクロソフト) の場合、シンボリック リンクは、それが指すファイルへのパスを含むテキスト ファイルに変換されます。代わりに、シンボリック リンクを実際の Windows シンボリック リンクに変換したいと思います。
これに対する私の(更新された)解決策は次のとおりです。
- 「シンボリック リンク」テキスト ファイルを再帰的に検索するチェックアウト後のスクリプトを作成します。
- Windowsのシンボリックリンクに置き換えます(mkリンク)をダミーの「シンボリックリンク」と同じ名前と拡張子で作成します。
- .git/info/excludeファイルにエントリを追加して、これらのWindowsシンボリックリンクを無視します。
私はこれを実装していませんが、これがこの問題に対する確実なアプローチであると信じています。
- このアプローチには、何か欠点があると思いますか?
- このチェックアウト後のスクリプトは実装可能でしょうか? つまり、Git が作成するダミーの「シンボリックリンク」ファイルを再帰的に見つけることはできますか?
ベストアンサー1
更新ノート
シンボリック リンクやgit
Windows 上での *nix システムとのリポジトリの共有の問題に苦労しているほとんどの Windows 開発者にとって、このトピックは、Windows の理解をmklink
少し深めて開発者モードをオンにすれば解決済みの問題です。
これを見てより現代的な答え次の Git ハックに関する詳細な議論に入る前に。
古いシステム:
少し前にまったく同じ質問をしましたが (ここでではなく、一般的な質問として)、最終的に OP の提案と非常によく似た解決策にたどり着きました。最終的に使用した解決策を投稿します。
しかし、まずは OP の 3 つの質問に直接答えたいと思います。
Q: 「このアプローチには、何か欠点があると思いますか?」
A: 提案されたソリューションには確かにいくつかの欠点があります。主に、リポジトリ汚染の可能性が高まること、または「Windows シンボリックリンク」状態にあるときに重複ファイルを誤って追加することに関するものです。(これについては、以下の「制限」で詳しく説明します。)
Q: 「このチェックアウト後のスクリプトは実装可能ですか? つまり、git が作成するダミーの「シンボリックリンク」ファイルを再帰的に見つけることができますか?」
A: はい、チェックアウト後のスクリプトは実装可能です。文字通りのポスト
git checkout
ステップではないかもしれませんが、以下のソリューションは私のニーズを十分に満たしており、文字通りのチェックアウト後のスクリプトは必要ありませんでした。
Q: 「すでにそのような脚本に取り組んだ人はいますか?」
A: はい!
ソリューション:
私たちの開発者は、OP とほぼ同じ状況にあります。つまり、Windows と Unix のようなホスト、多数の git シンボリック リンクを含むリポジトリとサブモジュールが混在しており、MsysGit のリリース バージョンでは、Windows ホスト上でこれらのシンボリック リンクをインテリジェントに処理するためのネイティブ サポートが (まだ) ありません。
Git が特別なファイルモードでシンボリックリンクをコミットするという事実を指摘してくれた Josh Lee に感謝します120000
。この情報により、Windows ホスト上で Git シンボリックリンクの作成と操作を可能にするいくつかの Git エイリアスを追加することが可能になりました。
Windows で git シンボリックリンクを作成する
git config --global alias.add-symlink '!'"$(cat <<'ETX' __git_add_symlink() { if [ $# -ne 2 ] || [ "$1" = "-h" ]; then printf '%b\n' \ 'usage: git add-symlink <source_file_or_dir> <target_symlink>\n' \ 'Create a symlink in a git repository on a Windows host.\n' \ 'Note: source MUST be a path relative to the location of target' [ "$1" = "-h" ] && return 0 || return 2 fi source_file_or_dir=${1#./} source_file_or_dir=${source_file_or_dir%/} target_symlink=${2#./} target_symlink=${target_symlink%/} target_symlink="${GIT_PREFIX}${target_symlink}" target_symlink=${target_symlink%/.} : "${target_symlink:=.}" if [ -d "$target_symlink" ]; then target_symlink="${target_symlink%/}/${source_file_or_dir##*/}" fi case "$target_symlink" in (*/*) target_dir=${target_symlink%/*} ;; (*) target_dir=$GIT_PREFIX ;; esac target_dir=$(cd "$target_dir" && pwd) if [ ! -e "${target_dir}/${source_file_or_dir}" ]; then printf 'error: git-add-symlink: %s: No such file or directory\n' \ "${target_dir}/${source_file_or_dir}" >&2 printf '(Source MUST be a path relative to the location of target!)\n' >&2 return 2 fi git update-index --add --cacheinfo 120000 \ "$(printf '%s' "$source_file_or_dir" | git hash-object -w --stdin)" \ "${target_symlink}" \ && git checkout -- "$target_symlink" \ && printf '%s -> %s\n' "${target_symlink#$GIT_PREFIX}" "$source_file_or_dir" \ || return $? } __git_add_symlink ETX )"
使用法:
git add-symlink <source_file_or_dir> <target_symlink>
、ここで、ソース ファイルまたはディレクトリに対応する引数は、ターゲット シンボリック リンクに対する相対パスの形式を取る必要があります。このエイリアスは、通常 を使用するのと同じように使用できますln
。たとえば、リポジトリツリー:
dir/ dir/foo/ dir/foo/bar/ dir/foo/bar/baz (file containing "I am baz") dir/foo/bar/lnk_file (symlink to ../../../file) file (file containing "I am file") lnk_bar (symlink to dir/foo/bar/)
Windows では次のように作成できます。
git init mkdir -p dir/foo/bar/ echo "I am baz" > dir/foo/bar/baz echo "I am file" > file git add -A git commit -m "Add files" git add-symlink ../../../file dir/foo/bar/lnk_file git add-symlink dir/foo/bar/ lnk_bar git commit -m "Add symlinks"
Git シンボリックリンクを NTFS ハードリンク + ジャンクションに置き換える
git config --global alias.rm-symlinks '!'"$(cat <<'ETX' __git_rm_symlinks() { case "$1" in (-h) printf 'usage: git rm-symlinks [symlink] [symlink] [...]\n' return 0 esac ppid=$$ case $# in (0) git ls-files -s | grep -E '^120000' | cut -f2 ;; (*) printf '%s\n' "$@" ;; esac | while IFS= read -r symlink; do case "$symlink" in (*/*) symdir=${symlink%/*} ;; (*) symdir=. ;; esac git checkout -- "$symlink" src="${symdir}/$(cat "$symlink")" posix_to_dos_sed='s_^/\([A-Za-z]\)_\1:_;s_/_\\\\_g' doslnk=$(printf '%s\n' "$symlink" | sed "$posix_to_dos_sed") dossrc=$(printf '%s\n' "$src" | sed "$posix_to_dos_sed") if [ -f "$src" ]; then rm -f "$symlink" cmd //C mklink //H "$doslnk" "$dossrc" elif [ -d "$src" ]; then rm -f "$symlink" cmd //C mklink //J "$doslnk" "$dossrc" else printf 'error: git-rm-symlink: Not a valid source\n' >&2 printf '%s =/=> %s (%s =/=> %s)...\n' \ "$symlink" "$src" "$doslnk" "$dossrc" >&2 false fi || printf 'ESC[%d]: %d\n' "$ppid" "$?" git update-index --assume-unchanged "$symlink" done | awk ' BEGIN { status_code = 0 } /^ESC\['"$ppid"'\]: / { status_code = $2 ; next } { print } END { exit status_code } ' } __git_rm_symlinks ETX )" git config --global alias.rm-symlink '!git rm-symlinks' # for back-compat.
使用法:
git rm-symlinks [symlink] [symlink] [...]
このエイリアスは、git シンボリックリンクを 1 つずつ、または一度にすべて削除できます。シンボリックリンクは、NTFS ハードリンク (ファイルの場合) または NTFS ジャンクション (ディレクトリの場合) に置き換えられます。「真の」NTFS シンボリックリンクよりもハードリンク + ジャンクションを使用する利点は、それらを作成するのに昇格された UAC 権限が必要ないことです。
サブモジュールからシンボリックリンクを削除するには、git の組み込み反復サポートを使用します。
git submodule foreach --recursive git rm-symlinks
しかし、このような思い切った行動のたびに、逆転が起こるのは良いことです...
Windows で git シンボリックリンクを復元する
git config --global alias.checkout-symlinks '!'"$(cat <<'ETX' __git_checkout_symlinks() { case "$1" in (-h) printf 'usage: git checkout-symlinks [symlink] [symlink] [...]\n' return 0 esac case $# in (0) git ls-files -s | grep -E '^120000' | cut -f2 ;; (*) printf '%s\n' "$@" ;; esac | while IFS= read -r symlink; do git update-index --no-assume-unchanged "$symlink" rmdir "$symlink" >/dev/null 2>&1 git checkout -- "$symlink" printf 'Restored git symlink: %s -> %s\n' "$symlink" "$(cat "$symlink")" done } __git_checkout_symlinks ETX )" git config --global alias.co-symlinks '!git checkout-symlinks'
使用法:
git checkout-symlinks [symlink] [symlink] [...]
は を元に戻しgit rm-symlinks
、リポジトリを自然な状態に効果的に復元します (変更内容はそのまま残りますが)。サブモジュールの場合:
git submodule foreach --recursive git checkout-symlinks
制限事項:
パスにスペースが含まれるディレクトリ/ファイル/シンボリックリンクは動作するはずです。しかし、タブや改行の場合はどうなるでしょうか? YMMV... (つまり、動作しないので、そうしないでください。)
あなた自身または他の人が、
git checkout-symlinks
のような広範囲にわたる影響を及ぼす可能性のあることを行う前にを忘れた場合git add -A
、ローカル リポジトリが汚染された状態になる可能性があります。以前の「サンプル リポジトリ」を使用します。
echo "I am nuthafile" > dir/foo/bar/nuthafile echo "Updating file" >> file git add -A git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # new file: dir/foo/bar/nuthafile # modified: file # deleted: lnk_bar # POLLUTION # new file: lnk_bar/baz # POLLUTION # new file: lnk_bar/lnk_file # POLLUTION # new file: lnk_bar/nuthafile # POLLUTION #
おっと…
このため、Windows ユーザーがプロジェクトをビルドする前とビルドした後に実行する手順として、これらのエイリアスを含めると便利です。チェックアウト後やプッシュ前ではありません。ただし、状況はそれぞれ異なります。これらのエイリアスは私にとって十分に便利なので、実際のチェックアウト後のソリューションは必要ありませんでした。
参考文献:
http://git-scm.com/book/en/Git-内部-Git-オブジェクト
http://technet.microsoft.com/ja-jp/library/cc753194 より
最終更新日: 2019-03-13
- POSIX準拠(もちろん、これらの
mklink
呼び出しは除きます)—もうバシズム! - スペースを含むディレクトリとファイルがサポートされています。
- ゼロおよびゼロ以外の終了ステータス コード (それぞれ要求されたコマンドの成功/失敗を伝える) が適切に保存/返されるようになりました。
- エイリアス
add-symlink
は次のように機能します1 行目リポジトリのルート ディレクトリだけでなく、リポジトリ内の任意のディレクトリから使用できます。 rm-symlink
エイリアス (単数形) はエイリアス (複数形) に置き換えられました。エイリアス(rm-symlinks
複数形) は、git シンボリック リンクを NTFS ハードリンク + ジャンクションに選択的に変換するために、複数の引数 (または引数なし、以前のようにリポジトリ全体のすべてのシンボリック リンクを検索) を受け入れるようになりました。- エイリアス
checkout-symlinks
も更新され、前述の変換を選択的に反転するために複数の引数(または引数なし、つまりすべて)を受け入れるようになりました。
最終注記:さまざまな理由で古いバージョンにまだ固執しているユーザーのために、Bash 3.2 (および 3.1) を使用してこれらのエイリアスのロードと実行をテストしましたが、これらのバージョンほど古いバージョンはパーサーのバグで有名であることに注意してください。これらのエイリアスのいずれかをインストールしようとしているときに問題が発生した場合は、まずシェルをアップグレードしてください (Bash の場合は、CTRL+X、CTRL+V でバージョンを確認してください)。または、ターミナル エミュレータに貼り付けてインストールしようとしている場合は、ファイルに貼り付けてソースを取得する方がうまくいく場合があります。例:
. ./git-win-symlinks.sh