在 dcomitting 时避免(重复)使用不同的哈希值提交

Avoiding (duplicate) commits with different hashes when dcomitting

Apologies for the long question; I thought it best to include as much information as possible.

问题

如何使 Gitlab 托管的存储库与(权威的)svn 存储库保持同步并避免在使用 git svn dcommit 时重复提交?

设置

我在本地服务器上托管了一个 svn 存储库。远程团队(无权访问此服务器)正在使用 git 开发存储库中软件的子树。出于商业目的,svn 存储库被认为是发布等目的的权威。因此,我使用 git-svn 来保持团队同步。

存储库信息

Consider the above arrangement unchangeable, so please don't suggest an alternative arrangement. I'm interested in answers that address my specific, lower-level question which I promise is coming shortly =)

初始克隆

存储库最初是从 svn 克隆到 Gitlab 的,如下所示:

git svn clone --prefix=svn --preserve-empty-dirs svn://project gitclone
git remote add origin https://gitlab/me/project.git
git push --set-upstream origin master

问题

当我在 git 分支上开发一个功能并将其集成回 svn 存储库时,我最终在 git 提交历史记录中有很多重复提交。 svn 提交历史看起来不错。我确定这是因为使用 git svn dcommit 创建的返回 svn 的提交与 git 方面的 'original' 提交具有不同的哈希值。这是一个示例流程:

git 开发者

$ git clone https://gitlab/me/project.git
$ git checkout -b git-f2
Switched to a new branch 'git-f2'

$ mkdir git-f2
$ touch git-f2/git-f2.txt
$ git add .
$ git commit -m "Add f2"
[git-f2 686a513] Add f2
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 git-f2/git-f2.txt

$ echo "some text" > f2/f2.txt
$ git commit -m "Update f2"
[git-f2 e84af9a] Update f2
 1 file changed, 1 insertion(+)

$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.

$ git merge git-f2
Updating dc4d50b..e84af9a
Fast-forward
 git-f2/git-f2.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 git-f2/git-f2.txt

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)
nothing to commit, working tree clean

$ git push
Counting objects: 8, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (8/8), 724 bytes | 0 bytes/s, done.
Total 8 (delta 0), reused 0 (delta 0)
To https://gitlab.mydomain.com/me/project.git
   dc4d50b..e84af9a  master -> master

我(回购同步器)

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean

$ git fetch
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 8 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (8/8), done.
From https://gitlab.mydomain.com/me/project
   dc4d50b..e84af9a  master     -> origin/master

$ git status
On branch master
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)
nothing to commit, working tree clean

$ git pull
Updating dc4d50b..e84af9a
Fast-forward
 git-f2/git-f2.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 git-f2/git-f2.txt

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean

此时,git log 在我的(同步器)本地主机上显示与在远程主机上相同的提交:

$ git log --format=oneline
commit e84af9ae738d782dfa5499cfb93b3dcb73cbf179 (HEAD -> master, origin/master)
commit 686a513eaf0083ad234e383f7e543df19431eff5
commit dc4d50bd66f36595d539c4f0c2ad70079c277315 (svn/git-svn)
commit 7e330320ac7d36331a8fb525f63fdf60f4ee070f

但是当我 dcommit 时,提交 686a51e84af9 会在我的本地主机上使用新的哈希值(分别为 ed7b23605348 重现):

$ git svn dcommit --use-log-author --add-author-from
Committing to svn://project ...
        A       git-f2/git-f2.txt
Committed r306
        A       git-f2/git-f2.txt
r306 = ed7b23e5abe29c09ff4483d811c1d645916e075b (refs/remotes/svn/git-svn)
        M       git-f2/git-f2.txt
Committed r307
        M       git-f2/git-f2.txt
r307 = 605348ec24142e2d382b295dbb34aa20c507fad9 (refs/remotes/svn/git-svn)
No changes between e84af9ae738d782dfa5499cfb93b3dcb73cbf179 and refs/remotes/svn/git-svn
Resetting to the latest refs/remotes/svn/git-svn

现在,我不再跟踪 Gitlab master,原因很清楚:

$ git status
On branch master
Your branch and 'origin/master' have diverged,
and have 2 and 2 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
nothing to commit, working tree clean

