411

I'm converting everything over to Git for my own personal use and I found some old versions of a file already in the repository. How do I commit it to the history in the correct order according the file's "date modified" so I have an accurate history of the file?

I was told something like this would work:

git filter-branch --env-filter="GIT_AUTHOR_DATE=... --index-filter "git commit path/to/file --date " --tag-name-filter cat -- --all  
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
unknown
  • 4,129
  • 3
  • 16
  • 7
  • 2
    Short and simple answers : http://stackoverflow.com/a/34639957/2708266 – Yash May 20 '16 at 09:08
  • 85
    I wonder if people looking for the answer to this just want to keep their GitHub "contribution streak" continuous this way – ZitRo Jun 26 '17 at 19:49
  • 24
    @ZitRo yes. And simply setting `git commit --date="xxx day ago" -m "yyy"` is sufficient for that purpose if anyone is wondering. – Vlas Sokolov Jun 07 '20 at 15:47
  • 1
    https://alexpeattie.com/blog/working-with-dates-in-git/ : if looking for a gentle explanation – thepurpleowl Jun 10 '20 at 17:26
  • 18
    @ZitRo a better use of this is to make all of your commits on a personal project occur on the weekend. Just in case an employer wants to claim your work, CYA. – Bruno Bronosky Jul 05 '20 at 00:56
  • @VlasSokolov I just tried what you suggested, and it sets the correct date and time locally, however when I push to GitHub, it has the actual time of the commit – Rich Court Apr 07 '21 at 10:18
  • @RichCourt interesting, it worked for me before. Could it be they changed something on their end? – Vlas Sokolov Apr 08 '21 at 08:04
  • @VlasSokolov Yeah I guess they must have. As far as I can tell they now show the time the commit wash pushed. It _kind of_ makes sense, because that's the time it was committed to the master branch on origin. At least I assume that's their logic. Doesn't help people who want a specific time shown, and also isn't very useful if you create a bunch of commits without pushing for a while, or if you're trying to match `git log` with what you're seeing on GitHub. – Rich Court Apr 09 '21 at 10:20
  • @RichCourt just made a test of this, committing with `--date="1 day ago"` worked as I originally suggested for the purpose of GitHub "green squares" view. So the earlier advice still stands, I guess. – Vlas Sokolov Jul 14 '21 at 11:21
  • 1
    Just in case, a crude hack - you can also change your system time, commit and then change it back. – Bojan Krkic Oct 18 '21 at 22:37

15 Answers15

347

This is what worked for me:

git commit --date="10 day ago" -m "Your commit message" 
Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Hyder B.
  • 10,900
  • 5
  • 51
  • 60
  • 43
    This worked perfectly. I am surprized that `--date` also supports human-readable relative date format. – ZitRo May 18 '17 at 12:01
  • @ZitRo why not 10 days? – Alex78191 Jul 12 '18 at 11:30
  • 46
    Warning: `git --date` will only modify the $GIT_AUTHOR_DATE ... so depending on the circumstances you'll see the current date attached to the commit ($GIT_COMMITTER_DATE) – Guido U. Draheim Sep 13 '18 at 10:04
  • 11
    Tagging on to what @GuidoU.Draheim said. You can check a commit's full details using `git show --format=fuller`. There, you'll see the `AuthorDate` is the date you specified, but the `CommitDate` is the actual date of the commit. – d4nyll Sep 13 '18 at 15:22
  • 1
    So there is no way to change the CommitDate or to make a completely past dated commit? @d4nyll – Rahul Jan 28 '20 at 13:35
  • 7
    If it's the last commit, or a range of commits, then add `git rebase --committer-date-is-author-date ` where `` is the last commit/branch/tag/... that you don't want to change. It does what it says on the tin. Caveat emptor: obviously doesn't work if it's not the last commits on your branch. – Amedee Van Gasse Apr 23 '21 at 06:49
254

The advice you were given is flawed. Unconditionally setting GIT_AUTHOR_DATE in an --env-filter would rewrite the date of every commit. Also, it would be unusual to use git commit inside --index-filter.

You are dealing with multiple, independent problems here.

Specifying Dates Other Than “now”

Each commit has two dates: the author date and the committer date. You can override each by supplying values through the environment variables GIT_AUTHOR_DATE and GIT_COMMITTER_DATE for any command that writes a new commit. See “Date Formats” in git-commit(1) or the below:

Git internal format = <unix timestamp> <time zone offset>, e.g.  1112926393 +0200
RFC 2822            = e.g. Thu, 07 Apr 2005 22:13:13 +0200
ISO 8601            = e.g. 2005-04-07T22:13:13

