Show More
@@ -0,0 +1,45 b'' | |||||
|
1 | #!/bin/sh | |||
|
2 | ||||
|
3 | cat >> readlink.py <<EOF | |||
|
4 | import os | |||
|
5 | import sys | |||
|
6 | ||||
|
7 | for f in sys.argv[1:]: | |||
|
8 | print f, '->', os.readlink(f) | |||
|
9 | EOF | |||
|
10 | ||||
|
11 | hg init a | |||
|
12 | cd a | |||
|
13 | ln -s nothing dangling | |||
|
14 | hg add dangling | |||
|
15 | hg commit -m 'add symlink' -d '0 0' | |||
|
16 | ||||
|
17 | hg tip -v | |||
|
18 | hg manifest --debug | |||
|
19 | echo '% rev 0:' | |||
|
20 | python ../readlink.py dangling | |||
|
21 | ||||
|
22 | rm dangling | |||
|
23 | ln -s void dangling | |||
|
24 | hg commit -m 'change symlink' | |||
|
25 | echo '% rev 1:' | |||
|
26 | python ../readlink.py dangling | |||
|
27 | ||||
|
28 | echo '% modifying link' | |||
|
29 | rm dangling | |||
|
30 | ln -s empty dangling | |||
|
31 | python ../readlink.py dangling | |||
|
32 | ||||
|
33 | echo '% reverting to rev 0:' | |||
|
34 | hg revert -r 0 -a | |||
|
35 | python ../readlink.py dangling | |||
|
36 | ||||
|
37 | echo '% backups:' | |||
|
38 | python ../readlink.py *.orig | |||
|
39 | ||||
|
40 | rm *.orig | |||
|
41 | hg up -C | |||
|
42 | echo '% copies' | |||
|
43 | hg cp -v dangling dangling2 | |||
|
44 | hg st -Cmard | |||
|
45 | python ../readlink.py dangling dangling2 |
@@ -0,0 +1,28 b'' | |||||
|
1 | changeset: 0:cabd88b706fc | |||
|
2 | tag: tip | |||
|
3 | user: test | |||
|
4 | date: Thu Jan 01 00:00:00 1970 +0000 | |||
|
5 | files: dangling | |||
|
6 | description: | |||
|
7 | add symlink | |||
|
8 | ||||
|
9 | ||||
|
10 | 2564acbe54bbbedfbf608479340b359f04597f80 644 dangling | |||
|
11 | % rev 0: | |||
|
12 | dangling -> nothing | |||
|
13 | % rev 1: | |||
|
14 | dangling -> void | |||
|
15 | % modifying link | |||
|
16 | dangling -> empty | |||
|
17 | % reverting to rev 0: | |||
|
18 | reverting dangling | |||
|
19 | dangling -> nothing | |||
|
20 | % backups: | |||
|
21 | dangling.orig -> empty | |||
|
22 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |||
|
23 | % copies | |||
|
24 | copying dangling to dangling2 | |||
|
25 | A dangling2 | |||
|
26 | dangling | |||
|
27 | dangling -> void | |||
|
28 | dangling2 -> void |
@@ -23,20 +23,13 b' class Config:' | |||||
23 | if not os.path.exists(os.path.join(d, '.hg')): |
|
23 | if not os.path.exists(os.path.join(d, '.hg')): | |
24 | raise ConfigError("%s: not a mercurial repository" % d) |
|
24 | raise ConfigError("%s: not a mercurial repository" % d) | |
25 |
|
25 | |||
26 | try: |
|
|||
27 | cfg = Config(sys.argv) |
|
|||
28 | except ConfigError, inst: |
|
|||
29 | print str(inst) |
|
|||
30 | usage() |
|
|||
31 | sys.exit(1) |
|
|||
32 |
|
||||
33 | def collect(src): |
|
26 | def collect(src): | |
34 | seplen = len(os.path.sep) |
|
27 | seplen = len(os.path.sep) | |
35 | candidates = [] |
|
28 | candidates = [] | |
36 | for dirpath, dirnames, filenames in os.walk(src): |
|
29 | for dirpath, dirnames, filenames in os.walk(src): | |
37 | relpath = dirpath[len(src) + seplen:] |
|
30 | relpath = dirpath[len(src) + seplen:] | |
38 | for filename in filenames: |
|
31 | for filename in filenames: | |
39 |
if not |
|
32 | if not filename.endswith('.i'): | |
40 | continue |
|
33 | continue | |
41 | st = os.stat(os.path.join(dirpath, filename)) |
|
34 | st = os.stat(os.path.join(dirpath, filename)) | |
42 | candidates.append((os.path.join(relpath, filename), st)) |
|
35 | candidates.append((os.path.join(relpath, filename), st)) | |
@@ -44,25 +37,56 b' def collect(src):' | |||||
44 | return candidates |
|
37 | return candidates | |
45 |
|
38 | |||
46 | def prune(candidates, dst): |
|
39 | def prune(candidates, dst): | |
|
40 | def getdatafile(path): | |||
|
41 | if not path.endswith('.i'): | |||
|
42 | return None, None | |||
|
43 | df = path[:-1] + 'd' | |||
|
44 | try: | |||
|
45 | st = os.stat(df) | |||
|
46 | except OSError: | |||
|
47 | return None, None | |||
|
48 | return df, st | |||
|
49 | ||||
|
50 | def linkfilter(dst, st): | |||
|
51 | try: | |||
|
52 | ts = os.stat(dst) | |||
|
53 | except OSError: | |||
|
54 | # Destination doesn't have this file? | |||
|
55 | return False | |||
|
56 | if st.st_ino == ts.st_ino: | |||
|
57 | return False | |||
|
58 | if st.st_dev != ts.st_dev: | |||
|
59 | # No point in continuing | |||
|
60 | raise Exception('Source and destination are on different devices') | |||
|
61 | if st.st_size != ts.st_size: | |||
|
62 | # TODO: compare revlog heads | |||
|
63 | return False | |||
|
64 | return st | |||
|
65 | ||||
47 | targets = [] |
|
66 | targets = [] | |
48 | for fn, st in candidates: |
|
67 | for fn, st in candidates: | |
49 | tgt = os.path.join(dst, fn) |
|
68 | tgt = os.path.join(dst, fn) | |
50 | try: |
|
69 | ts = linkfilter(tgt, st) | |
51 | ts = os.stat(tgt) |
|
70 | if not ts: | |
52 | except OSError: |
|
|||
53 | # Destination doesn't have this file? |
|
|||
54 | continue |
|
|||
55 | if st.st_ino == ts.st_ino: |
|
|||
56 | continue |
|
|||
57 | if st.st_dev != ts.st_dev: |
|
|||
58 | raise Exception('Source and destination are on different devices') |
|
|||
59 | if st.st_size != ts.st_size: |
|
|||
60 | continue |
|
71 | continue | |
61 | targets.append((fn, ts.st_size)) |
|
72 | targets.append((fn, ts.st_size)) | |
|
73 | df, ts = getdatafile(tgt) | |||
|
74 | if df: | |||
|
75 | targets.append((fn[:-1] + 'd', ts.st_size)) | |||
62 |
|
76 | |||
63 | return targets |
|
77 | return targets | |
64 |
|
78 | |||
65 | def relink(src, dst, files): |
|
79 | def relink(src, dst, files): | |
|
80 | def relinkfile(src, dst): | |||
|
81 | bak = dst + '.bak' | |||
|
82 | os.rename(dst, bak) | |||
|
83 | try: | |||
|
84 | os.link(src, dst) | |||
|
85 | except OSError: | |||
|
86 | os.rename(bak, dst) | |||
|
87 | raise | |||
|
88 | os.remove(bak) | |||
|
89 | ||||
66 | CHUNKLEN = 65536 |
|
90 | CHUNKLEN = 65536 | |
67 | relinked = 0 |
|
91 | relinked = 0 | |
68 | savedbytes = 0 |
|
92 | savedbytes = 0 | |
@@ -81,21 +105,22 b' def relink(src, dst, files):' | |||||
81 | if sin: |
|
105 | if sin: | |
82 | continue |
|
106 | continue | |
83 | try: |
|
107 | try: | |
84 |
|
|
108 | relinkfile(source, tgt) | |
85 | try: |
|
|||
86 | os.link(source, tgt) |
|
|||
87 | except OSError: |
|
|||
88 | os.rename(tgt + '.bak', tgt) |
|
|||
89 | raise |
|
|||
90 | print 'Relinked %s' % f |
|
109 | print 'Relinked %s' % f | |
91 | relinked += 1 |
|
110 | relinked += 1 | |
92 | savedbytes += sz |
|
111 | savedbytes += sz | |
93 | os.remove(tgt + '.bak') |
|
|||
94 | except OSError, inst: |
|
112 | except OSError, inst: | |
95 | print '%s: %s' % (tgt, str(inst)) |
|
113 | print '%s: %s' % (tgt, str(inst)) | |
96 |
|
114 | |||
97 | print 'Relinked %d files (%d bytes reclaimed)' % (relinked, savedbytes) |
|
115 | print 'Relinked %d files (%d bytes reclaimed)' % (relinked, savedbytes) | |
98 |
|
116 | |||
|
117 | try: | |||
|
118 | cfg = Config(sys.argv) | |||
|
119 | except ConfigError, inst: | |||
|
120 | print str(inst) | |||
|
121 | usage() | |||
|
122 | sys.exit(1) | |||
|
123 | ||||
99 | src = os.path.join(cfg.src, '.hg') |
|
124 | src = os.path.join(cfg.src, '.hg') | |
100 | dst = os.path.join(cfg.dst, '.hg') |
|
125 | dst = os.path.join(cfg.dst, '.hg') | |
101 | candidates = collect(src) |
|
126 | candidates = collect(src) |
@@ -2255,7 +2255,8 b' def revert(ui, repo, *pats, **opts):' | |||||
2255 | def handle(xlist, dobackup): |
|
2255 | def handle(xlist, dobackup): | |
2256 | xlist[0].append(abs) |
|
2256 | xlist[0].append(abs) | |
2257 | update[abs] = 1 |
|
2257 | update[abs] = 1 | |
2258 |
if dobackup and not opts['no_backup'] and |
|
2258 | if (dobackup and not opts['no_backup'] and | |
|
2259 | (os.path.islink(rel) or os.path.exists(rel))): | |||
2259 | bakname = "%s.orig" % rel |
|
2260 | bakname = "%s.orig" % rel | |
2260 | ui.note(_('saving current version of %s as %s\n') % |
|
2261 | ui.note(_('saving current version of %s as %s\n') % | |
2261 | (rel, bakname)) |
|
2262 | (rel, bakname)) |
@@ -102,11 +102,6 b' class localrepository(repo.repository):' | |||||
102 | self.filterpats = {} |
|
102 | self.filterpats = {} | |
103 | self.transhandle = None |
|
103 | self.transhandle = None | |
104 |
|
104 | |||
105 | self._link = lambda x: False |
|
|||
106 | if util.checklink(self.root): |
|
|||
107 | r = self.root # avoid circular reference in lambda |
|
|||
108 | self._link = lambda x: util.is_link(os.path.join(r, x)) |
|
|||
109 |
|
||||
110 | self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root) |
|
105 | self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root) | |
111 |
|
106 | |||
112 | def url(self): |
|
107 | def url(self): | |
@@ -505,6 +500,9 b' class localrepository(repo.repository):' | |||||
505 | def wfile(self, f, mode='r'): |
|
500 | def wfile(self, f, mode='r'): | |
506 | return self.wopener(f, mode) |
|
501 | return self.wopener(f, mode) | |
507 |
|
502 | |||
|
503 | def _link(self, f): | |||
|
504 | return os.path.islink(self.wjoin(f)) | |||
|
505 | ||||
508 | def _filter(self, filter, filename, data): |
|
506 | def _filter(self, filter, filename, data): | |
509 | if filter not in self.filterpats: |
|
507 | if filter not in self.filterpats: | |
510 | l = [] |
|
508 | l = [] | |
@@ -1052,10 +1050,11 b' class localrepository(repo.repository):' | |||||
1052 |
|
1050 | |||
1053 | def copy(self, source, dest, wlock=None): |
|
1051 | def copy(self, source, dest, wlock=None): | |
1054 | p = self.wjoin(dest) |
|
1052 | p = self.wjoin(dest) | |
1055 | if not os.path.exists(p): |
|
1053 | if not (os.path.exists(p) or os.path.islink(p)): | |
1056 | self.ui.warn(_("%s does not exist!\n") % dest) |
|
1054 | self.ui.warn(_("%s does not exist!\n") % dest) | |
1057 | elif not os.path.isfile(p): |
|
1055 | elif not (os.path.isfile(p) or os.path.islink(p)): | |
1058 |
self.ui.warn(_("copy failed: %s is not a file |
|
1056 | self.ui.warn(_("copy failed: %s is not a file or a " | |
|
1057 | "symbolic link\n") % dest) | |||
1059 | else: |
|
1058 | else: | |
1060 | if not wlock: |
|
1059 | if not wlock: | |
1061 | wlock = self.wlock() |
|
1060 | wlock = self.wlock() |
@@ -614,11 +614,18 b' def unlink(f):' | |||||
614 |
|
614 | |||
615 | def copyfile(src, dest): |
|
615 | def copyfile(src, dest): | |
616 | "copy a file, preserving mode" |
|
616 | "copy a file, preserving mode" | |
617 | try: |
|
617 | if os.path.islink(src): | |
618 | shutil.copyfile(src, dest) |
|
618 | try: | |
619 | shutil.copymode(src, dest) |
|
619 | os.unlink(dest) | |
620 | except shutil.Error, inst: |
|
620 | except: | |
621 | raise Abort(str(inst)) |
|
621 | pass | |
|
622 | os.symlink(os.readlink(src), dest) | |||
|
623 | else: | |||
|
624 | try: | |||
|
625 | shutil.copyfile(src, dest) | |||
|
626 | shutil.copymode(src, dest) | |||
|
627 | except shutil.Error, inst: | |||
|
628 | raise Abort(str(inst)) | |||
622 |
|
629 | |||
623 | def copyfiles(src, dst, hardlink=None): |
|
630 | def copyfiles(src, dst, hardlink=None): | |
624 | """Copy a directory tree using hardlinks if possible""" |
|
631 | """Copy a directory tree using hardlinks if possible""" | |
@@ -785,7 +792,7 b' def checklink(path):' | |||||
785 | def linkfunc(path, fallback): |
|
792 | def linkfunc(path, fallback): | |
786 | '''return an is_link() function with default to fallback''' |
|
793 | '''return an is_link() function with default to fallback''' | |
787 | if checklink(path): |
|
794 | if checklink(path): | |
788 |
return lambda x: is |
|
795 | return lambda x: os.path.islink(os.path.join(path, x)) | |
789 | return fallback |
|
796 | return fallback | |
790 |
|
797 | |||
791 | # Platform specific variants |
|
798 | # Platform specific variants | |
@@ -961,10 +968,6 b' else:' | |||||
961 | else: |
|
968 | else: | |
962 | os.chmod(f, s & 0666) |
|
969 | os.chmod(f, s & 0666) | |
963 |
|
970 | |||
964 | def is_link(f): |
|
|||
965 | """check whether a file is a symlink""" |
|
|||
966 | return (os.lstat(f).st_mode & 0120000 == 0120000) |
|
|||
967 |
|
||||
968 | def set_link(f, mode): |
|
971 | def set_link(f, mode): | |
969 | """make a file a symbolic link/regular file |
|
972 | """make a file a symbolic link/regular file | |
970 |
|
973 | |||
@@ -972,7 +975,7 b' else:' | |||||
972 | if a link is changed to a file, its link data become its contents |
|
975 | if a link is changed to a file, its link data become its contents | |
973 | """ |
|
976 | """ | |
974 |
|
977 | |||
975 |
m = is |
|
978 | m = os.path.islink(f) | |
976 | if m == bool(mode): |
|
979 | if m == bool(mode): | |
977 | return |
|
980 | return | |
978 |
|
981 |
General Comments 0
You need to be logged in to leave comments.
Login now