##// END OF EJS Templates
extdiff: add 3-way diff for merge changesets...
Sune Foldager -
r9512:e7bde468 default
parent child Browse files
Show More
@@ -42,9 +42,9 b' fast (at least faster than having to com'
42 '''
42 '''
43
43
44 from mercurial.i18n import _
44 from mercurial.i18n import _
45 from mercurial.node import short
45 from mercurial.node import short, nullid
46 from mercurial import cmdutil, util, commands
46 from mercurial import cmdutil, util, commands
47 import os, shlex, shutil, tempfile
47 import os, shlex, shutil, tempfile, re
48
48
49 def snapshot(ui, repo, files, node, tmproot):
49 def snapshot(ui, repo, files, node, tmproot):
50 '''snapshot files as of some revision
50 '''snapshot files as of some revision
@@ -69,7 +69,7 b' def snapshot(ui, repo, files, node, tmpr'
69 for fn in files:
69 for fn in files:
70 wfn = util.pconvert(fn)
70 wfn = util.pconvert(fn)
71 if not wfn in ctx:
71 if not wfn in ctx:
72 # skipping new file after a merge ?
72 # File doesn't exist; could be a bogus modify
73 continue
73 continue
74 ui.note(' %s\n' % wfn)
74 ui.note(' %s\n' % wfn)
75 dest = os.path.join(base, wfn)
75 dest = os.path.join(base, wfn)
@@ -96,52 +96,95 b' def dodiff(ui, repo, diffcmd, diffopts, '
96
96
97 revs = opts.get('rev')
97 revs = opts.get('rev')
98 change = opts.get('change')
98 change = opts.get('change')
99 args = ' '.join(diffopts)
100 do3way = '$parent2' in args
99
101
100 if revs and change:
102 if revs and change:
101 msg = _('cannot specify --rev and --change at the same time')
103 msg = _('cannot specify --rev and --change at the same time')
102 raise util.Abort(msg)
104 raise util.Abort(msg)
103 elif change:
105 elif change:
104 node2 = repo.lookup(change)
106 node2 = repo.lookup(change)
105 node1 = repo[node2].parents()[0].node()
107 node1a, node1b = repo.changelog.parents(node2)
106 else:
108 else:
107 node1, node2 = cmdutil.revpair(repo, revs)
109 node1a, node2 = cmdutil.revpair(repo, revs)
110 if not revs:
111 node1b = repo.dirstate.parents()[1]
112 else:
113 node1b = nullid
114
115 # Disable 3-way merge if there is only one parent
116 if do3way:
117 if node1b == nullid:
118 do3way = False
108
119
109 matcher = cmdutil.match(repo, pats, opts)
120 matcher = cmdutil.match(repo, pats, opts)
110 modified, added, removed = repo.status(node1, node2, matcher)[:3]
121 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher)[:3])
111 if not (modified or added or removed):
122 if do3way:
112 return 0
123 mod_b, add_b, rem_b = map(set, repo.status(node1b, node2, matcher)[:3])
124 else:
125 mod_b, add_b, rem_b = set(), set(), set()
126 modadd = mod_a | add_a | mod_b | add_b
127 common = modadd | rem_a | rem_b
128 if not common:
129 return 0
113
130
114 tmproot = tempfile.mkdtemp(prefix='extdiff.')
131 tmproot = tempfile.mkdtemp(prefix='extdiff.')
115 dir2root = ''
116 try:
132 try:
117 # Always make a copy of node1
133 # Always make a copy of node1a (and node1b, if applicable)
118 dir1 = snapshot(ui, repo, modified + removed, node1, tmproot)[0]
134 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
119 changes = len(modified) + len(removed) + len(added)
135 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot)[0]
136 if do3way:
137 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
138 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot)[0]
139 else:
140 dir1b = None
141
142 fns_and_mtime = []
120
143
121 # If node2 in not the wc or there is >1 change, copy it
144 # If node2 in not the wc or there is >1 change, copy it
122 if node2 or changes > 1:
145 dir2root = ''
123 dir2, fns_and_mtime = snapshot(ui, repo, modified + added, node2, tmproot)
146 if node2:
147 dir2 = snapshot(ui, repo, modadd, node2, tmproot)[0]
148 elif len(common) > 1:
149 #we only actually need to get the files to copy back to the working
150 #dir in this case (because the other cases are: diffing 2 revisions
151 #or single file -- in which case the file is already directly passed
152 #to the diff tool).
153 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot)
124 else:
154 else:
125 # This lets the diff tool open the changed file directly
155 # This lets the diff tool open the changed file directly
126 dir2 = ''
156 dir2 = ''
127 dir2root = repo.root
157 dir2root = repo.root
128 fns_and_mtime = []
129
158
130 # If only one change, diff the files instead of the directories
159 # If only one change, diff the files instead of the directories
131 if changes == 1 :
160 # Handle bogus modifies correctly by checking if the files exist
132 if len(modified):
161 if len(common) == 1:
133 dir1 = os.path.join(dir1, util.localpath(modified[0]))
162 common_file = util.localpath(common.pop())
134 dir2 = os.path.join(dir2root, dir2, util.localpath(modified[0]))
163 dir1a = os.path.join(dir1a, common_file)
135 elif len(removed) :
164 if not os.path.isfile(os.path.join(tmproot, dir1a)):
136 dir1 = os.path.join(dir1, util.localpath(removed[0]))
165 dir1a = os.devnull
137 dir2 = os.devnull
166 if do3way:
138 else:
167 dir1b = os.path.join(dir1b, common_file)
139 dir1 = os.devnull
168 if not os.path.isfile(os.path.join(tmproot, dir1b)):
140 dir2 = os.path.join(dir2root, dir2, util.localpath(added[0]))
169 dir1b = os.devnull
170 dir2 = os.path.join(dir2root, dir2, common_file)
141
171
142 cmdline = ('%s %s %s %s' %
172 # Function to quote file/dir names in the argument string
143 (util.shellquote(diffcmd), ' '.join(diffopts),
173 # When not operating in 3-way mode, an empty string is returned for parent2
144 util.shellquote(dir1), util.shellquote(dir2)))
174 replace = dict(parent=dir1a, parent1=dir1a, parent2=dir1b, child=dir2)
175 def quote(match):
176 key = match.group()[1:]
177 if not do3way and key == 'parent2':
178 return ''
179 return util.shellquote(replace[key])
180
181 # Match parent2 first, so 'parent1?' will match both parent1 and parent
182 regex = '\$(parent2|parent1?|child)'
183 if not do3way and not re.search(regex, args):
184 args += ' $parent1 $child'
185 args = re.sub(regex, quote, args)
186 cmdline = util.shellquote(diffcmd) + ' ' + args
187
145 ui.debug('running %r in %s\n' % (cmdline, tmproot))
188 ui.debug('running %r in %s\n' % (cmdline, tmproot))
146 util.system(cmdline, cwd=tmproot)
189 util.system(cmdline, cwd=tmproot)
147
190
General Comments 0
You need to be logged in to leave comments. Login now