git rebase 而不改变提交时间戳

发布于 2024-09-04 09:23:39 字数 591 浏览 4 评论 0原文

在保留提交时间戳的同时执行 git rebase 是否有意义?

我相信结果是新分支不一定按时间顺序有提交日期。 理论上这可能吗? (例如使用管道命令;这里只是好奇)

如果理论上可行,那么在实践中是否可以使用变基而不更改时间戳?

例如,假设我有以下树:

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

现在,如果我在 master 上重新设置 oldbranch 的基础,则提交日期会从 1984 年 2 月更改为 2010 年 6 月。是否可以更改该行为以便提交时间戳不会更改?最终我会得到这样的结论:

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

这有道理吗? git 中是否允许有一个历史记录,其中旧提交有一个更新的提交作为父提交?

Would it make sense to perform git rebase while preserving the commit timestamps?

I believe a consequence would be that the new branch will not necessarily have commit dates chronologically. Is that theoretically possible at all? (e.g. using plumbing commands; just curious here)

If it is theoretically possible, then is it possible in practice with rebase, not to change the timestamps?

For example, assume I have the following tree:

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

Now, if I rebase oldbranch on master, the date of the commit changes from feb 1984 to jun 2010. Is it possible to change that behaviour so that the commit timestamp is not changed? In the end I would thus obtain:

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

Would that make sense at all? Is it even allowed in git to have a history where an old commit has a more recent commit as a parent?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

岁月静好 2024-09-11 09:23:39

