-
Notifications
You must be signed in to change notification settings - Fork 13
/
annex-abslink
executable file
·73 lines (64 loc) · 2.54 KB
/
annex-abslink
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
#!/usr/bin/env python3
# annex abslink -- convert git-annex relative symlinks to absolute
#
# By default git-annex creates relative symlinks, which make annexed files hard
# to manage over SMB as moving them to a different level causes the symlink to
# break and the file to "disappear".
import argparse
import os
import re
import stat
import subprocess
def find_git_dir(base):
r = subprocess.run(["git", "-C", base, "rev-parse", "--git-dir"],
stdout=subprocess.PIPE,
text=True,
check=True)
return r.stdout.rstrip("\n")
parser = argparse.ArgumentParser()
parser.add_argument("-n", "--dry-run", action="store_true")
parser.add_argument("-v", "--verbose", action="store_true")
parser.add_argument("path", nargs="*")
args = parser.parse_args()
pid = os.getpid()
n_good = 0
n_fixed = 0
n_other = 0
n_total = 0
for walk_base in args.path or ["."]:
for walk_dir, subdirs, files in os.walk(walk_base):
git_dir = find_git_dir(walk_dir)
for name in files:
path = os.path.join(walk_dir, name)
st = os.lstat(path)
if not stat.S_ISLNK(st.st_mode):
continue
n_total += 1
orig_target = os.readlink(path)
if "/.git/annex/objects/" not in f"/{orig_target}":
n_other += 1
continue
if orig_target.startswith("/"):
n_good += 1
continue
# Do the rewrite manually in an annex-specific manner instead of
# using os.path.abspath(), so that it will work even with
# already-broken (i.e. moved) symlinks.
#abs_target = os.path.join(os.path.dirname(path), orig_target)
#abs_target = os.path.abspath(abs_target)
m = re.search(r"\.git/(annex/objects/.+)", orig_target)
abs_target = os.path.join(git_dir, m[1])
if args.dry_run or args.verbose:
print(path)
if args.dry_run:
print(" <-", orig_target)
print(" =>", abs_target)
else:
backup_path = f"{path}~{pid}"
os.rename(path, backup_path)
os.symlink(abs_target, path)
os.chown(path, st.st_uid, st.st_gid, follow_symlinks=False)
os.utime(path, (st.st_atime+1, st.st_mtime+1), follow_symlinks=False)
os.unlink(backup_path)
n_fixed += 1
print(f"Found {n_total} symlinks, {n_fixed} updated, {n_good} already absolute, {n_other} non-annex")