1 # git-gui branch merge support
2 # Copyright (C) 2006, 2007 Shawn Pearce
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.
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.
39 foreach path [array names file_states] {
40 switch -glob -- [lindex $file_states($path) 0] {
42 continue; # and pray it works!
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.
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.
71 set i [$w_list curselection]
73 return [lindex [lindex $list $i] 0]
78 method _visualize {} {
81 do_gitk [list $rev --not HEAD]
86 global HEAD current_branch
93 set cmd [list git merge $name]
94 set msg "Merging $current_branch and $name"
96 set cons [console::new "Merge" $cmd]
97 console::exec $cons $cmd [cb _finish $cons]
99 wm protocol $w WM_DELETE_WINDOW {}
103 method _finish {cons ok} {
104 console::done $cons $ok
106 set msg {Merge completed successfully.}
108 set msg {Merge failed. Conflict resolution is required.}
111 rescan [list ui_status $msg]
115 constructor dialog {} {
116 global current_branch
119 if {![_can_merge $this]} {
124 set fmt {list %(objectname) %(*objectname) %(refname) %(subject)}
125 set fr_fd [git_read for-each-ref \
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
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
150 lappend list [list $n $line]
154 set list [lsort -unique $list]
157 wm title $top "[appname] ([reponame]): Merge"
159 wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
162 set _visualize [cb _visualize]
163 set _start [cb _start]
166 -text "Merge Into $current_branch" \
168 pack $w.header -side top -fill x
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 \
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
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
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] \
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]
226 wm protocol $w WM_DELETE_WINDOW {}
234 namespace eval merge {
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.
247 if {![lock_index abort]} return
249 if {[string match *merge* $commit_type]} {
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...}
269 proc _reset_wait {fd} {
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.}}