git rebase без изменения отметок времени коммита

157

Имеет ли смысл выполнять git rebase, сохраняя отметки времени коммита?

Я считаю, что следствием этого будет то, что новая ветвь не обязательно будет иметь даты фиксации в хронологическом порядке. Это теоретически возможно вообще? (например, используя сантехнические команды; просто любопытно здесь)

Если это теоретически возможно, то возможно ли на практике с перебазированием не изменять временные метки?

Например, предположим, у меня есть следующее дерево:

master <jun 2010>
  |
  :
  :
  :     oldbranch <feb 1984>
  :     /
oldcommit <jan 1984>

Теперь, если я перебазироваться oldbranchна master, дата фиксации изменений с февраля 1984 по июнь 2010 Можно ли изменить это поведение так , что фиксация метки времени не меняется? В итоге я бы получил:

      oldbranch <feb 1984>
      /
 master <jun 2010>
    |
    :

Будет ли это иметь смысл вообще? Разрешено ли даже в git иметь историю, в которой старый коммит имеет более свежий коммит в качестве родителя?

Оливье Вердиер
источник
3
Забавно, что ответом на этот вопрос действительно является то, что «вам ничего не нужно делать - вот как это работает по умолчанию». Но теперь предположим, что вы хотите, чтобы коммит был отсортирован в правильном порядке дат при выполнении ребазирования (что вполне естественно, если подумать). Теперь я не смог найти, как этого добиться, и опубликовал свой q как stackoverflow.com/questions/12270357/really-flatten-a-git-merge
pfalcon
1
Давид упоминает еще один вариант для сброса зафиксировавшего даты: git rebase --committer-date-is-author-date SHA. Смотрите мой отредактированный ответ ниже
VonC
Я просто написал обширный ответ на похожий вопрос , автор которого попробовал ответы, объясненные здесь, и не смог применить их удовлетворительным образом.
Аксиак

Ответы:

149

