Как можно изменить временную метку старого коммита в Git?

747

Ответы на вопрос Как изменить существующие неопубликованные коммиты? опишите способ изменения предыдущих сообщений коммита, которые еще не были переданы в апстрим. Новые сообщения наследуют временные метки оригинальных коммитов. Это кажется логичным, но есть ли способ также переустановить время?

Dhskjlkakdh
источник
34
git commit --amend --reset-author
Эрик М. Шпренгель

Ответы:

536

Используйте git filter-branchс фильтром env, который устанавливает GIT_AUTHOR_DATEи GIT_COMMITTER_DATEдля конкретного хэша коммита, который вы хотите исправить.

Это аннулирует это и все будущие хэши.

Пример:

Если вы хотите изменить даты коммита 119f9ecf58069b265ab22f1f97d2b648faf932e0, вы можете сделать это примерно так:

git filter-branch --env-filter \
    'if [ $GIT_COMMIT = 119f9ecf58069b265ab22f1f97d2b648faf932e0 ]
     then
         export GIT_AUTHOR_DATE="Fri Jan 2 21:38:53 2009 -0800"
         export GIT_COMMITTER_DATE="Sat May 19 01:01:01 2007 -0700"
     fi'
Dustin
источник
4
См. «ФОРМАТЫ ДАТЫ» kernel.org/pub/software/scm/git/docs/git-commit.html
Дастин
8
Это нашло правильное значение, но просто установка этих переменных фактически не влияла на дату старого коммита.
IQAndreas
36
Что вы подразумеваете под «Это аннулирует это и все будущие хеши»?
EpicDavi
16
EpicDavi: Это означает, что вам придется принудительно отправлять данные в любой удаленный репозиторий, и любой, кто получит коммит или любые будущие коммиты, должен будет сбросить и вытащить, или удалить и клонировать с нуля. Насколько я знаю, нет способа, который бы обошел это.
EriF89
4
Так же, как примечание для начинающих, короткий хеш не работает в операторе if, используйте длинные SHA-1
40 детективов
780

Вы можете сделать интерактивную перебазировку и выбрать редактировать для коммита, дату которого вы хотели бы изменить. Когда процесс rebase останавливается для внесения изменений в коммит, который вы вводите, например:

git commit --amend --date="Wed Feb 16 14:00 2011 +0100"

После этого вы продолжаете интерактивный ребаз.

ОБНОВЛЕНИЕ (в ответ на комментарий Studgeek): чтобы изменить дату фиксации вместо даты автора:

GIT_COMMITTER_DATE="Wed Feb 16 14:00 2011 +0100" git commit --amend

Строки выше устанавливают переменную окружения GIT_COMMITTER_DATE, которая используется в коммите на изменение.

Все проверено в Git Bash.

Пол Пладийс
источник
22
@nschum --date = "" и --data "non-date-text" все выдают одно и то же, принимая дату сейчас.
Пол Пладийс
12
в git версии 1.7.7.1 использование --date = "now" приводит к фатальному: недопустимый формат даты: сейчас
Арагорн
4
Когда коммит, дата которого вы хотите изменить, является самым последним коммитом, вам не нужно делать rebase, вы можете просто сделатьgit commit --amend
Eponymous
7
Вместо экспорта GIT_COMMITTER_DATE = "", попробуйте сбросить GIT_COMMITTER_DATE.
Марк Э. Хаас
2
Я использую --no-edit, чтобы вы могли использовать в автоматизированных сценариях! + var fixedDate = strftime(new Date(), "%c"); + var result = shelljs.exec("git commit --amend --date=\"" + fixedDate + "\" --no-edit");
Марчелло де Сэйлс
392

Лучший способ обработать все эти предложения в одной команде:

LC_ALL=C GIT_COMMITTER_DATE="$(date)" git commit --amend --no-edit --date "$(date)"

Это установит дату последнего коммита и дату автора как "прямо сейчас".

