]>
Commit | Line | Data |
---|---|---|
042c2325 AG |
1 | # git-gui merge conflict resolution |
2 | # parts based on git-mergetool (c) 2006 Theodore Y. Ts'o | |
3 | ||
4 | proc merge_resolve_one {stage} { | |
5 | global current_diff_path | |
6 | ||
7 | switch -- $stage { | |
8b56a18d CS |
8 | 1 { set targetquestion [mc "Force resolution to the base version?"] } |
9 | 2 { set targetquestion [mc "Force resolution to this branch?"] } | |
10 | 3 { set targetquestion [mc "Force resolution to the other branch?"] } | |
042c2325 AG |
11 | } |
12 | ||
8b56a18d CS |
13 | set op_question [strcat $targetquestion "\n" \ |
14 | [mc "Note that the diff shows only conflicting changes. | |
042c2325 AG |
15 | |
16 | %s will be overwritten. | |
17 | ||
18 | This operation can be undone only by restarting the merge." \ | |
8b56a18d | 19 | [short_path $current_diff_path]]] |
042c2325 AG |
20 | |
21 | if {[ask_popup $op_question] eq {yes}} { | |
22 | merge_load_stages $current_diff_path [list merge_force_stage $stage] | |
23 | } | |
24 | } | |
25 | ||
0aea2842 | 26 | proc merge_stage_workdir {path {lno {}}} { |
3e34838c | 27 | global current_diff_path diff_active |
0aea2842 | 28 | global current_diff_side ui_workdir |
3e34838c AG |
29 | |
30 | if {$diff_active} return | |
31 | ||
0aea2842 AG |
32 | if {$path ne $current_diff_path || $ui_workdir ne $current_diff_side} { |
33 | show_diff $path $ui_workdir $lno {} [list do_merge_stage_workdir $path] | |
3e34838c AG |
34 | } else { |
35 | do_merge_stage_workdir $path | |
36 | } | |
37 | } | |
38 | ||
39 | proc do_merge_stage_workdir {path} { | |
40 | global current_diff_path is_conflict_diff | |
41 | ||
42 | if {$path ne $current_diff_path} return; | |
43 | ||
44 | if {$is_conflict_diff} { | |
45 | if {[ask_popup [mc "File %s seems to have unresolved conflicts, still stage?" \ | |
46 | [short_path $path]]] ne {yes}} { | |
47 | return | |
48 | } | |
49 | } | |
50 | ||
51 | merge_add_resolution $path | |
52 | } | |
53 | ||
042c2325 | 54 | proc merge_add_resolution {path} { |
29853b90 | 55 | global current_diff_path ui_workdir |
042c2325 | 56 | |
29853b90 | 57 | set after [next_diff_after_action $ui_workdir $path {} {^_?U}] |
042c2325 AG |
58 | |
59 | update_index \ | |
60 | [mc "Adding resolution for %s" [short_path $path]] \ | |
61 | [list $path] \ | |
62 | [concat $after [list ui_ready]] | |
63 | } | |
64 | ||
65 | proc merge_force_stage {stage} { | |
66 | global current_diff_path merge_stages | |
67 | ||
68 | if {$merge_stages($stage) ne {}} { | |
69 | git checkout-index -f --stage=$stage -- $current_diff_path | |
70 | } else { | |
71 | file delete -- $current_diff_path | |
72 | } | |
73 | ||
74 | merge_add_resolution $current_diff_path | |
75 | } | |
76 | ||
77 | proc merge_load_stages {path cont} { | |
78 | global merge_stages_fd merge_stages merge_stages_buf | |
79 | ||
80 | if {[info exists merge_stages_fd]} { | |
81 | catch { kill_file_process $merge_stages_fd } | |
82 | catch { close $merge_stages_fd } | |
83 | } | |
84 | ||
85 | set merge_stages(0) {} | |
86 | set merge_stages(1) {} | |
87 | set merge_stages(2) {} | |
88 | set merge_stages(3) {} | |
89 | set merge_stages_buf {} | |
90 | ||
e27430e7 | 91 | set merge_stages_fd [eval git_read ls-files -u -z -- {$path}] |
042c2325 AG |
92 | |
93 | fconfigure $merge_stages_fd -blocking 0 -translation binary -encoding binary | |
94 | fileevent $merge_stages_fd readable [list read_merge_stages $merge_stages_fd $cont] | |
95 | } | |
96 | ||
97 | proc read_merge_stages {fd cont} { | |
98 | global merge_stages_buf merge_stages_fd merge_stages | |
99 | ||
100 | append merge_stages_buf [read $fd] | |
101 | set pck [split $merge_stages_buf "\0"] | |
102 | set merge_stages_buf [lindex $pck end] | |
103 | ||
104 | if {[eof $fd] && $merge_stages_buf ne {}} { | |
105 | lappend pck {} | |
106 | set merge_stages_buf {} | |
107 | } | |
108 | ||
109 | foreach p [lrange $pck 0 end-1] { | |
110 | set fcols [split $p "\t"] | |
111 | set cols [split [lindex $fcols 0] " "] | |
112 | set stage [lindex $cols 2] | |
113 | ||
114 | set merge_stages($stage) [lrange $cols 0 1] | |
115 | } | |
116 | ||
117 | if {[eof $fd]} { | |
118 | close $fd | |
119 | unset merge_stages_fd | |
120 | eval $cont | |
121 | } | |
122 | } | |
7e30682c AG |
123 | |
124 | proc merge_resolve_tool {} { | |
125 | global current_diff_path | |
126 | ||
127 | merge_load_stages $current_diff_path [list merge_resolve_tool2] | |
128 | } | |
129 | ||
130 | proc merge_resolve_tool2 {} { | |
131 | global current_diff_path merge_stages | |
132 | ||
133 | # Validate the stages | |
134 | if {$merge_stages(2) eq {} || | |
135 | [lindex $merge_stages(2) 0] eq {120000} || | |
136 | [lindex $merge_stages(2) 0] eq {160000} || | |
137 | $merge_stages(3) eq {} || | |
138 | [lindex $merge_stages(3) 0] eq {120000} || | |
139 | [lindex $merge_stages(3) 0] eq {160000} | |
140 | } { | |
141 | error_popup [mc "Cannot resolve deletion or link conflicts using a tool"] | |
142 | return | |
143 | } | |
144 | ||
145 | if {![file exists $current_diff_path]} { | |
146 | error_popup [mc "Conflict file does not exist"] | |
147 | return | |
148 | } | |
149 | ||
150 | # Determine the tool to use | |
151 | set tool [get_config merge.tool] | |
152 | if {$tool eq {}} { set tool meld } | |
153 | ||
154 | set merge_tool_path [get_config "mergetool.$tool.path"] | |
155 | if {$merge_tool_path eq {}} { | |
156 | switch -- $tool { | |
157 | emerge { set merge_tool_path "emacs" } | |
48c74a58 | 158 | araxis { set merge_tool_path "compare" } |
7e30682c AG |
159 | default { set merge_tool_path $tool } |
160 | } | |
161 | } | |
162 | ||
163 | # Make file names | |
164 | set filebase [file rootname $current_diff_path] | |
165 | set fileext [file extension $current_diff_path] | |
166 | set basename [lindex [file split $current_diff_path] end] | |
167 | ||
168 | set MERGED $current_diff_path | |
169 | set BASE "./$MERGED.BASE$fileext" | |
170 | set LOCAL "./$MERGED.LOCAL$fileext" | |
171 | set REMOTE "./$MERGED.REMOTE$fileext" | |
172 | set BACKUP "./$MERGED.BACKUP$fileext" | |
173 | ||
174 | set base_stage $merge_stages(1) | |
175 | ||
176 | # Build the command line | |
177 | switch -- $tool { | |
0e0f4504 | 178 | araxis { |
7e30682c | 179 | if {$base_stage ne {}} { |
0e0f4504 SS |
180 | set cmdline [list "$merge_tool_path" -wait -merge -3 -a1 \ |
181 | -title1:"'$MERGED (Base)'" -title2:"'$MERGED (Local)'" \ | |
182 | -title3:"'$MERGED (Remote)'" \ | |
183 | "$BASE" "$LOCAL" "$REMOTE" "$MERGED"] | |
7e30682c | 184 | } else { |
0e0f4504 SS |
185 | set cmdline [list "$merge_tool_path" -wait -2 \ |
186 | -title1:"'$MERGED (Local)'" -title2:"'$MERGED (Remote)'" \ | |
187 | "$LOCAL" "$REMOTE" "$MERGED"] | |
7e30682c AG |
188 | } |
189 | } | |
f3768a67 SS |
190 | bc3 { |
191 | if {$base_stage ne {}} { | |
60468d6c | 192 | set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" "-mergeoutput=$MERGED"] |
f3768a67 | 193 | } else { |
60468d6c | 194 | set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" "-mergeoutput=$MERGED"] |
f3768a67 SS |
195 | } |
196 | } | |
0e0f4504 | 197 | ecmerge { |
7e30682c | 198 | if {$base_stage ne {}} { |
0e0f4504 | 199 | set cmdline [list "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --default --mode=merge3 --to="$MERGED"] |
7e30682c | 200 | } else { |
0e0f4504 | 201 | set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" --default --mode=merge2 --to="$MERGED"] |
7e30682c AG |
202 | } |
203 | } | |
0e0f4504 SS |
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 | } | |
7e30682c AG |
212 | } |
213 | gvimdiff { | |
214 | set cmdline [list "$merge_tool_path" -f "$LOCAL" "$MERGED" "$REMOTE"] | |
215 | } | |
0e0f4504 | 216 | kdiff3 { |
7e30682c | 217 | if {$base_stage ne {}} { |
0e0f4504 SS |
218 | set cmdline [list "$merge_tool_path" --auto --L1 "$MERGED (Base)" \ |
219 | --L2 "$MERGED (Local)" --L3 "$MERGED (Remote)" -o "$MERGED" "$BASE" "$LOCAL" "$REMOTE"] | |
7e30682c | 220 | } else { |
0e0f4504 SS |
221 | set cmdline [list "$merge_tool_path" --auto --L1 "$MERGED (Local)" \ |
222 | --L2 "$MERGED (Remote)" -o "$MERGED" "$LOCAL" "$REMOTE"] | |
7e30682c AG |
223 | } |
224 | } | |
0e0f4504 SS |
225 | meld { |
226 | set cmdline [list "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"] | |
227 | } | |
7e30682c AG |
228 | opendiff { |
229 | if {$base_stage ne {}} { | |
230 | set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED"] | |
231 | } else { | |
232 | set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$MERGED"] | |
233 | } | |
234 | } | |
0e0f4504 SS |
235 | p4merge { |
236 | set cmdline [list "$merge_tool_path" "$BASE" "$REMOTE" "$LOCAL" "$MERGED"] | |
7e30682c | 237 | } |
0e0f4504 | 238 | tkdiff { |
7e30682c | 239 | if {$base_stage ne {}} { |
0e0f4504 | 240 | set cmdline [list "$merge_tool_path" -a "$BASE" -o "$MERGED" "$LOCAL" "$REMOTE"] |
7e30682c | 241 | } else { |
0e0f4504 | 242 | set cmdline [list "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"] |
7e30682c AG |
243 | } |
244 | } | |
0e0f4504 SS |
245 | vimdiff { |
246 | error_popup [mc "Not a GUI merge tool: '%s'" $tool] | |
247 | return | |
248 | } | |
48c74a58 AG |
249 | winmerge { |
250 | if {$base_stage ne {}} { | |
251 | # This tool does not support 3-way merges. | |
252 | # Use the 'conflict file' resolution feature instead. | |
253 | set cmdline [list "$merge_tool_path" -e -ub "$MERGED"] | |
254 | } else { | |
255 | set cmdline [list "$merge_tool_path" -e -ub -wl \ | |
256 | -dl "Theirs File" -dr "Mine File" "$REMOTE" "$LOCAL" "$MERGED"] | |
257 | } | |
258 | } | |
0e0f4504 | 259 | xxdiff { |
48c74a58 | 260 | if {$base_stage ne {}} { |
0e0f4504 SS |
261 | set cmdline [list "$merge_tool_path" -X --show-merged-pane \ |
262 | -R {Accel.SaveAsMerged: "Ctrl-S"} \ | |
263 | -R {Accel.Search: "Ctrl+F"} \ | |
264 | -R {Accel.SearchForward: "Ctrl-G"} \ | |
265 | --merged-file "$MERGED" "$LOCAL" "$BASE" "$REMOTE"] | |
48c74a58 | 266 | } else { |
0e0f4504 SS |
267 | set cmdline [list "$merge_tool_path" -X --show-merged-pane \ |
268 | -R {Accel.SaveAsMerged: "Ctrl-S"} \ | |
269 | -R {Accel.Search: "Ctrl+F"} \ | |
270 | -R {Accel.SearchForward: "Ctrl-G"} \ | |
271 | --merged-file "$MERGED" "$LOCAL" "$REMOTE"] | |
48c74a58 AG |
272 | } |
273 | } | |
7e30682c AG |
274 | default { |
275 | error_popup [mc "Unsupported merge tool '%s'" $tool] | |
276 | return | |
277 | } | |
278 | } | |
279 | ||
280 | merge_tool_start $cmdline $MERGED $BACKUP [list $BASE $LOCAL $REMOTE] | |
281 | } | |
282 | ||
283 | proc delete_temp_files {files} { | |
284 | foreach fname $files { | |
285 | file delete $fname | |
286 | } | |
287 | } | |
288 | ||
289 | proc merge_tool_get_stages {target stages} { | |
290 | global merge_stages | |
291 | ||
292 | set i 1 | |
293 | foreach fname $stages { | |
294 | if {$merge_stages($i) eq {}} { | |
295 | file delete $fname | |
48c74a58 | 296 | catch { close [open $fname w] } |
7e30682c AG |
297 | } else { |
298 | # A hack to support autocrlf properly | |
299 | git checkout-index -f --stage=$i -- $target | |
300 | file rename -force -- $target $fname | |
301 | } | |
302 | incr i | |
303 | } | |
304 | } | |
305 | ||
306 | proc merge_tool_start {cmdline target backup stages} { | |
307 | global merge_stages mtool_target mtool_tmpfiles mtool_fd mtool_mtime | |
308 | ||
309 | if {[info exists mtool_fd]} { | |
310 | if {[ask_popup [mc "Merge tool is already running, terminate it?"]] eq {yes}} { | |
311 | catch { kill_file_process $mtool_fd } | |
312 | catch { close $mtool_fd } | |
313 | unset mtool_fd | |
314 | ||
315 | set old_backup [lindex $mtool_tmpfiles end] | |
316 | file rename -force -- $old_backup $mtool_target | |
317 | delete_temp_files $mtool_tmpfiles | |
318 | } else { | |
319 | return | |
320 | } | |
321 | } | |
322 | ||
323 | # Save the original file | |
324 | file rename -force -- $target $backup | |
325 | ||
326 | # Get the blobs; it destroys $target | |
327 | if {[catch {merge_tool_get_stages $target $stages} err]} { | |
328 | file rename -force -- $backup $target | |
329 | delete_temp_files $stages | |
330 | error_popup [mc "Error retrieving versions:\n%s" $err] | |
331 | return | |
332 | } | |
333 | ||
334 | # Restore the conflict file | |
335 | file copy -force -- $backup $target | |
336 | ||
337 | # Initialize global state | |
338 | set mtool_target $target | |
339 | set mtool_mtime [file mtime $target] | |
340 | set mtool_tmpfiles $stages | |
341 | ||
342 | lappend mtool_tmpfiles $backup | |
343 | ||
344 | # Force redirection to avoid interpreting output on stderr | |
345 | # as an error, and launch the tool | |
346 | lappend cmdline {2>@1} | |
347 | ||
348 | if {[catch { set mtool_fd [_open_stdout_stderr $cmdline] } err]} { | |
349 | delete_temp_files $mtool_tmpfiles | |
350 | error_popup [mc "Could not start the merge tool:\n\n%s" $err] | |
351 | return | |
352 | } | |
353 | ||
354 | ui_status [mc "Running merge tool..."] | |
355 | ||
356 | fconfigure $mtool_fd -blocking 0 -translation binary -encoding binary | |
357 | fileevent $mtool_fd readable [list read_mtool_output $mtool_fd] | |
358 | } | |
359 | ||
360 | proc read_mtool_output {fd} { | |
361 | global mtool_fd mtool_tmpfiles | |
362 | ||
363 | read $fd | |
364 | if {[eof $fd]} { | |
365 | unset mtool_fd | |
366 | ||
367 | fconfigure $fd -blocking 1 | |
368 | merge_tool_finish $fd | |
369 | } | |
370 | } | |
371 | ||
372 | proc merge_tool_finish {fd} { | |
373 | global mtool_tmpfiles mtool_target mtool_mtime | |
374 | ||
375 | set backup [lindex $mtool_tmpfiles end] | |
376 | set failed 0 | |
377 | ||
378 | # Check the return code | |
379 | if {[catch {close $fd} err]} { | |
380 | set failed 1 | |
381 | if {$err ne {child process exited abnormally}} { | |
382 | error_popup [strcat [mc "Merge tool failed."] "\n\n$err"] | |
383 | } | |
384 | } | |
385 | ||
7e30682c AG |
386 | # Finish |
387 | if {$failed} { | |
388 | file rename -force -- $backup $mtool_target | |
389 | delete_temp_files $mtool_tmpfiles | |
390 | ui_status [mc "Merge tool failed."] | |
391 | } else { | |
fb25092a | 392 | if {[is_config_true mergetool.keepbackup]} { |
7e30682c AG |
393 | file rename -force -- $backup "$mtool_target.orig" |
394 | } | |
395 | ||
396 | delete_temp_files $mtool_tmpfiles | |
397 | ||
d3bcf55d | 398 | reshow_diff |
7e30682c AG |
399 | } |
400 | } |