2014 年 6 月更新:David Fraser 提到在评论中还详细介绍了解决方案“在重定 git 分支时更改时间戳”,使用选项 --committer-date-is-author-date(最初于 2009 年 1 月在 提交 3f01ad6

注意,--committer-date-is-author-date选项似乎留下了作者时间戳,并将提交者时间戳设置为与原始作者时间戳相同,这就是OP Olivier Verdier 想要的。

我找到了具有正确日期的最后一次提交,并执行了以下操作:

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

请参阅 git am< /a>:

--committer-date-is-author-date

默认情况下,该命令将电子邮件中的日期记录为提交作者日期,并使用提交创建时间作为提交者日期。
这允许用户通过使用与作者日期相同的值来谎报提交者日期

注意:在 Git 2.29(2020 年第 4 季度)中,git rebase --committer-date-is-author-date--ignore-date 也适用于:

  • 交互式 rebase (rebase -i/rebase --interactive)
  • 获取根提交 (git rebase --root)

请参阅“在变基 g​​it 分支时更改时间戳"。


(原始答案,2012 年 6 月)

您可以尝试进行非交互式 rebase
(参见上面:Git 2.29,2020 年第 4 季度,它也可以与交互式变基一起使用)

git rebase --ignore-date

(来自此 SO 答案

这将传递给 git am,其中提到:

 --ignore-date

默认情况下,该命令将电子邮件中的日期记录为提交作者日期,并使用提交创建时间作为提交者日期。
这允许用户通过使用与提交者日期相同的值来谎报作者日期。

对于 git rebase,此选项“与 - 不兼容” - 互动选项。”

由于 您可以随意更改时间戳旧提交日期(使用 git filter-branch ),我想你可以用你想要/需要的任何提交日期顺序来组织你的 Git 历史记录,甚至是 将其设定为未来!


正如 Olivier 在他的问题中提到的那样,作者日期永远不会因变基而改变;
来自 Pro Git 书籍

  • 作者是最初创作该作品的人,
  • 而提交者是最后应用该工作的人。

因此,如果您向项目发送补丁并且其中一位核心成员应用了该补丁,那么你们俩都会获得荣誉。

更明确的是,在这种情况下,奥利维尔评论道:

--ignore-date 的作用与我想要实现的目标相反
也就是说,它会删除作者的时间戳并将其替换为提交时间戳!
所以我的问题的正确答案是:
不要执行任何操作,因为 git rebase 默认情况下实际上不会更改作者的时间戳。


正如 DylanYoung 添加 评论,使用“如何在 git 期间通过哈希识别冲突的提交变基?”:

使用 SEQUENCE_EDITOR 变量和 rebase 交互,您只需循环当前 todo 列表并添加一个将 GIT_COMMITER_DATE 设置为todo 中每次提交之前的原始提交日期。

这有点不那么繁琐,因为你有原始提交的列表(你不必侵入 git 内部来找到它),但需要更多的工作,因为你必须立即处理整个列表。 –

一旦您能够识别原始提交,您就可以执行以下操作:

git rebase -x 'GIT_COMMITTER_DATE="git show -s --format=%ci ``get_current_commit``" git commit --amend --no-edit

Update June 2014: David Fraser mentions in the comments a solution also detailed in "Change timestamps while rebasing git branch", using the option --committer-date-is-author-date (introduced initially in Jan. 2009 in commit 3f01ad6

Note that the --committer-date-is-author-date option seems to leave the author timestamp, and set the committer timestamp to be the same as the original author timestamp, which is what the OP Olivier Verdier wanted.

I found the last commit with the correct date and did:

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

See git am:

--committer-date-is-author-date

By default the command records the date from the e-mail message as the commit author date, and uses the time of commit creation as the committer date.
This allows the user to lie about the committer date by using the same value as the author date.

Note: with Git 2.29 (Q4 2020), git rebase --committer-date-is-author-date or --ignore-date will also work with:

  • interactive rebase (rebase -i/rebase --interactive)
  • for the root commit (git rebase --root)

See "Change timestamps while rebasing git branch".


(Original answer, June 2012)

You could try, for a non-interactive rebase
(see just above: with Git 2.29, Q4 2020, that will work with an interactive rebase as well)

git rebase --ignore-date

(from this SO answer)

This is passed to git am, which mentions:

 --ignore-date

By default the command records the date from the e-mail message as the commit author date, and uses the time of commit creation as the committer date.
This allows the user to lie about the author date by using the same value as the committer date.

For git rebase, this option is "Incompatible with the --interactive option."

Since you can change at will the timestamp of old commit date (with git filter-branch), I suppose you can organize your Git history with whatever commit date order you want/need, even set it to the future!.


As Olivier mentions in his question, the author date is never changed by a rebase;
From the Pro Git Book:

  • The author is the person who originally wrote the work,
  • whereas the committer is the person who last applied the work.

So, if you send in a patch to a project and one of the core members applies the patch, both of you get credit.

To be extra clear, in this instance, as Olivier comments:

the --ignore-date does the opposite of what I was trying to achieve!
Namely, it erases the author's timestamp and replace them with the commits timestamps!
So the right answer to my question is:
Do not do anything, since git rebase does actually not change authors' timestamps by default.


As DylanYoung adds in the comments, using "How to identify conflicting commits by hash during git rebase?":

Using the SEQUENCE_EDITOR variable and rebase interactive, you would just loop over the current todo list and add a command setting the GIT_COMMITER_DATE to the date of the original commit before each commit in the todo.

It's a bit less fiddly because you have the list of original commits to start with (you don't have to hack into git internals to find it), but a bit more work because you have to handle the entire list at once. –

Once you're able to identify the original commit you can do something like:

git rebase -x 'GIT_COMMITTER_DATE="git show -s --format=%ci ``get_current_commit``" git commit --amend --no-edit
浅沫记忆 2024-09-11 09:23:39

如果您已经搞砸了提交日期(可能通过变基)并希望将它们重置为相应的作者日期,您可以运行:

git filter-branch --env-filter 'GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE;导出 GIT_COMMITTER_DATE'

If you've already screwed up the commit dates (perhaps with a rebase) and want to reset them to their corresponding author dates, you can run:

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

筱武穆 2024-09-11 09:23:39

Von C 的一个关键问题帮助我理解了正在发生的事情:当您进行 rebase 时,提交者 时间戳会发生变化,但作者 时间戳不会发生变化,这突然间一切都有意义了。所以我的问题其实不够精确。

答案是 rebase 实际上不会改变作者的时间戳(你不需要为此做任何事情),这非常适合我。

A crucial question of Von C helped me understand what is going on: when your rebase, the committer's timestamp changes, but not the author's timestamp, which suddenly all makes sense. So my question was actually not precise enough.

The answer is that rebase actually doesn't change the author's timestamps (you don't need to do anything for that), which suits me perfectly.

撞了怀 2024-09-11 09:23:39

默认情况下,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/ refs。

此外,您可以对作者的时间戳执行类似的操作。

例如,如果某些提交的作者时间戳是无序的,并且
在不重新排列这些提交的情况下,我们只希望作者的时间戳显示在
命令,那么以下命令会有帮助。

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'

By default, git rebase will set the committer's timestamp to the time when the
new commit is created, but keep the author's timestamp intact. Most of time,
this is the desired behavior, but at some scenarios, we dot not wish to change
the commiter's timestamp either. How can we accomplish that? Well, here is the
trick I usually do.

First, make sure each of the commits you are about to rebase has a unique
commit message and author timestamp (This is where is trick needs improvements, currently it suits my needs though).

Before the rebase, record the committer's timestamp, author's timestamp and commit message of all the commits which will be rebased to a file.

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

Then, let the actual rebase take place.

Finally, we replace the current committer's timestamp with the one recorded in the file if the commit message is the same by using 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'

If something goes wrong, just checkout git reflog or all the refs/original/ refs.

Furthormore, you can do the similar thing to the author's timestamp.

For example, if the author's timestamp of some commits are out of order, and
without rearrange these commits, we just want the author's timestamp to show in
order, then the following commands will help.

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'
千寻… 2024-09-11 09:23:39

真正的解决方案似乎来自Reddit。稍微扩充一下,如下:

git -c rebase.instructionFormat='%s%nexec GIT_COMMITTER_DATE="%cD" git commit --amend --no-edit --allow-empty --allow-empty-message' rebase -i

The real solution seems to be from Reddit. Augmented slightly, here it is:

git -c rebase.instructionFormat='%s%nexec GIT_COMMITTER_DATE="%cD" git commit --amend --no-edit --allow-empty --allow-empty-message' rebase -i
我不咬妳我踢妳 2024-09-11 09:23:39

post-rewrite 钩子

此钩子适用于所有 git rebasegit pull --rebasegit commit --amend

.git/hooks/post-rewrite

set -eu
echo post-rewrite
if [ ! "${CIROSANTILLI_GITHOOKS_DISABLE:-0}" = 1 ]; then
  declare -a olds
  declare -A oldnew
  while IFS= read -r line; do
    echo "$line"
    old="$(echo "$line" | cut -d ' ' -f1)"
    new="$(echo "$line" | cut -d ' ' -f2)"
    oldnew[$old]="$new"
    olds+=("$old")
    news+=("$new")
  done
  git reset --hard "${news[0]}~"
  for old in "${olds[@]}"; do
    new="${oldnew[$old]}"
    git cherry-pick "$new" &>/dev/null
    olddate="$(git log --format='%cd' -n 1 "$old")"
    CIROSANTILLI_GITHOOKS_DISABLE=1 \
      GIT_COMMITTER_DATE="$olddate" \
      git commit \
      --amend \
      --no-edit \
      --no-verify \
      &>/dev/null \
    ;
  done
  echo
fi

GitHub 上游

不要忘记:

chmod +x .git/hooks/post-rewrite

在有人最终修补配置以将其设置为默认之前,这是在所选存储库上默认执行 --committer-date-is-author-date 的好方法。

它也适用于 --committer-date-is-author-date ,它似乎没有在 git pull --rebase 上公开。

另请参阅:

在 git 2.19、Ubuntu 18.04 上测试。

post-rewrite hook

This hook works for all of git rebase, git pull --rebase and git commit --amend.

.git/hooks/post-rewrite

set -eu
echo post-rewrite
if [ ! "${CIROSANTILLI_GITHOOKS_DISABLE:-0}" = 1 ]; then
  declare -a olds
  declare -A oldnew
  while IFS= read -r line; do
    echo "$line"
    old="$(echo "$line" | cut -d ' ' -f1)"
    new="$(echo "$line" | cut -d ' ' -f2)"
    oldnew[$old]="$new"
    olds+=("$old")
    news+=("$new")
  done
  git reset --hard "${news[0]}~"
  for old in "${olds[@]}"; do
    new="${oldnew[$old]}"
    git cherry-pick "$new" &>/dev/null
    olddate="$(git log --format='%cd' -n 1 "$old")"
    CIROSANTILLI_GITHOOKS_DISABLE=1 \
      GIT_COMMITTER_DATE="$olddate" \
      git commit \
      --amend \
      --no-edit \
      --no-verify \
      &>/dev/null \
    ;
  done
  echo
fi

GitHub upstream.

Don't forget to:

chmod +x .git/hooks/post-rewrite

This is a good way to do --committer-date-is-author-date by default on chosen repos, before someone finally patches a config to set it by default.

And it also works with --committer-date-is-author-date which does not seem to be exposed on git pull --rebase.

See also:

Tested on git 2.19, Ubuntu 18.04.

万人眼中万个我 2024-09-11 09:23:39

最近,我创建了一个实用程序来解决这个问题!

转到我的 Bash 框架 https://github.com/hopeseekr/BashScripts/

这里是脚本本身的直接下载链接: git-same -sig-time

git-same-sig-time [FIRST COMMIT]

之前(在 GitHub 上“Rebase and pull”之后):

不带签名的 git log

运行 /code/BashScripts/git-same-sig-time 5f5de8b 后:

带签名的 git 日志

然后你需要用力推动:

git push -f origin trunk

Recently, I have created a utility to fix this!

Go to my Bash framework at https://github.com/hopeseekr/BashScripts/

Here is a direct download link to the script itself: git-same-sig-time.

git-same-sig-time [FIRST COMMIT]

Before (after "Rebase and pull" on GitHub):

git log without signatures

After running /code/BashScripts/git-same-sig-time 5f5de8b:

git log with signatures

Then you'd need to do a force push:

git push -f origin trunk
简美 2024-09-11 09:23:39

这是我在案例中使用的命令:

[email protected] git rebase --root -x "bash -c 'git commit --amend --reset-author -CHEAD --date=\"\$(git show --format=%ad -s)\"'"

这里 git show --format=%ad -s 提取当前日期,并 --date 重新执行它。并且 rebase --root -x 执行命令 bash -c 'git commit --amend --reset-author -CHEAD --date="$(git show --format=% ad -s)"' 每次提交。

This is the command I used for my case:

[email protected] git rebase --root -x "bash -c 'git commit --amend --reset-author -CHEAD --date=\"\$(git show --format=%ad -s)\"'"

Here git show --format=%ad -s extracts the current date and --date re-enforces it. And rebase --root -x executes the command bash -c 'git commit --amend --reset-author -CHEAD --date="$(git show --format=%ad -s)"' for each commit.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文