Люк Эресман
источник
22
Это прекрасно работает для редактирования определенных коммитов во время интерактивной перебазировки.
Friederbluemle
2
Вы также можете добавить псевдоним к оболочке
kaleissin
14
Кажется, что Git не знает языковой формат даты, поэтому, чтобы быть полностью правильным, вам нужно сделать это примерно так:LANG= GIT_COMMITTER_DATE="`date`" git commit --amend --date "`date`"
Михал Гурал
Я делаю это, когда я перебазирую и сдавливаю ветку, чтобы она создала один коммит с обновленной отметкой времени.
Люк Эресман
12
Вы также можете просто сделать --date "now". Git> = 2 будет интерпретировать это.
Wisbucky
189

Просто делай git commit --amend --reset-author --no-edit. Для старых коммитов вы можете сделать интерактивную перебазировку и выбрать editкоммит, дату которого вы хотите изменить.

git rebase -i <ref>

Затем измените коммит --reset-authorи --no-editизмените дату автора на текущую дату:

git commit --amend --reset-author --no-edit

Наконец, продолжите интерактивную ребазу:

git rebase --continue
Gypaetus
источник
5
хороший призыв к использованию --reset-author, это новое в git 1.6.6 (ссылка gitlog.wordpress.com/2010/01/13/git-1-6-6 )
Тим Абелл
1
Это прекрасно работает, чтобы Github показывал коммиты перебазированного PR в правильном порядке, поскольку они упорядочивают их по меткам времени, и без этого трюка все метки времени могут быть одинаковыми.
Натан Лонг
4
Примечание --reset-authorбудет сбрасывать как Автор, так и Дата автора до настоящего времени.
Wisbucky
изменит ли это «ДАТУ КОМИТЕРА» одновременно?
luochen1990
134

Я написал скрипт и пакет Homebrew для этого. Супер прост в установке, вы можете найти его на PotatoLabs/git-redateстранице GitHub .

Синтаксис:

git redate -c 3

Вам просто нужно запустить, git redateи вы сможете редактировать все даты в vim из последних 5 коммитов (также есть -cопция для того, сколько коммитов вы хотите вернуть, по умолчанию это 5). Дайте мне знать, если у вас есть какие-либо вопросы, комментарии или предложения!

введите описание изображения здесь

bigpotato
источник
2
Отличный материал, хотя мне пришлось использовать vim, а не nano
howdoyouturnthison
Спасибо @Edmund за отличный сценарий. Я не смог увидеть дату для редактирования в vi после запуска git redate -c. Я вижу только% cI | XXXXXXXXXXXXXXXX | Первоначальный коммит. Не могли бы вы помочь? Спасибо
Kiem Nguyen
@KiemNguyen не могли бы вы попробовать просто git redate (без -c)?
bigpotato
4
полностью согласен с Миной и @howdoyouturnthison здесь, почему бы вам не сделать его независимым от редактора через переменную окружения EDITOR? (также я на Linux, а не Mac ...)
ympostor
3
Спасибо @ Эдмунд! На всякий случай у вашего скрипта есть проблема с обработкой значения по умолчанию для COMMITS. Если он не установлен, следующий код применяет фильтры только для (я думаю / нашел) последнего коммита. "git filter-branch -f --env-filter" $ ENVFILTER "HEAD ~ $ COMMITS..HEAD> / dev / null"
Григорий Энтин
104

Каждый коммит связан с двумя датами, датой коммиттера и датой автора. Вы можете просмотреть эти даты с:

git log --format=fuller

Если вы хотите изменить дату автора и дату принятия последних 6 коммитов, вы можете просто использовать интерактивное перебазирование:

git rebase -i HEAD~6

,

pick c95a4b7 Modification 1
pick 1bc0b44 Modification 2
pick de19ad3 Modification 3
pick c110e7e Modification 4
pick 342256c Modification 5
pick 5108205 Modification 6

# Rebase eadedca..5108205 onto eadedca (6 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit

Для всех фиксаций , где вы хотите , чтобы изменить дату, заменить pickна edit(или просто e), а затем сохранить и выйти из редактора.

Теперь вы можете изменить каждый коммит, указав дату автора и дату коммиттера в формате ISO-8601:

GIT_COMMITTER_DATE="2017-10-08T09:51:07" git commit --amend --date="2017-10-08T09:51:07"

Первая дата - это дата коммита, вторая - дата автора.

Затем перейдите к следующему коммиту с:

git rebase --continue

Повторяйте процесс, пока вы не исправите все свои коммиты. Проверьте вашу прогрессию с git status.

