summaryrefslogtreecommitdiff
path: root/bin/annex-to-annex-reinject
blob: 023ebe3b5ff0e5b41d1399b104eb54db9268bef9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/perl
# PODNAME: annex-to-annex-reinject
# ABSTRACT: use 'git annex reinject' to redo annex-to-annex

# Copyright (C) 2019-2020 Sean Whitton
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

=head1 SYNOPSIS



=head1 DESCRIPTION



=cut

use 5.028;
use strict;
use warnings;

use autodie;
use Git::Annex;
use File::Basename qw(basename dirname);
use File::chmod;
$File::chmod::UMASK = 0;
use File::Path qw(rmtree);
use File::Spec::Functions qw(rel2abs);
use File::Find;
use Try::Tiny;

die "usage: annex-to-annex-reinject SOURCEANNEX DESTANNEX\n" unless @ARGV == 2;

my $source = Git::Annex->new($ARGV[0]);
my $dest   = Git::Annex->new($ARGV[1]);
#<<<
try {
    $source->git->rev_parse({ git_dir => 1 });
} catch {
    die "$ARGV[0] doesn't look like a git repository ..\n";
};
try {
    $dest->git->rev_parse({ git_dir => 1 });
} catch {
    die "$ARGV[1] doesn't look like a git repository ..\n";
};
#>>>

# `git annex reinject` doesn't work in a bare repo atm
my $use_worktree
  = ($dest->git->rev_parse({ is_bare_repository => 1 }))[0] eq 'true';
my ($temp, $worktree);
if ($use_worktree) {
    $temp = tempdir(CLEANUP => 1, DIR => dirname $ARGV[1]);
    say "bare repo; our git worktree is in $temp";
    $dest->git->worktree("add", { force => 1, detach => 1 },
        rel2abs($temp), "synced/master");
}

my ($source_uuid) = $source->git->config('annex.uuid');
die "couldn't get source annex uuid" unless $source_uuid =~ /\A[a-z0-9-]+\z/;
my $spk = $source->batch("setpresentkey");

my ($source_objects_dir)
  = $source->git->rev_parse({ git_path => 1 }, "annex/objects");
$source_objects_dir = rel2abs $source_objects_dir, $ARGV[0];
my $reinject_from = $use_worktree ? $temp : $ARGV[1];
say "reinjecting from $source_objects_dir into $reinject_from";
find({
        wanted => sub {
            -f or return;
            say "\nconsidering $_";
            my $dir = dirname $_;
            chmod "u+w", $dir, $_;
            system "git", "-C", $reinject_from, "annex", "reinject",
              "--known", $_;
            if (-e $_) {
                chmod "u-w", $dir, $_;
            } else {
                my $key = basename $_;
                say "telling setpresentkey process '$key $source_uuid 0'";
                say for $spk->say("$key $source_uuid 0");
                # alt. to setpresentkey:
                # say "fscking key $key in $ARGV[0]";
                # system 'git', '-C', $ARGV[0], 'annex', 'fsck',
                #     '--numcopies=1', '--key', $key;
                say "cleaning up empty dirs";
                foreach my $d ($dir, dirname($dir), dirname(dirname($dir))) {
                    last unless is_empty_dir($d);
                    rmdir $d;
                }
            }
        },
        no_chdir => 1
    },
    $source_objects_dir
);
if ($use_worktree) {
    # we can't use `git worktree remove` because the way git-annex
    # worktree support works breaks that command: git-annex replaces
    # the .git worktree file with a symlink
    rmtree $temp;
    $dest->git->worktree("prune");
}

# cause setpresentkey changes to be recorded in git-annex branch
undef $spk;
sleep 1;
$source->git->annex("merge");

sub is_empty_dir {
    -d $_[0] or return 0;
    opendir(my $dirh, $_[0]);
    my @files = grep { $_ ne '.' && $_ ne '..' } readdir $dirh;
    return @files == 0;
}