web-dev-qa-db-ja.com

WindowsのGit Symlinks

開発者は、WindowsベースとUnixベースのOSを組み合わせて使用​​します。したがって、Unixマシンで作成されたシンボリックリンクは、Windows開発者にとって問題になります。 Windows(msysgit)では、シンボリックリンクは、それが指すファイルへのパスを持つテキストファイルに変換されます。代わりに、シンボリックリンクを実際のWindowsシンボリックリンクに変換したいと思います。

更新済み)解決策は次のとおりです。

  • 「シンボリックリンク」テキストファイルを再帰的に検索するチェックアウト後スクリプトを記述します。
  • ダミーの「symlink」と同じ名前と拡張子を持つWindowsシンボリックリンク(mklinkを使用)に置き換えます
  • .git/info/excludeにエントリを追加して、これらのウィンドウのシンボリックリンクを無視します

私はこれを実装していませんが、これはこの問題に対する堅実なアプローチだと思います。

質問:

  1. このアプローチには、もしあれば、マイナス面はありますか?
  2. このチェックアウト後スクリプトは実装可能ですか?すなわち、gitが作成するダミーの「シンボリックリンク」ファイルを再帰的に見つけることができますか?
  3. すでにそのようなスクリプトに取り組んでいる人はいますか?
217
Ken Hirakawa

おそらく次のコマンドを使用して、120000のモードを持つファイルを検索することにより、シンボリックリンクを見つけることができます。

git ls-files -s | awk '/120000/{print $4}'

リンクを置き換えたら、それらをgit update-index --assume-unchangedにリストするのではなく、.git/info/excludeで未変更としてマークすることをお勧めします。

94
Josh Lee

私はこのまったく同じ質問をしばらく前に尋ねていました(ここではありませんが、一般的なことです)が、OPの命題と非常によく似た解決策を思い付きました。最初に質問1 2および3に直接回答し、次に使用したソリューションを投稿します。

  1. 実際に提案されたソリューションには、主にリポジトリ汚染の可能性の増加、または「Windowsシンボリックリンク」状態にあるときに誤って重複ファイルを追加することに関して、いくつかの欠点があります。 (これについては、以下の「制限事項」を参照してください。)
  2. はい、チェックアウト後スクリプトを実装できます!多分、リテラルのpostgit checkoutステップとしてではないかもしれませんが、以下のソリューションは、リテラルのポストチェックアウトスクリプトが必要ないほど十分に私のニーズを満たしました。
  3. はい!

ソリューション:

開発者はOPとほぼ同じ状況にあります:WindowsとUnixのようなホスト、リポジトリとサブモジュールの混合物、多くのgitシンボリックリンク、およびWindowsホストでこれらのシンボリックリンクをインテリジェントに処理するためのMsysGitのリリースバージョンにはネイティブサポートはありません。