Ортомала Локни
источник
1
Я последовал за этим и оказался на «оторванной голове»!
Саймон Х,
1
@Simon H Я снова проверил мой ответ, и он работает хорошо. Вы должны были напечатать что-то другое или вы уже были в отстраненной голове. Если вы хотите вернуться с отстраненной головы, сделайте git checkout name-of-current-branch.
Ортомала Локни
4
Это самый лучший и самый простой ответ. Небольшой совет: использовать --no-edit в git commit --amend --no-edit --date=2017-10-08T09:51:07держать старое сообщение коммита.
Мариуш Павельски
2
Вы также можете обновление , GIT_COMMITTER_DATE как описано здесь eddmann.com/posts/...
smihael
2
@smihael Спасибо за ссылку. Я включил ваше предложение в мой ответ.
Ортомала Локни
62
git commit --amend --date="now"
Харальд Нордгрен
источник
Коротко и точно. Именно то, что мне было нужно. Спасибо!
typoerrpr
44

Основываясь на theosp «s ответ , я написал скрипт git-cdc(для изменения даты совершения) , что я положил мне PATH.

Имя важно: в git-xxxлюбом месте PATHвы можете ввести:

git xxx
# here
git cdc ... 

Этот скрипт находится в bash, даже в Windows (поскольку Git будет вызывать его из среды msys )

#!/bin/bash
# commit
# date YYYY-mm-dd HH:MM:SS

commit="$1" datecal="$2"
temp_branch="temp-rebasing-branch"
current_branch="$(git rev-parse --abbrev-ref HEAD)"

date_timestamp=$(date -d "$datecal" +%s)
date_r=$(date -R -d "$datecal")

if [[ -z "$commit" ]]; then
    exit 0
fi

git checkout -b "$temp_branch" "$commit"
GIT_COMMITTER_DATE="$date_timestamp" GIT_AUTHOR_DATE="$date_timestamp" git commit --amend --no-edit --date "$date_r"
git checkout "$current_branch"
git rebase  --autostash --committer-date-is-author-date "$commit" --onto "$temp_branch"
git branch -d "$temp_branch"

С этим вы можете ввести:

git cdc @~ "2014-07-04 20:32:45"

Это сбрасывает дату авторизации / коммита коммита до HEAD ( @~) на указанную дату.

git cdc @~ "2 days ago"

Это сбрасывает дату авторизации / фиксации комита до HEAD ( @~) в тот же час, но 2 дня назад.


Илья Семенов упоминает в комментариях :

