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.
}}