Unix timestamps are signed 32 bit integers (64 bit on some systems today, or so I understand). On some software products this allows you to use dates going back as far as 1903 or so.
However, when I try the following:
git commit -m "this is a test commit" --date="1960-04-07 18:00:00"
I receive an "fatal: invalid date format" error.
This isn't very practical (I'm not a time-traveler), but I've been wondering about using git for historical purposes. Can this be forced with a git plumbing command? On a related note: does git always use a 32 bit timestamp, or does this depend on the environment it's built on?
As far back as commit c64b9b8 (git 0.99, May 2005), time has always been referred as
<date>
Date in 'seconds since epoch'
commit 6eb8ae0 defines date as an unsigned long
since April 2005.
TLDR;
date before unix epoch can be stored, but cannot be sure to be correctly represented.
Trying to represent a date before has been attempted before: see this thread
The date I'm trying to set is October 4, 1958, that is around timestamp -354808800.
First technique, using the commit --date
flags with ISO 8601: it says
"invalid date
".
Second technique, described in the git ml archive, not using the porcelain:
git commit
git cat-file -p HEAD > tmp.txt
# at this point, edit the file to replace the timestamp
git hash-object -t commit -w tmp.txt
#=> 2ee8fcc02658e23219143f5bcfe6f9a4615745f9
git update-ref -m 'commit: foo' refs/heads/master \
2ee8fcc02658e23219143f5bcfe6f9a4615745f9
Commit date is effectively updated, but git show
clamps the date to zero (Jan 1 1970
). tig(1)
displays 55 years ago
so the actual commit date is properly stored.
Last issue: when trying to push this commit to a remote repository:
#=> remote: error: object 2ee8fcc02658e23219143f5bcfe6f9a4615745f9:invalid
# author/committer line - bad date
#=> remote: fatal: Error in object
#=> error: unpack failed: index-pack abnormal exit
Finally, when running test-date
from git sources:
./test-date show -354808800
#=> -354808800 -> in the future
The discussion at the time mentioned:
I am not sure there isn't some unportability at the lowest level.
We freely interchange between time_t and unsigned long in the low-level date code. It probably happens to work because casting the bits back and forth between signed and unsigned types generally works, as long as you end up with the type that you want.
But it isn't necessarily portable, and there can be subtle bugs. See, for example, my recent 9ba0f033.
The good news that this is purely a code problem. The data format is fine. It would just take somebody going through the code and switching all "unsigned long
" to "long long
" (or time_t
, or even "gittime_t
" if we want to abstract it).
and fixing the parser algorithm at least in tm_to_time_t()
It is not just "sed s/unsigned long/long long
", but rather checking every change to make sure that you aren't introducing new bugs, and that the code correctly handles signed
types.
Which is why nobody has done it. ;)
Git internally maintains dates as a Unix timestamp and an offset from UTC, so it is not possible to set a commit date before the Unix time epoch.
You must be running a different version of Git than I have on my CentOS 6.5 system (which provides Git 1.7.1), at least yours gives you an error message... If I try to set an impossible commit date using Git 1.7.1, the commit succeeds and silently uses the current time for the commit date; if I try the same operation using a "possible" commit date, it succeeds and records the intended commit date.
You could store them, but nothing would show them like you expected (yet).
The internal Git format stores the commit data as a numeric string. e.g.:
$ git cat-file -p aa2706463fdeb51d6f9d0e267113b251888cf7f5
...
author Junio C Hamano <gitster@pobox.com> 1383318892 -0700
committer Junio C Hamano <gitster@pobox.com> 1383318892 -0700
I don't believe that it's required that this number be positive. However, most implementations, including git
, parse it as an unsigned number and won't display those dates correctly. For example, in builtin/blame.c
:
*time = strtoul(ident.date_begin, NULL, 10);
So; using negative times isn't something you can easily do with current tools, or display with current tools, but Git's model means that commits including them would survive unchanged in a repository.
I just twiddled around with git hash-object and created the following commit object:
tree 5efb9bc29c482e023e40e0a2b3b7e49cec842034
author x <x@x.com> -134607600 -0500
committer z <z@z.com> 1402404632 -0600
blah blah
You'll note that the author date is set to a negative number. I then used git update-ref to try to link the commit object in... no luck, I get the following output when I do a git log:
$ git log
commit 2303e7012001a3cc1c3dec806d0902008e1257a8
Author: x <x@x.com>
Date: (null)
blah blah
Sourcetree is similarly confused:
Parents:
Author: x <x@x.com>
Date: Monday, January 01, 0001 12:00:00 AM
Committer: z <z@z.com>
Commit Date: Tuesday, June 10, 2014 7:50:32 AM
(I think I forgot to include the parent commit object...)
Similarly, dates in the far future do not work either (indicating that it probably isn't treating this as a 64 bit value even if it is one).
I'm not sure if this answer qualifies as a "no you cannot do this" or a "you can, but it doesn't work well". One would need an modified client to be able to see the git logs with correct dates, removing any possible benefit for this. If someone knows a nifty git log command that would parse those timestamps correctly (are they gone at that point?!?!) please correct me.