##// END OF EJS Templates
fncache: clean up fncache during strips...
Durham Goode -
r20885:f49d60fa default
parent child Browse files
Show More
@@ -1,176 +1,178 b''
1 # repair.py - functions for repository repair for mercurial
1 # repair.py - functions for repository repair for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 # Copyright 2007 Matt Mackall
4 # Copyright 2007 Matt Mackall
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from mercurial import changegroup
9 from mercurial import changegroup
10 from mercurial.node import short
10 from mercurial.node import short
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12 import os
12 import os
13 import errno
13 import errno
14
14
15 def _bundle(repo, bases, heads, node, suffix, compress=True):
15 def _bundle(repo, bases, heads, node, suffix, compress=True):
16 """create a bundle with the specified revisions as a backup"""
16 """create a bundle with the specified revisions as a backup"""
17 cg = repo.changegroupsubset(bases, heads, 'strip')
17 cg = repo.changegroupsubset(bases, heads, 'strip')
18 backupdir = repo.join("strip-backup")
18 backupdir = repo.join("strip-backup")
19 if not os.path.isdir(backupdir):
19 if not os.path.isdir(backupdir):
20 os.mkdir(backupdir)
20 os.mkdir(backupdir)
21 name = os.path.join(backupdir, "%s-%s.hg" % (short(node), suffix))
21 name = os.path.join(backupdir, "%s-%s.hg" % (short(node), suffix))
22 if compress:
22 if compress:
23 bundletype = "HG10BZ"
23 bundletype = "HG10BZ"
24 else:
24 else:
25 bundletype = "HG10UN"
25 bundletype = "HG10UN"
26 return changegroup.writebundle(cg, name, bundletype)
26 return changegroup.writebundle(cg, name, bundletype)
27
27
28 def _collectfiles(repo, striprev):
28 def _collectfiles(repo, striprev):
29 """find out the filelogs affected by the strip"""
29 """find out the filelogs affected by the strip"""
30 files = set()
30 files = set()
31
31
32 for x in xrange(striprev, len(repo)):
32 for x in xrange(striprev, len(repo)):
33 files.update(repo[x].files())
33 files.update(repo[x].files())
34
34
35 return sorted(files)
35 return sorted(files)
36
36
37 def _collectbrokencsets(repo, files, striprev):
37 def _collectbrokencsets(repo, files, striprev):
38 """return the changesets which will be broken by the truncation"""
38 """return the changesets which will be broken by the truncation"""
39 s = set()
39 s = set()
40 def collectone(revlog):
40 def collectone(revlog):
41 _, brokenset = revlog.getstrippoint(striprev)
41 _, brokenset = revlog.getstrippoint(striprev)
42 s.update([revlog.linkrev(r) for r in brokenset])
42 s.update([revlog.linkrev(r) for r in brokenset])
43
43
44 collectone(repo.manifest)
44 collectone(repo.manifest)
45 for fname in files:
45 for fname in files:
46 collectone(repo.file(fname))
46 collectone(repo.file(fname))
47
47
48 return s
48 return s
49
49
50 def strip(ui, repo, nodelist, backup="all", topic='backup'):
50 def strip(ui, repo, nodelist, backup="all", topic='backup'):
51 repo = repo.unfiltered()
51 repo = repo.unfiltered()
52 repo.destroying()
52 repo.destroying()
53
53
54 cl = repo.changelog
54 cl = repo.changelog
55 # TODO handle undo of merge sets
55 # TODO handle undo of merge sets
56 if isinstance(nodelist, str):
56 if isinstance(nodelist, str):
57 nodelist = [nodelist]
57 nodelist = [nodelist]
58 striplist = [cl.rev(node) for node in nodelist]
58 striplist = [cl.rev(node) for node in nodelist]
59 striprev = min(striplist)
59 striprev = min(striplist)
60
60
61 keeppartialbundle = backup == 'strip'
61 keeppartialbundle = backup == 'strip'
62
62
63 # Some revisions with rev > striprev may not be descendants of striprev.
63 # Some revisions with rev > striprev may not be descendants of striprev.
64 # We have to find these revisions and put them in a bundle, so that
64 # We have to find these revisions and put them in a bundle, so that
65 # we can restore them after the truncations.
65 # we can restore them after the truncations.
66 # To create the bundle we use repo.changegroupsubset which requires
66 # To create the bundle we use repo.changegroupsubset which requires
67 # the list of heads and bases of the set of interesting revisions.
67 # the list of heads and bases of the set of interesting revisions.
68 # (head = revision in the set that has no descendant in the set;
68 # (head = revision in the set that has no descendant in the set;
69 # base = revision in the set that has no ancestor in the set)
69 # base = revision in the set that has no ancestor in the set)
70 tostrip = set(striplist)
70 tostrip = set(striplist)
71 for rev in striplist:
71 for rev in striplist:
72 for desc in cl.descendants([rev]):
72 for desc in cl.descendants([rev]):
73 tostrip.add(desc)
73 tostrip.add(desc)
74
74
75 files = _collectfiles(repo, striprev)
75 files = _collectfiles(repo, striprev)
76 saverevs = _collectbrokencsets(repo, files, striprev)
76 saverevs = _collectbrokencsets(repo, files, striprev)
77
77
78 # compute heads
78 # compute heads
79 saveheads = set(saverevs)
79 saveheads = set(saverevs)
80 for r in xrange(striprev + 1, len(cl)):
80 for r in xrange(striprev + 1, len(cl)):
81 if r not in tostrip:
81 if r not in tostrip:
82 saverevs.add(r)
82 saverevs.add(r)
83 saveheads.difference_update(cl.parentrevs(r))
83 saveheads.difference_update(cl.parentrevs(r))
84 saveheads.add(r)
84 saveheads.add(r)
85 saveheads = [cl.node(r) for r in saveheads]
85 saveheads = [cl.node(r) for r in saveheads]
86
86
87 # compute base nodes
87 # compute base nodes
88 if saverevs:
88 if saverevs:
89 descendants = set(cl.descendants(saverevs))
89 descendants = set(cl.descendants(saverevs))
90 saverevs.difference_update(descendants)
90 saverevs.difference_update(descendants)
91 savebases = [cl.node(r) for r in saverevs]
91 savebases = [cl.node(r) for r in saverevs]
92 stripbases = [cl.node(r) for r in tostrip]
92 stripbases = [cl.node(r) for r in tostrip]
93
93
94 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
94 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
95 # is much faster
95 # is much faster
96 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
96 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
97 if newbmtarget:
97 if newbmtarget:
98 newbmtarget = repo[newbmtarget[0]].node()
98 newbmtarget = repo[newbmtarget[0]].node()
99 else:
99 else:
100 newbmtarget = '.'
100 newbmtarget = '.'
101
101
102 bm = repo._bookmarks
102 bm = repo._bookmarks
103 updatebm = []
103 updatebm = []
104 for m in bm:
104 for m in bm:
105 rev = repo[bm[m]].rev()
105 rev = repo[bm[m]].rev()
106 if rev in tostrip:
106 if rev in tostrip:
107 updatebm.append(m)
107 updatebm.append(m)
108
108
109 # create a changegroup for all the branches we need to keep
109 # create a changegroup for all the branches we need to keep
110 backupfile = None
110 backupfile = None
111 if backup == "all":
111 if backup == "all":
112 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
112 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
113 repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
113 repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
114 repo.ui.log("backupbundle", "saved backup bundle to %s\n", backupfile)
114 repo.ui.log("backupbundle", "saved backup bundle to %s\n", backupfile)
115 if saveheads or savebases:
115 if saveheads or savebases:
116 # do not compress partial bundle if we remove it from disk later
116 # do not compress partial bundle if we remove it from disk later
117 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
117 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
118 compress=keeppartialbundle)
118 compress=keeppartialbundle)
119
119
120 mfst = repo.manifest
120 mfst = repo.manifest
121
121
122 tr = repo.transaction("strip")
122 tr = repo.transaction("strip")
123 offset = len(tr.entries)
123 offset = len(tr.entries)
124
124
125 try:
125 try:
126 tr.startgroup()
126 tr.startgroup()
127 cl.strip(striprev, tr)
127 cl.strip(striprev, tr)
128 mfst.strip(striprev, tr)
128 mfst.strip(striprev, tr)
129 for fn in files:
129 for fn in files:
130 repo.file(fn).strip(striprev, tr)
130 repo.file(fn).strip(striprev, tr)
131 tr.endgroup()
131 tr.endgroup()
132
132
133 try:
133 try:
134 for i in xrange(offset, len(tr.entries)):
134 for i in xrange(offset, len(tr.entries)):
135 file, troffset, ignore = tr.entries[i]
135 file, troffset, ignore = tr.entries[i]
136 repo.sopener(file, 'a').truncate(troffset)
136 repo.sopener(file, 'a').truncate(troffset)
137 if troffset == 0:
138 repo.store.markremoved(file)
137 tr.close()
139 tr.close()
138 except: # re-raises
140 except: # re-raises
139 tr.abort()
141 tr.abort()
140 raise
142 raise
141
143
142 if saveheads or savebases:
144 if saveheads or savebases:
143 ui.note(_("adding branch\n"))
145 ui.note(_("adding branch\n"))
144 f = open(chgrpfile, "rb")
146 f = open(chgrpfile, "rb")
145 gen = changegroup.readbundle(f, chgrpfile)
147 gen = changegroup.readbundle(f, chgrpfile)
146 if not repo.ui.verbose:
148 if not repo.ui.verbose:
147 # silence internal shuffling chatter
149 # silence internal shuffling chatter
148 repo.ui.pushbuffer()
150 repo.ui.pushbuffer()
149 repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True)
151 repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True)
150 if not repo.ui.verbose:
152 if not repo.ui.verbose:
151 repo.ui.popbuffer()
153 repo.ui.popbuffer()
152 f.close()
154 f.close()
153 if not keeppartialbundle:
155 if not keeppartialbundle:
154 os.unlink(chgrpfile)
156 os.unlink(chgrpfile)
155
157
156 # remove undo files
158 # remove undo files
157 for undofile in repo.undofiles():
159 for undofile in repo.undofiles():
158 try:
160 try:
159 os.unlink(undofile)
161 os.unlink(undofile)
160 except OSError, e:
162 except OSError, e:
161 if e.errno != errno.ENOENT:
163 if e.errno != errno.ENOENT:
162 ui.warn(_('error removing %s: %s\n') % (undofile, str(e)))
164 ui.warn(_('error removing %s: %s\n') % (undofile, str(e)))
163
165
164 for m in updatebm:
166 for m in updatebm:
165 bm[m] = repo[newbmtarget].node()
167 bm[m] = repo[newbmtarget].node()
166 bm.write()
168 bm.write()
167 except: # re-raises
169 except: # re-raises
168 if backupfile:
170 if backupfile:
169 ui.warn(_("strip failed, full bundle stored in '%s'\n")
171 ui.warn(_("strip failed, full bundle stored in '%s'\n")
170 % backupfile)
172 % backupfile)
171 elif saveheads:
173 elif saveheads:
172 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
174 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
173 % chgrpfile)
175 % chgrpfile)
174 raise
176 raise
175
177
176 repo.destroyed()
178 repo.destroyed()
@@ -1,529 +1,544 b''
1 # store.py - repository store handling for Mercurial
1 # store.py - repository store handling for Mercurial
2 #
2 #
3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import scmutil, util, parsers
9 import scmutil, util, parsers
10 import os, stat, errno
10 import os, stat, errno
11
11
12 _sha = util.sha1
12 _sha = util.sha1
13
13
14 # This avoids a collision between a file named foo and a dir named
14 # This avoids a collision between a file named foo and a dir named
15 # foo.i or foo.d
15 # foo.i or foo.d
16 def _encodedir(path):
16 def _encodedir(path):
17 '''
17 '''
18 >>> _encodedir('data/foo.i')
18 >>> _encodedir('data/foo.i')
19 'data/foo.i'
19 'data/foo.i'
20 >>> _encodedir('data/foo.i/bla.i')
20 >>> _encodedir('data/foo.i/bla.i')
21 'data/foo.i.hg/bla.i'
21 'data/foo.i.hg/bla.i'
22 >>> _encodedir('data/foo.i.hg/bla.i')
22 >>> _encodedir('data/foo.i.hg/bla.i')
23 'data/foo.i.hg.hg/bla.i'
23 'data/foo.i.hg.hg/bla.i'
24 >>> _encodedir('data/foo.i\\ndata/foo.i/bla.i\\ndata/foo.i.hg/bla.i\\n')
24 >>> _encodedir('data/foo.i\\ndata/foo.i/bla.i\\ndata/foo.i.hg/bla.i\\n')
25 'data/foo.i\\ndata/foo.i.hg/bla.i\\ndata/foo.i.hg.hg/bla.i\\n'
25 'data/foo.i\\ndata/foo.i.hg/bla.i\\ndata/foo.i.hg.hg/bla.i\\n'
26 '''
26 '''
27 return (path
27 return (path
28 .replace(".hg/", ".hg.hg/")
28 .replace(".hg/", ".hg.hg/")
29 .replace(".i/", ".i.hg/")
29 .replace(".i/", ".i.hg/")
30 .replace(".d/", ".d.hg/"))
30 .replace(".d/", ".d.hg/"))
31
31
32 encodedir = getattr(parsers, 'encodedir', _encodedir)
32 encodedir = getattr(parsers, 'encodedir', _encodedir)
33
33
34 def decodedir(path):
34 def decodedir(path):
35 '''
35 '''
36 >>> decodedir('data/foo.i')
36 >>> decodedir('data/foo.i')
37 'data/foo.i'
37 'data/foo.i'
38 >>> decodedir('data/foo.i.hg/bla.i')
38 >>> decodedir('data/foo.i.hg/bla.i')
39 'data/foo.i/bla.i'
39 'data/foo.i/bla.i'
40 >>> decodedir('data/foo.i.hg.hg/bla.i')
40 >>> decodedir('data/foo.i.hg.hg/bla.i')
41 'data/foo.i.hg/bla.i'
41 'data/foo.i.hg/bla.i'
42 '''
42 '''
43 if ".hg/" not in path:
43 if ".hg/" not in path:
44 return path
44 return path
45 return (path
45 return (path
46 .replace(".d.hg/", ".d/")
46 .replace(".d.hg/", ".d/")
47 .replace(".i.hg/", ".i/")
47 .replace(".i.hg/", ".i/")
48 .replace(".hg.hg/", ".hg/"))
48 .replace(".hg.hg/", ".hg/"))
49
49
50 def _buildencodefun():
50 def _buildencodefun():
51 '''
51 '''
52 >>> enc, dec = _buildencodefun()
52 >>> enc, dec = _buildencodefun()
53
53
54 >>> enc('nothing/special.txt')
54 >>> enc('nothing/special.txt')
55 'nothing/special.txt'
55 'nothing/special.txt'
56 >>> dec('nothing/special.txt')
56 >>> dec('nothing/special.txt')
57 'nothing/special.txt'
57 'nothing/special.txt'
58
58
59 >>> enc('HELLO')
59 >>> enc('HELLO')
60 '_h_e_l_l_o'
60 '_h_e_l_l_o'
61 >>> dec('_h_e_l_l_o')
61 >>> dec('_h_e_l_l_o')
62 'HELLO'
62 'HELLO'
63
63
64 >>> enc('hello:world?')
64 >>> enc('hello:world?')
65 'hello~3aworld~3f'
65 'hello~3aworld~3f'
66 >>> dec('hello~3aworld~3f')
66 >>> dec('hello~3aworld~3f')
67 'hello:world?'
67 'hello:world?'
68
68
69 >>> enc('the\x07quick\xADshot')
69 >>> enc('the\x07quick\xADshot')
70 'the~07quick~adshot'
70 'the~07quick~adshot'
71 >>> dec('the~07quick~adshot')
71 >>> dec('the~07quick~adshot')
72 'the\\x07quick\\xadshot'
72 'the\\x07quick\\xadshot'
73 '''
73 '''
74 e = '_'
74 e = '_'
75 winreserved = [ord(x) for x in '\\:*?"<>|']
75 winreserved = [ord(x) for x in '\\:*?"<>|']
76 cmap = dict([(chr(x), chr(x)) for x in xrange(127)])
76 cmap = dict([(chr(x), chr(x)) for x in xrange(127)])
77 for x in (range(32) + range(126, 256) + winreserved):
77 for x in (range(32) + range(126, 256) + winreserved):
78 cmap[chr(x)] = "~%02x" % x
78 cmap[chr(x)] = "~%02x" % x
79 for x in range(ord("A"), ord("Z") + 1) + [ord(e)]:
79 for x in range(ord("A"), ord("Z") + 1) + [ord(e)]:
80 cmap[chr(x)] = e + chr(x).lower()
80 cmap[chr(x)] = e + chr(x).lower()
81 dmap = {}
81 dmap = {}
82 for k, v in cmap.iteritems():
82 for k, v in cmap.iteritems():
83 dmap[v] = k
83 dmap[v] = k
84 def decode(s):
84 def decode(s):
85 i = 0
85 i = 0
86 while i < len(s):
86 while i < len(s):
87 for l in xrange(1, 4):
87 for l in xrange(1, 4):
88 try:
88 try:
89 yield dmap[s[i:i + l]]
89 yield dmap[s[i:i + l]]
90 i += l
90 i += l
91 break
91 break
92 except KeyError:
92 except KeyError:
93 pass
93 pass
94 else:
94 else:
95 raise KeyError
95 raise KeyError
96 return (lambda s: ''.join([cmap[c] for c in s]),
96 return (lambda s: ''.join([cmap[c] for c in s]),
97 lambda s: ''.join(list(decode(s))))
97 lambda s: ''.join(list(decode(s))))
98
98
99 _encodefname, _decodefname = _buildencodefun()
99 _encodefname, _decodefname = _buildencodefun()
100
100
101 def encodefilename(s):
101 def encodefilename(s):
102 '''
102 '''
103 >>> encodefilename('foo.i/bar.d/bla.hg/hi:world?/HELLO')
103 >>> encodefilename('foo.i/bar.d/bla.hg/hi:world?/HELLO')
104 'foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o'
104 'foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o'
105 '''
105 '''
106 return _encodefname(encodedir(s))
106 return _encodefname(encodedir(s))
107
107
108 def decodefilename(s):
108 def decodefilename(s):
109 '''
109 '''
110 >>> decodefilename('foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o')
110 >>> decodefilename('foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o')
111 'foo.i/bar.d/bla.hg/hi:world?/HELLO'
111 'foo.i/bar.d/bla.hg/hi:world?/HELLO'
112 '''
112 '''
113 return decodedir(_decodefname(s))
113 return decodedir(_decodefname(s))
114
114
115 def _buildlowerencodefun():
115 def _buildlowerencodefun():
116 '''
116 '''
117 >>> f = _buildlowerencodefun()
117 >>> f = _buildlowerencodefun()
118 >>> f('nothing/special.txt')
118 >>> f('nothing/special.txt')
119 'nothing/special.txt'
119 'nothing/special.txt'
120 >>> f('HELLO')
120 >>> f('HELLO')
121 'hello'
121 'hello'
122 >>> f('hello:world?')
122 >>> f('hello:world?')
123 'hello~3aworld~3f'
123 'hello~3aworld~3f'
124 >>> f('the\x07quick\xADshot')
124 >>> f('the\x07quick\xADshot')
125 'the~07quick~adshot'
125 'the~07quick~adshot'
126 '''
126 '''
127 winreserved = [ord(x) for x in '\\:*?"<>|']
127 winreserved = [ord(x) for x in '\\:*?"<>|']
128 cmap = dict([(chr(x), chr(x)) for x in xrange(127)])
128 cmap = dict([(chr(x), chr(x)) for x in xrange(127)])
129 for x in (range(32) + range(126, 256) + winreserved):
129 for x in (range(32) + range(126, 256) + winreserved):
130 cmap[chr(x)] = "~%02x" % x
130 cmap[chr(x)] = "~%02x" % x
131 for x in range(ord("A"), ord("Z") + 1):
131 for x in range(ord("A"), ord("Z") + 1):
132 cmap[chr(x)] = chr(x).lower()
132 cmap[chr(x)] = chr(x).lower()
133 return lambda s: "".join([cmap[c] for c in s])
133 return lambda s: "".join([cmap[c] for c in s])
134
134
135 lowerencode = getattr(parsers, 'lowerencode', None) or _buildlowerencodefun()
135 lowerencode = getattr(parsers, 'lowerencode', None) or _buildlowerencodefun()
136
136
137 # Windows reserved names: con, prn, aux, nul, com1..com9, lpt1..lpt9
137 # Windows reserved names: con, prn, aux, nul, com1..com9, lpt1..lpt9
138 _winres3 = ('aux', 'con', 'prn', 'nul') # length 3
138 _winres3 = ('aux', 'con', 'prn', 'nul') # length 3
139 _winres4 = ('com', 'lpt') # length 4 (with trailing 1..9)
139 _winres4 = ('com', 'lpt') # length 4 (with trailing 1..9)
140 def _auxencode(path, dotencode):
140 def _auxencode(path, dotencode):
141 '''
141 '''
142 Encodes filenames containing names reserved by Windows or which end in
142 Encodes filenames containing names reserved by Windows or which end in
143 period or space. Does not touch other single reserved characters c.
143 period or space. Does not touch other single reserved characters c.
144 Specifically, c in '\\:*?"<>|' or ord(c) <= 31 are *not* encoded here.
144 Specifically, c in '\\:*?"<>|' or ord(c) <= 31 are *not* encoded here.
145 Additionally encodes space or period at the beginning, if dotencode is
145 Additionally encodes space or period at the beginning, if dotencode is
146 True. Parameter path is assumed to be all lowercase.
146 True. Parameter path is assumed to be all lowercase.
147 A segment only needs encoding if a reserved name appears as a
147 A segment only needs encoding if a reserved name appears as a
148 basename (e.g. "aux", "aux.foo"). A directory or file named "foo.aux"
148 basename (e.g. "aux", "aux.foo"). A directory or file named "foo.aux"
149 doesn't need encoding.
149 doesn't need encoding.
150
150
151 >>> s = '.foo/aux.txt/txt.aux/con/prn/nul/foo.'
151 >>> s = '.foo/aux.txt/txt.aux/con/prn/nul/foo.'
152 >>> _auxencode(s.split('/'), True)
152 >>> _auxencode(s.split('/'), True)
153 ['~2efoo', 'au~78.txt', 'txt.aux', 'co~6e', 'pr~6e', 'nu~6c', 'foo~2e']
153 ['~2efoo', 'au~78.txt', 'txt.aux', 'co~6e', 'pr~6e', 'nu~6c', 'foo~2e']
154 >>> s = '.com1com2/lpt9.lpt4.lpt1/conprn/com0/lpt0/foo.'
154 >>> s = '.com1com2/lpt9.lpt4.lpt1/conprn/com0/lpt0/foo.'
155 >>> _auxencode(s.split('/'), False)
155 >>> _auxencode(s.split('/'), False)
156 ['.com1com2', 'lp~749.lpt4.lpt1', 'conprn', 'com0', 'lpt0', 'foo~2e']
156 ['.com1com2', 'lp~749.lpt4.lpt1', 'conprn', 'com0', 'lpt0', 'foo~2e']
157 >>> _auxencode(['foo. '], True)
157 >>> _auxencode(['foo. '], True)
158 ['foo.~20']
158 ['foo.~20']
159 >>> _auxencode([' .foo'], True)
159 >>> _auxencode([' .foo'], True)
160 ['~20.foo']
160 ['~20.foo']
161 '''
161 '''
162 for i, n in enumerate(path):
162 for i, n in enumerate(path):
163 if not n:
163 if not n:
164 continue
164 continue
165 if dotencode and n[0] in '. ':
165 if dotencode and n[0] in '. ':
166 n = "~%02x" % ord(n[0]) + n[1:]
166 n = "~%02x" % ord(n[0]) + n[1:]
167 path[i] = n
167 path[i] = n
168 else:
168 else:
169 l = n.find('.')
169 l = n.find('.')
170 if l == -1:
170 if l == -1:
171 l = len(n)
171 l = len(n)
172 if ((l == 3 and n[:3] in _winres3) or
172 if ((l == 3 and n[:3] in _winres3) or
173 (l == 4 and n[3] <= '9' and n[3] >= '1'
173 (l == 4 and n[3] <= '9' and n[3] >= '1'
174 and n[:3] in _winres4)):
174 and n[:3] in _winres4)):
175 # encode third letter ('aux' -> 'au~78')
175 # encode third letter ('aux' -> 'au~78')
176 ec = "~%02x" % ord(n[2])
176 ec = "~%02x" % ord(n[2])
177 n = n[0:2] + ec + n[3:]
177 n = n[0:2] + ec + n[3:]
178 path[i] = n
178 path[i] = n
179 if n[-1] in '. ':
179 if n[-1] in '. ':
180 # encode last period or space ('foo...' -> 'foo..~2e')
180 # encode last period or space ('foo...' -> 'foo..~2e')
181 path[i] = n[:-1] + "~%02x" % ord(n[-1])
181 path[i] = n[:-1] + "~%02x" % ord(n[-1])
182 return path
182 return path
183
183
184 _maxstorepathlen = 120
184 _maxstorepathlen = 120
185 _dirprefixlen = 8
185 _dirprefixlen = 8
186 _maxshortdirslen = 8 * (_dirprefixlen + 1) - 4
186 _maxshortdirslen = 8 * (_dirprefixlen + 1) - 4
187
187
188 def _hashencode(path, dotencode):
188 def _hashencode(path, dotencode):
189 digest = _sha(path).hexdigest()
189 digest = _sha(path).hexdigest()
190 le = lowerencode(path).split('/')[1:]
190 le = lowerencode(path).split('/')[1:]
191 parts = _auxencode(le, dotencode)
191 parts = _auxencode(le, dotencode)
192 basename = parts[-1]
192 basename = parts[-1]
193 _root, ext = os.path.splitext(basename)
193 _root, ext = os.path.splitext(basename)
194 sdirs = []
194 sdirs = []
195 sdirslen = 0
195 sdirslen = 0
196 for p in parts[:-1]:
196 for p in parts[:-1]:
197 d = p[:_dirprefixlen]
197 d = p[:_dirprefixlen]
198 if d[-1] in '. ':
198 if d[-1] in '. ':
199 # Windows can't access dirs ending in period or space
199 # Windows can't access dirs ending in period or space
200 d = d[:-1] + '_'
200 d = d[:-1] + '_'
201 if sdirslen == 0:
201 if sdirslen == 0:
202 t = len(d)
202 t = len(d)
203 else:
203 else:
204 t = sdirslen + 1 + len(d)
204 t = sdirslen + 1 + len(d)
205 if t > _maxshortdirslen:
205 if t > _maxshortdirslen:
206 break
206 break
207 sdirs.append(d)
207 sdirs.append(d)
208 sdirslen = t
208 sdirslen = t
209 dirs = '/'.join(sdirs)
209 dirs = '/'.join(sdirs)
210 if len(dirs) > 0:
210 if len(dirs) > 0:
211 dirs += '/'
211 dirs += '/'
212 res = 'dh/' + dirs + digest + ext
212 res = 'dh/' + dirs + digest + ext
213 spaceleft = _maxstorepathlen - len(res)
213 spaceleft = _maxstorepathlen - len(res)
214 if spaceleft > 0:
214 if spaceleft > 0:
215 filler = basename[:spaceleft]
215 filler = basename[:spaceleft]
216 res = 'dh/' + dirs + filler + digest + ext
216 res = 'dh/' + dirs + filler + digest + ext
217 return res
217 return res
218
218
219 def _hybridencode(path, dotencode):
219 def _hybridencode(path, dotencode):
220 '''encodes path with a length limit
220 '''encodes path with a length limit
221
221
222 Encodes all paths that begin with 'data/', according to the following.
222 Encodes all paths that begin with 'data/', according to the following.
223
223
224 Default encoding (reversible):
224 Default encoding (reversible):
225
225
226 Encodes all uppercase letters 'X' as '_x'. All reserved or illegal
226 Encodes all uppercase letters 'X' as '_x'. All reserved or illegal
227 characters are encoded as '~xx', where xx is the two digit hex code
227 characters are encoded as '~xx', where xx is the two digit hex code
228 of the character (see encodefilename).
228 of the character (see encodefilename).
229 Relevant path components consisting of Windows reserved filenames are
229 Relevant path components consisting of Windows reserved filenames are
230 masked by encoding the third character ('aux' -> 'au~78', see _auxencode).
230 masked by encoding the third character ('aux' -> 'au~78', see _auxencode).
231
231
232 Hashed encoding (not reversible):
232 Hashed encoding (not reversible):
233
233
234 If the default-encoded path is longer than _maxstorepathlen, a
234 If the default-encoded path is longer than _maxstorepathlen, a
235 non-reversible hybrid hashing of the path is done instead.
235 non-reversible hybrid hashing of the path is done instead.
236 This encoding uses up to _dirprefixlen characters of all directory
236 This encoding uses up to _dirprefixlen characters of all directory
237 levels of the lowerencoded path, but not more levels than can fit into
237 levels of the lowerencoded path, but not more levels than can fit into
238 _maxshortdirslen.
238 _maxshortdirslen.
239 Then follows the filler followed by the sha digest of the full path.
239 Then follows the filler followed by the sha digest of the full path.
240 The filler is the beginning of the basename of the lowerencoded path
240 The filler is the beginning of the basename of the lowerencoded path
241 (the basename is everything after the last path separator). The filler
241 (the basename is everything after the last path separator). The filler
242 is as long as possible, filling in characters from the basename until
242 is as long as possible, filling in characters from the basename until
243 the encoded path has _maxstorepathlen characters (or all chars of the
243 the encoded path has _maxstorepathlen characters (or all chars of the
244 basename have been taken).
244 basename have been taken).
245 The extension (e.g. '.i' or '.d') is preserved.
245 The extension (e.g. '.i' or '.d') is preserved.
246
246
247 The string 'data/' at the beginning is replaced with 'dh/', if the hashed
247 The string 'data/' at the beginning is replaced with 'dh/', if the hashed
248 encoding was used.
248 encoding was used.
249 '''
249 '''
250 path = encodedir(path)
250 path = encodedir(path)
251 ef = _encodefname(path).split('/')
251 ef = _encodefname(path).split('/')
252 res = '/'.join(_auxencode(ef, dotencode))
252 res = '/'.join(_auxencode(ef, dotencode))
253 if len(res) > _maxstorepathlen:
253 if len(res) > _maxstorepathlen:
254 res = _hashencode(path, dotencode)
254 res = _hashencode(path, dotencode)
255 return res
255 return res
256
256
257 def _pathencode(path):
257 def _pathencode(path):
258 de = encodedir(path)
258 de = encodedir(path)
259 if len(path) > _maxstorepathlen:
259 if len(path) > _maxstorepathlen:
260 return _hashencode(de, True)
260 return _hashencode(de, True)
261 ef = _encodefname(de).split('/')
261 ef = _encodefname(de).split('/')
262 res = '/'.join(_auxencode(ef, True))
262 res = '/'.join(_auxencode(ef, True))
263 if len(res) > _maxstorepathlen:
263 if len(res) > _maxstorepathlen:
264 return _hashencode(de, True)
264 return _hashencode(de, True)
265 return res
265 return res
266
266
267 _pathencode = getattr(parsers, 'pathencode', _pathencode)
267 _pathencode = getattr(parsers, 'pathencode', _pathencode)
268
268
269 def _plainhybridencode(f):
269 def _plainhybridencode(f):
270 return _hybridencode(f, False)
270 return _hybridencode(f, False)
271
271
272 def _calcmode(vfs):
272 def _calcmode(vfs):
273 try:
273 try:
274 # files in .hg/ will be created using this mode
274 # files in .hg/ will be created using this mode
275 mode = vfs.stat().st_mode
275 mode = vfs.stat().st_mode
276 # avoid some useless chmods
276 # avoid some useless chmods
277 if (0777 & ~util.umask) == (0777 & mode):
277 if (0777 & ~util.umask) == (0777 & mode):
278 mode = None
278 mode = None
279 except OSError:
279 except OSError:
280 mode = None
280 mode = None
281 return mode
281 return mode
282
282
283 _data = ('data 00manifest.d 00manifest.i 00changelog.d 00changelog.i'
283 _data = ('data 00manifest.d 00manifest.i 00changelog.d 00changelog.i'
284 ' phaseroots obsstore')
284 ' phaseroots obsstore')
285
285
286 class basicstore(object):
286 class basicstore(object):
287 '''base class for local repository stores'''
287 '''base class for local repository stores'''
288 def __init__(self, path, vfstype):
288 def __init__(self, path, vfstype):
289 vfs = vfstype(path)
289 vfs = vfstype(path)
290 self.path = vfs.base
290 self.path = vfs.base
291 self.createmode = _calcmode(vfs)
291 self.createmode = _calcmode(vfs)
292 vfs.createmode = self.createmode
292 vfs.createmode = self.createmode
293 self.rawvfs = vfs
293 self.rawvfs = vfs
294 self.vfs = scmutil.filtervfs(vfs, encodedir)
294 self.vfs = scmutil.filtervfs(vfs, encodedir)
295 self.opener = self.vfs
295 self.opener = self.vfs
296
296
297 def join(self, f):
297 def join(self, f):
298 return self.path + '/' + encodedir(f)
298 return self.path + '/' + encodedir(f)
299
299
300 def _walk(self, relpath, recurse):
300 def _walk(self, relpath, recurse):
301 '''yields (unencoded, encoded, size)'''
301 '''yields (unencoded, encoded, size)'''
302 path = self.path
302 path = self.path
303 if relpath:
303 if relpath:
304 path += '/' + relpath
304 path += '/' + relpath
305 striplen = len(self.path) + 1
305 striplen = len(self.path) + 1
306 l = []
306 l = []
307 if self.rawvfs.isdir(path):
307 if self.rawvfs.isdir(path):
308 visit = [path]
308 visit = [path]
309 readdir = self.rawvfs.readdir
309 readdir = self.rawvfs.readdir
310 while visit:
310 while visit:
311 p = visit.pop()
311 p = visit.pop()
312 for f, kind, st in readdir(p, stat=True):
312 for f, kind, st in readdir(p, stat=True):
313 fp = p + '/' + f
313 fp = p + '/' + f
314 if kind == stat.S_IFREG and f[-2:] in ('.d', '.i'):
314 if kind == stat.S_IFREG and f[-2:] in ('.d', '.i'):
315 n = util.pconvert(fp[striplen:])
315 n = util.pconvert(fp[striplen:])
316 l.append((decodedir(n), n, st.st_size))
316 l.append((decodedir(n), n, st.st_size))
317 elif kind == stat.S_IFDIR and recurse:
317 elif kind == stat.S_IFDIR and recurse:
318 visit.append(fp)
318 visit.append(fp)
319 l.sort()
319 l.sort()
320 return l
320 return l
321
321
322 def datafiles(self):
322 def datafiles(self):
323 return self._walk('data', True)
323 return self._walk('data', True)
324
324
325 def topfiles(self):
325 def topfiles(self):
326 # yield manifest before changelog
326 # yield manifest before changelog
327 return reversed(self._walk('', False))
327 return reversed(self._walk('', False))
328
328
329 def walk(self):
329 def walk(self):
330 '''yields (unencoded, encoded, size)'''
330 '''yields (unencoded, encoded, size)'''
331 # yield data files first
331 # yield data files first
332 for x in self.datafiles():
332 for x in self.datafiles():
333 yield x
333 yield x
334 for x in self.topfiles():
334 for x in self.topfiles():
335 yield x
335 yield x
336
336
337 def copylist(self):
337 def copylist(self):
338 return ['requires'] + _data.split()
338 return ['requires'] + _data.split()
339
339
340 def write(self, tr):
340 def write(self, tr):
341 pass
341 pass
342
342
343 def invalidatecaches(self):
343 def invalidatecaches(self):
344 pass
344 pass
345
345
346 def markremoved(self, fn):
347 pass
348
346 def __contains__(self, path):
349 def __contains__(self, path):
347 '''Checks if the store contains path'''
350 '''Checks if the store contains path'''
348 path = "/".join(("data", path))
351 path = "/".join(("data", path))
349 # file?
352 # file?
350 if self.vfs.exists(path + ".i"):
353 if self.vfs.exists(path + ".i"):
351 return True
354 return True
352 # dir?
355 # dir?
353 if not path.endswith("/"):
356 if not path.endswith("/"):
354 path = path + "/"
357 path = path + "/"
355 return self.vfs.exists(path)
358 return self.vfs.exists(path)
356
359
357 class encodedstore(basicstore):
360 class encodedstore(basicstore):
358 def __init__(self, path, vfstype):
361 def __init__(self, path, vfstype):
359 vfs = vfstype(path + '/store')
362 vfs = vfstype(path + '/store')
360 self.path = vfs.base
363 self.path = vfs.base
361 self.createmode = _calcmode(vfs)
364 self.createmode = _calcmode(vfs)
362 vfs.createmode = self.createmode
365 vfs.createmode = self.createmode
363 self.rawvfs = vfs
366 self.rawvfs = vfs
364 self.vfs = scmutil.filtervfs(vfs, encodefilename)
367 self.vfs = scmutil.filtervfs(vfs, encodefilename)
365 self.opener = self.vfs
368 self.opener = self.vfs
366
369
367 def datafiles(self):
370 def datafiles(self):
368 for a, b, size in self._walk('data', True):
371 for a, b, size in self._walk('data', True):
369 try:
372 try:
370 a = decodefilename(a)
373 a = decodefilename(a)
371 except KeyError:
374 except KeyError:
372 a = None
375 a = None
373 yield a, b, size
376 yield a, b, size
374
377
375 def join(self, f):
378 def join(self, f):
376 return self.path + '/' + encodefilename(f)
379 return self.path + '/' + encodefilename(f)
377
380
378 def copylist(self):
381 def copylist(self):
379 return (['requires', '00changelog.i'] +
382 return (['requires', '00changelog.i'] +
380 ['store/' + f for f in _data.split()])
383 ['store/' + f for f in _data.split()])
381
384
382 class fncache(object):
385 class fncache(object):
383 # the filename used to be partially encoded
386 # the filename used to be partially encoded
384 # hence the encodedir/decodedir dance
387 # hence the encodedir/decodedir dance
385 def __init__(self, vfs):
388 def __init__(self, vfs):
386 self.vfs = vfs
389 self.vfs = vfs
387 self.entries = None
390 self.entries = None
388 self._dirty = False
391 self._dirty = False
389
392
390 def _load(self):
393 def _load(self):
391 '''fill the entries from the fncache file'''
394 '''fill the entries from the fncache file'''
392 self._dirty = False
395 self._dirty = False
393 try:
396 try:
394 fp = self.vfs('fncache', mode='rb')
397 fp = self.vfs('fncache', mode='rb')
395 except IOError:
398 except IOError:
396 # skip nonexistent file
399 # skip nonexistent file
397 self.entries = set()
400 self.entries = set()
398 return
401 return
399 self.entries = set(decodedir(fp.read()).splitlines())
402 self.entries = set(decodedir(fp.read()).splitlines())
400 if '' in self.entries:
403 if '' in self.entries:
401 fp.seek(0)
404 fp.seek(0)
402 for n, line in enumerate(fp):
405 for n, line in enumerate(fp):
403 if not line.rstrip('\n'):
406 if not line.rstrip('\n'):
404 t = _('invalid entry in fncache, line %s') % (n + 1)
407 t = _('invalid entry in fncache, line %s') % (n + 1)
405 raise util.Abort(t)
408 raise util.Abort(t)
406 fp.close()
409 fp.close()
407
410
408 def write(self, tr):
411 def write(self, tr):
409 if self._dirty:
412 if self._dirty:
410 tr.addbackup('fncache')
413 tr.addbackup('fncache')
411 fp = self.vfs('fncache', mode='wb', atomictemp=True)
414 fp = self.vfs('fncache', mode='wb', atomictemp=True)
412 if self.entries:
415 if self.entries:
413 fp.write(encodedir('\n'.join(self.entries) + '\n'))
416 fp.write(encodedir('\n'.join(self.entries) + '\n'))
414 fp.close()
417 fp.close()
415 self._dirty = False
418 self._dirty = False
416
419
417 def add(self, fn):
420 def add(self, fn):
418 if self.entries is None:
421 if self.entries is None:
419 self._load()
422 self._load()
420 if fn not in self.entries:
423 if fn not in self.entries:
421 self._dirty = True
424 self._dirty = True
422 self.entries.add(fn)
425 self.entries.add(fn)
423
426
427 def remove(self, fn):
428 if self.entries is None:
429 self._load()
430 try:
431 self.entries.remove(fn)
432 self._dirty = True
433 except KeyError:
434 pass
435
424 def __contains__(self, fn):
436 def __contains__(self, fn):
425 if self.entries is None:
437 if self.entries is None:
426 self._load()
438 self._load()
427 return fn in self.entries
439 return fn in self.entries
428
440
429 def __iter__(self):
441 def __iter__(self):
430 if self.entries is None:
442 if self.entries is None:
431 self._load()
443 self._load()
432 return iter(self.entries)
444 return iter(self.entries)
433
445
434 class _fncachevfs(scmutil.abstractvfs, scmutil.auditvfs):
446 class _fncachevfs(scmutil.abstractvfs, scmutil.auditvfs):
435 def __init__(self, vfs, fnc, encode):
447 def __init__(self, vfs, fnc, encode):
436 scmutil.auditvfs.__init__(self, vfs)
448 scmutil.auditvfs.__init__(self, vfs)
437 self.fncache = fnc
449 self.fncache = fnc
438 self.encode = encode
450 self.encode = encode
439
451
440 def __call__(self, path, mode='r', *args, **kw):
452 def __call__(self, path, mode='r', *args, **kw):
441 if mode not in ('r', 'rb') and path.startswith('data/'):
453 if mode not in ('r', 'rb') and path.startswith('data/'):
442 self.fncache.add(path)
454 self.fncache.add(path)
443 return self.vfs(self.encode(path), mode, *args, **kw)
455 return self.vfs(self.encode(path), mode, *args, **kw)
444
456
445 def join(self, path):
457 def join(self, path):
446 if path:
458 if path:
447 return self.vfs.join(self.encode(path))
459 return self.vfs.join(self.encode(path))
448 else:
460 else:
449 return self.vfs.join(path)
461 return self.vfs.join(path)
450
462
451 class fncachestore(basicstore):
463 class fncachestore(basicstore):
452 def __init__(self, path, vfstype, dotencode):
464 def __init__(self, path, vfstype, dotencode):
453 if dotencode:
465 if dotencode:
454 encode = _pathencode
466 encode = _pathencode
455 else:
467 else:
456 encode = _plainhybridencode
468 encode = _plainhybridencode
457 self.encode = encode
469 self.encode = encode
458 vfs = vfstype(path + '/store')
470 vfs = vfstype(path + '/store')
459 self.path = vfs.base
471 self.path = vfs.base
460 self.pathsep = self.path + '/'
472 self.pathsep = self.path + '/'
461 self.createmode = _calcmode(vfs)
473 self.createmode = _calcmode(vfs)
462 vfs.createmode = self.createmode
474 vfs.createmode = self.createmode
463 self.rawvfs = vfs
475 self.rawvfs = vfs
464 fnc = fncache(vfs)
476 fnc = fncache(vfs)
465 self.fncache = fnc
477 self.fncache = fnc
466 self.vfs = _fncachevfs(vfs, fnc, encode)
478 self.vfs = _fncachevfs(vfs, fnc, encode)
467 self.opener = self.vfs
479 self.opener = self.vfs
468
480
469 def join(self, f):
481 def join(self, f):
470 return self.pathsep + self.encode(f)
482 return self.pathsep + self.encode(f)
471
483
472 def getsize(self, path):
484 def getsize(self, path):
473 return self.rawvfs.stat(path).st_size
485 return self.rawvfs.stat(path).st_size
474
486
475 def datafiles(self):
487 def datafiles(self):
476 existing = []
488 existing = []
477 for f in sorted(self.fncache):
489 for f in sorted(self.fncache):
478 ef = self.encode(f)
490 ef = self.encode(f)
479 try:
491 try:
480 yield f, ef, self.getsize(ef)
492 yield f, ef, self.getsize(ef)
481 existing.append(f)
493 existing.append(f)
482 except OSError, err:
494 except OSError, err:
483 if err.errno != errno.ENOENT:
495 if err.errno != errno.ENOENT:
484 raise
496 raise
485
497
486 def copylist(self):
498 def copylist(self):
487 d = ('data dh fncache phaseroots obsstore'
499 d = ('data dh fncache phaseroots obsstore'
488 ' 00manifest.d 00manifest.i 00changelog.d 00changelog.i')
500 ' 00manifest.d 00manifest.i 00changelog.d 00changelog.i')
489 return (['requires', '00changelog.i'] +
501 return (['requires', '00changelog.i'] +
490 ['store/' + f for f in d.split()])
502 ['store/' + f for f in d.split()])
491
503
492 def write(self, tr):
504 def write(self, tr):
493 self.fncache.write(tr)
505 self.fncache.write(tr)
494
506
495 def invalidatecaches(self):
507 def invalidatecaches(self):
496 self.fncache.entries = None
508 self.fncache.entries = None
497
509
510 def markremoved(self, fn):
511 self.fncache.remove(fn)
512
498 def _exists(self, f):
513 def _exists(self, f):
499 ef = self.encode(f)
514 ef = self.encode(f)
500 try:
515 try:
501 self.getsize(ef)
516 self.getsize(ef)
502 return True
517 return True
503 except OSError, err:
518 except OSError, err:
504 if err.errno != errno.ENOENT:
519 if err.errno != errno.ENOENT:
505 raise
520 raise
506 # nonexistent entry
521 # nonexistent entry
507 return False
522 return False
508
523
509 def __contains__(self, path):
524 def __contains__(self, path):
510 '''Checks if the store contains path'''
525 '''Checks if the store contains path'''
511 path = "/".join(("data", path))
526 path = "/".join(("data", path))
512 # check for files (exact match)
527 # check for files (exact match)
513 e = path + '.i'
528 e = path + '.i'
514 if e in self.fncache and self._exists(e):
529 if e in self.fncache and self._exists(e):
515 return True
530 return True
516 # now check for directories (prefix match)
531 # now check for directories (prefix match)
517 if not path.endswith('/'):
532 if not path.endswith('/'):
518 path += '/'
533 path += '/'
519 for e in self.fncache:
534 for e in self.fncache:
520 if e.startswith(path) and self._exists(e):
535 if e.startswith(path) and self._exists(e):
521 return True
536 return True
522 return False
537 return False
523
538
524 def store(requirements, path, vfstype):
539 def store(requirements, path, vfstype):
525 if 'store' in requirements:
540 if 'store' in requirements:
526 if 'fncache' in requirements:
541 if 'fncache' in requirements:
527 return fncachestore(path, vfstype, 'dotencode' in requirements)
542 return fncachestore(path, vfstype, 'dotencode' in requirements)
528 return encodedstore(path, vfstype)
543 return encodedstore(path, vfstype)
529 return basicstore(path, vfstype)
544 return basicstore(path, vfstype)
@@ -1,503 +1,516 b''
1 $ echo "[extensions]" >> $HGRCPATH
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "strip=" >> $HGRCPATH
2 $ echo "strip=" >> $HGRCPATH
3
3
4 $ restore() {
4 $ restore() {
5 > hg unbundle -q .hg/strip-backup/*
5 > hg unbundle -q .hg/strip-backup/*
6 > rm .hg/strip-backup/*
6 > rm .hg/strip-backup/*
7 > }
7 > }
8 $ teststrip() {
8 $ teststrip() {
9 > hg up -C $1
9 > hg up -C $1
10 > echo % before update $1, strip $2
10 > echo % before update $1, strip $2
11 > hg parents
11 > hg parents
12 > hg --traceback strip $2
12 > hg --traceback strip $2
13 > echo % after update $1, strip $2
13 > echo % after update $1, strip $2
14 > hg parents
14 > hg parents
15 > restore
15 > restore
16 > }
16 > }
17
17
18 $ hg init test
18 $ hg init test
19 $ cd test
19 $ cd test
20
20
21 $ echo foo > bar
21 $ echo foo > bar
22 $ hg ci -Ama
22 $ hg ci -Ama
23 adding bar
23 adding bar
24
24
25 $ echo more >> bar
25 $ echo more >> bar
26 $ hg ci -Amb
26 $ hg ci -Amb
27
27
28 $ echo blah >> bar
28 $ echo blah >> bar
29 $ hg ci -Amc
29 $ hg ci -Amc
30
30
31 $ hg up 1
31 $ hg up 1
32 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
32 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 $ echo blah >> bar
33 $ echo blah >> bar
34 $ hg ci -Amd
34 $ hg ci -Amd
35 created new head
35 created new head
36
36
37 $ echo final >> bar
37 $ echo final >> bar
38 $ hg ci -Ame
38 $ hg ci -Ame
39
39
40 $ hg log
40 $ hg log
41 changeset: 4:443431ffac4f
41 changeset: 4:443431ffac4f
42 tag: tip
42 tag: tip
43 user: test
43 user: test
44 date: Thu Jan 01 00:00:00 1970 +0000
44 date: Thu Jan 01 00:00:00 1970 +0000
45 summary: e
45 summary: e
46
46
47 changeset: 3:65bd5f99a4a3
47 changeset: 3:65bd5f99a4a3
48 parent: 1:ef3a871183d7
48 parent: 1:ef3a871183d7
49 user: test
49 user: test
50 date: Thu Jan 01 00:00:00 1970 +0000
50 date: Thu Jan 01 00:00:00 1970 +0000
51 summary: d
51 summary: d
52
52
53 changeset: 2:264128213d29
53 changeset: 2:264128213d29
54 user: test
54 user: test
55 date: Thu Jan 01 00:00:00 1970 +0000
55 date: Thu Jan 01 00:00:00 1970 +0000
56 summary: c
56 summary: c
57
57
58 changeset: 1:ef3a871183d7
58 changeset: 1:ef3a871183d7
59 user: test
59 user: test
60 date: Thu Jan 01 00:00:00 1970 +0000
60 date: Thu Jan 01 00:00:00 1970 +0000
61 summary: b
61 summary: b
62
62
63 changeset: 0:9ab35a2d17cb
63 changeset: 0:9ab35a2d17cb
64 user: test
64 user: test
65 date: Thu Jan 01 00:00:00 1970 +0000
65 date: Thu Jan 01 00:00:00 1970 +0000
66 summary: a
66 summary: a
67
67
68
68
69 $ teststrip 4 4
69 $ teststrip 4 4
70 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
70 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 % before update 4, strip 4
71 % before update 4, strip 4
72 changeset: 4:443431ffac4f
72 changeset: 4:443431ffac4f
73 tag: tip
73 tag: tip
74 user: test
74 user: test
75 date: Thu Jan 01 00:00:00 1970 +0000
75 date: Thu Jan 01 00:00:00 1970 +0000
76 summary: e
76 summary: e
77
77
78 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
79 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
80 % after update 4, strip 4
80 % after update 4, strip 4
81 changeset: 3:65bd5f99a4a3
81 changeset: 3:65bd5f99a4a3
82 tag: tip
82 tag: tip
83 parent: 1:ef3a871183d7
83 parent: 1:ef3a871183d7
84 user: test
84 user: test
85 date: Thu Jan 01 00:00:00 1970 +0000
85 date: Thu Jan 01 00:00:00 1970 +0000
86 summary: d
86 summary: d
87
87
88 $ teststrip 4 3
88 $ teststrip 4 3
89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 % before update 4, strip 3
90 % before update 4, strip 3
91 changeset: 4:443431ffac4f
91 changeset: 4:443431ffac4f
92 tag: tip
92 tag: tip
93 user: test
93 user: test
94 date: Thu Jan 01 00:00:00 1970 +0000
94 date: Thu Jan 01 00:00:00 1970 +0000
95 summary: e
95 summary: e
96
96
97 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
98 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
99 % after update 4, strip 3
99 % after update 4, strip 3
100 changeset: 1:ef3a871183d7
100 changeset: 1:ef3a871183d7
101 user: test
101 user: test
102 date: Thu Jan 01 00:00:00 1970 +0000
102 date: Thu Jan 01 00:00:00 1970 +0000
103 summary: b
103 summary: b
104
104
105 $ teststrip 1 4
105 $ teststrip 1 4
106 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
107 % before update 1, strip 4
107 % before update 1, strip 4
108 changeset: 1:ef3a871183d7
108 changeset: 1:ef3a871183d7
109 user: test
109 user: test
110 date: Thu Jan 01 00:00:00 1970 +0000
110 date: Thu Jan 01 00:00:00 1970 +0000
111 summary: b
111 summary: b
112
112
113 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
113 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
114 % after update 1, strip 4
114 % after update 1, strip 4
115 changeset: 1:ef3a871183d7
115 changeset: 1:ef3a871183d7
116 user: test
116 user: test
117 date: Thu Jan 01 00:00:00 1970 +0000
117 date: Thu Jan 01 00:00:00 1970 +0000
118 summary: b
118 summary: b
119
119
120 $ teststrip 4 2
120 $ teststrip 4 2
121 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 % before update 4, strip 2
122 % before update 4, strip 2
123 changeset: 4:443431ffac4f
123 changeset: 4:443431ffac4f
124 tag: tip
124 tag: tip
125 user: test
125 user: test
126 date: Thu Jan 01 00:00:00 1970 +0000
126 date: Thu Jan 01 00:00:00 1970 +0000
127 summary: e
127 summary: e
128
128
129 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
129 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
130 % after update 4, strip 2
130 % after update 4, strip 2
131 changeset: 3:443431ffac4f
131 changeset: 3:443431ffac4f
132 tag: tip
132 tag: tip
133 user: test
133 user: test
134 date: Thu Jan 01 00:00:00 1970 +0000
134 date: Thu Jan 01 00:00:00 1970 +0000
135 summary: e
135 summary: e
136
136
137 $ teststrip 4 1
137 $ teststrip 4 1
138 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
138 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
139 % before update 4, strip 1
139 % before update 4, strip 1
140 changeset: 4:264128213d29
140 changeset: 4:264128213d29
141 tag: tip
141 tag: tip
142 parent: 1:ef3a871183d7
142 parent: 1:ef3a871183d7
143 user: test
143 user: test
144 date: Thu Jan 01 00:00:00 1970 +0000
144 date: Thu Jan 01 00:00:00 1970 +0000
145 summary: c
145 summary: c
146
146
147 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
147 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
148 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
148 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
149 % after update 4, strip 1
149 % after update 4, strip 1
150 changeset: 0:9ab35a2d17cb
150 changeset: 0:9ab35a2d17cb
151 tag: tip
151 tag: tip
152 user: test
152 user: test
153 date: Thu Jan 01 00:00:00 1970 +0000
153 date: Thu Jan 01 00:00:00 1970 +0000
154 summary: a
154 summary: a
155
155
156 $ teststrip null 4
156 $ teststrip null 4
157 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
157 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
158 % before update null, strip 4
158 % before update null, strip 4
159 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
159 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
160 % after update null, strip 4
160 % after update null, strip 4
161
161
162 $ hg log
162 $ hg log
163 changeset: 4:264128213d29
163 changeset: 4:264128213d29
164 tag: tip
164 tag: tip
165 parent: 1:ef3a871183d7
165 parent: 1:ef3a871183d7
166 user: test
166 user: test
167 date: Thu Jan 01 00:00:00 1970 +0000
167 date: Thu Jan 01 00:00:00 1970 +0000
168 summary: c
168 summary: c
169
169
170 changeset: 3:443431ffac4f
170 changeset: 3:443431ffac4f
171 user: test
171 user: test
172 date: Thu Jan 01 00:00:00 1970 +0000
172 date: Thu Jan 01 00:00:00 1970 +0000
173 summary: e
173 summary: e
174
174
175 changeset: 2:65bd5f99a4a3
175 changeset: 2:65bd5f99a4a3
176 user: test
176 user: test
177 date: Thu Jan 01 00:00:00 1970 +0000
177 date: Thu Jan 01 00:00:00 1970 +0000
178 summary: d
178 summary: d
179
179
180 changeset: 1:ef3a871183d7
180 changeset: 1:ef3a871183d7
181 user: test
181 user: test
182 date: Thu Jan 01 00:00:00 1970 +0000
182 date: Thu Jan 01 00:00:00 1970 +0000
183 summary: b
183 summary: b
184
184
185 changeset: 0:9ab35a2d17cb
185 changeset: 0:9ab35a2d17cb
186 user: test
186 user: test
187 date: Thu Jan 01 00:00:00 1970 +0000
187 date: Thu Jan 01 00:00:00 1970 +0000
188 summary: a
188 summary: a
189
189
190
190
191 $ hg up -C 2
191 $ hg up -C 2
192 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
192 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
193 $ hg merge 4
193 $ hg merge 4
194 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
194 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
195 (branch merge, don't forget to commit)
195 (branch merge, don't forget to commit)
196
196
197 before strip of merge parent
197 before strip of merge parent
198
198
199 $ hg parents
199 $ hg parents
200 changeset: 2:65bd5f99a4a3
200 changeset: 2:65bd5f99a4a3
201 user: test
201 user: test
202 date: Thu Jan 01 00:00:00 1970 +0000
202 date: Thu Jan 01 00:00:00 1970 +0000
203 summary: d
203 summary: d
204
204
205 changeset: 4:264128213d29
205 changeset: 4:264128213d29
206 tag: tip
206 tag: tip
207 parent: 1:ef3a871183d7
207 parent: 1:ef3a871183d7
208 user: test
208 user: test
209 date: Thu Jan 01 00:00:00 1970 +0000
209 date: Thu Jan 01 00:00:00 1970 +0000
210 summary: c
210 summary: c
211
211
212 $ hg strip 4
212 $ hg strip 4
213 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
213 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
214 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
214 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
215
215
216 after strip of merge parent
216 after strip of merge parent
217
217
218 $ hg parents
218 $ hg parents
219 changeset: 1:ef3a871183d7
219 changeset: 1:ef3a871183d7
220 user: test
220 user: test
221 date: Thu Jan 01 00:00:00 1970 +0000
221 date: Thu Jan 01 00:00:00 1970 +0000
222 summary: b
222 summary: b
223
223
224 $ restore
224 $ restore
225
225
226 $ hg up
226 $ hg up
227 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
227 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
228 $ hg log -G
228 $ hg log -G
229 @ changeset: 4:264128213d29
229 @ changeset: 4:264128213d29
230 | tag: tip
230 | tag: tip
231 | parent: 1:ef3a871183d7
231 | parent: 1:ef3a871183d7
232 | user: test
232 | user: test
233 | date: Thu Jan 01 00:00:00 1970 +0000
233 | date: Thu Jan 01 00:00:00 1970 +0000
234 | summary: c
234 | summary: c
235 |
235 |
236 | o changeset: 3:443431ffac4f
236 | o changeset: 3:443431ffac4f
237 | | user: test
237 | | user: test
238 | | date: Thu Jan 01 00:00:00 1970 +0000
238 | | date: Thu Jan 01 00:00:00 1970 +0000
239 | | summary: e
239 | | summary: e
240 | |
240 | |
241 | o changeset: 2:65bd5f99a4a3
241 | o changeset: 2:65bd5f99a4a3
242 |/ user: test
242 |/ user: test
243 | date: Thu Jan 01 00:00:00 1970 +0000
243 | date: Thu Jan 01 00:00:00 1970 +0000
244 | summary: d
244 | summary: d
245 |
245 |
246 o changeset: 1:ef3a871183d7
246 o changeset: 1:ef3a871183d7
247 | user: test
247 | user: test
248 | date: Thu Jan 01 00:00:00 1970 +0000
248 | date: Thu Jan 01 00:00:00 1970 +0000
249 | summary: b
249 | summary: b
250 |
250 |
251 o changeset: 0:9ab35a2d17cb
251 o changeset: 0:9ab35a2d17cb
252 user: test
252 user: test
253 date: Thu Jan 01 00:00:00 1970 +0000
253 date: Thu Jan 01 00:00:00 1970 +0000
254 summary: a
254 summary: a
255
255
256
256
257 2 is parent of 3, only one strip should happen
257 2 is parent of 3, only one strip should happen
258
258
259 $ hg strip "roots(2)" 3
259 $ hg strip "roots(2)" 3
260 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
260 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
261 $ hg log -G
261 $ hg log -G
262 @ changeset: 2:264128213d29
262 @ changeset: 2:264128213d29
263 | tag: tip
263 | tag: tip
264 | user: test
264 | user: test
265 | date: Thu Jan 01 00:00:00 1970 +0000
265 | date: Thu Jan 01 00:00:00 1970 +0000
266 | summary: c
266 | summary: c
267 |
267 |
268 o changeset: 1:ef3a871183d7
268 o changeset: 1:ef3a871183d7
269 | user: test
269 | user: test
270 | date: Thu Jan 01 00:00:00 1970 +0000
270 | date: Thu Jan 01 00:00:00 1970 +0000
271 | summary: b
271 | summary: b
272 |
272 |
273 o changeset: 0:9ab35a2d17cb
273 o changeset: 0:9ab35a2d17cb
274 user: test
274 user: test
275 date: Thu Jan 01 00:00:00 1970 +0000
275 date: Thu Jan 01 00:00:00 1970 +0000
276 summary: a
276 summary: a
277
277
278 $ restore
278 $ restore
279 $ hg log -G
279 $ hg log -G
280 o changeset: 4:443431ffac4f
280 o changeset: 4:443431ffac4f
281 | tag: tip
281 | tag: tip
282 | user: test
282 | user: test
283 | date: Thu Jan 01 00:00:00 1970 +0000
283 | date: Thu Jan 01 00:00:00 1970 +0000
284 | summary: e
284 | summary: e
285 |
285 |
286 o changeset: 3:65bd5f99a4a3
286 o changeset: 3:65bd5f99a4a3
287 | parent: 1:ef3a871183d7
287 | parent: 1:ef3a871183d7
288 | user: test
288 | user: test
289 | date: Thu Jan 01 00:00:00 1970 +0000
289 | date: Thu Jan 01 00:00:00 1970 +0000
290 | summary: d
290 | summary: d
291 |
291 |
292 | @ changeset: 2:264128213d29
292 | @ changeset: 2:264128213d29
293 |/ user: test
293 |/ user: test
294 | date: Thu Jan 01 00:00:00 1970 +0000
294 | date: Thu Jan 01 00:00:00 1970 +0000
295 | summary: c
295 | summary: c
296 |
296 |
297 o changeset: 1:ef3a871183d7
297 o changeset: 1:ef3a871183d7
298 | user: test
298 | user: test
299 | date: Thu Jan 01 00:00:00 1970 +0000
299 | date: Thu Jan 01 00:00:00 1970 +0000
300 | summary: b
300 | summary: b
301 |
301 |
302 o changeset: 0:9ab35a2d17cb
302 o changeset: 0:9ab35a2d17cb
303 user: test
303 user: test
304 date: Thu Jan 01 00:00:00 1970 +0000
304 date: Thu Jan 01 00:00:00 1970 +0000
305 summary: a
305 summary: a
306
306
307
307
308 2 different branches: 2 strips
308 2 different branches: 2 strips
309
309
310 $ hg strip 2 4
310 $ hg strip 2 4
311 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
311 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
312 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
312 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
313 $ hg log -G
313 $ hg log -G
314 o changeset: 2:65bd5f99a4a3
314 o changeset: 2:65bd5f99a4a3
315 | tag: tip
315 | tag: tip
316 | user: test
316 | user: test
317 | date: Thu Jan 01 00:00:00 1970 +0000
317 | date: Thu Jan 01 00:00:00 1970 +0000
318 | summary: d
318 | summary: d
319 |
319 |
320 @ changeset: 1:ef3a871183d7
320 @ changeset: 1:ef3a871183d7
321 | user: test
321 | user: test
322 | date: Thu Jan 01 00:00:00 1970 +0000
322 | date: Thu Jan 01 00:00:00 1970 +0000
323 | summary: b
323 | summary: b
324 |
324 |
325 o changeset: 0:9ab35a2d17cb
325 o changeset: 0:9ab35a2d17cb
326 user: test
326 user: test
327 date: Thu Jan 01 00:00:00 1970 +0000
327 date: Thu Jan 01 00:00:00 1970 +0000
328 summary: a
328 summary: a
329
329
330 $ restore
330 $ restore
331
331
332 2 different branches and a common ancestor: 1 strip
332 2 different branches and a common ancestor: 1 strip
333
333
334 $ hg strip 1 "2|4"
334 $ hg strip 1 "2|4"
335 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
335 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
336 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
336 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
337 $ restore
337 $ restore
338
338
339 verify fncache is kept up-to-date
340
341 $ touch a
342 $ hg ci -qAm a
343 $ cat .hg/store/fncache | sort
344 data/a.i
345 data/bar.i
346 $ hg strip tip
347 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
348 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
349 $ cat .hg/store/fncache
350 data/bar.i
351
339 stripping an empty revset
352 stripping an empty revset
340
353
341 $ hg strip "1 and not 1"
354 $ hg strip "1 and not 1"
342 abort: empty revision set
355 abort: empty revision set
343 [255]
356 [255]
344
357
345 remove branchy history for qimport tests
358 remove branchy history for qimport tests
346
359
347 $ hg strip 3
360 $ hg strip 3
348 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
361 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
349
362
350
363
351 strip of applied mq should cleanup status file
364 strip of applied mq should cleanup status file
352
365
353 $ echo "mq=" >> $HGRCPATH
366 $ echo "mq=" >> $HGRCPATH
354 $ hg up -C 3
367 $ hg up -C 3
355 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
368 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
356 $ echo fooagain >> bar
369 $ echo fooagain >> bar
357 $ hg ci -mf
370 $ hg ci -mf
358 $ hg qimport -r tip:2
371 $ hg qimport -r tip:2
359
372
360 applied patches before strip
373 applied patches before strip
361
374
362 $ hg qapplied
375 $ hg qapplied
363 2.diff
376 2.diff
364 3.diff
377 3.diff
365 4.diff
378 4.diff
366
379
367 stripping revision in queue
380 stripping revision in queue
368
381
369 $ hg strip 3
382 $ hg strip 3
370 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
383 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
371 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
384 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
372
385
373 applied patches after stripping rev in queue
386 applied patches after stripping rev in queue
374
387
375 $ hg qapplied
388 $ hg qapplied
376 2.diff
389 2.diff
377
390
378 stripping ancestor of queue
391 stripping ancestor of queue
379
392
380 $ hg strip 1
393 $ hg strip 1
381 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
394 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
382 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
395 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
383
396
384 applied patches after stripping ancestor of queue
397 applied patches after stripping ancestor of queue
385
398
386 $ hg qapplied
399 $ hg qapplied
387
400
388 Verify strip protects against stripping wc parent when there are uncommitted mods
401 Verify strip protects against stripping wc parent when there are uncommitted mods
389
402
390 $ echo b > b
403 $ echo b > b
391 $ hg add b
404 $ hg add b
392 $ hg ci -m 'b'
405 $ hg ci -m 'b'
393 $ hg log --graph
406 $ hg log --graph
394 @ changeset: 1:7519abd79d14
407 @ changeset: 1:7519abd79d14
395 | tag: tip
408 | tag: tip
396 | user: test
409 | user: test
397 | date: Thu Jan 01 00:00:00 1970 +0000
410 | date: Thu Jan 01 00:00:00 1970 +0000
398 | summary: b
411 | summary: b
399 |
412 |
400 o changeset: 0:9ab35a2d17cb
413 o changeset: 0:9ab35a2d17cb
401 user: test
414 user: test
402 date: Thu Jan 01 00:00:00 1970 +0000
415 date: Thu Jan 01 00:00:00 1970 +0000
403 summary: a
416 summary: a
404
417
405
418
406 $ echo c > b
419 $ echo c > b
407 $ echo c > bar
420 $ echo c > bar
408 $ hg strip tip
421 $ hg strip tip
409 abort: local changes found
422 abort: local changes found
410 [255]
423 [255]
411 $ hg strip tip --keep
424 $ hg strip tip --keep
412 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
425 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
413 $ hg log --graph
426 $ hg log --graph
414 @ changeset: 0:9ab35a2d17cb
427 @ changeset: 0:9ab35a2d17cb
415 tag: tip
428 tag: tip
416 user: test
429 user: test
417 date: Thu Jan 01 00:00:00 1970 +0000
430 date: Thu Jan 01 00:00:00 1970 +0000
418 summary: a
431 summary: a
419
432
420 $ hg status
433 $ hg status
421 M bar
434 M bar
422 ? b
435 ? b
423
436
424 Strip adds, removes, modifies with --keep
437 Strip adds, removes, modifies with --keep
425
438
426 $ touch b
439 $ touch b
427 $ hg add b
440 $ hg add b
428 $ hg commit -mb
441 $ hg commit -mb
429 $ touch c
442 $ touch c
430
443
431 ... with a clean working dir
444 ... with a clean working dir
432
445
433 $ hg add c
446 $ hg add c
434 $ hg rm bar
447 $ hg rm bar
435 $ hg commit -mc
448 $ hg commit -mc
436 $ hg status
449 $ hg status
437 $ hg strip --keep tip
450 $ hg strip --keep tip
438 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
451 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
439 $ hg status
452 $ hg status
440 ! bar
453 ! bar
441 ? c
454 ? c
442
455
443 ... with a dirty working dir
456 ... with a dirty working dir
444
457
445 $ hg add c
458 $ hg add c
446 $ hg rm bar
459 $ hg rm bar
447 $ hg commit -mc
460 $ hg commit -mc
448 $ hg status
461 $ hg status
449 $ echo b > b
462 $ echo b > b
450 $ echo d > d
463 $ echo d > d
451 $ hg strip --keep tip
464 $ hg strip --keep tip
452 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
465 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
453 $ hg status
466 $ hg status
454 M b
467 M b
455 ! bar
468 ! bar
456 ? c
469 ? c
457 ? d
470 ? d
458 $ cd ..
471 $ cd ..
459
472
460 stripping many nodes on a complex graph (issue3299)
473 stripping many nodes on a complex graph (issue3299)
461
474
462 $ hg init issue3299
475 $ hg init issue3299
463 $ cd issue3299
476 $ cd issue3299
464 $ hg debugbuilddag '@a.:a@b.:b.:x<a@a.:a<b@b.:b<a@a.:a'
477 $ hg debugbuilddag '@a.:a@b.:b.:x<a@a.:a<b@b.:b<a@a.:a'
465 $ hg strip 'not ancestors(x)'
478 $ hg strip 'not ancestors(x)'
466 saved backup bundle to $TESTTMP/issue3299/.hg/strip-backup/*-backup.hg (glob)
479 saved backup bundle to $TESTTMP/issue3299/.hg/strip-backup/*-backup.hg (glob)
467
480
468 test hg strip -B bookmark
481 test hg strip -B bookmark
469
482
470 $ cd ..
483 $ cd ..
471 $ hg init bookmarks
484 $ hg init bookmarks
472 $ cd bookmarks
485 $ cd bookmarks
473 $ hg debugbuilddag '..<2.*1/2:m<2+3:c<m+3:a<2.:b'
486 $ hg debugbuilddag '..<2.*1/2:m<2+3:c<m+3:a<2.:b'
474 $ hg bookmark -r 'a' 'todelete'
487 $ hg bookmark -r 'a' 'todelete'
475 $ hg bookmark -r 'b' 'B'
488 $ hg bookmark -r 'b' 'B'
476 $ hg bookmark -r 'b' 'nostrip'
489 $ hg bookmark -r 'b' 'nostrip'
477 $ hg bookmark -r 'c' 'delete'
490 $ hg bookmark -r 'c' 'delete'
478 $ hg up -C todelete
491 $ hg up -C todelete
479 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
492 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
480 $ hg strip -B nostrip
493 $ hg strip -B nostrip
481 bookmark 'nostrip' deleted
494 bookmark 'nostrip' deleted
482 abort: empty revision set
495 abort: empty revision set
483 [255]
496 [255]
484 $ hg strip -B todelete
497 $ hg strip -B todelete
485 bookmark 'todelete' deleted
498 bookmark 'todelete' deleted
486 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
499 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
487 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
500 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
488 $ hg id -ir dcbb326fdec2
501 $ hg id -ir dcbb326fdec2
489 abort: unknown revision 'dcbb326fdec2'!
502 abort: unknown revision 'dcbb326fdec2'!
490 [255]
503 [255]
491 $ hg id -ir d62d843c9a01
504 $ hg id -ir d62d843c9a01
492 d62d843c9a01
505 d62d843c9a01
493 $ hg bookmarks
506 $ hg bookmarks
494 B 9:ff43616e5d0f
507 B 9:ff43616e5d0f
495 delete 6:2702dd0c91e7
508 delete 6:2702dd0c91e7
496 $ hg strip -B delete
509 $ hg strip -B delete
497 bookmark 'delete' deleted
510 bookmark 'delete' deleted
498 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
511 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
499 $ hg id -ir 6:2702dd0c91e7
512 $ hg id -ir 6:2702dd0c91e7
500 abort: unknown revision '2702dd0c91e7'!
513 abort: unknown revision '2702dd0c91e7'!
501 [255]
514 [255]
502
515
503 $ cd ..
516 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now