On Tue, 19 Jan 2016 05:41:57 -0800 (PST)
Berin Loritsch <be...@d-haven.org> wrote:

> We're a C# development shop, and I've gone through the trouble of
> making a script that will safely migrate stand-alone SVN projects
> into a Git repository without problems.
[...]
> Let's say we have the following SVN structure:
> 
>    - Common
>       - trunk
>       - branches
>       - tags
>    - Common.WPF
>       - trunk
>       - branches
>       - tags
>    - Common.WinForms
>       - trunk
>       - branches
>       - tags
>    - Unit Tests
>       - trunk
>       - branches
>       - tags
>    
> 
> I want to create one Git repository that looks like this:
> 
>    - Common
>       - Core
>       - WPF
>       - WinForms
>       - Test
>    
> All while preserving the history for each of these.
[...]

I'd do it using the `git subtree` and `git filter-branch` tools.

Before jumping into details, I'd also recommend up front to perform the
conversion on a POSIX-compatible machine (that is, anything based on
GNU/Linux or {Free|Open}BSD would do; and so would Mac OS X) to save
time: those commands are notoriously slow on Windows, and you will need
to do multiple rounds of trial runs until you get them work the way you
want.  So I'd say it might be quicker to get a VM with a Linux-based
distro up and running and then do the work there -- pushing the results
to a shared repo when it's done.

Back to the problem.

The general idea is outlined below.


At first, you'll need to convert the history of your Subversion
repository to a local Git repository.

This is done using `git svn`, and you'll need to dance around its
settings to get proper mapping of your Subversion repo structure
to the branches of your Git repo.

To be honest, I did not yet have a case similar to yours, so I'm not
exactly sure this will 100% work; you might need to create several
Git repos to fetch each of your Subversion subprojects separately.
This is okay, as it's easy to fetch anything you want from one
local Git repo into another.

In the end, you'll end up with either a single Git repo with multiple
branches -- each representing the "trunk" of the corresponding
Subversion subproject -- or a set of such repos.


On the second step, you'll need to "stitch" these histories together to
produce a single Git repository.  This is easily (and I mean it) done
using the `git subtree add` command which produces a merge commit which
has the line of history with the tip commit N "joined" with the history
of the current branch -- with its data located at the specified prefix
(that is, directory).

To explain it in more simple words, given that you have extracted the
"trunk" history of the "Common/Core" Subversion folder into the "master"
branch of a local Git repo "core", you can now do this:

  $ git init main
  $ cd main
  $ touch .keep
  $ git add .keep
  $ git commit -m "Initial commit"

  ... OK, so we now have some history in our synthetic repo

  $ git fetch ../core master:core
  $ git subtree add -P Common/Core core

The last command will produce a merge commit whose first parent is our
dummy commit with a single empty file and another one is the whole
history of the Common/Core/trunk folder of your Subversion repo --
located at the proper prefix -- that is, under the Common/Core
subdirectory on checkout.


You can now obviously repeat these steps (except for initializing the
main repository) for each of the Git repos obtained on the first step.

That would get you almost what you want.  "Almost" is because once
you'll want to follow the history of any ex-subrepo past the merge
commit recorded by `git subtree add` which joined its history with the
mainline, you'll get the problem of all the "before-the-join" commits
from the ex-subrepo having its files without that prefix you specified
when called `git subtree add`.

If you're okay with this, you can just stop here.  Otherwise you'll
need to use `git filter-branch` on the branches representing your
ex-subrepo trunks before joining them with the mainline.  The script
passed to the `git filter-branch` invocation should change each commit
ensuring all its files are under the required target prefix -- such as
"Common/Core" for the source "Common/Core/trunk" Subversion folder.

The proper invocation to do this is discussed in [1], for instance
(note that it implies using a sh-compatible shell, moreover the example
from the answer implies bash due to those `[[ ... ]]'), and you'll be
able to google more.

So, with modification, the approach is to filter each of your source
branch so that all the commits on them have their files located under
the target prefixes (subdirectories) for those branches, verify the
conversion happened OK by inspecting the output of `git log --name-only`
or something like this, then merely merge the resulting branches
into the mainline.  Note that I mean simple merging, not using
`git subtree add`: since your filtered branches will already contain
their contents at the right prefix, there's no need to "reposition"
them when merging -- a normal merge will have the files at the right
place.

Hope this helps.

1. http://stackoverflow.com/a/4042965/720999

-- 
You received this message because you are subscribed to the Google Groups "Git 
for human beings" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to git-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to