|
12 | 12 |
|
13 | 13 | use Git::SVN; |
14 | 14 | use Git::SVN::Log; |
| 15 | +use Git::SVN::Migration; |
15 | 16 | use Git::SVN::Utils qw(fatal can_compress); |
16 | 17 |
|
17 | 18 | use Git qw( |
@@ -2041,263 +2042,6 @@ sub gc_directory { |
2041 | 2042 | } |
2042 | 2043 |
|
2043 | 2044 |
|
2044 | | -package Git::SVN::Migration; |
2045 | | -# these version numbers do NOT correspond to actual version numbers |
2046 | | -# of git nor git-svn. They are just relative. |
2047 | | -# |
2048 | | -# v0 layout: .git/$id/info/url, refs/heads/$id-HEAD |
2049 | | -# |
2050 | | -# v1 layout: .git/$id/info/url, refs/remotes/$id |
2051 | | -# |
2052 | | -# v2 layout: .git/svn/$id/info/url, refs/remotes/$id |
2053 | | -# |
2054 | | -# v3 layout: .git/svn/$id, refs/remotes/$id |
2055 | | -# - info/url may remain for backwards compatibility |
2056 | | -# - this is what we migrate up to this layout automatically, |
2057 | | -# - this will be used by git svn init on single branches |
2058 | | -# v3.1 layout (auto migrated): |
2059 | | -# - .rev_db => .rev_db.$UUID, .rev_db will remain as a symlink |
2060 | | -# for backwards compatibility |
2061 | | -# |
2062 | | -# v4 layout: .git/svn/$repo_id/$id, refs/remotes/$repo_id/$id |
2063 | | -# - this is only created for newly multi-init-ed |
2064 | | -# repositories. Similar in spirit to the |
2065 | | -# --use-separate-remotes option in git-clone (now default) |
2066 | | -# - we do not automatically migrate to this (following |
2067 | | -# the example set by core git) |
2068 | | -# |
2069 | | -# v5 layout: .rev_db.$UUID => .rev_map.$UUID |
2070 | | -# - newer, more-efficient format that uses 24-bytes per record |
2071 | | -# with no filler space. |
2072 | | -# - use xxd -c24 < .rev_map.$UUID to view and debug |
2073 | | -# - This is a one-way migration, repositories updated to the |
2074 | | -# new format will not be able to use old git-svn without |
2075 | | -# rebuilding the .rev_db. Rebuilding the rev_db is not |
2076 | | -# possible if noMetadata or useSvmProps are set; but should |
2077 | | -# be no problem for users that use the (sensible) defaults. |
2078 | | -use strict; |
2079 | | -use warnings; |
2080 | | -use Carp qw/croak/; |
2081 | | -use File::Path qw/mkpath/; |
2082 | | -use File::Basename qw/dirname basename/; |
2083 | | - |
2084 | | -our $_minimize; |
2085 | | -use Git qw( |
2086 | | - command |
2087 | | - command_noisy |
2088 | | - command_output_pipe |
2089 | | - command_close_pipe |
2090 | | -); |
2091 | | - |
2092 | | -sub migrate_from_v0 { |
2093 | | - my $git_dir = $ENV{GIT_DIR}; |
2094 | | - return undef unless -d $git_dir; |
2095 | | - my ($fh, $ctx) = command_output_pipe(qw/rev-parse --symbolic --all/); |
2096 | | - my $migrated = 0; |
2097 | | - while (<$fh>) { |
2098 | | - chomp; |
2099 | | - my ($id, $orig_ref) = ($_, $_); |
2100 | | - next unless $id =~ s#^refs/heads/(.+)-HEAD$#$1#; |
2101 | | - next unless -f "$git_dir/$id/info/url"; |
2102 | | - my $new_ref = "refs/remotes/$id"; |
2103 | | - if (::verify_ref("$new_ref^0")) { |
2104 | | - print STDERR "W: $orig_ref is probably an old ", |
2105 | | - "branch used by an ancient version of ", |
2106 | | - "git-svn.\n", |
2107 | | - "However, $new_ref also exists.\n", |
2108 | | - "We will not be able ", |
2109 | | - "to use this branch until this ", |
2110 | | - "ambiguity is resolved.\n"; |
2111 | | - next; |
2112 | | - } |
2113 | | - print STDERR "Migrating from v0 layout...\n" if !$migrated; |
2114 | | - print STDERR "Renaming ref: $orig_ref => $new_ref\n"; |
2115 | | - command_noisy('update-ref', $new_ref, $orig_ref); |
2116 | | - command_noisy('update-ref', '-d', $orig_ref, $orig_ref); |
2117 | | - $migrated++; |
2118 | | - } |
2119 | | - command_close_pipe($fh, $ctx); |
2120 | | - print STDERR "Done migrating from v0 layout...\n" if $migrated; |
2121 | | - $migrated; |
2122 | | -} |
2123 | | - |
2124 | | -sub migrate_from_v1 { |
2125 | | - my $git_dir = $ENV{GIT_DIR}; |
2126 | | - my $migrated = 0; |
2127 | | - return $migrated unless -d $git_dir; |
2128 | | - my $svn_dir = "$git_dir/svn"; |
2129 | | - |
2130 | | - # just in case somebody used 'svn' as their $id at some point... |
2131 | | - return $migrated if -d $svn_dir && ! -f "$svn_dir/info/url"; |
2132 | | - |
2133 | | - print STDERR "Migrating from a git-svn v1 layout...\n"; |
2134 | | - mkpath([$svn_dir]); |
2135 | | - print STDERR "Data from a previous version of git-svn exists, but\n\t", |
2136 | | - "$svn_dir\n\t(required for this version ", |
2137 | | - "($::VERSION) of git-svn) does not exist.\n"; |
2138 | | - my ($fh, $ctx) = command_output_pipe(qw/rev-parse --symbolic --all/); |
2139 | | - while (<$fh>) { |
2140 | | - my $x = $_; |
2141 | | - next unless $x =~ s#^refs/remotes/##; |
2142 | | - chomp $x; |
2143 | | - next unless -f "$git_dir/$x/info/url"; |
2144 | | - my $u = eval { ::file_to_s("$git_dir/$x/info/url") }; |
2145 | | - next unless $u; |
2146 | | - my $dn = dirname("$git_dir/svn/$x"); |
2147 | | - mkpath([$dn]) unless -d $dn; |
2148 | | - if ($x eq 'svn') { # they used 'svn' as GIT_SVN_ID: |
2149 | | - mkpath(["$git_dir/svn/svn"]); |
2150 | | - print STDERR " - $git_dir/$x/info => ", |
2151 | | - "$git_dir/svn/$x/info\n"; |
2152 | | - rename "$git_dir/$x/info", "$git_dir/svn/$x/info" or |
2153 | | - croak "$!: $x"; |
2154 | | - # don't worry too much about these, they probably |
2155 | | - # don't exist with repos this old (save for index, |
2156 | | - # and we can easily regenerate that) |
2157 | | - foreach my $f (qw/unhandled.log index .rev_db/) { |
2158 | | - rename "$git_dir/$x/$f", "$git_dir/svn/$x/$f"; |
2159 | | - } |
2160 | | - } else { |
2161 | | - print STDERR " - $git_dir/$x => $git_dir/svn/$x\n"; |
2162 | | - rename "$git_dir/$x", "$git_dir/svn/$x" or |
2163 | | - croak "$!: $x"; |
2164 | | - } |
2165 | | - $migrated++; |
2166 | | - } |
2167 | | - command_close_pipe($fh, $ctx); |
2168 | | - print STDERR "Done migrating from a git-svn v1 layout\n"; |
2169 | | - $migrated; |
2170 | | -} |
2171 | | - |
2172 | | -sub read_old_urls { |
2173 | | - my ($l_map, $pfx, $path) = @_; |
2174 | | - my @dir; |
2175 | | - foreach (<$path/*>) { |
2176 | | - if (-r "$_/info/url") { |
2177 | | - $pfx .= '/' if $pfx && $pfx !~ m!/$!; |
2178 | | - my $ref_id = $pfx . basename $_; |
2179 | | - my $url = ::file_to_s("$_/info/url"); |
2180 | | - $l_map->{$ref_id} = $url; |
2181 | | - } elsif (-d $_) { |
2182 | | - push @dir, $_; |
2183 | | - } |
2184 | | - } |
2185 | | - foreach (@dir) { |
2186 | | - my $x = $_; |
2187 | | - $x =~ s!^\Q$ENV{GIT_DIR}\E/svn/!!o; |
2188 | | - read_old_urls($l_map, $x, $_); |
2189 | | - } |
2190 | | -} |
2191 | | - |
2192 | | -sub migrate_from_v2 { |
2193 | | - my @cfg = command(qw/config -l/); |
2194 | | - return if grep /^svn-remote\..+\.url=/, @cfg; |
2195 | | - my %l_map; |
2196 | | - read_old_urls(\%l_map, '', "$ENV{GIT_DIR}/svn"); |
2197 | | - my $migrated = 0; |
2198 | | - |
2199 | | - require Git::SVN; |
2200 | | - foreach my $ref_id (sort keys %l_map) { |
2201 | | - eval { Git::SVN->init($l_map{$ref_id}, '', undef, $ref_id) }; |
2202 | | - if ($@) { |
2203 | | - Git::SVN->init($l_map{$ref_id}, '', $ref_id, $ref_id); |
2204 | | - } |
2205 | | - $migrated++; |
2206 | | - } |
2207 | | - $migrated; |
2208 | | -} |
2209 | | - |
2210 | | -sub minimize_connections { |
2211 | | - require Git::SVN; |
2212 | | - require Git::SVN::Ra; |
2213 | | - |
2214 | | - my $r = Git::SVN::read_all_remotes(); |
2215 | | - my $new_urls = {}; |
2216 | | - my $root_repos = {}; |
2217 | | - foreach my $repo_id (keys %$r) { |
2218 | | - my $url = $r->{$repo_id}->{url} or next; |
2219 | | - my $fetch = $r->{$repo_id}->{fetch} or next; |
2220 | | - my $ra = Git::SVN::Ra->new($url); |
2221 | | - |
2222 | | - # skip existing cases where we already connect to the root |
2223 | | - if (($ra->{url} eq $ra->{repos_root}) || |
2224 | | - ($ra->{repos_root} eq $repo_id)) { |
2225 | | - $root_repos->{$ra->{url}} = $repo_id; |
2226 | | - next; |
2227 | | - } |
2228 | | - |
2229 | | - my $root_ra = Git::SVN::Ra->new($ra->{repos_root}); |
2230 | | - my $root_path = $ra->{url}; |
2231 | | - $root_path =~ s#^\Q$ra->{repos_root}\E(/|$)##; |
2232 | | - foreach my $path (keys %$fetch) { |
2233 | | - my $ref_id = $fetch->{$path}; |
2234 | | - my $gs = Git::SVN->new($ref_id, $repo_id, $path); |
2235 | | - |
2236 | | - # make sure we can read when connecting to |
2237 | | - # a higher level of a repository |
2238 | | - my ($last_rev, undef) = $gs->last_rev_commit; |
2239 | | - if (!defined $last_rev) { |
2240 | | - $last_rev = eval { |
2241 | | - $root_ra->get_latest_revnum; |
2242 | | - }; |
2243 | | - next if $@; |
2244 | | - } |
2245 | | - my $new = $root_path; |
2246 | | - $new .= length $path ? "/$path" : ''; |
2247 | | - eval { |
2248 | | - $root_ra->get_log([$new], $last_rev, $last_rev, |
2249 | | - 0, 0, 1, sub { }); |
2250 | | - }; |
2251 | | - next if $@; |
2252 | | - $new_urls->{$ra->{repos_root}}->{$new} = |
2253 | | - { ref_id => $ref_id, |
2254 | | - old_repo_id => $repo_id, |
2255 | | - old_path => $path }; |
2256 | | - } |
2257 | | - } |
2258 | | - |
2259 | | - my @emptied; |
2260 | | - foreach my $url (keys %$new_urls) { |
2261 | | - # see if we can re-use an existing [svn-remote "repo_id"] |
2262 | | - # instead of creating a(n ugly) new section: |
2263 | | - my $repo_id = $root_repos->{$url} || $url; |
2264 | | - |
2265 | | - my $fetch = $new_urls->{$url}; |
2266 | | - foreach my $path (keys %$fetch) { |
2267 | | - my $x = $fetch->{$path}; |
2268 | | - Git::SVN->init($url, $path, $repo_id, $x->{ref_id}); |
2269 | | - my $pfx = "svn-remote.$x->{old_repo_id}"; |
2270 | | - |
2271 | | - my $old_fetch = quotemeta("$x->{old_path}:". |
2272 | | - "$x->{ref_id}"); |
2273 | | - command_noisy(qw/config --unset/, |
2274 | | - "$pfx.fetch", '^'. $old_fetch . '$'); |
2275 | | - delete $r->{$x->{old_repo_id}}-> |
2276 | | - {fetch}->{$x->{old_path}}; |
2277 | | - if (!keys %{$r->{$x->{old_repo_id}}->{fetch}}) { |
2278 | | - command_noisy(qw/config --unset/, |
2279 | | - "$pfx.url"); |
2280 | | - push @emptied, $x->{old_repo_id} |
2281 | | - } |
2282 | | - } |
2283 | | - } |
2284 | | - if (@emptied) { |
2285 | | - my $file = $ENV{GIT_CONFIG} || "$ENV{GIT_DIR}/config"; |
2286 | | - print STDERR <<EOF; |
2287 | | -The following [svn-remote] sections in your config file ($file) are empty |
2288 | | -and can be safely removed: |
2289 | | -EOF |
2290 | | - print STDERR "[svn-remote \"$_\"]\n" foreach @emptied; |
2291 | | - } |
2292 | | -} |
2293 | | - |
2294 | | -sub migration_check { |
2295 | | - migrate_from_v0(); |
2296 | | - migrate_from_v1(); |
2297 | | - migrate_from_v2(); |
2298 | | - minimize_connections() if $_minimize; |
2299 | | -} |
2300 | | - |
2301 | 2045 | package Git::IndexInfo; |
2302 | 2046 | use strict; |
2303 | 2047 | use warnings; |
|
0 commit comments