38 lookup = repo.lookup |
47 lookup = repo.lookup |
39 else: |
48 else: |
40 raise error.Abort(_('either two or three arguments required')) |
49 raise error.Abort(_('either two or three arguments required')) |
41 a = r.ancestor(lookup(rev1), lookup(rev2)) |
50 a = r.ancestor(lookup(rev1), lookup(rev2)) |
42 ui.write('%d:%s\n' % (r.rev(a), hex(a))) |
51 ui.write('%d:%s\n' % (r.rev(a), hex(a))) |
|
52 |
|
53 @command('debugbuilddag', |
|
54 [('m', 'mergeable-file', None, _('add single file mergeable changes')), |
|
55 ('o', 'overwritten-file', None, _('add single file all revs overwrite')), |
|
56 ('n', 'new-file', None, _('add new file at each rev'))], |
|
57 _('[OPTION]... [TEXT]')) |
|
58 def debugbuilddag(ui, repo, text=None, |
|
59 mergeable_file=False, |
|
60 overwritten_file=False, |
|
61 new_file=False): |
|
62 """builds a repo with a given DAG from scratch in the current empty repo |
|
63 |
|
64 The description of the DAG is read from stdin if not given on the |
|
65 command line. |
|
66 |
|
67 Elements: |
|
68 |
|
69 - "+n" is a linear run of n nodes based on the current default parent |
|
70 - "." is a single node based on the current default parent |
|
71 - "$" resets the default parent to null (implied at the start); |
|
72 otherwise the default parent is always the last node created |
|
73 - "<p" sets the default parent to the backref p |
|
74 - "*p" is a fork at parent p, which is a backref |
|
75 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs |
|
76 - "/p2" is a merge of the preceding node and p2 |
|
77 - ":tag" defines a local tag for the preceding node |
|
78 - "@branch" sets the named branch for subsequent nodes |
|
79 - "#...\\n" is a comment up to the end of the line |
|
80 |
|
81 Whitespace between the above elements is ignored. |
|
82 |
|
83 A backref is either |
|
84 |
|
85 - a number n, which references the node curr-n, where curr is the current |
|
86 node, or |
|
87 - the name of a local tag you placed earlier using ":tag", or |
|
88 - empty to denote the default parent. |
|
89 |
|
90 All string valued-elements are either strictly alphanumeric, or must |
|
91 be enclosed in double quotes ("..."), with "\\" as escape character. |
|
92 """ |
|
93 |
|
94 if text is None: |
|
95 ui.status(_("reading DAG from stdin\n")) |
|
96 text = ui.fin.read() |
|
97 |
|
98 cl = repo.changelog |
|
99 if len(cl) > 0: |
|
100 raise error.Abort(_('repository is not empty')) |
|
101 |
|
102 # determine number of revs in DAG |
|
103 total = 0 |
|
104 for type, data in dagparser.parsedag(text): |
|
105 if type == 'n': |
|
106 total += 1 |
|
107 |
|
108 if mergeable_file: |
|
109 linesperrev = 2 |
|
110 # make a file with k lines per rev |
|
111 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)] |
|
112 initialmergedlines.append("") |
|
113 |
|
114 tags = [] |
|
115 |
|
116 wlock = lock = tr = None |
|
117 try: |
|
118 wlock = repo.wlock() |
|
119 lock = repo.lock() |
|
120 tr = repo.transaction("builddag") |
|
121 |
|
122 at = -1 |
|
123 atbranch = 'default' |
|
124 nodeids = [] |
|
125 id = 0 |
|
126 ui.progress(_('building'), id, unit=_('revisions'), total=total) |
|
127 for type, data in dagparser.parsedag(text): |
|
128 if type == 'n': |
|
129 ui.note(('node %s\n' % str(data))) |
|
130 id, ps = data |
|
131 |
|
132 files = [] |
|
133 fctxs = {} |
|
134 |
|
135 p2 = None |
|
136 if mergeable_file: |
|
137 fn = "mf" |
|
138 p1 = repo[ps[0]] |
|
139 if len(ps) > 1: |
|
140 p2 = repo[ps[1]] |
|
141 pa = p1.ancestor(p2) |
|
142 base, local, other = [x[fn].data() for x in (pa, p1, |
|
143 p2)] |
|
144 m3 = simplemerge.Merge3Text(base, local, other) |
|
145 ml = [l.strip() for l in m3.merge_lines()] |
|
146 ml.append("") |
|
147 elif at > 0: |
|
148 ml = p1[fn].data().split("\n") |
|
149 else: |
|
150 ml = initialmergedlines |
|
151 ml[id * linesperrev] += " r%i" % id |
|
152 mergedtext = "\n".join(ml) |
|
153 files.append(fn) |
|
154 fctxs[fn] = context.memfilectx(repo, fn, mergedtext) |
|
155 |
|
156 if overwritten_file: |
|
157 fn = "of" |
|
158 files.append(fn) |
|
159 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id) |
|
160 |
|
161 if new_file: |
|
162 fn = "nf%i" % id |
|
163 files.append(fn) |
|
164 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id) |
|
165 if len(ps) > 1: |
|
166 if not p2: |
|
167 p2 = repo[ps[1]] |
|
168 for fn in p2: |
|
169 if fn.startswith("nf"): |
|
170 files.append(fn) |
|
171 fctxs[fn] = p2[fn] |
|
172 |
|
173 def fctxfn(repo, cx, path): |
|
174 return fctxs.get(path) |
|
175 |
|
176 if len(ps) == 0 or ps[0] < 0: |
|
177 pars = [None, None] |
|
178 elif len(ps) == 1: |
|
179 pars = [nodeids[ps[0]], None] |
|
180 else: |
|
181 pars = [nodeids[p] for p in ps] |
|
182 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn, |
|
183 date=(id, 0), |
|
184 user="debugbuilddag", |
|
185 extra={'branch': atbranch}) |
|
186 nodeid = repo.commitctx(cx) |
|
187 nodeids.append(nodeid) |
|
188 at = id |
|
189 elif type == 'l': |
|
190 id, name = data |
|
191 ui.note(('tag %s\n' % name)) |
|
192 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name)) |
|
193 elif type == 'a': |
|
194 ui.note(('branch %s\n' % data)) |
|
195 atbranch = data |
|
196 ui.progress(_('building'), id, unit=_('revisions'), total=total) |
|
197 tr.close() |
|
198 |
|
199 if tags: |
|
200 repo.vfs.write("localtags", "".join(tags)) |
|
201 finally: |
|
202 ui.progress(_('building'), None) |
|
203 release(tr, lock, wlock) |