58

I'm trying to find a command to get the last modified date of a file in a local git repo.

I have created the repo and done one commit. I only had to edit one file and will commit it, but I was wondering what the last modification data of the file in the repo is (not the commit date).

I only have the command line, and git log ./path/to/filename.php only gives me the commit date

iownbey
  • 69
  • 5
HMR
  • 37,593
  • 24
  • 91
  • 160
  • Git works in terms of blobs, not files. Each commit corresponds to a blob and can contain many files, all of which are created atomically. Git log should give you the full log of the file, the earliest date is the file creation date in Git. – hd1 Mar 19 '14 at 06:09
  • Would "ls -l" not do the trick? – Paul Hicks Mar 19 '14 at 06:09
  • @PaulHicks No, I just modified the file. I would like to know last modification date of the file in the git repo. Doesn't git maintain versions of files and their properties? (like last mod date?). Maybe it doesn't though. – HMR Mar 19 '14 at 06:27
  • @hd1 I guess the file properties aren't saved so I only know the last commit date. Wanted to know when the last mod date of the file in git was but I guess I should have checked that before editing the file :-) – HMR Mar 19 '14 at 06:30
  • @HMR I just pointed out (in [my answer below](http://stackoverflow.com/a/22498088/6309)) a script which *can* save those timestamps, and restore them on checkout; – VonC Mar 19 '14 at 06:36

6 Answers6

92

The correct way to do this is to use git log as follows.

git log -1 --pretty="format:%ci" /path/to/repo/anyfile.any

-1 restricts it to the very last time the file changed

%ci is just one of the date formats you can choose from others here at https://git-scm.com/docs/pretty-formats

Dexygen
  • 12,287
  • 13
  • 80
  • 147
MitchellK
  • 2,322
  • 1
  • 16
  • 25
  • 1
    What if I would like to list last commit date for each file in my current directory? Like `ls -la`, except using `git` modification date rather than system modification date. – Apollys supports Monica Oct 28 '19 at 23:30
  • 1
    This is the answer I was looking for. I just wouldn't say it is fool proof and 100% accurate, what if an older commit is rebased onto a newer commit? It will show the timestamp of the last and thus in this case older commit. This is what I would expect, because git is a proper time machine, but users should be aware of that. – Christian Hujer Nov 21 '19 at 09:56
  • 2
    Your current working directory needs to be in the git repo, otherwise you get `fatal: not a git repository (or any of the parent directories): .git` – Boris Verkhovskiy Feb 03 '20 at 20:30
  • 2
    @Boris Instead of changing the current directory, you can also specify `-C /some/git/dir` to let git know where the repo is located. – Stéphane May 25 '20 at 20:15
  • 1
    I partly agree with your statement regarding the fool proof; it is 100 % good if you remove the `)`. – Timo Mar 24 '22 at 20:40
6

MitchellK's answer exactly fit my needs, setting my local files' last written times to what's in git. Here's a little C# LinqPad script to automate the process:

var root = new DirectoryInfo(@"C:\gitlab\mydirectory\");

Directory.SetCurrentDirectory(root.FullName); // Give git some context

var files = root.GetFiles("*.*", SearchOption.AllDirectories);

foreach (var file in files)
{
  var results = Util.Cmd("git", 
                         $"log -1 --pretty=\"format:%ci\" \"{file.FullName}\"",
                         true);
  
  var lastUpdatedString = results.FirstOrDefault();
  if (lastUpdatedString == null)
  {
    Console.WriteLine($"{file.FullName} did not have a last updated date!!");
    continue;
  }
  
  var dt = DateTimeOffset.Parse(lastUpdatedString);
  if (file.LastWriteTimeUtc != dt.UtcDateTime)
  {
    Console.WriteLine($"{file.FullName}, {file.LastWriteTimeUtc} => {dt.UtcDateTime}");
    file.LastWriteTimeUtc = dt.UtcDateTime;
  }
  else
  {
    Console.WriteLine($"{file.FullName} already updated.");
  }
}
Marcus Mangelsdorf
  • 2,852
  • 1
  • 30
  • 40
David Peters
  • 1,938
  • 1
  • 20
  • 18
6

Get the last modified timestamp

git log -1 --pretty="format:%ct" /path/to/repo/anyfile.any)
Jože Ws
  • 1,754
  • 17
  • 12
2

The crux of the question, though, is do you want to see the file's last modification timestamp, or do you want to change the file's timestamp to what you think it should be (eg, the last modified time)? From the question, the OP seems to wish to see the file's timestamp, not necessarily manipulate it.

TL;DR: Just run this, and git ls will show you last modified for all files in the pwd. NOTE: The $' is part of the command, so copy the whole line

$'!cd -- ${GIT_PREFIX:-.};\nfor i in *; \ndo \n  ls -ld $i | awk \'BEGIN { ORS="";} {print $1, $2, $3, $4; printf("%7s ",$5)}\'; \n  gd=$(git log -1 --date=local --format="%ad" -- "$i"); \n  [ -z "$gd" ] && gd=$(ls -ld "$i" | awk \'{print $6, $7, $8}\'); \n  if [ $(date -d "$gd" +%s) -le $(date -d "6 months ago" +%s) ] ; \n    then dfmt=\'%b %_d  %Y\'; \n    else dfmt=\'%b %_d %H:%M\'; \n  fi; \n  echo -n "$(date -d "$gd" +"$dfmt") "; \n  ls -d --color=always "$i"; \ndone'

For me, I just want to see the last modified date, not go mucking around with actual timestamps on the filesystem.

So, I bothered to write a bash script that gives me an ls-style output that displays the git repo's last modified date (ie, the author's timestamp) instead of the timestamp on disk. It's a rather rough cut, but it works the way I want it to.

cd -- ${GIT_PREFIX:-.};
for i in *; 
do 
  ls -ld $i | awk 'BEGIN { ORS="";} {print $1, $2, $3, $4; printf("%7s ",$5)}'; 
  gd=$(git log -1 --date=local --format="%ad" -- "$i"); 
  [ -z "$gd" ] && gd=$(ls -ld "$i" | awk '{print $6, $7, $8}'); 
  if [ $(date -d "$gd" +%s) -le $(date -d "6 months ago" +%s) ] ; 
    then dfmt='%b %_d  %Y'; 
    else dfmt='%b %_d %H:%M'; 
  fi; 
  echo -n "$(date -d "$gd" +"$dfmt") "; 
  ls -d --color=always "$i"; 
done

Save this file as, eg, your ~/scripts/git-ls. Assuming that's the path you use, then import in into your git config with

echo "git config --global alias.ls $(printf '%q' "`echo -n \!; cat ~/scripts/git-ls`")" | bash -

It builds the git config command, and then hands it off to a bash sub-shell to execute it.

Pros:

  • It displays all files - even ones not in the repo
  • It looks exactly like a standard ls output
  • It preserves the file colors I want
  • I can edit (and test) the script by running ~/scripts/git-ls directly
  • git help ls still shows the definition as a readable script

Cons:

  • It is inefficient; you see the output scroll
    • "It's not a bug - it's a feature!"... Well, it makes it obvious whether you ran git ls or just plain ls
  • It doesn't handle any arguments
    • Not file globs, but output can be piped to grep
    • Not any flags to ls
Randall
  • 2,859
  • 1
  • 21
  • 24
1

Git doesn't record the last modification date, only the commit/author dates for a all commit (which can include more than one file).

You would need to run a script in order to amend a commit with the last modification date of a particular file (not very useful if said commit has more than one file in it).
See an example at "Git: Change timestamp after pushing".

Another option would be to record those timestamp in a separate file, and amend your commit that way: see "What's the equivalent of use-commit-times for git?".

That includes:

  1. mtimestore - core script providing 3 options:
    • -a (save all - for initialization in already existing repo (works with git-versed files)),
    • -s (to save staged changes), and
    • -r to restore them.
  2. pre-commit hook
  3. post-checkout hook

Incidentally, this is the post where I explained 5 years ago why Git doesn't record timestamps.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Thank you for your answer. I will from here on out check the last change date first. If I do `git add .` and then `git status` I'll get all the files changed since last commit anyway. – HMR Mar 19 '14 at 13:22
1

Here's a version of Randall's answer that works for me on macOS 12.4 using zsh:

for i in *;
do
  ls -ld $i | awk 'BEGIN { ORS="";} {print $1, $2, $3, $4; printf("%7s ",$5)}';
  gd=$(git log -1 --date=local --format="%ad" -- "$i");
  if [ -z "$gd" ] ;
  then printf " --------------------------- $i\n";
  else
    dfmt="%a %b %d %T %Y";
    printf "$(date -j -f "$dfmt" "$gd" "%+%Y") ";
    ls -d --color=always "$i";
  fi;
done

Most of the differences are in the date commands, which seem to be different on Mac. And I also chose to print out dashes if the file has no date associated with it in git (I felt that was less confusing than using the local file system's date).

As Randall mentioned, you can easily add a git alias for this by saving this to a file like ~/scripts/git-ls and then running:

echo "git config --global alias.ls $(printf '%q' "`echo -n \!; cat ~/scripts/git-ls`")" | bash -
Logan
  • 1,575
  • 1
  • 17
  • 26
  • 1
    Yes, indeed Mac's implementation of `date` is *radically different* from Linux, and I hadn't taken that into consideration in my answer. Thanks for adding this in. I can't easily test this, but I'm going to vote it up anyway since it looks really through and presumably works for you :- ) – Randall Feb 01 '23 at 16:12