【问题标题】:Git clone changes file modification timegit clone 更改文件修改时间
【发布时间】:2014-03-11 05:18:46
【问题描述】:

当我使用“git clone ...”命令克隆 Git 存储库时,我本地存储库中的所有克隆文件都具有与发出 git clone 命令时相同的修改时间和日期。

有没有办法克隆一个远程 Git 存储库每个文件的实际修改时间

【问题讨论】:

  • 这是操作系统问题,不是 Git 问题。
  • 可以从git log -n1 -- file获取上次修改时间;这就是git 的用途。
  • 我不太明白“这就是 git 的用途”的说法。为什么要模组。不像 CVS 那样节省时间?
  • @Amadan:你只能得到最后一次提交时间,而不是最后一次修改文件的时间。
  • @turnt 这不是问题...程序可以更改它们创建的文件的修改时间,因此这是程序的选择

标签: git clone


【解决方案1】:

Git does not record timestamp for the files,因为它是一个分布式 VCS(意味着您计算机上的时间可能与我的不同:没有时间和日期的“中心”概念)

解释了不记录元数据的官方论点in this answer

但您可以找到尝试恢复有意义日期的脚本,like this one(或 simpler version of the same idea)。

【讨论】:

  • 但是可以在远端节省本地时间。根据修改时间编译文件时,如何解决构建问题?
  • 好的,感谢您的回复和建议的解决方案。我看到了讨论,但是因为 git 是版本控制系统而不节省 mod 时间的论点在我看来并不强大。我使用 CVS 多年,它有这个功能,而且它不会伤害它。事实上,简单的 ls -ltr 命令会显示从 CVS 存储库中检出的修改文件的顺序。
  • 构建依赖于文件的修改时间这一事实实际上是将其存储为元数据的一部分的原因。如果 mtime is 更新到提交时间,您必须在签出较旧的提交后开始一个干净的构建,因为文件将被认为比相应的派生文件更旧并且不会导致它们被重建。 (显然,假设一个构建系统依赖于 mtime。)
  • 它可以节省UTC时间,不是吗?
  • @VonC,您永远无法保证您的时间是正确的。不过,这不是放弃使用时间的原因。
【解决方案2】:

您可以检索 Git 存储库中所有文件的最后修改日期(最后提交时间)。请参阅How to retrieve the last modification date of all files in a Git repository

然后使用touch命令更改修改日期:

git ls-tree -r --name-only HEAD | while read filename; do
  unixtime=$(git log -1 --format="%at" -- "${filename}")
  touchtime=$(date -d @$unixtime +'%Y%m%d%H%M.%S')
  touch -t ${touchtime} "${filename}"
done

另请参阅我的要点here

【讨论】:

  • 太棒了!奇迹般有效。对我们来说,这很关键,因此我们可以加快基于 Makefile 的构建。
  • 这就是答案。除了一项更改,如果文件名带有空格,您应该在 $filename 周围添加引号。
  • @P.T.我看到现在已经添加了引号
  • 这是怎么回答的.. 你说“你可以检索 git 存储库中所有文件的最后修改日期。(lat commit time)”
  • @barlop git history 并不总是实际编辑的历史,它可以通过修改和 rebase -i 来修改。这是作者选择呈现的逻辑历史,其中提交是逻辑上原子,同时更改一组文件。如果您想记录“file1 在 file2 之前更改”,那么它们之间必须存在一个点 - 这些必须是单独的提交。
【解决方案3】:

重置 mtime 的另一个选项是git-restore-mtime

sudo apt install git-restore-mtime # Debian/Ubuntu example
git clone <myurl>
cd <mydir>
git restore-mtime