Gitが特別なファイルモード120000でシンボリックリンクをコミットするという事実を指摘してくれたJosh Leeに感謝します。この情報を使用して、Windowsホストでgitシンボリックリンクの作成と操作を可能にするいくつかのgitエイリアスを追加できます。

  1. 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"
    
  2. 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
    

    しかし、このような抜本的なアクションの場合、逆転は素晴らしいことです...

  3. 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を元に戻し、リポジトリを自然な状態に効果的に復元します(変更を除き、shouldはそのままです)。

    そして、サブモジュールの場合:

    git submodule foreach --recursive git checkout-symlinks
    
  4. 制限:

    • パスにスペースが含まれるディレクトリ/ファイル/シンボリックリンクが機能するはずです。しかし、タブまたは改行? YMMV…(つまり、これはしないでください。は動作しません

    • 自分や他の人がgit checkout-symlinksのような広範囲に及ぶ可能性のある結果を出す前にgit add -Aを忘れると、ローカルリポジトリは汚染状態になる可能性があります。

      以前の「example repo」を使用して:

      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-Internals-Git-Objects

http://technet.Microsoft.com/en-us/library/cc753194

最終更新:2019-03-13

  • POSIX準拠(もちろん、mklink呼び出しを除く)—もうありません Bashisms
  • スペースが含まれるディレクトリとファイルがサポートされます。
  • ゼロおよびゼロ以外の終了ステータスコード(それぞれ、要求されたコマンドの成功/失敗の通信用)が適切に保存/返されるようになりました。
  • add-symlinkエイリアスは ln(1) のように動作するようになり、リポジトリのルートディレクトリだけでなく、リポジトリ内の任意のディレクトリから使用できます。
  • rm-symlinkエイリアス(単数形)は、rm-symlinksエイリアス(複数形)に置き換わりました。これは、gitシンボリックリンクをNTFSハードリンクに選択的に変換するために、複数の引数を受け入れるようになりました。 +ジャンクション。
  • checkout-symlinksエイリアスも更新され、前述の変換を選択的に反転するために、複数の引数(またはまったく引数なし==すべて)を受け入れるようになりました。

最後の注意:このような古代バージョンにまだ固執しているかもしれない人のために、Bash 3.2(さらには3.1)を使用してこれらのエイリアスのロードと実行をテストしましたがさまざまな理由で、これらと同じくらい古いバージョンはパーサーのバグで悪名高いことに注意してください。これらのエイリアスをインストールしようとして問題が発生した場合、最初に検討する必要があるのは、シェルのアップグレードです(Bashの場合、CTRL + X、CTRL + Vでバージョンを確認します)。または、ターミナルエミュレータに貼り付けてインストールしようとしている場合は、ファイルに貼り付けて、代わりにソースを作成することもできます。として

. ./git-win-symlinks.sh

がんばろう!

173
Mark G.

git scm (testet 2.11.1)の最新バージョンでは、シンボリックリンクを有効にできます。ただし、シンボリックリンクを使用してリポジトリを再度クローンする必要がありますgit clone -c core.symlinks=true <URL>。このコマンドは管理者権限で実行する必要があります。 Windowsでmklinkを使用してシンボリックリンクを作成することもできます。 wiki を確認してください。

enter image description here

58
sirlunchalot

Msysgitで実装する必要がありますが、2つの欠点があります。

  • 古いバージョンはディレクトリジャンクションのみをサポートしているため、シンボリックリンクはWindows Vista以降でのみ使用できます(2011年には問題になりませんが、それでも...)。
  • (大きなもの)Microsoftはシンボリックリンクをセキュリティリスクと見なしているため、デフォルトでは管理者のみがシンボリックリンクを作成できます。 gitプロセスの特権を昇格するか、fstoolを使用して、作業するすべてのマシンでこの動作を変更する必要があります。

私はクイック検索を行いましたが、これに関して積極的に行われている作業があります。問題 224 を参照してください。

14
djs

これらの回答の多くがここに投稿されてからGITで物事が変わったので、現在のウィンドウでシンボリックリンクを正しく動作させるための正しい手順です

2018年8月


1。 gitがシンボリックリンクサポートと共にインストールされていることを確認してください

During the install of git on windows

2。シンボリックリンクの代わりにハードリンクを作成するようにBashに指示します

編集-(gitフォルダー)/etc/bash.bashrc

下部に追加-MSYS=winsymlinks:nativestrict

3。シンボリックリンクを使用するようにgit configを設定します

git config core.symlinks true

または

git clone -c core.symlinks=true <URL>

注:これをグローバルgit構成に追加しようとしましたが、現時点では機能していません。各リポジトリに追加することをお勧めします。

4。リポジトリをプルする

注:最新バージョンのWindows 10で開発者モードを有効にしていない限り、管理者としてbashを実行してシンボリックリンクを作成する必要があります

5。すべてのシンボリックリンクをリセット(オプション)既存のリポジトリがある場合、またはサブモジュールを使用している場合、シンボリックリンクが正しく作成されていないことがわかります。コマンド。

find -type l -delete
git reset --hard

注:これにより、最後のコミット以降の変更がリセットされるため、最初にコミットしたことを確認してください

9
Simon

リポジトリ内でシンボリックリンクを使用しないことをお勧めします。リポジトリ内に実際のコンテンツを保存し、コンテンツを指すシンボリックリンクをリポジトリの外側に配置します。

たとえば、レポを使用して、* nixでのサイトのホスティングとwinでのホスティングを比較しているとします。コンテンツをレポジトリに保存します。/httpRepoContentc:\httpRepoContentを言うことができます。これはGIT、SVNなどを介して同期されるフォルダーです。

次に、Webサーバーのコンテンツフォルダー(/var/wwwc:\program files\web server\www {名前は重要ではありませんが、必要に応じて編集します})をリポジトリ内のコンテンツへのシンボリックリンクに置き換えます。 Webサーバーはコンテンツを実際には「正しい」場所にあると見なしますが、ソース管理を使用できます。

ただし、リポジトリでシンボリックリンクを使用する必要がある場合は、何らかの種類の事前/事後コミットスクリプトのようなものを調べる必要があります。これらを使用して、たとえばフォーマッタを介してコードファイルを解析するなどのことを実行できることを知っているので、プラットフォーム間でシンボリックリンクを変換できるはずです。

共通のソース管理、SVN GIT MGに対してこれらのスクリプトを実行する方法を学ぶのに適した場所を知っている人がいれば、コメントを追加してください。

9
thecoshman

簡単な答え:開発者モードを有効にできる場合、それらはうまくサポートされています。

から https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/

Windows 10 Creators Updateでは、ユーザー(管理者権限を持つ)が最初に開発者モードを有効にでき、その後、コンピューターのユーザーはコマンドラインコンソールを昇格させることなくmklinkコマンドを実行できます。

何がこの変化を引き起こしたのですか?シンボリックリンクの可用性と使用は、現代の開発者にとって大きな問題です。

Gitのような多くの一般的な開発ツールとnpmのようなパッケージマネージャーは、それぞれリポジトリまたはパッケージを作成するときにシンボリックリンクを認識して保持します。これらのリポジトリまたはパッケージが他の場所に復元されると、シンボリックリンクも復元され、ディスクスペース(およびユーザーの時間)が無駄にならないようにします。

「Creator's update」の他のすべてのアナウンスを見逃すのは簡単ですが、開発者モードを有効にすると、昇格した権限なしでシンボリックリンクを作成できます。デフォルトではサポートされていないため、サポートを再インストールし、有効にする必要があります。

Symbolic Links aren't enabled by default

9
Orangutech

Vista、Win7、またはそれ以上で CygWin を使用している場合、ネイティブgitコマンドは Android Studio などのWindowsアプリで認識される「適切な」シンボリックリンクを作成できます。 CYGWIN環境変数を設定して、winsymlinks:nativeまたはwinsymlinks:nativestrictを含めるだけです。

export CYGWIN="$CYGWIN winsymlinks:native"

これのマイナス面(そして重要な点)は、CygWinシェルがこれらの種類のシンボリックリンクを作成するために必要なOS権限を持つために「管理者として実行」する必要があることです。ただし、一度作成すると、useに特別な権限は必要ありません。別の開発者によってリポジトリで変更されない限り、その後gitは通常のユーザー権限で正常に実行されます。

個人的には、このonlyを使用するのは、この追加の難しさのために、Windowsアプリ(つまり、CygWin以外)によってナビゲートされるシンボリックリンクです。

このオプションの詳細については、このSOの質問を参照してください。 Windows 7でcygwinでシンボリックリンクを作成する方法

7
Brian White

Josh Leeの答えに基づいて、ファイルのみのリポジトリ内のシンボリックリンクを変換するバッチスクリプトを次に示します。管理者権限の追加チェックを伴うスクリプトは https://Gist.github.com/Quazistax/8daf09080bf54b4c7641 にあります。

@echo off
pushd "%~dp0"
setlocal EnableDelayedExpansion

for /f "tokens=3,*" %%e in ('git ls-files -s ^| findstr /R /C:"^120000"') do (
     call :processFirstLine %%f
)
REM pause
goto :eof

:processFirstLine
@echo.
@echo FILE:    %1

dir "%~f1" | find "<SYMLINK>" >NUL && (
  @echo FILE already is a symlink
  goto :eof
)

for /f "usebackq tokens=*" %%l in ("%~f1") do (
  @echo LINK TO: %%l

  del "%~f1"
  if not !ERRORLEVEL! == 0 (
    @echo FAILED: del
    goto :eof
  )

  setlocal
  call :expandRelative linkto "%1" "%%l"
  mklink "%~f1" "!linkto!"
  endlocal
  if not !ERRORLEVEL! == 0 (
    @echo FAILED: mklink
    @echo reverting deletion...
    git checkout -- "%~f1"
    goto :eof
  )

  git update-index --assume-unchanged "%1"
  if not !ERRORLEVEL! == 0 (
    @echo FAILED: git update-index --assume-unchanged
    goto :eof
  )
  @echo SUCCESS
  goto :eof
)
goto :eof

:: param1 = result variable
:: param2 = reference path from which relative will be resolved
:: param3 = relative path
:expandRelative
  pushd .
  cd "%~dp2"
  set %1=%~f3
  popd
goto :eof
5
Quazistax

私は、Windows上のUNIXシンボリックリンクに対処する簡単なソリューションを探していました。上記のGitエイリアスに感謝します。 rm-symlinksには、エイリアスが誤って2回実行された場合に宛先フォルダーのファイルが削除されないようにするための最適化が1つあります。ループ内の新しいif条件を観察して、ロジックが実行される前にファイルがディレクトリへのリンクになっていないことを確認してください。

git config --global alias.rm-symlinks '!__git_rm_symlinks(){
for symlink in $(git ls-files -s | egrep "^120000" | cut -f2); do
    *if [ -d "$symlink" ]; then
      continue
    fi*
    git rm-symlink "$symlink"
    git update-index --assume-unchanged "$symlink"
done
}; __git_rm_symlinksenter 
2
prgrminglover

ドキュメントルートとgitリポジトリディレクトリの間で常にsymリンクを使用します。私はそれらを分離しておくのが好きです。 Windowsでは、mklink/jオプションを使用します。ジャンクションはgitを正常に動作させるようです:

>mklink /j <location(path) of link> <source of link>

例えば:

>mklink /j c:\gitRepos\Posts C:\Bitnami\wamp\Apache2\htdocs\Posts

2
mmv_sat

使用する1つの簡単なトリックは、git add --allを続けて2回呼び出すことです。

たとえば、Windows 7のコミットスクリプト呼び出し:

$ git add --all
$ git add --all

最初の追加では、リンクをテキストとして扱い、削除するフォルダーを追加します。

2番目の追加は、リンクを正しくトラバースし、ファイルを復元して削除を取り消します。

他の提案されたソリューションのいくつかよりもエレガントではありませんが、シンボリックリンクが追加された従来の環境の一部に対する単純な修正です。

1
Mike Panoff