Automatically synchronizing a Subversion repository and a Git repository

痴心易碎 提交于 2019-12-02 18:37:56
Daniel Hershcovich

Here is what I've come up with:

  1. Create the git-svn repository if it doesn't exist already:

    git svn init --std-layout <svn_url> <git-svn_path>
    

    The master branch is created automatically to track the trunk.

  2. To avoid name ambiguities with Subversion-tracking branches, make the original Subversion branches show as remotes/svn/<branch name>: go to the newly-created git-svn repository and run

    git config svn-remote.svn.fetch trunk:refs/remotes/svn/trunk
    git config svn-remote.svn.branches branches/*:refs/remotes/svn/*
    git config svn-remote.svn.tags tags/*:refs/remotes/svn/tags/*
    
    rm .git/refs/remotes/*
    git svn fetch
    
  3. Create a Subversion-tracking branch for each Subversion branch:

    for BRANCH in $(svn ls <svn_url>/branches/); do
        git branch $BRANCH remotes/svn/$BRANCH
    done
    
  4. Make sure no non-Subversion-tracking branches are created on the central Git repository:

    # Used by hooks/update:
    git config hooks.denyCreateBranch true
    git config hooks.allowDeleteBranch false
    
    cp .git/hooks/update.sample .git/hooks/update
    chmod +x .git/hooks/update
    
  5. Allow pushing to the central Git repository:

    git config receive.denyCurrentBranch ignore
    git config receive.denyNonFastForwards true
    git config push.default current
    

    And create the post-receive hook to reset and send the commits to Subversion:

    cat .git/hooks/post-receive
    
        #!/bin/sh
    
        date >> receive.log
        git reset --quiet --hard
        while read LINE
        do
            BRANCH=${LINE##*/}
            echo Updating $BRANCH
            git checkout --quiet --force $BRANCH
            git svn dcommit
        done 2>&1 | tee -a receive.log
        git checkout --quiet --force master
    
    chmod +x .git/hooks/post-receive
    

    The reset is necessary, because otherwise the current branch is out-of-date after each receive.

  6. Finally, create the hook to get updates from Subversion:

    cat .git/hooks/svn-rebase-all
    
        #!/bin/sh
    
        date >> .git/svn-rebase.log
        git reset --quiet --hard
        for REF in .git/refs/heads/*
        do
            BRANCH=${REF##*/}
            echo Updating $BRANCH
            git checkout --quiet --force $BRANCH
            git svn rebase
        done 2>&1 | tee -a .git/svn-rebase.log
        git checkout --quiet --force master
    
    chmod +x .git/hooks/svn-rebase-all
    

    And call it from the Subversion post-commit hook:

    cat <svn_path>/hooks/post-commit
    
        cd <git_path>
        . .git/hooks/svn-rebase-all
    
    chmod +x <svn_path>/hooks/post-commit
    

Instead of using a single git-svn central repository, one can use a bare central Git repository and an intermediate non-bare git-svn repository, as in this answer. I chose to use one non-bare git-svn repository which is also the central repository.

Anyone can work on the project using Git by cloning <git_path> and pushing to it, or using Subversion by checking out <svn_url> and committing to it.

You're better off with just having each developer learn to use git-svn directly. There's simply too much of an impedance mismatch between the git and SVN models to be able to robustly implement what you are looking for. The only way you could get it to work even nearly reliably would be to enact the same sort of restrictions that git-svn would have, but with more moving parts that could break. In my opinion, your revision control system is not the sort of thing that you want to be just partially reliable.

Alternatively just ditch SVN altogether and move all the way to git, if possible.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!