Git: 更改提交者信息

7

我正在使用这个脚本来修改提交:

rm -rf repo

echo "clonning $1"
git clone $1 repo

cd repo
git checkout dev

echo "setting remote origin to $2"
git remote set-url origin $2

array=( 'email1@gmail.com' 'email2@gmail.com' )
for OLD_EMAIL in "${array[@]}"
do
  echo $OLD_EMAIL
  git filter-branch -f --env-filter '
  CORRECT_NAME="New name"
  CORRECT_EMAIL="new@email.com"
  if [ "$GIT_COMMITTER_EMAIL" = '$OLD_EMAIL' ]
  then
      export GIT_COMMITTER_NAME="$CORRECT_NAME"
      export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
  fi
  if [ "$GIT_AUTHOR_EMAIL" = '$OLD_EMAIL' ]
  then
      export GIT_AUTHOR_NAME="$CORRECT_NAME"
      export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
  fi
  ' --tag-name-filter cat -- --tags
done
echo "Authors list:"
git log --format='%cE' | sort -u
echo -n "Push to destination (y/n)? "
read answer
if echo "$answer" | grep -iq "^y" ;then
    git push
else
    echo Aborted
fi

cd ../

它从第一个仓库中提取数据,修改提交者信息并推送到第二个仓库。

问题出现在如果有人直接在第二个仓库中提交代码,我该如何将这些变更应用到第一个仓库?


只是好奇:您想要两个存储库中的所有更改吗?为什么不能只使用一个存储库?这只是关于提交者信息吗? - Michael Jaros
是的,第一个仓库应该包含原始提交记录,而第二个仓库则包含修改后的提交记录。 - stkvtflw
@larsks 如果“不提交到第二个仓库”是一个选项,我就不会问了。正确合并更改的关键在于了解Bash,以便编写脚本来轻松完成它。 - stkvtflw
我需要撤销更改。我认为可以通过拉取两个仓库并查看原始提交历史,在第二个仓库中替换名称和电子邮件来实现。问题是 - 我不太懂bash。 - stkvtflw
你不需要修改提交历史记录来还原更改。如果你已经通过重写整个历史记录对仓库进行了二分,那么最好的方法可能是尝试回到单一历史记录并从那里向前工作。是什么促使你首先尝试重写历史记录?自那以后这两个存储库有多大的差异? - LightBender
显示剩余5条评论
2个回答

6
如果我理解您的问题正确(在阅读评论后),您的存储库当前看起来像这样:

Initial State

第一个仓库中的提交记录(a-d)已被修改,以创建备用提交记录(a'-d'),这些记录被推送到第二个仓库,然后添加了其他提交记录(e-g)。
重新编辑您的历史记录
由于两个仓库中的身份信息没有一对一的关系,因此尝试使用filter-branch修改a'-d'以恢复原始历史记录,虽然在理论上是可能的,但需要一种方法来积极识别“原始提交记录”而不需要唯一标识提交记录的信息(其哈希值)。
提交记录基本上由几个信息组成:
1. 树的哈希值 2. 提交记录的父提交记录的哈希值 3. 作者的身份信息 4. 作者的时间戳 5. 提交者的身份信息 6. 提交记录的时间戳 7. 提交记录的消息 8. 所有信息的大小

所有这些内容都被哈希以创建您提交的唯一标识符。在更改2、3、5和8之后,我们留下了树,它不一定是唯一的;时间戳,也不一定是唯一的;以及提交消息,也不一定是唯一的。

很可能您只需比较树和一个时间戳就能得到一个相当匹配的结果,因此让我们为这种情况编写一些伪代码。

# create a variable to hold the information from teh current commit
pseudoidentifier=$TREE + $AUTHOR_TIMESTAMP

# go to the first repo
cd /path/to/firstrepo

# output the log | grep to search | sed to remove everything after delimeter
oldhash=`git log --format="{hash}~{tree}{authortimestamp}" | grep pseudoidenfier | sed "s/~.+$//"`

# get the new identity using a custom formatted show command
newidentity=`git show -q --format="{formatted identity}" $oldhash`

