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.
72 foreach i
[$w_list curselection
] {
73 lappend r
[lindex [lindex $list $i] 0]
78 method _visualize
{} {
79 set revs
[_refs
$this]
80 if {$revs eq
{}} return
81 lappend revs
--not HEAD
86 global HEAD current_branch
88 set cmd
[list git merge
]
89 set names
[_refs
$this]
90 set revcnt
[llength $names]
95 } elseif
{$revcnt == 1} {
97 } elseif
{$revcnt <= 15} {
103 "Use octopus merge strategy?
105 You are merging $revcnt branches at once. This requires using the octopus merge driver, which may not succeed if there are file-level conflicts.
116 -title [wm title
$w] \
118 -message "Too many branches selected.
120 You have requested to merge $revcnt branches in an octopus merge. This exceeds Git's internal limit of 15 branches per merge.
122 Please select fewer branches. To merge more than 15 branches, merge the branches in batches.
127 set msg
"Merging $current_branch, [join $names {, }]"
129 set cons
[console::new "Merge" $msg]
130 console::exec $cons $cmd [cb _finish
$revcnt $cons]
132 wm protocol
$w WM_DELETE_WINDOW
{}
136 method _finish
{revcnt cons ok
} {
137 console::done $cons $ok
139 set msg
{Merge completed successfully.
}
142 info_popup
"Octopus merge failed.
144 Your merge of $revcnt branches has failed.
146 There are file-level conflicts between the branches which must be resolved manually.
148 The working directory will now be reset.
150 You can attempt this merge again by merging only one branch at a time." $w
152 set fd
[git_read read-tree
--reset -u HEAD
]
153 fconfigure $fd -blocking 0 -translation binary
154 fileevent $fd readable
[cb _reset_wait
$fd]
155 ui_status
{Aborting... please wait...
}
159 set msg
{Merge failed. Conflict resolution is required.
}
162 rescan
[list ui_status
$msg]
166 constructor dialog
{} {
167 global current_branch
170 if {![_can_merge
$this]} {
175 set fmt
{list %(objectname
) %(*objectname
) %(refname
) %(subject
)}
176 set fr_fd
[git_read for-each-ref
\
183 fconfigure $fr_fd -translation binary
184 while {[gets $fr_fd line
] > 0} {
185 set line
[eval $line]
186 set ref
[lindex $line 2]
187 regsub ^refs
/(heads|remotes|tags
)/ $ref {} ref
188 set subj
($ref) [lindex $line 3]
189 lappend sha1
([lindex $line 0]) $ref
190 if {[lindex $line 1] ne
{}} {
191 lappend sha1
([lindex $line 1]) $ref
197 set fr_fd
[git_read rev-list
--all --not HEAD
]
198 while {[gets $fr_fd line
] > 0} {
199 if {[catch {set ref
$sha1($line)}]} continue
201 lappend list [list $n $line]
205 set list [lsort -unique $list]
208 wm title
$top "[appname] ([reponame]): Merge"
210 wm geometry
$top "+[winfo rootx .]+[winfo rooty .]"
213 set _visualize
[cb _visualize
]
214 set _start
[cb _start
]
217 -text "Merge Into $current_branch" \
219 pack $w.header
-side top
-fill x
222 button $w.buttons.visualize
-text Visualize
-command $_visualize
223 pack $w.buttons.visualize
-side left
224 button $w.buttons.create
-text Merge
-command $_start
225 pack $w.buttons.create
-side right
226 button $w.buttons.cancel
\
228 -command [cb _cancel
]
229 pack $w.buttons.cancel
-side right
-padx 5
230 pack $w.buttons
-side bottom
-fill x
-pady 10 -padx 10
232 labelframe $w.
source -text {Source Branches
}
233 set w_list
$w.
source.l
238 -selectmode extended
\
239 -yscrollcommand [list $w.
source.sby
set]
240 scrollbar $w.
source.sby
-command [list $w_list yview
]
241 pack $w.
source.sby
-side right
-fill y
242 pack $w_list -side left
-fill both
-expand 1
243 pack $w.
source -fill both
-expand 1 -pady 5 -padx 5
246 set n
[lindex $ref 0]
247 if {[string length
$n] > 20} {
248 set n
"[string range $n 0 16]..."
250 $w_list insert end
[format {%s
%-20s %s
} \
251 [string range
[lindex $ref 1] 0 5] \
253 $subj([lindex $ref 0])]
256 bind $w_list <Key-K
> [list event generate
%W
<Shift-Key-Up
>]
257 bind $w_list <Key-J
> [list event generate
%W
<Shift-Key-Down
>]
258 bind $w_list <Key-k
> [list event generate
%W
<Key-Up
>]
259 bind $w_list <Key-j
> [list event generate
%W
<Key-Down
>]
260 bind $w_list <Key-h
> [list event generate
%W
<Key-Left
>]
261 bind $w_list <Key-l
> [list event generate
%W
<Key-Right
>]
262 bind $w_list <Key-v
> $_visualize
264 bind $w <$M1B-Key
-Return
> $_start
265 bind $w <Visibility
> [cb _visible
]
266 bind $w <Key-Escape
> [cb _cancel
]
267 wm protocol
$w WM_DELETE_WINDOW
[cb _cancel
]
277 wm protocol
$w WM_DELETE_WINDOW
{}
285 namespace eval merge
{
288 global HEAD commit_type file_states
290 if {[string match amend
* $commit_type]} {
291 info_popup
{Cannot abort
while amending.
293 You must finish amending this commit.
298 if {![lock_index abort
]} return
300 if {[string match
*merge
* $commit_type]} {
306 if {[ask_popup
"Abort $op?
308 Aborting the current $op will cause *ALL* uncommitted changes to be lost.
310 Continue with aborting the current $op?"] eq
{yes
}} {
311 set fd
[git_read read-tree
--reset -u HEAD
]
312 fconfigure $fd -blocking 0 -translation binary
313 fileevent $fd readable
[namespace code
[list _reset_wait
$fd]]
314 ui_status
{Aborting... please wait...
}
320 proc _reset_wait
{fd
} {
328 $ui_comm delete
0.0 end
329 $ui_comm edit modified false
331 catch {file delete
[gitdir MERGE_HEAD
]}
332 catch {file delete
[gitdir rr-cache MERGE_RR
]}
333 catch {file delete
[gitdir SQUASH_MSG
]}
334 catch {file delete
[gitdir MERGE_MSG
]}
335 catch {file delete
[gitdir GITGUI_MSG
]}
337 rescan
{ui_status
{Abort completed. Ready.
}}