Обновление, июнь 2014 года: Дэвид Фрейзер упоминает в комментариях решение, подробно описанное в разделе « Изменение временных меток при перебазировании ветки git », с использованием параметра --committer-date-is-author-date(впервые представлен в январе 2009 года в коммите 3f01ad6.

Обратите внимание, что эта --committer-date-is-author-dateопция, похоже, оставляет метку времени автора и устанавливает метку времени коммиттера такой же, как и оригинальная метка времени автора, чего и хотел OP Olivier Verdier .

Я нашел последний коммит с правильной датой и сделал:

git rebase --committer-date-is-author-date SHA

Смотрите git am:

--committer-date-is-author-date

По умолчанию команда записывает дату из сообщения электронной почты в качестве даты автора коммита и использует время создания коммита в качестве даты коммиттера.
Это позволяет пользователю лгать о дате коммиттера, используя то же значение, что и дата автора .


(Оригинальный ответ, июнь 2012 г.)

Вы можете попробовать, для неинтерактивного перебазирования

git rebase --ignore-date

(из этого ТАКОГО ответа )

Это передается git am, который упоминает:

 --ignore-date

По умолчанию команда записывает дату из сообщения электронной почты в качестве даты автора коммита и использует время создания коммита в качестве даты коммиттера.
Это позволяет пользователю лгать о дате автора, используя то же значение, что и дата коммиттера.

Для git rebaseэтого параметра «Несовместимо с параметром --interactive».

Поскольку вы можете по желанию изменить временную метку старой даты фиксации (с помощью git filter-branch), я полагаю, вы можете организовать свою историю Git с любым порядком дат фиксации, который вы хотите / нуждаетесь, даже установить его на будущее! ,


Как упоминает Оливье в своем вопросе, дата автора никогда не изменяется путем перебазирования;
Из Pro Git Book :

  • Автор - человек, который первоначально написал работу,
  • тогда как коммиттер - это человек, который последним применил работу.

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

Чтобы быть более ясным, в данном случае, как отмечает Оливье:

--ignore-dateделает противоположное тому , что я пытался достичь !
А именно, он стирает метку времени автора и заменяет ее метками времени коммитов!
Таким образом, правильный ответ на мой вопрос:
ничего не делайте, так git rebase как фактически не изменяет метки времени авторов по умолчанию.


VonC
источник
1
Интересно о произвольных датах совершения. Однако git rebase --ignore-dateне работает. Это изменяет даты перебазированных коммитов.
Оливье Вердиер
@Olivier: странно: ты сделал нон -interactive перебазирования? А между датой автора и датой коммиттера мы обязательно будем следить за «правильной» датой?
VonC
1
Благодаря VonC, разнице между меткой времени автора и коммиттера, вот что делает все это внезапно понятным. Я написал ответ на свой вопрос в своем посте, но не стесняйтесь адаптировать свой ответ, чтобы отразить это.
Оливье Вердиер
4
если быть более точным: то --ignore-date, что я пытался достичь, противоположно тому, чего я пытался достичь! А именно, он стирает метку времени автора и заменяет ее метками времени коммитов! Таким образом, правильный ответ на мой вопрос: ничего не делать, так git rebaseкак на самом деле не изменяет метки времени авторов по умолчанию.
Оливье Вердиер
5
Обратите внимание, что эта --committer-date-is-author-dateопция, похоже, оставляет метку времени автора и устанавливает метку времени коммиттера такой же, как оригинальная метка времени автора, чего и хотел Оливье ...
Дэвид Фрейзер
118

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

git filter-branch --env-filter 'GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE; export GIT_COMMITTER_DATE'

Энди
источник
1
Я только что попробовал это, но без эффекта. Я получил на следующие результаты: WARNING: Ref 'refs/heads/master' is unchanged. Я использую git версии 1.7.9.5 в Linux (64-разрядная версия)
Маркус Н.
20
Я хотел бы добавить другой подход, если вы уже облажались, но не хотите перебирать всю историю: git rebase --committer-date-is-author-date <base_branch> таким образом, git сбрасывает дату фиксации только для коммитов, примененных к <base_branch> (что, вероятно, то же название ветки, которое вы использовали, когда облажались).
спикер
Принятый ответ не сработал в 2016 году, но ответ @ докладчика сработал!
Теодор Р. Смит
2
Ответ @ спикера не сработал в октябре 2016 года, но Энди сработал!
Амеди Ван Гасс
2
Это не работает в Windows. Я смог заставить его работать с помощью Windows Bash.
vaindil
33

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

Ответ в том, что rebase на самом деле не меняет метки автора (вам ничего не нужно для этого делать), что мне подходит.

Оливье Вердиер
источник
3
+1 - у меня есть псевдоним git ( coderwall.com/p/euwpig/a-better-git-log ), который, очевидно, использует метку времени коммиттера, что меня смущало . Gitk и git log показывают временную метку автора.
1615903
15

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

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

Перед перебазированием запишите метку времени коммиттера, метку автора и сообщение о коммите всех коммитов, которые будут перебазированы в файл.

#NOTE: BASE is the commit where your rebase begins
git log --pretty='%ct %at %s' BASE..HEAD > hashlog

Затем позвольте фактической перезагрузке иметь место.

Наконец, мы заменяем временную метку текущего коммиттера на метку, записанную в файле, если сообщение о фиксации совпадает с использованием git filter-branch.

 git filter-branch --env-filter '__date=$(__log=$(git log -1 --pretty="%at %s" $GIT_COMMIT); grep -m 1 "$__log" ../../hashlog | cut -d" " -f1); test -n "$__date" && export GIT_COMMITTER_DATE=$__date || cat'

Если что-то пойдет не так, просто проверьте git reflogили все refs/original/ссылки.

Furthormore, вы можете сделать то же самое с отметкой времени автора.

Например, если временная метка автора некоторых коммитов не в порядке, и без перестановки этих коммитов мы просто хотим, чтобы метка времени автора показывалась по порядку, тогда помогут следующие команды.

git log --pretty='%at %s' COMMIT1..COMMIT2 > hashlog
join -1 1 -2 1 <(cat hashlog | cut -f 1 | sort -nr | awk '{ print NR" "$1 }') <(cat hashlog | awk '{ print NR" "$0 }') | cut -d" " -f2,4- > hashlog_
mv hashlog_ hashlog
git filter-branch --env-filter '__date=$(__log=$(git log -1 --pretty="%s" $GIT_COMMIT); grep -m 1 "$__log" ../../hashlog | cut -d" " -f1); test -n "$__date" && export GIT_AUTHOR_DATE=$__date || cat'
weynhamz
источник
Это отличный трюк! Это позволило мне переписать только 75 коммитов вместо 1100+, используя другие ответы.
Аудуну
Это фантастика! Есть ли способ изменить скрипт, чтобы также сохранить оригинальный коммиттер?
Дэвид ДеМар
@DavidDeMar должен быть таким же, просто измените журнал git --pretty, чтобы записать исходное письмо, и соответственно измените скрипт.
Вейнхамз