@fe4hazw

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Git 管理の特定のバージョンの特定のフォルダを外部にコピーしたい

Q&A

Closed

解決したいこと

Git で特定のバージョン(ブランチやタグで指定)の特定のフォルダ(リポジトリルートからのパス)の内容を指定の場所(リポジトリ外のパス)にコピーしたいです。
良い方法はありますでしょうか?

自分で試したこと

シンプルな方法として、対象のバージョンを checkout して、フォルダをコピー後にもとのバージョンに戻すという方法があります。
しかし、手間が多く、一旦 checkout しなくても、何らかの git コマンドで直接取ってこれるのでは?と思います。

特にファイルが多く、作業中ブランチとコピーしたいバージョン間で差分が大きいと checkout するのにも時間がかかります。
作業中のファイルがあればそれを stash や仮コミットしないと checkout できないのでそういった手間も発生します。

良い方法があればご教授お願いします。

0 likes

2Answer

リポジトリルートで以下を実行してください。フォルダパスはリポジトリルートからの相対パス(訂正:リポジトリ内で今いる場所からの相対パス)で、スペース区切りで複数個指定できます。

git --work-tree=出力先フォルダパス checkout バージョン -- フォルダパス

この方法では、フォルダが深い階層にある場合、中間階層のフォルダも作成されます。たとえばフォルダパスが foo/bar/dir なら出力は 出力先フォルダ/foo/bar/dir です。中間階層のフォルダに他のファイルがあっても出力には含まれません。


中間階層を作らずに出力したい場合、以下のように書けます。

git archive バージョン フォルダパス \
    | tar x -C 出力先フォルダパス --strip-components=取り除く階層数
0Like

Comments

  1. @fe4hazw

    Questioner

    ありがとうございます。
    git archive を使う方法についてですが、フォルダパスの指定が期待どおり動かないのですが対処方法はご存知でしょうか?

    指定したパスは今いるフォルダからの相対パスになり、かつ今のブランチに存在するフォルダしか対象にならないようです。

    コマンドを実行するのはリポジトリ内ですが、どの階層でも行えるようにしたいため、リポジトリルートからのパスにしたいです。
    パスを / から始めてみましたが、リポジトリ外のファイルシステムのルートになってしまうようでした。

    (リポジトリルート)/foo/bar のフォルダは branch1 にしかなく、今いる master ブランチには存在しないとします。
    そのときに、 branch1foo/bar を指定すると、この場合もフォルダが存在しないエラーになりました。

    エラーメッセージは次のようなものでした。

    fatal: pathspec 'foo/bar' did not match any files
    
  2. 指定したパスは今いるフォルダからの相対パスになり

    ご指摘の通りでした、回答を訂正しました。

    リポジトリルートからのパスにしたい

    リポジトリルートはパスを :/ から始めることで表現できます。(git archive に限らず、リポジトリ内のファイルパスを指定する状況ならどのサブコマンドでも使えます。)

    master のどこかのフォルダにいるとして、 git archive branch1 :/foo/bar と指定すればいけます。

  3. @fe4hazw

    Questioner

    ありがとうございます。
    :/ 試してみましたが、うまく行かないようです。
    以下詳細ですがおかしなところありますでしょうか?

    # バージョン確認
    root@cb87b95ff33a:/opt/f# git version
    git version 2.30.2
    
    # master にする
    root@cb87b95ff33a:/opt/f# git reset --hard master
    HEAD is now at 5114c27 0
    
    # ファイル確認
    root@cb87b95ff33a:/opt/f# tree
    .
    |-- a
    |-- b
    `-- foo
        `-- bar
            `-- baz
                `-- 0.txt
    
    3 directories, 3 files
    
    # リポジトリルートで単純に foo/bar を試す
    root@cb87b95ff33a:/opt/f# git archive master foo/bar
    pax_global_header(略
    
    # :/ をつけて :/foo/bar を試す
    root@cb87b95ff33a:/opt/f# git archive master :/foo/bar
    pax_global_header(略
    
    # foo に移動
    root@cb87b95ff33a:/opt/f# cd foo
    
    # リポジトリルート以外で :/foo/bar を試す
    root@cb87b95ff33a:/opt/f/foo# git archive master :/foo/bar
    fatal: pathspec ':/foo/bar' did not match any files
    
    
  4. @fe4hazw

    Questioner

    ありがとうございます。
    2.41.0 にして出力には成功できました。
    しかし、今いる階層よって tar ファイルの中身が変わるようです。

    • 現在位置がリポジトリのルートの場合
      • tar の直下に foo フォルダが存在
    • 現在位置が foo の場合
      • tar の直下に bar フォルダが存在

    階層が違うと、

    --strip-components=取り除く階層数

    に指定する階層数が可変になってしまいます。
    回避方法はありますでしょうか?

  5. git -C "$(git rev-parse --show-toplevel)" archive master :/foo/bar とすることで、今いる階層がどこであれ、リポジトリルートで git を実行したかのような出力にできます。

  6. @fe4hazw

    Questioner

    ありがとうございます。
    期待する動きになりました。
    -C "$(git rev-parse --show-toplevel)" を使うと自然とリポジトリルートになるので :/ を付ける必要はなくなりますね。
    質問はこれでクローズしようと思います。
    長い間お付き合いありがとうございました。

Your answer might help someone💌