The only command that writes a new commit during normal use is git commit. It also has a --date option that lets you directly specify the author date. Your anticipated usage includes git filter-branch --env-filter also uses the environment variables mentioned above (these are part of the “env” after which the option is named; see “Options” in git-filter-branch(1) and the underlying “plumbing” command git-commit-tree(1).

Inserting a File Into a Single ref History

If your repository is very simple (i.e. you only have a single branch, no tags), then you can probably use git rebase to do the work.

In the following commands, use the object name (SHA-1 hash) of the commit instead of “A”. Do not forget to use one of the “date override” methods when you run git commit.

---A---B---C---o---o---o   master

git checkout master
git checkout A~0
git add path/to/file
git commit --date='whenever'
git tag ,new-commit -m'delete me later'
git checkout -
git rebase --onto ,new-commit A
git tag -d ,new-commit

---A---N                      (was ",new-commit", but we delete the tag)
        \
         B'---C'---o---o---o   master

If you wanted to update A to include the new file (instead of creating a new commit where it was added), then use git commit --amend instead of git commit. The result would look like this:

---A'---B'---C'---o---o---o   master

The above works as long as you can name the commit that should be the parent of your new commit. If you actually want your new file to be added via a new root commit (no parents), then you need something a bit different:

B---C---o---o---o   master

git checkout master
git checkout --orphan new-root
git rm -rf .
git add path/to/file
GIT_AUTHOR_DATE='whenever' git commit
git checkout -
git rebase --root --onto new-root
git branch -d new-root

N                       (was new-root, but we deleted it)
 \
  B'---C'---o---o---o   master

git checkout --orphan is relatively new (Git 1.7.2), but there are other ways of doing the same thing that work on older versions of Git.

Inserting a File Into a Multi-ref History

If your repository is more complex (i.e. it has more than one ref (branches, tags, etc.)), then you will probably need to use git filter-branch. Before using git filter-branch, you should make a backup copy of your entire repository. A simple tar archive of your entire working tree (including the .git directory) is sufficient. git filter-branch does make backup refs, but it is often easier to recover from a not-quite-right filtering by just deleting your .git directory and restoring it from your backup.

Note: The examples below use the lower-level command git update-index --add instead of git add. You could use git add, but you would first need to copy the file from some external location to the expected path (--index-filter runs its command in a temporary GIT_WORK_TREE that is empty).

If you want your new file to be added to every existing commit, then you can do this:

new_file=$(git hash-object -w path/to/file)
git filter-branch \
  --index-filter \
    'git update-index --add --cacheinfo 100644 '"$new_file"' path/to/file' \
  --tag-name-filter cat \
  -- --all
git reset --hard

I do not really see any reason to change the dates of the existing commits with --env-filter 'GIT_AUTHOR_DATE=…'. If you did use it, you would have make it conditional so that it would rewrite the date for every commit.

If you want your new file to appear only in the commits after some existing commit (“A”), then you can do this:

file_path=path/to/file
before_commit=$(git rev-parse --verify A)
file_blob=$(git hash-object -w "$file_path")
git filter-branch \
  --index-filter '

    if x=$(git rev-list -1 "$GIT_COMMIT" --not '"$before_commit"') &&
       test -n "$x"; then
         git update-index --add --cacheinfo 100644 '"$file_blob $file_path"'
    fi

  ' \
  --tag-name-filter cat \
  -- --all
git reset --hard

If you want the file to be added via a new commit that is to be inserted into the middle of your history, then you will need to generate the new commit prior to using git filter-branch and add --parent-filter to git filter-branch:

file_path=path/to/file
before_commit=$(git rev-parse --verify A)

git checkout master
git checkout "$before_commit"
git add "$file_path"
git commit --date='whenever'
new_commit=$(git rev-parse --verify HEAD)
file_blob=$(git rev-parse --verify HEAD:"$file_path")
git checkout -

git filter-branch \
  --parent-filter "sed -e s/$before_commit/$new_commit/g" \
  --index-filter '

    if x=$(git rev-list -1 "$GIT_COMMIT" --not '"$new_commit"') &&
       test -n "$x"; then
         git update-index --add --cacheinfo 100644 '"$file_blob $file_path"'
    fi

  ' \
  --tag-name-filter cat \
  -- --all
git reset --hard

You could also arrange for the file to be first added in a new root commit: create your new root commit via the “orphan” method from the git rebase section (capture it in new_commit), use the unconditional --index-filter, and a --parent-filter like "sed -e \"s/^$/-p $new_commit/\"".

Jason Pyeron
  • 2,388
  • 1
  • 22
  • 31
Chris Johnsen
  • 214,407
  • 26
  • 209
  • 186
  • In the first example of the use case, "Inserting a File Into a Multi-ref History":Is there a way to have `--index-filter` applied to the commits returned by `git rev-list` ? At the moment I'm seeing index filter applied to a sub-set of rev-list. Appreciate any insight. – Hedgehog Dec 13 '10 at 21:53
  • @Hedgehog: All the “multi-ref” examples use `-- --all` to process all the commits reachable from any ref. The last example shows how to change only certain commits (just test GIT_COMMIT for whatever you like). To only change a specific list of commits, you could save the list before filtering (e.g. `git rev-list … >/tmp/commits_to_rewrite`), then test for membership inside the filter (e.g. `if grep -qF "$GIT_COMMIT" /tmp/commits_to_rewrite; then git update-index …`). What, exactly, are you trying to accomplish? You might want to start a new question if it is too much to explain in comments. – Chris Johnsen Dec 14 '10 at 02:55
  • In my case, I have a small project that I was meaning to place under source control. (I hadn't done so, because it's a solo project and I hadn't decided which system to use). My "source control" was just a matter of duplicating my entire project tree into a new directory, and renaming the previous-version directory. I'm new to Git (after previously using SCCS, RCS, and an ugly proprietary RCS derivative that resembles CVS), so don't worry about talking down to me. I get the impression that the answer involves a variation of the "Inserting a File Into a Single ref History" section. Correct? – Steve Jul 10 '14 at 20:29
  • 1
    @Steve: The “single-ref” scenario applies if your history was from a single, line of development (i.e. it would make sense if all the snapshots were viewed as successive points of a single, linear branch). The “multi-ref” scenario applies if you have (or have had) multiple branches in your history. Unless your history is complicated (“forked” versions for different clients, maintained a “bugfix” branch while new work happened back in “development”, etc.), then you are probably looking at a “single-ref” situation. *However*, it seems like your situation differs from that of the question… – Chris Johnsen Jul 11 '14 at 00:02
  • 1
    @Steve: Since you have a series of historical directory-snapshots—and you do not already have a Git repository—then you can probably just use [`import-directories.perl`](https://git.kernel.org/cgit/git/git.git/tree/contrib/fast-import/import-directories.perl) from Git’s `contrib/` (or `import-tars.perl`, or `import-zips.py`…) to create a new Git repository from your snapshots (even with “old” timestamps). The `rebase`/`filter-branch` techniques in my answer are only needed if you want to insert a file that was “left out” of an existing repository’s history. – Chris Johnsen Jul 11 '14 at 00:03
196

You can create the commit as usual, but when you commit, set the environment variables GIT_AUTHOR_DATE and GIT_COMMITTER_DATE to the appropriate datetimes.

Of course, this will make the commit at the tip of your branch (i.e., in front of the current HEAD commit). If you want to push it back farther in the repo, you have to get a bit fancy. Let's say you have this history:

o--o--o--o--o

And you want your new commit (marked as "X") to appear second:

o--X--o--o--o--o

The easiest way would be to branch from the first commit, add your new commit, then rebase all other commits on top of the new one. Like so:

$ git checkout -b new_commit $desired_parent_of_new_commit
$ git add new_file
$ GIT_AUTHOR_DATE='your date' GIT_COMMITTER_DATE='your date' git commit -m 'new (old) files'
$ git checkout master
$ git rebase new_commit
$ git branch -d new_commit
mipadi
  • 398,885
  • 90
  • 523
  • 479
  • One of the files would have to be the very first, in my case. – unknown Oct 14 '10 at 01:37
  • 37
    i always find this good answer, but then have to scurry off to find the date format, so here it is for next time 'Fri Jul 26 19:32:10 2013 -0400' – MeBigFatGuy Jul 27 '13 at 16:00
  • 139
    `GIT_AUTHOR_DATE='Fri Jul 26 19:32:10 2013 -0400' GIT_COMMITTER_DATE='Fri Jul 26 19:32:10 2013 -0400' git commit` – Xeoncross Nov 26 '13 at 17:02
  • 26
    There's more, for example the rather mnemonic `2013-07-26T19:32:10`: https://www.kernel.org/pub/software/scm/git/docs/git-commit.html#_date_formats – Marian Apr 27 '15 at 23:29
  • 5
    By the way, the '-0400' part speicfies the timezone offset. Be aware of selecting it properly...because if not, your time will change based on that. For example in the case of myself, who live in Chicago, i had to choose '-0600' (North America CST). You can find the codes in here: http://www.timeanddate.com/time/zones/ – evaldeslacasa Nov 04 '15 at 17:14
  • 32
    since the date needs to be repeated, I found it easier to create another variable: `THE_TIME='2019-03-30T8:20:00 -0500' GIT_AUTHOR_DATE=$THE_TIME GIT_COMMITTER_DATE=$THE_TIME git commit -m 'commit message here'` – Frank Henard Apr 05 '19 at 19:08
  • 2
    If you want to get the file time automatically: If you want to get the file time automatically: `THE_TIME=$(stat -c %y myfile)` – David Refoua Oct 06 '20 at 12:32
  • 2
    Since time zone is included it's better to use "git log" command then copy the date string and modify it. – Ali Sed May 14 '21 at 06:59
  • 1
    How do I insert my new commits before the first commit? – All The Rage Apr 23 '22 at 00:18
72

This is an old question but I recently stumbled upon it.

git commit --date='year-month-day hour:minutes:seconds' -m "message"

So it would look something like this: git commit --date='2021-01-01 12:12:00' -m "message" worked properly and verified it on GitHub and GitLab.

Maifee Ul Asad
  • 3,992
  • 6
  • 38
  • 86
Manav Mehra
  • 737
  • 5
  • 3
64

To make a commit that looks like it was done in the past you have to set both GIT_AUTHOR_DATE and GIT_COMMITTER_DATE:

GIT_AUTHOR_DATE=$(date -d'...') GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" git commit -m '...'

where date -d'...' can be exact date like 2019-01-01 12:00:00 or relative like 5 months ago 24 days ago.

To see both dates in git log use:

git log --pretty=fuller

This also works for merge commits:

GIT_AUTHOR_DATE=$(date -d'...') GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" git merge <branchname> --no-ff
mx0
  • 6,445
  • 12
  • 49
  • 54
  • 1
    Warning: Here `date` command gives date in local language and your command gives error for non-English users. So, whoever wants to use this, should set language to English in command-line e.g. `LC_TIME=en_US.utf8` – Celuk Apr 12 '22 at 11:00
45

In my case over time I had saved a bunch of versions of myfile as myfile_bak, myfile_old, myfile_2010, backups/myfile etc. I wanted to put myfile's history in git using their modification dates. So rename the oldest to myfile, git add myfile, then git commit --date=(modification date from ls -l) myfile, rename next oldest to myfile, another git commit with --date, repeat...

To automate this somewhat, you can use shell-foo to get the modification time of the file. I started with ls -l and cut, but stat(1) is more direct

git commit --date="`stat -c %y myfile`" myfile
skierpage
  • 2,514
  • 21
  • 19
  • 2
    Nice. I've certainly had files from before my _git days_ that I wanted to use the file modified time for. However, doesn't this only set the commit date (not the author date?) – Xeoncross Nov 26 '13 at 16:04
  • 1
    From git-scm.com/docs/git-commit: `--date` "Override the author date used in the commit." `git log`'s Date seems to be the AuthorDate, `git log --pretty=fuller` shows both AuthorDate and CommitDate. – skierpage Feb 20 '15 at 20:18
  • 24
    The `git commit` option `--date` will only modify the `GIT_AUTHOR_DATE`, not `GIT_COMMITTER_DATE`. As the [Pro Git Book](http://progit.org/book/ch2-3.html) explains: _"The author is the person who originally wrote the work, whereas the committer is the person who last applied the work."_ In the context of dates, the `GIT_AUTHOR_DATE` is the date the file was changed, whereas the `GIT_COMMITTER_DATE` is the date it was committed. It is important here to note that by default, `git log` displays author dates as “Date” but then uses commit dates for filtering when given a `--since` option. – Christopher Aug 25 '15 at 05:20
  • There is no -c option for stat in OS X 10.11.1 – thinsoldier Feb 19 '16 at 01:24
  • 1
    The equivalent for `stat -c %y` in macOS (and other BSD variants) is `stat -f %m`. – victorlin Jun 02 '19 at 22:29
  • There are multiple answers already, I will just note that if you are familiar with JS and want to manipulate your Github graph and "draw" something specific there you may check the npm module: https://www.npmjs.com/package/@halkin/github-activity – AlexHalkin Mar 22 '23 at 13:49
43

The following is what I use to commit changes on foo to N=1 days in the past:

git add foo
git commit -m "Update foo"
git commit --amend --date="$(date -v-1d)"

If you want to commit to a even older date, say 3 days back, just change the date argument: date -v-3d.

That's really useful when you forget to commit something yesterday, for instance.

UPDATE: --date also accepts expressions like --date "3 days ago" or even --date "yesterday". So we can reduce it to one line command:

git add foo ; git commit --date "yesterday" -m "Update"
Vilson Vieira
  • 717
  • 8
  • 7
  • 12
    Warning: `git --date` will only modify the $GIT_AUTHOR_DATE ... so depending on the circumstances you'll see the current date attached to the commit ($GIT_COMMITTER_DATE) – Guido U. Draheim Sep 13 '18 at 10:03
  • mixing 2 approaches together makes almost perfect fit: `git commit --amend --date="$(stat -c %y fileToCopyMTimeFrom)"` – andrej Apr 05 '20 at 11:18
  • Awesome! Same command with a human readable date `git commit --amend --date="$(date -R -d '2020-06-15 16:31')"` – pixelbrackets Jun 17 '20 at 14:28
22

In my case, while using the --date option, my git process crashed. May be I did something terrible. And as a result some index.lock file appeared. So I manually deleted the .lock files from .git folder and executed, for all modified files to be commited in passed dates and it worked this time. Thanx for all the answers here.

git commit --date="`date --date='2 day ago'`" -am "update"
JstRoRR
  • 3,693
  • 2
  • 19
  • 20
  • 5
    See [comment above on `git commit --date`](http://stackoverflow.com/questions/3895453/how-do-i-make-a-git-commit-in-the-past#comment52277002_9778940). Also, your example commit message should be amended to discourage poor one-line messages like these @JstRoRR – Christopher Aug 25 '15 at 05:29
  • 4
    Warning: `git --date` will only modify the $GIT_AUTHOR_DATE ... so depending on the circumstances you'll see the current date attached to the commit ($GIT_COMMITTER_DATE) – Guido U. Draheim Sep 13 '18 at 10:02
  • Don't use backtick for a command substitution https://stackoverflow.com/a/22709390/446792 – mx0 Feb 24 '23 at 08:35
11
  1. git --date changes only GIT_AUTHOR_DATE but many git apps, e.g., GitHub shows GIT_COMMITTER_DATE. Make sure to change GIT_COMMITTER_DATE too.
  2. git does not aware time zone in default UNIX date output. Make sure to format the datetime format in a compatible format such as ISO8601.

Complete example in OS X (Change both GIT_COMMITTER_DATE and GIT_AUTHOR_DATE to 4 hours ago):

x=$(date -v -4H +%Y-%m-%dT%H:%M:%S%z); export GIT_COMMITTER_DATE=$x; git commit --amend --date $x
petertc
  • 3,607
  • 1
  • 31
  • 36
10

The simple answer you are looking for:

GIT_AUTHOR_DATE="2020-10-24T18:00:00 +0200" GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE git commit

Mind the timezone string and set a proper one for your timezone. i.e. +0200, -0400

Maciej Krawczyk
  • 14,825
  • 5
  • 55
  • 67
7

Or just use a fake-git-history to generate it for a specific data range.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
rel1x
  • 2,351
  • 4
  • 34
  • 62
4

Pre-Step.

  • Pull all data from the remote to the local repository.

  • we are using the --amend and --date switches.

The exact command is as follows:

$ git commit --amend --date="YYYY-MM-DD HH:MM:SS"

3

You can always change a date on your computer, make a commit, then change the date back and push.

Nik
  • 9,063
  • 7
  • 66
  • 81
2
git commit --date='year-month-day hour:minutes:seconds' -m "message"

git push 
Muhammet
  • 320
  • 1
  • 10
2

So if you want to commit something on Git in the past date, you simply use these commands that help you to do so.

git commit --amend --no-edit --date="Sat Jun 5 20:00:00 2021 -0600"
  1. To make a commit in the past date, you just want to add your changes in the local repository by running git add .
  2. Commit changes using git commit -m "Your commit message".
  3. Run this git commit --amend --no-edit --date="Sat Jun 5 20:00:00 2021 -0600" command after a commit to amend the last commit with the timestamp noted. The --no-edit will leave the message as-is.
  4. Push changes to GitHub or whichever platform you use by running git push.

Make sure you have to change the date and time according to your preference. This will create a commit to the particular date in the past and you do not lose your GitHub streak.

akshay_sushir
  • 1,483
  • 11
  • 9
  • In case you want to create some specific set of commits for your Github graph - take a look at this module https://www.npmjs.com/package/@halkin/github-activity – AlexHalkin Mar 22 '23 at 13:47