]> git.donarmstrong.com Git - don.git/blob - posts/migrating_from_svn_to_git_and_git_annex.mdwn
Merge branch 'master' of ssh://linnode.donarmstrong.com/sites/donarmstrong/don
[don.git] / posts / migrating_from_svn_to_git_and_git_annex.mdwn
1 [[!meta title="Migrating from Subversion to git with git-annex"]] 
2
3 Recently, I've started converting many of my subversion repositories
4 to git, some of which contain fairly large files (2-3G). However, git
5 can be slow to deal with repositories with large files, and it also
6 isn't able to selectively discard unneeded files when disk space is
7 pressing. Thankfully,
8 [git-annex](https://www.google.com/search?q=git-annex) resolves most
9 of these problems with git, but the process required to use git-annex
10 on a converted subversion repository is slightly complicated.
11
12 Basic conversion of svn to git
13 ------------------------------
14 The basic conversion of svn to git is done using git-svn:
15
16      git svn clone file:///srv/svn/foo --no-metadata -A authors.txt -T trunk foo
17
18 where /srv/svn/foo is the subversion repository, authors.txt is a list
19 of `login = Full Name <email@example.com>` pairs matching each of the
20 subversion commit authors, and foo is the git repository to create.
21
22 git-svn has a ton of useful options, but the basic invocation above is
23 all I'm concerned with.
24
25 Migrating large files from git into git-annex
26 ---------------------------------------------
27
28 In order to migrate from git to a git+git-annex setup, we'll have to
29 walk the entire commit history, and edit each commit to instead store
30 large files in git-annex, replacing the large file with a symlink, and
31 finally eliminate all of the references to the old large objects, and
32 do garbage collection.
33
34 Because we may have the same file move around, we're going to use the
35 git-annex SHA1 backend instead of the default WORM backend which is
36 based on filename and size, and init git-annex.
37
38       cd foo; echo '* annex.backend=SHA1' > .git/info/attributes
39           git annex init
40
41 Then, we're going to filter out the large files using `git
42 filter-branch`. To do that, we'll first, we'll create a little helper
43 script `git_annex_add.sh`, which will remove the file from the git
44 repository, add to git annex, and fix up the symlinks:
45
46      #!/bin/bash
47      f="$1";
48      git rm --cached "${f}";
49      git annex add "${f}";
50      annexdest="$(/bin/readlink -v ${f})";
51      ln -sf "${annexdest#../../}" "${f}";
52      echo -n "Added: "
53      ls -l "${f}";
54
55 Then we will run filter-branch, and annex all files larger than 5
56 megabytes.
57 [Tweak the find command if you want to do something different.]
58
59      git filter-branch  --tag-name-filter cat --tree-filter \
60     'find . -ipath \*.git\* -prune -o -path \*.temp\* -prune -o -size +5M -type f -print0|xargs -0 -r -n1 ~/git_annex_add.sh;
61      git reset HEAD .git-rewrite; :' -- master
62
63 This operation will take a while.
64 [It would be better to do this during the initial svn→git conversion, but since that requires more knowledge of git-svn, svn, git, and git-annex internals than I have, and I only have to do this once for each repository, it's not worth my time.]
65
66 Now we have successfully switched everything to using git-annex, and
67 we need to clean out the old references to the files:
68
69      rm .git/svn -rf;
70          rm -rf .git/refs/original .git/refs/remote/trunk .git/refs/remote/git-svn;
71          git reflog expire --expire=now --all
72          git gc --prune=now
73          git gc --prune=now --aggressive
74
75 (I'm not sure if the last two commands need to be separate; I'm cargo
76 culting a bit there.)
77
78 Storing all git-annex files in a remote repository
79 --------------------------------------------------
80
81 Because git-annex allows you to easily throw away files which are no
82 longer referred to by the tip of any branch using git annex unneeded
83 (and because I'd like all of the files on my central remote
84 repository), I'm going to shove all of the git annex files into the
85 remote bare repository. Normally, you would use `git annex copy
86 --to=remote;` to do this, but because that only copies needed files,
87 not everything, we'll have to do it manually.
88
89 First, create the remote repository:
90
91      git init --bare /srv/git/foo.git
92          cd /srv/git/foo.git; git annex init foo.example.com
93          
94 Add the remote to the local repository, push to the remote, and sync
95 the objects and sync the annex:
96
97      git remote add origin ssh://foo.example.com/srv/git/foo.git
98          git push origin master
99          rsync -avP .git/annex/objects ssh://foo.example.com/srv/git/foo.git/annex/.;
100          git annex sync
101          
102 Finally, on the remote, run `git annex fsck` to clean up the links to
103 the imported objects:
104
105          cd /srv/git/foo.git; git annex fsck;
106
107 Unresolved issues
108 -----------------
109 I don't know if the above works properly for branches. I suspect that
110 it does not. I also have not exhaustively tested this methodology to
111 verify that all of the history is present in every case. But hopefully
112 this post (or some modification of it) will be helpful to you.
113
114 Credit
115 ------
116
117 Many of the methodologies described here I originally found in
118 [tyger's git-annex forum post](http://git-annex.branchable.com/forum/migrate_existing_git_repository_to_git-annex/),
119 the `git gc` stuff came from random google searches about shrinking
120 git repositories, and the rsync suggestion came from joeyh (author of
121 git-annex) and the other helpful denizens of #vcs-home on
122 irc.oftc.net.
123
124
125 [[!tag debian tech git git-annex]]