Git 旧版本覆盖了SVN主干。项目和主干文件夹现在有不同的历史记录 背景

Git 旧版本覆盖了SVN主干。项目和主干文件夹现在有不同的历史记录 背景,git,svn,subgit,Git,Svn,Subgit,我们有一个包含多个项目的SVN存储库: ROOT - Project1 - trunk - Project2 - trunk 等等 然后我们开始迁移到git。我们设置subgit以保持SVN和GIT存储库同步(两种方式)。已经有几个月了,我们有一些开发人员使用svn,还有一些使用git,显然没有任何问题 上面的每个项目都成为git存储库,子git配置为: trunk = trunk:refs/heads/master branches = branches/*:refs/hea

我们有一个包含多个项目的SVN存储库:

ROOT
 - Project1
   - trunk
 - Project2
   - trunk
等等

然后我们开始迁移到git。我们设置subgit以保持SVN和GIT存储库同步(两种方式)。已经有几个月了,我们有一些开发人员使用svn,还有一些使用git,显然没有任何问题

上面的每个项目都成为git存储库,子git配置为:

trunk = trunk:refs/heads/master
branches = branches/*:refs/heads/*
tags = tags/*:refs/tags/*
shelves = shelves/*:refs/shelves/*
到目前为止还不错。从现在开始,让我们关注项目1

问题 我注意到最近在svn中完成的一些提交没有移植到git。 查看svn端的日志,我发现这些提交在svn中也处于“不一致”状态(日志部分匿名化):

现在,最后一次提交r6109看起来很可怕。似乎是说当前主干已被r5477覆盖

实际上,trunk文件夹的日志完全忽略了以下两个版本:

$ svn log -v Project1/trunk

------------------------------------------------------------------------
r6109 | user1 | 2015-01-13 12:47:43 +0100 (di, 13 jan 2015) | 1 line
Changed paths:
   R /Project1/trunk (from /Project1/trunk:5477)

Branch '/trunk' replaced from /trunk:5477
------------------------------------------------------------------------
r5477 | user2 | 2014-10-16 17:07:25 +0200 (do, 16 okt 2014) | 1 line
Changed paths:
   M /Project1/trunk/src/com/...
-- fix bug23
问题: 我想得到一些帮助,以确定目前的状况和可能的解决方案

我想发生了什么事(请帮帮我!):

  • 在某个时候,在同一个源上使用git的开发人员使用了
    git reset--hard
    将工作副本重置到本地主分支,该分支位于与svn r5477相对应的提交位置,并将其推送到远程主分支
  • 这一点由subgit翻译后,产生了svn修订版6109,上面写着“Branch'/trunk'替换自/trunk:5477”
  • 因为git存储库映射到了
    Project1/trunk
    ,所以已经用r5477重写了它
  • 但是,subgit甚至看不到文件夹
    Project1
    ,这就解释了为什么
    Project1
    的历史记录仍然包含现在丢失的提交日志(主干中的代码实际上回到了r5477版本)
有人能确认这可能是当前状态吗?我还是忽略了什么吗? 我不知道svn会允许您(在本例中是subgit)以不一致的历史结束

如果这是真的,谁能建议最好的解决方案是什么? 请注意,git存储库没有这部分历史记录,因为它只同步
Project1/trunk
,而不同步
Project1
。 我觉得这应该在svn存储库中修复。但我不知道怎么做

以下是可能的解决方案吗

$ cd Project1
$ svn merge -rHEAD:6089 .
$ svn ci

警告和/或更好的建议有没有人?

我会解释发生了什么事

Git存储库中有一个“master”的更新,它绑定到SVN中的主干。此外,更新是非快进的,默认情况下,如果没有
gitpush
命令的
-f
选项,Git不允许这样的推送。 非快进更新转换为分支替换,因为这是SVN存储库中最接近的模拟。例如,你可以比较<代码> Git日志主< /代码>之后,与代码> > SN log -V项目1 /主干< /代码>输出,并看到两者不包含R6088,并且第一个提交的列表将是R54 77(如果我们不考虑R6109没有变化的Git中没有模拟)。要查看
git日志
输出中的修订号,您可以按照的“4.6.建议的客户端git配置”部分中所述的方式配置git客户端,然后运行
git fetch
。所以我不认为SVN和Git存储库是不一致的

如果希望防止将来进行分支替换,则不应使用
git push-f
命令。如果要严格禁止此类更新,可以将服务器上Git配置的
receive.denynonfastforts
选项设置为
true

修理怎么样?有两种方法。如果您可以在Subversion历史记录中进行这样的提交,那么您可以再次替换
trunk
,但从trunk@6089. 您可以从SVN侧执行此操作,例如,分两步执行:

$ svn delete Project1/trunk
$ svn commit -m "Trunk deleted"
$ svn update
$ svn cp <URL of trunk>@6089 Project1/trunk
$ svn commit -m "Trunk recreated from r6089"
在客户端上进行此提交。请注意,此后您几乎不会注意到r6109的任何证据,
svn log
(以及
git log
)将直接跳转到最后一个替换源,即r6089

另一种(更糟糕的)方法是从SVN历史记录中删除r6109(我假设您没有对主干进行其他提交)。这可以通过“svnadmin转储/加载”程序完成。在服务器上运行以下命令

$ svnadmin dump path/to/your/current/svn/repository -r1:6108 > repo.dump
$ svnadmin create path/for/repaiered/svn/repository
$ svnadmin load path/for/repaiered/svn/repository < repo.dump
$svnadmin dump path/to/your/current/svn/repository-r1:6108>repo.dump
$svnadmin创建路径/for/repaiered/svn/repository
$svnadmin加载路径/for/repaiered/svn/repository
然后配置对此新存储库而不是SVN存储库的访问权限,并为其重新安装SubGit。某些SHA-1哈希可能会有所不同。有一种方法可以保留大部分散列,但这取决于您的子Git模式:本地或远程

你的合并方法怎么样?对我来说,这会使历史变得复杂。特别是如果您合并整个项目而不是一些分支,那么我不会执行该合并


如果您有任何问题,请随时联系support@subgit.com德米特里·帕夫伦科提供的答案是正确的。一个重要的信息是,Git提交和SVN修订之间存在1:1的关系,如一篇文章中所述

为了防止发生这些分支替换,我们不同步包含斜杠(默认子git配置)的分支,而是使用服务器端git钩子(
.git/hooks/user pre-receive
):

$ git update-ref refs/heads/master <SHA-1 of r6089 commit>
$ git push origin master -f
$ git fetch origin refs/svn/attic/trunk/6089:refs/heads/trunk6089
$ svnadmin dump path/to/your/current/svn/repository -r1:6108 > repo.dump
$ svnadmin create path/for/repaiered/svn/repository
$ svnadmin load path/for/repaiered/svn/repository < repo.dump
#!/bin/bash

check_ref() {

    OLD=$(git rev-parse $1)
    NEW=$(git rev-parse $2)
    REFNAME=$3

    ZERO="0000000000000000000000000000000000000000"

    if ! [[ $REFNAME == refs/heads/* ]]; then
            # Not a branch action, probably a tag
            return
    fi

    BRANCH_NAME=$(expr "$REFNAME" : "refs/heads/\(.*\)")
    FEATURE_BRANCH=$(expr "$BRANCH_NAME" : "\(.*/.*\)")

    if [ "$OLD" = "$ZERO" ]; then
            if [ ${FEATURE_BRANCH} ]; then
                    # Permit feature branch creation
                    return
            else
                    echo "*** Rejected creation of non-feature branch"
                    exit 1
            fi
    fi

    if [ "$NEW" = "$ZERO" ]; then
            if [ ${FEATURE_BRANCH} ]; then
                    # Permit feature branch deletion
                    return
            else
                    echo "*** Rejected deletion of non-feature branch"
                    exit 1
            fi
    fi

    if [ ${FEATURE_BRANCH} ]; then
            # Pushes to feature branches are always allowed
            return
    fi

    for COMMIT in `git rev-list $OLD ^$NEW`; do
            # $COMMIT is reachable from $OLD, but not $NEW -> Force push
            echo "*** Force push is not allowed on branch $REFNAME"
            exit 1
    done

    # Check for non-fast-forward merge
    ALL_REVS=`git rev-list $OLD..$NEW | wc -l`
    REVS_WITH_SINGLE_PARENT=`git rev-list $OLD..$NEW --max-parents=1 | wc -l`

    if [ ${ALL_REVS} -ne ${REVS_WITH_SINGLE_PARENT} ]; then
            echo "*** Non-fast-forward merge detected on branch $REFNAME"
            echo "*** Please rebase your work before pushing!"
            exit 1
    fi

    for COMMIT in `git rev-list $OLD..$NEW`; do
            # Checking commit $COMMIT
            git branch --contains $COMMIT | grep -q -v / && echo "*** Commit $COMMIT rejected because it is contained in other branches" && exit 1
    done

}

while read old new refname;
do
    check_ref $old $new $refname
done

exit 0