git-gui: Completely remove support for creating octopus merges
[alt-git.git] / lib / merge.tcl
blobe5a752507b55269daad163ae525e373dde44773c
1 # git-gui branch merge support
2 # Copyright (C) 2006, 2007 Shawn Pearce
4 class merge {
6 field w ; # top level window
7 field w_list ; # widget of available branches
8 field list ; # list of available branches
10 method _can_merge {} {
11 global HEAD commit_type file_states
13 if {[string match amend* $commit_type]} {
14 info_popup {Cannot merge while amending.
16 You must finish amending this commit before starting any type of merge.
18 return 0
21 if {[committer_ident] eq {}} {return 0}
22 if {![lock_index merge]} {return 0}
24 # -- Our in memory state should match the repository.
26 repository_state curType curHEAD curMERGE_HEAD
27 if {$commit_type ne $curType || $HEAD ne $curHEAD} {
28 info_popup {Last scanned state does not match repository state.
30 Another Git program has modified this repository since the last scan. A rescan must be performed before a merge can be performed.
32 The rescan will be automatically started now.
34 unlock_index
35 rescan ui_ready
36 return 0
39 foreach path [array names file_states] {
40 switch -glob -- [lindex $file_states($path) 0] {
41 _O {
42 continue; # and pray it works!
44 U? {
45 error_popup "You are in the middle of a conflicted merge.
47 File [short_path $path] has merge conflicts.
49 You must resolve them, add the file, and commit to complete the current merge. Only then can you begin another merge.
51 unlock_index
52 return 0
54 ?? {
55 error_popup "You are in the middle of a change.
57 File [short_path $path] is modified.
59 You should complete the current commit before starting a merge. Doing so will help you abort a failed merge, should the need arise.
61 unlock_index
62 return 0
67 return 1
70 method _rev {} {
71 set i [$w_list curselection]
72 if {$i >= 0} {
73 return [lindex [lindex $list $i] 0]
75 return {}
78 method _visualize {} {
79 set rev [_rev $this]
80 if {$rev ne {}} {
81 do_gitk [list $rev --not HEAD]
85 method _start {} {
86 global HEAD current_branch
88 set name [_rev $this]
89 if {$name eq {}} {
90 return
93 set cmd [list git merge $name]
94 set msg "Merging $current_branch and $name"
95 ui_status "$msg..."
96 set cons [console::new "Merge" $cmd]
97 console::exec $cons $cmd [cb _finish $cons]
99 wm protocol $w WM_DELETE_WINDOW {}
100 destroy $w
103 method _finish {cons ok} {
104 console::done $cons $ok
105 if {$ok} {
106 set msg {Merge completed successfully.}
107 } else {
108 set msg {Merge failed. Conflict resolution is required.}
110 unlock_index
111 rescan [list ui_status $msg]
112 delete_this
115 constructor dialog {} {
116 global current_branch
117 global M1B
119 if {![_can_merge $this]} {
120 delete_this
121 return
124 set fmt {list %(objectname) %(*objectname) %(refname) %(subject)}
125 set fr_fd [git_read for-each-ref \
126 --tcl \
127 --format=$fmt \
128 refs/heads \
129 refs/remotes \
130 refs/tags \
132 fconfigure $fr_fd -translation binary
133 while {[gets $fr_fd line] > 0} {
134 set line [eval $line]
135 set ref [lindex $line 2]
136 regsub ^refs/(heads|remotes|tags)/ $ref {} ref
137 set subj($ref) [lindex $line 3]
138 lappend sha1([lindex $line 0]) $ref
139 if {[lindex $line 1] ne {}} {
140 lappend sha1([lindex $line 1]) $ref
143 close $fr_fd
145 set list [list]
146 set fr_fd [git_read rev-list --all --not HEAD]
147 while {[gets $fr_fd line] > 0} {
148 if {[catch {set ref $sha1($line)}]} continue
149 foreach n $ref {
150 lappend list [list $n $line]
153 close $fr_fd
154 set list [lsort -unique $list]
156 make_toplevel top w
157 wm title $top "[appname] ([reponame]): Merge"
158 if {$top ne {.}} {
159 wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
162 set _visualize [cb _visualize]
163 set _start [cb _start]
165 label $w.header \
166 -text "Merge Into $current_branch" \
167 -font font_uibold
168 pack $w.header -side top -fill x
170 frame $w.buttons
171 button $w.buttons.visualize -text Visualize -command $_visualize
172 pack $w.buttons.visualize -side left
173 button $w.buttons.create -text Merge -command $_start
174 pack $w.buttons.create -side right
175 button $w.buttons.cancel \
176 -text {Cancel} \
177 -command [cb _cancel]
178 pack $w.buttons.cancel -side right -padx 5
179 pack $w.buttons -side bottom -fill x -pady 10 -padx 10
181 labelframe $w.source -text {Source Branches}
182 set w_list $w.source.l
183 listbox $w_list \
184 -height 10 \
185 -width 70 \
186 -font font_diff \
187 -selectmode browse \
188 -yscrollcommand [list $w.source.sby set]
189 scrollbar $w.source.sby -command [list $w_list yview]
190 pack $w.source.sby -side right -fill y
191 pack $w_list -side left -fill both -expand 1
192 pack $w.source -fill both -expand 1 -pady 5 -padx 5
194 foreach ref $list {
195 set n [lindex $ref 0]
196 if {[string length $n] > 20} {
197 set n "[string range $n 0 16]..."
199 $w_list insert end [format {%s %-20s %s} \
200 [string range [lindex $ref 1] 0 5] \
201 $n \
202 $subj([lindex $ref 0])]
205 bind $w_list <Key-K> [list event generate %W <Shift-Key-Up>]
206 bind $w_list <Key-J> [list event generate %W <Shift-Key-Down>]
207 bind $w_list <Key-k> [list event generate %W <Key-Up>]
208 bind $w_list <Key-j> [list event generate %W <Key-Down>]
209 bind $w_list <Key-h> [list event generate %W <Key-Left>]
210 bind $w_list <Key-l> [list event generate %W <Key-Right>]
211 bind $w_list <Key-v> $_visualize
213 bind $w <$M1B-Key-Return> $_start
214 bind $w <Visibility> [cb _visible]
215 bind $w <Key-Escape> [cb _cancel]
216 wm protocol $w WM_DELETE_WINDOW [cb _cancel]
217 tkwait window $w
220 method _visible {} {
221 grab $w
222 focus $w_list
225 method _cancel {} {
226 wm protocol $w WM_DELETE_WINDOW {}
227 unlock_index
228 destroy $w
229 delete_this
234 namespace eval merge {
236 proc reset_hard {} {
237 global HEAD commit_type file_states
239 if {[string match amend* $commit_type]} {
240 info_popup {Cannot abort while amending.
242 You must finish amending this commit.
244 return
247 if {![lock_index abort]} return
249 if {[string match *merge* $commit_type]} {
250 set op merge
251 } else {
252 set op commit
255 if {[ask_popup "Abort $op?
257 Aborting the current $op will cause *ALL* uncommitted changes to be lost.
259 Continue with aborting the current $op?"] eq {yes}} {
260 set fd [git_read read-tree --reset -u HEAD]
261 fconfigure $fd -blocking 0 -translation binary
262 fileevent $fd readable [namespace code [list _reset_wait $fd]]
263 ui_status {Aborting... please wait...}
264 } else {
265 unlock_index
269 proc _reset_wait {fd} {
270 global ui_comm
272 read $fd
273 if {[eof $fd]} {
274 close $fd
275 unlock_index
277 $ui_comm delete 0.0 end
278 $ui_comm edit modified false
280 catch {file delete [gitdir MERGE_HEAD]}
281 catch {file delete [gitdir rr-cache MERGE_RR]}
282 catch {file delete [gitdir SQUASH_MSG]}
283 catch {file delete [gitdir MERGE_MSG]}
284 catch {file delete [gitdir GITGUI_MSG]}
286 rescan {ui_status {Abort completed. Ready.}}