91169


git は、例えば CI などにおいて git clone をすると、それによりチェックアウトされたファイル達のファイル情報としての最終変更日時は、その
clone を行ったタイミングになります。

例えば [Nuxt Content](https://content.nuxtjs.org/)
など、データファイルの最終変更日時を利用するアプリケーションないしフレームワークを利用していると、このファイルの最終更新日時は、せっかくバージョン管理システムを利用しているので、そのファイルを最後にコミットした日時を反映していきたくなります。

# 質問

git において、チェックアウトされたファイルたち(Working Tree
のファイルたち)の、ファイルの最終変更日時を、現ブランチにおいてそのファイルを最後にコミットした日時に変更したいです。

これどうやったら実現できるでしょうか。


CC BY-SA 4.0
2022-09-18T14:45:49.717
91162
2022-10-08T16:05:10.873
754
question
git
git でチェックアウトされたファイルたちの最終変更日時を、コミット日時にしたい


Gitの標準機能としては用意されていないようです。自分でプログラムを作るか、誰かが作ったプログラムを使うしか無いと思われます。それらの作り方等は下記が参考になります。

  * [ファイルのタイムスタンプをコミット日時に合わせる - Qiita](https://qiita.com/mAster_rAdio/items/246fcab7984e50d7d66f)
  * [ブログズミ: [GIT] 「ファイルのタイムスタンプをコミット日時に合わせる」を爆速にした](https://srz-zumix.blogspot.com/2020/06/git.html)
  * [Groovyスクリプトで、ファイルの最終更新日時をgit clone/pullの日時ではなく、commit日時にしてみた | フューチャー技術ブログ](https://future-architect.github.io/articles/20220221a/)
  * [git pull した後にファイルの日付をコミット時のものに変更する - いろいろ備忘録日記](https://devlights.hatenablog.com/entry/2022/04/20/073000)

プロジェクトとして保守されている(？)ものとしては、最初のサイトで紹介されているrsyncのサポートファイルの一つとして提供されているgit-set-
file-
timesになると思います。Linuxであれば、rsyncパッケージをインストールすれば、`/usr/share/doc/rsync/support/git-
set-file-times`(Rocky9)や`/usr/share/rsync/scripts/git-set-file-
times`(Ubuntu22.04)として入りますので、それを使用するといいかと思います。


CC BY-SA 4.0
2022-09-19T04:00:30.907
91169
2022-09-19T04:00:30.907
7347
91162
answer


自分の場合は node 系のコンテナを使っていて、そのためだけに rsync を入れるのもどうかと思ったので、やりたいことを実現する node
スクリプトを自作しました。参考までに置いておきます。

[code]

    #!/usr/bin/env node
    
    const childProcess = require('node:child_process');
    const util = require('node:util');
    const fs = require('node:fs/promises')
    const execFile = util.promisify(childProcess.execFile);
    
    const readFilePaths = (dir) => {
      const readline = require('node:readline');
    
      const lsFiles = childProcess.spawn('git', ['ls-files', '--', dir]);
      const rl = readline.createInterface({
        input: lsFiles.stdout,
        crlfDelay: Infinity
      });
      return rl
    }
    
    const fixTimestamp = async (filePath) => {
      const gitLog = await execFile('git', ['log', '--pretty=%ct', '-n1', '--', filePath])
      const timestamp = gitLog.stdout.trim()
      console.log(`updating: ${filePath} to ${timestamp}`)
      return fs.utimes(filePath, timestamp, timestamp)
    }
    
    const main = async () => {
      const dirPath = process.argv.length > 2 ? process.argv[2] : '.'
      const promises = []
      for await (const filePath of readFilePaths(dirPath)) {
        promises.push(fixTimestamp(filePath))
      }
      await Promise.all(promises)
    }
    
    main()
    
[/code]

使い方: 第一引数に `git ls-files` に渡すディレクトリを指定(なければ `.` が与えられます)


CC BY-SA 4.0
2022-10-08T15:12:56.333
91500
2022-10-08T16:05:10.873
2022-10-08T16:05:10.873
754
754
91162
answer