Для OS X вы также можете установить GNU coreutils( brew install coreutils), добавить его в PATH( PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH") и затем использовать 2 days agoсинтаксис " ".

VonC
источник
1
Для меня это работало только с указанием даты и времени в одной цитате: в git cdc @~ "2014-07-04 20:32:45противном случае он не распознал бы время и, следовательно, получил бы время 00:00:00 (это становится третьим аргументом).
Песчу
3
Для OS X вы также можете установить GNU coreutils ( brew install coreutils), добавить его в PATH ( PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH") и затем использовать синтаксис «2 дня назад».
Илья Семенов
1
@IlyaSemenov Интересно. Я включил ваш комментарий в ответ для большей наглядности.
VonC
Я пытаюсь использовать ваш первый пример, но получаю «фатальный: недопустимый формат даты:». Какой формат даты ожидает Mac OS X?
usbsnowcrash
@usbsnowcrash не уверен на Mac. Второй пример " 2 days ago" работает?
VonC
25

Как редактировать несколько дат коммитов

Другие ответы не очень удобны для редактирования нескольких дат фиксации. Я вернулся к этому вопросу через несколько лет, чтобы поделиться техникой.

Чтобы изменить даты последних 4 коммитов:

git rebase -i HEAD~4

Отредактируйте ребазу следующим образом, вставив execстроки, чтобы изменить даты по мере необходимости:

pick 4ca564e Do something
exec git commit --amend --no-edit --date "1 Oct 2019 12:00:00 PDT"
pick 1670583 Add another thing
exec git commit --amend --no-edit --date "2 Oct 2019 12:00:00 PDT"
pick b54021c Add some tests
exec git commit --amend --no-edit --date "3 Oct 2019 12:00:00 PDT"
pick e8f6653 Fix the broken thing
exec git commit --amend --no-edit --date "4 Oct 2019 12:00:00 PDT"
Мэтт Монтэг
источник
Хорошее использование опции --amend/ --date. Проще, чем мой собственный ответ, используя переменные среды. Upvoted.
VonC
Можно ли использовать текущую дату / время в качестве параметра?
accfews
Это GIT_AUTHOR_DATEтолько обновления .
Блейз
Число рейнольдса «Можно ли использовать текущую дату / время в качестве параметра?»: «Сейчас» понимается как действительная дата, поэтому приведенные выше строки exec станутexec git commit --amend --no-edit --date "now"
Эндрю Ричардс
20

если это предыдущий последний коммит.

git rebase  -i HEAD~2
git commit --amend --date=now

если вы уже нажали на orgin и можете принудительно использовать:

git push --force 

если вы не можете форсировать толчок и если он толкается, вы не можете изменить коммит! ,

Сержио
источник
18

Вот удобный псевдоним, который изменяет время фиксации и автора последнего коммита на время, принятое date --date:

[alias]
    cd = "!d=\"$(date -d \"$1\")\" && shift && GIT_COMMITTER_DATE=\"$d\" \
            git commit --amend --date \"$d\""

Применение: git cd <date_arg>

Примеры:

git cd now  # update the last commit time to current time
git cd '1 hour ago'  # set time to 1 hour ago

Редактировать: вот более автоматизированная версия, которая проверяет, что индекс является чистым (без незафиксированных изменений) и повторно использует последнее сообщение о коммите, или дает сбой в противном случае (защита от ошибок):

[alias]
    cd = "!d=\"$(date -d \"$1\")\" && shift && \
        git diff-index --cached --quiet HEAD --ignore-submodules -- && \
        GIT_COMMITTER_DATE=\"$d\" git commit --amend -C HEAD --date \"$d\"" \
        || echo >&2 "error: date change failed: index not clean!"
eold
источник
17

Я создал этот пакет npm, чтобы изменить дату старых коммитов.

https://github.com/bitriddler/git-change-date

Пример использования:

npm install -g git-change-date
cd [your-directory]
git-change-date

Вам будет предложено выбрать коммит, который вы хотите изменить, а затем ввести новую дату.

Если вы хотите изменить коммит по конкретному хешу, запустите git-change-date --hash=[hash]

Карим Эльбахравы
источник
Я просто хотел сказать, что это здорово и прекрасно работает. Спасибо, вы сэкономили мне много времени!
Паранза
17

Следующая функция bash изменит время любого коммита в текущей ветви.

Будьте осторожны, чтобы не использовать, если вы уже выдвинули коммит или если вы используете коммит в другой ветке.

# rewrite_commit_date(commit, date_timestamp)
#
# !! Commit has to be on the current branch, and only on the current branch !!
# 
# Usage example:
#
# 1. Set commit 0c935403 date to now:
#
#   rewrite_commit_date 0c935403
#
# 2. Set commit 0c935403 date to 1402221655:
#
#   rewrite_commit_date 0c935403 1402221655
#
rewrite_commit_date () {
    local commit="$1" date_timestamp="$2"
    local date temp_branch="temp-rebasing-branch"
    local current_branch="$(git rev-parse --abbrev-ref HEAD)"

    if [[ -z "$date_timestamp" ]]; then
        date="$(date -R)"
    else
        date="$(date -R --date "@$date_timestamp")"
    fi

    git checkout -b "$temp_branch" "$commit"
    GIT_COMMITTER_DATE="$date" git commit --amend --date "$date"
    git checkout "$current_branch"
    git rebase "$commit" --onto "$temp_branch"
    git branch -d "$temp_branch"
}
theosp
источник
1
У вас есть ошибка: if [[ -z "$commit" ]]->if [[ -z "$date_timestamp" ]]
blueFast
Ницца! Я бы порекомендовал установить GIT_COMMITTER_DATE=в конце метода, чтобы предотвратить дальнейшие фиксации вручную, чтобы сохранить указанную дату.
петля
@loopkin, GIT_COMMITTER_DATE устанавливается только для команды «git commit», поэтому нет необходимости очищать ее впоследствии
nimrodm
@nimrodm, я только что проверил снова, и вы правы. Спасибо что подметил это.
петля
12

Чтобы изменить дату автора и дату принятия:

GIT_COMMITTER_DATE="Wed Sep 23 9:40 2015 +0200" git commit --amend --date "Wed Sep 23 9:40 2015 +0200"
Ян Н
источник
10

Если вы хотите получить точную дату другого коммита (скажем, вы перебазировали отредактированный коммит и хотите, чтобы в нем была дата оригинальной версии перед перебазой):

git commit --amend --date="$(git show -s --format=%ai a383243)"

Это исправляет дату фиксации HEAD, которая в точности совпадает с датой фиксации a383243 (включайте больше цифр, если есть неоднозначности). Также появится окно редактора, чтобы вы могли редактировать сообщение о коммите.

Это дата автора, о которой вы обычно заботитесь - см. Другие ответы на дату коммиттера.

Mr_and_Mrs_D
источник
7

Если вы хотите выполнить принятый ответ ( https://stackoverflow.com/a/454750/72809 ) в стандартной командной строке Windows, вам потребуется следующая команда:

git filter-branch -f --env-filter "if [ $GIT_COMMIT = 578e6a450ff5318981367fe1f6f2390ce60ee045 ]; then export GIT_AUTHOR_DATE='2009-10-16T16:00+03:00'; export GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE; fi"

Ноты:

  • Может быть возможно разделить команду на несколько строк (Windows поддерживает разделение строк символом carret ^), но мне это не удалось.
  • Вы можете написать даты ISO, сэкономив много времени на поиске правильного дня недели и общего разочарования по поводу порядка элементов.
  • Если вы хотите, чтобы дата Author и Committer была одинаковой, вы можете просто сослаться на ранее установленную переменную.

Большое спасибо в блоге Колина Свингена . Хотя его код не работал для меня, он помог мне найти правильное решение.

Питер
источник
7

Если коммит еще не выдвинут, я могу использовать что-то вроде этого:

git commit --amend --date=" Wed Mar 25 10:05:44 2020 +0300"

после этого git bash открывает редактор с уже примененной датой, поэтому вам нужно просто сохранить его, набрав в командном режиме редактора VI ": wq", и вы можете нажать его

Алекс Кумарав
источник
2
Просто добавьте к хорошему ответу: если вы не хотите редактировать сообщение о коммите (если вы просто хотите изменить дату фиксации), используйте --no-editопцию.
Антонио Виниций Менезес Медей
Кроме того, если фиксация уже была отправлена, вы все равно можете git push -fотправить измененную фиксацию с помощью (принудительное обновление). Это может иметь побочные эффекты, хотя. (особенно если у многих людей есть локальные клоны хранилища)
Антонио Виниций Менезес Медей
2

Уже есть много хороших ответов, но когда я хочу изменить дату для нескольких коммитов в один день или в один месяц, я не могу найти правильный ответ. Поэтому я создаю новый сценарий для этого с объяснением, надеюсь, он кому-нибудь поможет:

#!/bin/bash

# change GIT_AUTHOR_DATE for commit at Thu Sep 14 13:39:41 2017 +0800
# you can change the data_match to change all commits at any date, one day or one month
# you can also do the same for GIT_COMMITTER_DATE

git filter-branch --force --env-filter '

date_match="^Thu, 14 Sep 2017 13+"              

# GIT_AUTHOR_DATE will be @1505367581 +0800, Git internal format 
author_data=$GIT_AUTHOR_DATE;                   
author_data=${author_data#@}                  
author_data=${author_data% +0800}                # author_data is 1505367581     

oneday=$((24*60*60))

# author_data_str will be "Thu, 14 Sep 2017 13:39:41 +0800", RFC2822 format
author_data_str=`date -R -d @$author_data`      

if [[ $author_data_str =~ $date_match ]];
then
    # remove one day from author_data
    new_data_sec=$(($author_data-$oneday))
    # change to git internal format based on new_data_sec
    new_data="@$new_data_sec +0800"             
    export GIT_AUTHOR_DATE="$new_data"
fi
' --tag-name-filter cat -- --branches --tags

Дата будет изменена:

AuthorDate: Wed Sep 13 13:39:41 2017 +0800
detective0922
источник