3 # annex-ec: Use erasure codes for more efficient storage use in git-annex
4 # Copyright (C) 2026 Scott Worley
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Affero General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
20 this_volume_name
=$
(join -j1 <(git config get annex.uuid
) <(git cat-file
-p git-annex
:uuid.log
|sort) | cut
-d' ' -f2)
22 mapfile
-d , tmp
<<< "$1"
24 for x
in "${tmp[@]}";do
28 if [[ "$x" == "$this_volume_name" ]];then
39 EXAMPLE_SUFFIX
='.vol0000+9999.par2'
40 EXAMPLE_ANNEX_INTERNAL_PREFIX
='ingest-'
41 EXAMPLE_ANNEX_INTERNAL_SUFFIX
='-1-1173fd7'
42 num_separating_dashes
=$
((N
- 1))
43 overhead
=$
(( ${#EXAMPLE_SUFFIX} + ${#EXAMPLE_ANNEX_INTERNAL_PREFIX} + ${#EXAMPLE_ANNEX_INTERNAL_SUFFIX} + num_separating_dashes
))
44 available
=$
((FILENAME_MAX
- overhead
))
45 len
=$
((available
/ N
))
46 name
=$
(find "$@" -printf '%l\n' | sed -r 's/.*SHA256E-s[0-9]+--//;s/\..*//' | cut
-c-$len | tr \\n
-)
52 block_size_is_a_multiple_of
=4 # par2 requires that this be at least 4
54 while getopts b
:m
:r
:v
: opt
;do
56 b
) blocks_per_file
=$OPTARG;;
57 m
) block_size_is_a_multiple_of
=$OPTARG;;
58 r
) redundancy
=$OPTARG;;
59 v
) parse_volume_list
"$OPTARG";;
60 *) echo 'usage: annex-ec [-v remote1,remote2,...] [-r N] file file...' >&2; exit 1;;
65 if (( ${#volumes[@]} == 0 ));then
66 parse_volume_list
"here,$(git remote | tr \\n ,)"
69 N
=$
((${#volumes[@]} - redundancy
))
71 (( $# == N
)) || die
"Expected $N files in this group ($N + $redundancy = ${#volumes[@]}), but got $#"
76 max_size
=$
(find -L "$@" -printf '%s\n' | sort -nr | head -n1)
77 block_size
=$
(( ((max_size
/(block_size_is_a_multiple_of
*blocks_per_file
))+1) * block_size_is_a_multiple_of
))
83 # TODO: Make this robust against being interrupted here
84 echo '* annex.numcopies=1' >> ec
/.gitattributes
85 git add ec
/.gitattributes
88 par2 c
-u -n"$redundancy" -c"$((blocks_per_file * redundancy))" -s"$block_size" "$name.par2" "$@"
94 target_volume
="${volumes[i]}"
95 for volume
in "${volumes[@]}";do
96 if [[ "$volume" != here
]];then
97 if [[ "$volume" == "$target_volume" ]]; then
98 git annex copy
--to "$volume" "$f"
105 for f
in ec
/"$name.vol"*;do
106 target_volume
="${volumes[i]}"
108 if [[ "$target_volume" != here
]];then
109 git annex move
--to "$target_volume" "$f"
115 echo "${f// /[[:space:]]} annex.numcopies=1" >> .gitattributes
118 for volume
in here
"${volumes[@]}";do
122 target_volume
="${volumes[i]}"
123 if [[ "$volume" != "$target_volume" ]]; then
128 if [[ "$volume" == here
]];then
129 git annex drop
"${to_drop[@]}"
131 git annex drop
--from "$volume" "${to_drop[@]}"
134 git add .gitattributes