# parse out the name and email, probably with sed
CORRECT_NAME=`sed 's/pattern//' $newidentity`
CORRECT_EMAIL=`sed 's/pattern//' $newidentity`

# go to the second repo
cd /path/to/secondrepo

export GIT_COMMITTER_NAME="$CORRECT_NAME"
export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"

很遗憾,这样写会很慢,测试也会非常困难和耗时。可能需要多次重新运行整个代码。既然你的最终目标是重新合并代码,有几种其他选项可能会导致更少的麻烦并且更快。特别是如果您确实需要保留具有身份更新的第二个存储库。
替代方法
即使没有共同的历史记录,您仍然可以使用相对手动的方式将两者同步。在这种情况下,我建议使用以下三种方法之一。
一些预备工作
在开始之前,我们可以使用git show命令检查d和d'处的代码是否相同。
$ git show -q --format="%T" d
a017285da45ec06fc744815f33a2e22627f4a799
$ git show -q --format="%T" d'
a017285da45ec06fc744815f33a2e22627f4a799

这个命令将输出提交所指向的树对象,如果两个树匹配,则处理相同的代码。完全可能在没有匹配代码库的情况下执行以下过程,但在这种情况下,您可能需要解决冲突。这一步只是告诉您它们如何轻松地合并在一起。
樱桃挑选方法 如果您最初用于修改提交的存储库完好无损,则可以从两个分支中获取到一个单一的存储库,并尝试使用cherry-pick复制提交。
git checkout <branch at d>
git cherry-pick d'...g

(请注意,语法是3个点)这将将从d之后(但不包括d)到g之间的每个提交中的更改应用于d。创建新的提交e' - g'。

History after cherry-pick

补丁方法

如果您没有简单的方法将两个分支的更改合并到单个存储库中,您可以为第二个存储库上的提交创建一系列补丁,并将其应用于第一个存储库。

在第二个存储库中

git checkout <branch of g>
git format-patch --output-directory <dir> d'...g

(再次强调,语法是三个点)这将输出从d到g之间每个提交后(不包括d),产生一系列补丁文件。然后将这些文件复制到第一个仓库可以访问的位置,以应用这些补丁。

在第一个仓库中

git checkout <branch of d>
git am /path/to/patches/*

你最终会和使用“挑樱桃法”得到的结果一样。

History after patch

创建一个嫁接点

如果有很多冲突并且您不需要保留已更改的身份信息,则还可以使用git replace执行嫁接。

git replace --graft e d

这将创建一个以 d 作为父提交的 e 的副本,并添加一个引用,指示每当访问 e 时使用 e' 提交。实际上,使 d 成为两者的公共祖先,并允许您执行传统合并(h)。

enter image description here

然后呢?

将两个没有共同历史的仓库保持同步将会经常导致像这样的问题,并且随着两个仓库慢慢分歧(例如,当您解决冲突时),问题会越来越严重。随着时间的推移,这两种方法都需要越来越多的资源来维护这两个仓库。

我建议一旦两个仓库被同步,选择一个并从那时起专门使用它。如果您需要两个远程仓库,请将该仓库推送到两个仓库中。然后,您可以轻松使用任何经过验证的工作流程来维护这两个仓库。

如果这不是一个选项,我建议仔细检查您的两个仓库头部树是否经常完全相同。


尝试修改'a'-'d'以重新合并历史记录是行不通的,这只会创建第二组与原始提交相同作者的重复提交,但它们不会是相同的提交。<= 这不是真的 - 只需尝试玩弄单个提交,来回更改名称或电子邮件 - 您将看到恢复完全相同的名称和完全相同的电子邮件可以还原历史记录。 - stkvtflw
@stkvtflw 哦,我错了。我现在会删除错误的陈述,并在几个小时内扩展我的答案。 - LightBender

0

你有两个选项来完成这个任务:

  1. 如果你信任用户,你可以让他们更改他们的电子邮件(只针对此 Git 仓库或所有仓库,在所有仓库中添加 --global
git config user.email email@server.com
如果您想通过预提交 git 钩子来强制执行它,可以将其添加到第二个存储库中,并让所有人拉取新的更新。更多相关信息可以在这里这里找到。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接