Mercurial > traipse
comparison upmana/mercurial/copies.py @ 28:ff154cf3350c ornery-orc
Traipse 'OpenRPG' {100203-00}
Traipse is a distribution of OpenRPG that is designed to be easy to
setup and go. Traipse also makes it easy for developers to work on code
without fear of sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy'
and adds fixes to the code. 'Ornery-Orc's main goal is to offer more
advanced features and enhance the productivity of the user.
Update Summary (Stable)
New Features:
New Bookmarks Feature
New 'boot' command to remote admin
New confirmation window for sent nodes
Miniatures Layer pop up box allows users to turn off Mini labels, from
FlexiRPG
New Zoom Mouse plugin added
New Images added to Plugin UI
Switching to Element Tree
New Map efficiency, from FlexiRPG
New Status Bar to Update Manager
New TrueDebug Class in orpg_log (See documentation for usage)
New Portable Mercurial
New Tip of the Day, from Core and community
New Reference Syntax added for custom PC sheets
New Child Reference for gametree
New Parent Reference for gametree
New Gametree Recursion method, mapping, context sensitivity, and
effeciency..
New Features node with bonus nodes and Node Referencing help added
New Dieroller structure from Core
New DieRoller portability for odd Dice
New 7th Sea die roller; ie [7k3] = [7d10.takeHighest(3).open(10)]
New 'Mythos' System die roller added
New vs. die roller method for WoD; ie [3v3] = [3d10.vs(3)]. Included for
Mythos roller also
New Warhammer FRPG Die Roller (Special thanks to Puu-san for the
support)
New EZ_Tree Reference system. Push a button, Traipse the tree, get a
reference (Beta!)
New Grids act more like Spreadsheets in Use mode, with Auto Calc
Fixes:
Fix to allow for portability to an OpenSUSE linux OS
Fix to mplay_client for Fedora and OpenSUSE
Fix to Text based Server
Fix to Remote Admin Commands
Fix to Pretty Print, from Core
Fix to Splitter Nodes not being created
Fix to massive amounts of images loading, from Core
Fix to Map from gametree not showing to all clients
Fix to gametree about menus
Fix to Password Manager check on startup
Fix to PC Sheets from tool nodes. They now use the tabber_panel
Fix to Whiteboard ID to prevent random line or text deleting.
Fixes to Server, Remote Server, and Server GUI
Fix to Update Manager; cleaner clode for saved repositories
Fixes made to Settings Panel and now reactive settings when Ok is
pressed
Fixes to Alternity roller's attack roll. Uses a simple Tuple instead of
a Splice
Fix to Use panel of Forms and Tabbers. Now longer enters design mode
Fix made Image Fetching. New fetching image and new failed image
Fix to whiteboard ID's to prevent non updated clients from ruining the
fix.
default_manifest.xml renamed to default_upmana.xml
author | sirebral |
---|---|
date | Wed, 03 Feb 2010 22:16:49 -0600 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
27:51428d30c59e | 28:ff154cf3350c |
---|---|
1 # copies.py - copy detection for Mercurial | |
2 # | |
3 # Copyright 2008 Matt Mackall <mpm@selenic.com> | |
4 # | |
5 # This software may be used and distributed according to the terms of the | |
6 # GNU General Public License version 2, incorporated herein by reference. | |
7 | |
8 from i18n import _ | |
9 import util | |
10 import heapq | |
11 | |
12 def _nonoverlap(d1, d2, d3): | |
13 "Return list of elements in d1 not in d2 or d3" | |
14 return sorted([d for d in d1 if d not in d3 and d not in d2]) | |
15 | |
16 def _dirname(f): | |
17 s = f.rfind("/") | |
18 if s == -1: | |
19 return "" | |
20 return f[:s] | |
21 | |
22 def _dirs(files): | |
23 d = set() | |
24 for f in files: | |
25 f = _dirname(f) | |
26 while f not in d: | |
27 d.add(f) | |
28 f = _dirname(f) | |
29 return d | |
30 | |
31 def _findoldnames(fctx, limit): | |
32 "find files that path was copied from, back to linkrev limit" | |
33 old = {} | |
34 seen = set() | |
35 orig = fctx.path() | |
36 visit = [(fctx, 0)] | |
37 while visit: | |
38 fc, depth = visit.pop() | |
39 s = str(fc) | |
40 if s in seen: | |
41 continue | |
42 seen.add(s) | |
43 if fc.path() != orig and fc.path() not in old: | |
44 old[fc.path()] = (depth, fc.path()) # remember depth | |
45 if fc.rev() < limit and fc.rev() is not None: | |
46 continue | |
47 visit += [(p, depth - 1) for p in fc.parents()] | |
48 | |
49 # return old names sorted by depth | |
50 return [o[1] for o in sorted(old.values())] | |
51 | |
52 def _findlimit(repo, a, b): | |
53 "find the earliest revision that's an ancestor of a or b but not both" | |
54 # basic idea: | |
55 # - mark a and b with different sides | |
56 # - if a parent's children are all on the same side, the parent is | |
57 # on that side, otherwise it is on no side | |
58 # - walk the graph in topological order with the help of a heap; | |
59 # - add unseen parents to side map | |
60 # - clear side of any parent that has children on different sides | |
61 # - track number of interesting revs that might still be on a side | |
62 # - track the lowest interesting rev seen | |
63 # - quit when interesting revs is zero | |
64 | |
65 cl = repo.changelog | |
66 working = len(cl) # pseudo rev for the working directory | |
67 if a is None: | |
68 a = working | |
69 if b is None: | |
70 b = working | |
71 | |
72 side = {a: -1, b: 1} | |
73 visit = [-a, -b] | |
74 heapq.heapify(visit) | |
75 interesting = len(visit) | |
76 limit = working | |
77 | |
78 while interesting: | |
79 r = -heapq.heappop(visit) | |
80 if r == working: | |
81 parents = [cl.rev(p) for p in repo.dirstate.parents()] | |
82 else: | |
83 parents = cl.parentrevs(r) | |
84 for p in parents: | |
85 if p not in side: | |
86 # first time we see p; add it to visit | |
87 side[p] = side[r] | |
88 if side[p]: | |
89 interesting += 1 | |
90 heapq.heappush(visit, -p) | |
91 elif side[p] and side[p] != side[r]: | |
92 # p was interesting but now we know better | |
93 side[p] = 0 | |
94 interesting -= 1 | |
95 if side[r]: | |
96 limit = r # lowest rev visited | |
97 interesting -= 1 | |
98 return limit | |
99 | |
100 def copies(repo, c1, c2, ca, checkdirs=False): | |
101 """ | |
102 Find moves and copies between context c1 and c2 | |
103 """ | |
104 # avoid silly behavior for update from empty dir | |
105 if not c1 or not c2 or c1 == c2: | |
106 return {}, {} | |
107 | |
108 # avoid silly behavior for parent -> working dir | |
109 if c2.node() is None and c1.node() == repo.dirstate.parents()[0]: | |
110 return repo.dirstate.copies(), {} | |
111 | |
112 limit = _findlimit(repo, c1.rev(), c2.rev()) | |
113 m1 = c1.manifest() | |
114 m2 = c2.manifest() | |
115 ma = ca.manifest() | |
116 | |
117 def makectx(f, n): | |
118 if len(n) != 20: # in a working context? | |
119 if c1.rev() is None: | |
120 return c1.filectx(f) | |
121 return c2.filectx(f) | |
122 return repo.filectx(f, fileid=n) | |
123 | |
124 ctx = util.lrucachefunc(makectx) | |
125 copy = {} | |
126 fullcopy = {} | |
127 diverge = {} | |
128 | |
129 def checkcopies(f, m1, m2): | |
130 '''check possible copies of f from m1 to m2''' | |
131 c1 = ctx(f, m1[f]) | |
132 for of in _findoldnames(c1, limit): | |
133 fullcopy[f] = of # remember for dir rename detection | |
134 if of in m2: # original file not in other manifest? | |
135 # if the original file is unchanged on the other branch, | |
136 # no merge needed | |
137 if m2[of] != ma.get(of): | |
138 c2 = ctx(of, m2[of]) | |
139 ca = c1.ancestor(c2) | |
140 # related and named changed on only one side? | |
141 if ca and (ca.path() == f or ca.path() == c2.path()): | |
142 if c1 != ca or c2 != ca: # merge needed? | |
143 copy[f] = of | |
144 elif of in ma: | |
145 diverge.setdefault(of, []).append(f) | |
146 | |
147 repo.ui.debug(_(" searching for copies back to rev %d\n") % limit) | |
148 | |
149 u1 = _nonoverlap(m1, m2, ma) | |
150 u2 = _nonoverlap(m2, m1, ma) | |
151 | |
152 if u1: | |
153 repo.ui.debug(_(" unmatched files in local:\n %s\n") | |
154 % "\n ".join(u1)) | |
155 if u2: | |
156 repo.ui.debug(_(" unmatched files in other:\n %s\n") | |
157 % "\n ".join(u2)) | |
158 | |
159 for f in u1: | |
160 checkcopies(f, m1, m2) | |
161 for f in u2: | |
162 checkcopies(f, m2, m1) | |
163 | |
164 diverge2 = set() | |
165 for of, fl in diverge.items(): | |
166 if len(fl) == 1: | |
167 del diverge[of] # not actually divergent | |
168 else: | |
169 diverge2.update(fl) # reverse map for below | |
170 | |
171 if fullcopy: | |
172 repo.ui.debug(_(" all copies found (* = to merge, ! = divergent):\n")) | |
173 for f in fullcopy: | |
174 note = "" | |
175 if f in copy: note += "*" | |
176 if f in diverge2: note += "!" | |
177 repo.ui.debug(" %s -> %s %s\n" % (f, fullcopy[f], note)) | |
178 del diverge2 | |
179 | |
180 if not fullcopy or not checkdirs: | |
181 return copy, diverge | |
182 | |
183 repo.ui.debug(_(" checking for directory renames\n")) | |
184 | |
185 # generate a directory move map | |
186 d1, d2 = _dirs(m1), _dirs(m2) | |
187 invalid = set() | |
188 dirmove = {} | |
189 | |
190 # examine each file copy for a potential directory move, which is | |
191 # when all the files in a directory are moved to a new directory | |
192 for dst, src in fullcopy.iteritems(): | |
193 dsrc, ddst = _dirname(src), _dirname(dst) | |
194 if dsrc in invalid: | |
195 # already seen to be uninteresting | |
196 continue | |
197 elif dsrc in d1 and ddst in d1: | |
198 # directory wasn't entirely moved locally | |
199 invalid.add(dsrc) | |
200 elif dsrc in d2 and ddst in d2: | |
201 # directory wasn't entirely moved remotely | |
202 invalid.add(dsrc) | |
203 elif dsrc in dirmove and dirmove[dsrc] != ddst: | |
204 # files from the same directory moved to two different places | |
205 invalid.add(dsrc) | |
206 else: | |
207 # looks good so far | |
208 dirmove[dsrc + "/"] = ddst + "/" | |
209 | |
210 for i in invalid: | |
211 if i in dirmove: | |
212 del dirmove[i] | |
213 del d1, d2, invalid | |
214 | |
215 if not dirmove: | |
216 return copy, diverge | |
217 | |
218 for d in dirmove: | |
219 repo.ui.debug(_(" dir %s -> %s\n") % (d, dirmove[d])) | |
220 | |
221 # check unaccounted nonoverlapping files against directory moves | |
222 for f in u1 + u2: | |
223 if f not in fullcopy: | |
224 for d in dirmove: | |
225 if f.startswith(d): | |
226 # new file added in a directory that was moved, move it | |
227 df = dirmove[d] + f[len(d):] | |
228 if df not in copy: | |
229 copy[f] = df | |
230 repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f])) | |
231 break | |
232 | |
233 return copy, diverge |