I have beem using git for quite some time now, mainly git-svn. Now I want to convince my colleagues to switch from svn to git. But unfortunaly the precondition is that the svn repository keeps on living quite some time. So I searched for an solution and came up with the book:
Jon Loeliger's "Version Control with Git". I bought it and it is really good but I don't fully understand the guide to set up a git svn gatekeeper repo.
In Chapter 16, he describes a situation in which there is a Subversion repository, and at least a couple users that want to be using Git. He proposes a single "gatekeeper" git repository which is the only interface to subversion. After git svn cloneing the subversion repo (with --prefix=svn/), all the branches are then pushed to a bare repository (git push ../svn-bare.git 'refs/remotes/svn/:refs/heads/svn/', and other git users are told to clone this repo, which now contains local branches of all the svn remotes.
This part works and I think I fully understand it. But I don't get the next part:
If a developer that clones the bare repository pushes changes back from his repo to the bare repository and then I dcommit this in the bare repo to svn, the commits the user pushed are lost for good reason because of the replaced commits git-svn creates. Or am I wrong? How does this work?
The book says
Then, to merge back to subversion, in the gatekeeper repo, you do
git checkout svn/trunk (or other branch - this is checking out a detached head as svn/trunk is a remote) git merge --no-ff new-feature git svn dcommit
How can I checkout a branch in a bare repository? I don't think this works
This results in a merge commit on a detached head, and then the modified commit (after the git-svn-id line is added) is put on the real svn/trunk branch.
What is meant by real svn/trunk ?
The commit on the detached head is "worse than redundant. Using it for anything else eventually results in conflicts. So, just forget about that commit. If you haven't put it on a branch in the first place, it's that much easier to forget" (Jon Loeliger).
I'm a little confused. Has someone a better explanation for creating a git svn gatekeeper repo? I have searched the web and this site but I haven't found anything that seems suitable for me.
I'm so tired of wasting so many time with svn branching and merging, when collaborating with my colleagues.
How can I checkout a branch in bare repository? I don't think this works
Yes it does: you simply clone the bare repo locally, making a non-bare repo in the process, where you can checkout/create (again locally) as many branch as you want.
A bare repo is needed as an upstream repo where to push to. (see "git push
only for bare repositories?")
But in order to push anything to it, i.e for the other developers to push their changes in non-svn branches back to the gatekeeper repo, said other developer has to clone that bare repo first, make all the relevant modifications to the local copy, and push back to the bare repo.
Plus, you can setup some hook on that bare repo in order to validate your push: see "Hooks for git-svn".
Then to dcommit
, the gatekeeper also clone that gatekeeper repo, from which he/she will:
svn
' being the name of a remote repo), like 'svn/trunk
' for instancegit-svn dcommit
So to recap:
I have tried to automate the gatekeeper setup described by Jon Loeliger and got it working. He starts out very detailed about what steps to perform, but the part 'Merging Back into Subversion' is rather short. I have tried different setups with git-svn, also followed the excellent presentations/examples given by Thomas Ferris Nicolaisen and have used his example projects (with modifications) for testing out the 'gatekeeper setup':
@echo 1. Clone Subversion repo
cd %WDIR%\devs\adm
call git svn clone -s --prefix=svn/ http://localhost/svn-repos/company-repo/websites -- username adm
cd %WDIR%\devs\adm\websites
call git reset --hard svn/trunk
@echo ----------------------------------
@echo 2. Create bare repo
cd %WDIR%\devs\adm
mkdir websites.git
cd websites.git
call git init --bare
@echo ----------------------------------
@echo 3. Populate bare with content from gatekeeper
cd %WDIR%\devs\adm\websites
call git push --all ../websites.git
call git push ../websites.git "refs/remotes/svn/*:refs/heads/svn/*"
@echo ----------------------------------
@echo 4. Setup bare as a remote in gatekeeper and fetch branches
call git remote add bare_repo ../websites.git
call git fetch bare_repo
Step 4 is not described by Jon Loeliger but I guess that's what he ment.
When it's time for merging back to subversion do:
C:\tmp\devs\adm\websites>git fetch bare_repo
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (4/4), done.
From ../websites
e53fba9..2ac281c svn/trunk -> bare_repo/svn/trunk
Now we can follow the steps from the book:
C:\tmp\devs\adm\websites>git checkout svn/trunk
Note: checking out 'svn/trunk'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b new_branch_name
HEAD is now at e53fba9... [maven-release-plugin] prepare release kaksi
C:\tmp\devs\adm\websites>git merge --no-ff remotes/bare_repo/svn/trunk
Merge made by the 'recursive' strategy.
0 files changed
create mode 100644 web/howto.txt
create mode 100644 web/readme.txt
C:\tmp\devs\adm\websites>git svn dcommit
Committing to http://localhost/svn-repos/company-repo/websites/trunk ...
A web/howto.txt
A web/readme.txt
Committed r8
A web/readme.txt
A web/howto.txt
r8 = 28da267255ae56022bd4ed3c0f4886da1ac04944 (refs/remotes/svn/trunk)
No changes between current HEAD and refs/remotes/svn/trunk
Resetting to the latest refs/remotes/svn/trunk
My problem with this setup (and we have been warned earlier in the book) is that the history is squashed:
C:\tmp\devs\adm\svn\websites>svn log
------------------------------------------------------------------------
r8 | adm | 2012-05-12 23:21:11 +0200 (lø, 12 mai 2012) | 1 line
Merge remote-tracking branch 'remotes/bare_repo/svn/trunk' into HEAD
------------------------------------------------------------------------
Now consider this alternative for merging back into subversion:
C:\tmp\devs\adm\websites>git checkout -t svn/trunk
Branch trunk set up to track local ref refs/remotes/svn/trunk.
Switched to a new branch 'trunk'
C:\tmp\devs\adm\websites>git fetch bare_repo
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (4/4), done.
From ../websites
c188a72..b1b4237 svn/trunk -> bare_repo/svn/trunk
C:\tmp\devs\adm\websites>git rebase remotes/bare_repo/svn/trunk
First, rewinding head to replay your work on top of it...
Fast-forwarded trunk to remotes/bare_repo/svn/trunk.
C:\tmp\devs\adm\websites>git svn reset 2147483647
r7 = c188a72da6df2966e563e9e575b626d5b449400f (refs/remotes/svn/trunk)
C:\tmp\devs\adm\websites>git svn rebase
Current branch trunk is up to date.
C:\tmp\devs\adm\websites>git svn dcommit
Committing to http://localhost/svn-repos/company-repo/websites/trunk ...
A web/howto.txt
A web/readme.txt
Committed r8
A web/readme.txt
A web/howto.txt
r8 = 18b7c7b4693cc8e55098bd716c9259ed5570acf0 (refs/remotes/svn/trunk)
No changes between current HEAD and refs/remotes/svn/trunk
Resetting to the latest refs/remotes/svn/trunk
Now the commit-history is intact:
C:\tmp\devs\adm\svn\websites>svn log
------------------------------------------------------------------------
r8 | adm | 2012-05-12 23:51:48 +0200 (lø, 12 mai 2012) | 1 line
'ola added [readme.txt, howto.txt] on svn/trunk'
In order for this setup to work we need to use the 'git svn reset' command, or else the dcommit will fail the second time, because git-svn is confused about the current revision, and is behind (to same revision as when we created the bare-repo). This is probably because we used rebase, which in turn is necessary to get a nice linear history in subversion.
The big question is: What does 'git svn reset ' really do?
Is "forward reseting" a legitimate use of 'git svn reset' in this case?
I don't think you should ever push changes back to the bare repo. I've got an example setup here that you could try out:
http://blog.tfnico.com/2010/10/gitsvn-5-centralized-git-svn-mirror.html
In my setup (which I've been employing for 3-4 months without any problems), changes should always "flow" through the SVN repository. I don't really understand this gatekeeper technique..
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With