$ git log --graph --format=oneline
* 605348ec24142e2d382b295dbb34aa20c507fad9 (HEAD -> master, svn/git-svn) Update f2
* ed7b23e5abe29c09ff4483d811c1d645916e075b Add f2
* dc4d50bd66f36595d539c4f0c2ad70079c277315 <redacted>
* 7e330320ac7d36331a8fb525f63fdf60f4ee070f <redacted>

$ git status
On branch master
Your branch and 'origin/master' have diverged,
and have 2 and 2 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
nothing to commit, working tree clean

此时,执行 git pull 会导致合并提交被添加到我的本地主机。随后的 git push 留下以下图表:

$ git log origin/master --graph --format=oneline
*   e3bdac6d4fd58fbd006d777ceb3e87d31ee14ace (HEAD -> master, origin/master) GIT PULL Merge branch 'master' of https://gitlab.mydomain.com/me/project
|\
| * e84af9ae738d782dfa5499cfb93b3dcb73cbf179 Update f2
| * 686a513eaf0083ad234e383f7e543df19431eff5 Add f2
* | 605348ec24142e2d382b295dbb34aa20c507fad9 (svn/git-svn) Update f2
* | ed7b23e5abe29c09ff4483d811c1d645916e075b Add f2
|/
* dc4d50bd66f36595d539c4f0c2ad70079c277315 <redacted>
* 7e330320ac7d36331a8fb525f63fdf60f4ee070f <redacted>

svn 日志和历史记录很好,所以我想知道是否有任何方法可以避免在我执行 dcommit 时在我的本地主机上进行 'new' 提交。这似乎可以避免我在这里看到的问题。或者,由于我是 git 新手,我可能完全错了!

简而言之,这是 git svn dcommit 的机制:dcommit 根据 git 提交对 SVN 存储库进行修订,然后重写 git 提交.

git svn dcommit解释如下:

  • Commit each diff from the current branch directly to the SVN repository, and then rebase or reset (depending on whether or not there is a diff between SVN and head). This will create a revision in SVN for each commit in Git. The command will dcommit changes to svn repo based on the git commits

From git svn document

  • This takes all the commits you’ve made on top of the Subversion server code, does a Subversion commit for each, and then rewrites your local Git commit to include a unique identifier. This is important because it means that all the SHA-1 checksums for your commits change.

From Committing Back to Subversion

我们可以通过下图来说明:

在从您的回购同步器中的 git 远程回购拉取更改之后和 git svn dcommit 之前,假设回购中的提交历史如下:

…---A---B---C---D---E  master, origin/master
            |
        svn/git-svn

当您执行 git svn dcommit 时,它将根据 Dremotes/svn/git-svn 之后的新提交 E 创建修订。它还将使用新的提交 sha-1 值重写提交 DE(如下图中的提交 D'E')。所以执行命令后,提交历史将是:

              D'---E'  master
             /
…---A---B---C---D---E  origin/master
            |
        svn/git-svn

因此 git status 将显示 Your branch and 'origin/master' have diverged

由于您不想更改安排,您可以通过git push -f origin master[=45强制推送到您的repo同步器中的GitLab repo =].那么提交历史将是:

…---A---B---C---D'---E'  master, origin/master
            |
        svn/git-svn

作为提交一部分的所有元数据,如提交者姓名、提交者电子邮件、作者姓名、作者电子邮件、提交时间、作者时间、父提交、提交消息和等等都是SHA-1计算的一部分。

如果您 dcommit 将某些内容添加到 SVN,然后从 SVN 更新(dcommit 隐含地执行此操作),您刚刚进行的新 SVN 提交将在 Git 克隆中创建为新提交,包括来自实际 SVN 提交的提交者和作者信息,来自实际 SVN 提交的作者和提交时间以及 Git 提交消息(最后一行)中关于 SVN 提交的 git-svn 元数据。这当然会创建一个新的 Git 提交,具有相同的更改但 SHA-1 值不同。

因此,您总是必须将您在 Git 中所做的工作变基到 SVN 新移植的提交上,这将丢弃引入与 SVN 提交相同更改的 Git 版本。在您当前的设置中没有其他方法可以处理此问题。

对于像你这样的情况,SubGit 提供了 SVN 存储库的 Git 镜像,并允许并行使用 Git 和 SVN,只要你愿意,并执行自动同步。我没有这方面的经验,但我不知道这是否也适用于 SVN 存储库的子树。