【讨论】:

    【解决方案4】:

    这个 Linux 单线将解决所有文件的问题(不是文件夹 - 只是文件) - 它也将解决其中包含空格的文件的问题:

    git ls-files -z | xargs -0 -n1 -I{} -- git log -1 --format="%ai {}" {} | perl -ne 'chomp;next if(/'"'"'/);($d,$f)=(/(^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d(?: \+\d\d\d\d|)) (.*)/);print "d=$d f=$f\n"; `touch -d "$d" '"'"'$f'"'"'`;'
    

    【讨论】:

    • 非常非常好。凉爽的!!!!据我所知,不幸的是,某些文件可能尚未使用此解决方案进行转换。例如:1. 包含字符 ' (39 0027 ' APOSTROPHE) 的文件,2. 存储库根目录中的文件,3. 包含 ( 的文件(也可以是 ) )。也许您也可以抽出时间来看看这些具体案例?
    • 这是从 git repo 中检索的,所以只会给出提交日期。不是实际的文件上次修改日期/时间。
    【解决方案5】:

    添加到单行列表...

    for f in $(git ls-files) ; do touch -d $(git log -1 --format='%aI' "$f") "$f" ; done
    

    【讨论】:

      【解决方案6】:

      user11882487's answer 的较短变体,我觉得更容易理解:

      git ls-files | xargs -I{} git log -1 --date=format:%Y%m%d%H%M.%S --format='touch -t %ad "{}"' "{}" | $SHELL
      

      【讨论】:

        【解决方案7】:

        每个文件运行一次log -1 让我很恼火,所以我写了这个来一次性完成所有这些:

        ( # don't alter any modified-file stamps:
          git diff --name-status --no-find-copies --no-renames | awk '$1="D"' FS=$'\t' OFS=$'\t'
          git log --pretty=%cI --first-parent --name-status -m --no-find-copies --no-renames
        ) | awk ' NF==1 { date=$1 }
                  NF<2 || seen[$2]++ { next }
                  $1!="D" { print "touch -d",date,$2 }' FS=$'\t'
        

        它在十秒钟内完成 linux 历史记录(通过 shell 传输所有触摸命令需要一分钟)。

        这是破坏例如平分,而且我在〜甚至不开始试图重载文件系统时间戳的道路上,坚持这样做的人显然将不得不学习艰难的方式〜,但我可以看到,也许有一些工作流程,这真的不会伤害你。

        随便。但是,当然,不要盲目地这样做。

        【讨论】:

        • 你能让你的答案更加独立吗?例如,它是否遵循/操作git ls-files 的输出(而不是 xargs)? “每个文件运行一次日志 -1”指的是什么答案(四个答案有“log -1”)? (使用答案链接,因为用户名可能随时更改。)
        • @PeterMortensen 它按原样打印触摸命令,不需要添加任何内容。将它们通过 shell 进行管道传输,我认为“通过 shell 传输所有触摸命令”这一点是明确建议的。运行log -1 的任何答案都必须每个文件运行一次,我反对该方法。
        【解决方案8】:

        这适用于多个先前答案中的解决方案:

        使用%at 格式,然后使用touch -d \@$epochdelta,以避免日期时间转换问题。

        【讨论】:

          【解决方案9】:

          在 Python 中执行此操作比其他一些选项更简单,因为 os.utime 接受 git log 命令输出的 Unix 时间戳。此示例使用 GitPython,但也可以使用 subprocess.run 调用 git log

          import git
          from os import utime
          from pathlib import Path
          
          repo_path = "my_repo"
          repo = git.Repo(repo_path)
          
          for n in repo.tree().list_traverse():
              filepath = Path(repo.working_dir) / n.path
              unixtime = repo.git.log(
                  "-1", "--format='%at'", "--", n.path
              ).strip("'")
              if not unixtime.isnumeric():
                  raise ValueError(
                      f"git log gave non-numeric timestamp {unixtime} for {n.path}"
                  )
              utime(filepath, times=(int(unixtime), int(unixtime)))
          

          这与this answer 中的git restore-mtime 命令和the highest rated answer 中的脚本的结果相匹配。

          如果您在克隆后立即执行此操作,则可以重复使用传递给git.Repo.clone_fromto_path 参数,而不是访问Repo 对象上的working_dir 属性。

          【讨论】:

            猜你喜欢
            • 2011-01-15
            • 1970-01-01
            • 2014-07-03
            • 1970-01-01
            • 2014-10-24
            • 2014-10-12
            • 2017-03-30
            • 2015-09-09
            • 2015-04-04
            相关资源
            最近更新 更多