@@ -96,3 +96,255 @@ proc read_merge_stages {fd cont} {
9696 eval $cont
9797 }
9898}
99+
100+ proc merge_resolve_tool {} {
101+ global current_diff_path
102+
103+ merge_load_stages $current_diff_path [list merge_resolve_tool2]
104+ }
105+
106+ proc merge_resolve_tool2 {} {
107+ global current_diff_path merge_stages
108+
109+ # Validate the stages
110+ if {$merge_stages(2) eq {} ||
111+ [lindex $merge_stages(2) 0] eq {120000} ||
112+ [lindex $merge_stages(2) 0] eq {160000} ||
113+ $merge_stages(3) eq {} ||
114+ [lindex $merge_stages(3) 0] eq {120000} ||
115+ [lindex $merge_stages(3) 0] eq {160000}
116+ } {
117+ error_popup [mc " Cannot resolve deletion or link conflicts using a tool" ]
118+ return
119+ }
120+
121+ if {![file exists $current_diff_path ]} {
122+ error_popup [mc " Conflict file does not exist" ]
123+ return
124+ }
125+
126+ # Determine the tool to use
127+ set tool [get_config merge.tool]
128+ if {$tool eq {}} { set tool meld }
129+
130+ set merge_tool_path [get_config " mergetool.$tool .path" ]
131+ if {$merge_tool_path eq {}} {
132+ switch -- $tool {
133+ emerge { set merge_tool_path " emacs" }
134+ default { set merge_tool_path $tool }
135+ }
136+ }
137+
138+ # Make file names
139+ set filebase [file rootname $current_diff_path ]
140+ set fileext [file extension $current_diff_path ]
141+ set basename [lindex [file split $current_diff_path ] end]
142+
143+ set MERGED $current_diff_path
144+ set BASE " ./$MERGED .BASE$fileext "
145+ set LOCAL " ./$MERGED .LOCAL$fileext "
146+ set REMOTE " ./$MERGED .REMOTE$fileext "
147+ set BACKUP " ./$MERGED .BACKUP$fileext "
148+
149+ set base_stage $merge_stages(1)
150+
151+ # Build the command line
152+ switch -- $tool {
153+ kdiff3 {
154+ if {$base_stage ne {}} {
155+ set cmdline [list " $merge_tool_path " --auto --L1 " $MERGED (Base)" \
156+ --L2 " $MERGED (Local)" --L3 " $MERGED (Remote)" -o " $MERGED " " $BASE " " $LOCAL " " $REMOTE " ]
157+ } else {
158+ set cmdline [list " $merge_tool_path " --auto --L1 " $MERGED (Local)" \
159+ --L2 " $MERGED (Remote)" -o " $MERGED " " $LOCAL " " $REMOTE " ]
160+ }
161+ }
162+ tkdiff {
163+ if {$base_stage ne {}} {
164+ set cmdline [list " $merge_tool_path " -a " $BASE " -o " $MERGED " " $LOCAL " " $REMOTE " ]
165+ } else {
166+ set cmdline [list " $merge_tool_path " -o " $MERGED " " $LOCAL " " $REMOTE " ]
167+ }
168+ }
169+ meld {
170+ set cmdline [list " $merge_tool_path " " $LOCAL " " $MERGED " " $REMOTE " ]
171+ }
172+ gvimdiff {
173+ set cmdline [list " $merge_tool_path " -f " $LOCAL " " $MERGED " " $REMOTE " ]
174+ }
175+ xxdiff {
176+ if {$base_stage ne {}} {
177+ set cmdline [list " $merge_tool_path " -X --show-merged-pane \
178+ -R {Accel.SaveAsMerged: " Ctrl-S" } \
179+ -R {Accel.Search: " Ctrl+F" } \
180+ -R {Accel.SearchForward: " Ctrl-G" } \
181+ --merged-file " $MERGED " " $LOCAL " " $BASE " " $REMOTE " ]
182+ } else {
183+ set cmdline [list " $merge_tool_path " -X --show-merged-pane \
184+ -R {Accel.SaveAsMerged: " Ctrl-S" } \
185+ -R {Accel.Search: " Ctrl+F" } \
186+ -R {Accel.SearchForward: " Ctrl-G" } \
187+ --merged-file " $MERGED " " $LOCAL " " $REMOTE " ]
188+ }
189+ }
190+ opendiff {
191+ if {$base_stage ne {}} {
192+ set cmdline [list " $merge_tool_path " " $LOCAL " " $REMOTE " -ancestor " $BASE " -merge " $MERGED " ]
193+ } else {
194+ set cmdline [list " $merge_tool_path " " $LOCAL " " $REMOTE " -merge " $MERGED " ]
195+ }
196+ }
197+ ecmerge {
198+ if {$base_stage ne {}} {
199+ set cmdline [list " $merge_tool_path " " $BASE " " $LOCAL " " $REMOTE " --default --mode=merge3 --to=" $MERGED " ]
200+ } else {
201+ set cmdline [list " $merge_tool_path " " $LOCAL " " $REMOTE " --default --mode=merge2 --to=" $MERGED " ]
202+ }
203+ }
204+ emerge {
205+ if {$base_stage ne {}} {
206+ set cmdline [list " $merge_tool_path " -f emerge-files-with-ancestor-command \
207+ " $LOCAL " " $REMOTE " " $BASE " " $basename " ]
208+ } else {
209+ set cmdline [list " $merge_tool_path " -f emerge-files-command \
210+ " $LOCAL " " $REMOTE " " $basename " ]
211+ }
212+ }
213+ vimdiff {
214+ error_popup [mc " Not a GUI merge tool: '%s'" $tool ]
215+ return
216+ }
217+ default {
218+ error_popup [mc " Unsupported merge tool '%s'" $tool ]
219+ return
220+ }
221+ }
222+
223+ merge_tool_start $cmdline $MERGED $BACKUP [list $BASE $LOCAL $REMOTE ]
224+ }
225+
226+ proc delete_temp_files {files} {
227+ foreach fname $files {
228+ file delete $fname
229+ }
230+ }
231+
232+ proc merge_tool_get_stages {target stages} {
233+ global merge_stages
234+
235+ set i 1
236+ foreach fname $stages {
237+ if {$merge_stages($i) eq {}} {
238+ file delete $fname
239+ } else {
240+ # A hack to support autocrlf properly
241+ git checkout-index -f --stage=$i -- $target
242+ file rename -force -- $target $fname
243+ }
244+ incr i
245+ }
246+ }
247+
248+ proc merge_tool_start {cmdline target backup stages} {
249+ global merge_stages mtool_target mtool_tmpfiles mtool_fd mtool_mtime
250+
251+ if {[info exists mtool_fd]} {
252+ if {[ask_popup [mc " Merge tool is already running, terminate it?" ]] eq {yes}} {
253+ catch { kill_file_process $mtool_fd }
254+ catch { close $mtool_fd }
255+ unset mtool_fd
256+
257+ set old_backup [lindex $mtool_tmpfiles end]
258+ file rename -force -- $old_backup $mtool_target
259+ delete_temp_files $mtool_tmpfiles
260+ } else {
261+ return
262+ }
263+ }
264+
265+ # Save the original file
266+ file rename -force -- $target $backup
267+
268+ # Get the blobs; it destroys $target
269+ if {[catch {merge_tool_get_stages $target $stages } err]} {
270+ file rename -force -- $backup $target
271+ delete_temp_files $stages
272+ error_popup [mc " Error retrieving versions:\n %s" $err ]
273+ return
274+ }
275+
276+ # Restore the conflict file
277+ file copy -force -- $backup $target
278+
279+ # Initialize global state
280+ set mtool_target $target
281+ set mtool_mtime [file mtime $target ]
282+ set mtool_tmpfiles $stages
283+
284+ lappend mtool_tmpfiles $backup
285+
286+ # Force redirection to avoid interpreting output on stderr
287+ # as an error, and launch the tool
288+ lappend cmdline {2>@1}
289+
290+ if {[catch { set mtool_fd [_open_stdout_stderr $cmdline ] } err]} {
291+ delete_temp_files $mtool_tmpfiles
292+ error_popup [mc " Could not start the merge tool:\n\n %s" $err ]
293+ return
294+ }
295+
296+ ui_status [mc " Running merge tool..." ]
297+
298+ fconfigure $mtool_fd -blocking 0 -translation binary -encoding binary
299+ fileevent $mtool_fd readable [list read_mtool_output $mtool_fd ]
300+ }
301+
302+ proc read_mtool_output {fd} {
303+ global mtool_fd mtool_tmpfiles
304+
305+ read $fd
306+ if {[eof $fd ]} {
307+ unset mtool_fd
308+
309+ fconfigure $fd -blocking 1
310+ merge_tool_finish $fd
311+ }
312+ }
313+
314+ proc merge_tool_finish {fd} {
315+ global mtool_tmpfiles mtool_target mtool_mtime
316+
317+ set backup [lindex $mtool_tmpfiles end]
318+ set failed 0
319+
320+ # Check the return code
321+ if {[catch {close $fd } err]} {
322+ set failed 1
323+ if {$err ne {child process exited abnormally}} {
324+ error_popup [strcat [mc " Merge tool failed." ] " \n\n $err " ]
325+ }
326+ }
327+
328+ # Check the modification time of the target file
329+ if {!$failed && [file mtime $mtool_target ] eq $mtool_mtime } {
330+ if {[ask_popup [mc " File %s unchanged, still accept as resolved?" \
331+ [short_path $mtool_target ]]] ne {yes}} {
332+ set failed 1
333+ }
334+ }
335+
336+ # Finish
337+ if {$failed } {
338+ file rename -force -- $backup $mtool_target
339+ delete_temp_files $mtool_tmpfiles
340+ ui_status [mc " Merge tool failed." ]
341+ } else {
342+ if {[is_config_true merge.keepbackup]} {
343+ file rename -force -- $backup " $mtool_target .orig"
344+ }
345+
346+ delete_temp_files $mtool_tmpfiles
347+
348+ merge_add_resolution $mtool_target
349+ }
350+ }
0 commit comments