2 # Tcl ignores the next line -*- tcl -*- \
5 set appvers
{@@GIT_VERSION@@
}
7 Copyright ©
2006, 2007 Shawn Pearce
, Paul Mackerras.
9 This program is free software
; you can redistribute it and
/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation
; either version
2 of the License
, or
12 (at your option
) any later version.
14 This program is distributed
in the hope that it will be useful
,
15 but WITHOUT ANY WARRANTY
; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License
for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program
; if not
, write to the Free Software
21 Foundation
, Inc.
, 59 Temple Place
, Suite
330, Boston
, MA
02111-1307 USA
}
23 ######################################################################
27 set _appname
[lindex
[file split $argv0] end
]
43 return [eval [concat
[list
file join $_gitdir] $args]]
48 if {$_gitexec eq
{}} {
49 if {[catch
{set _gitexec
[exec git
--exec-path]} err
]} {
50 error
"Git not installed?\n\n$err"
56 return [eval [concat
[list
file join $_gitexec] $args]]
65 global tcl_platform tk_library
66 if {[tk windowingsystem
] eq
{aqua
}} {
74 if {$tcl_platform(platform
) eq
{windows
}} {
81 global tcl_platform _iscygwin
82 if {$_iscygwin eq
{}} {
83 if {$tcl_platform(platform
) eq
{windows
}} {
84 if {[catch
{set p
[exec cygpath
--windir]} err
]} {
96 proc is_enabled
{option
} {
97 global enabled_options
98 if {[catch
{set on
$enabled_options($option)}]} {return 0}
102 proc enable_option
{option
} {
103 global enabled_options
104 set enabled_options
($option) 1
107 proc disable_option
{option
} {
108 global enabled_options
109 set enabled_options
($option) 0
112 ######################################################################
116 proc is_many_config
{name
} {
117 switch
-glob -- $name {
126 proc is_config_true
{name
} {
128 if {[catch
{set v
$repo_config($name)}]} {
130 } elseif
{$v eq
{true
} ||
$v eq
{1} ||
$v eq
{yes}} {
137 proc load_config
{include_global
} {
138 global repo_config global_config default_config
140 array
unset global_config
141 if {$include_global} {
143 set fd_rc
[open
"| git repo-config --global --list" r
]
144 while {[gets
$fd_rc line
] >= 0} {
145 if {[regexp
{^
([^
=]+)=(.
*)$
} $line line name value
]} {
146 if {[is_many_config
$name]} {
147 lappend global_config
($name) $value
149 set global_config
($name) $value
157 array
unset repo_config
159 set fd_rc
[open
"| git repo-config --list" r
]
160 while {[gets
$fd_rc line
] >= 0} {
161 if {[regexp
{^
([^
=]+)=(.
*)$
} $line line name value
]} {
162 if {[is_many_config
$name]} {
163 lappend repo_config
($name) $value
165 set repo_config
($name) $value
172 foreach name
[array names default_config
] {
173 if {[catch
{set v
$global_config($name)}]} {
174 set global_config
($name) $default_config($name)
176 if {[catch
{set v
$repo_config($name)}]} {
177 set repo_config
($name) $default_config($name)
182 proc save_config
{} {
183 global default_config font_descs
184 global repo_config global_config
185 global repo_config_new global_config_new
187 foreach option
$font_descs {
188 set name
[lindex
$option 0]
189 set font
[lindex
$option 1]
190 font configure
$font \
191 -family $global_config_new(gui.
$font^^family
) \
192 -size $global_config_new(gui.
$font^^size
)
193 font configure
${font}bold \
194 -family $global_config_new(gui.
$font^^family
) \
195 -size $global_config_new(gui.
$font^^size
)
196 set global_config_new
(gui.
$name) [font configure
$font]
197 unset global_config_new
(gui.
$font^^family
)
198 unset global_config_new
(gui.
$font^^size
)
201 foreach name
[array names default_config
] {
202 set value
$global_config_new($name)
203 if {$value ne
$global_config($name)} {
204 if {$value eq
$default_config($name)} {
205 catch
{exec git repo-config
--global --unset $name}
207 regsub
-all "\[{}\]" $value {"} value
208 exec git repo-config --global $name $value
210 set global_config($name) $value
211 if {$value eq $repo_config($name)} {
212 catch {exec git repo-config --unset $name}
213 set repo_config($name) $value
218 foreach name [array names default_config] {
219 set value $repo_config_new($name)
220 if {$value ne $repo_config($name)} {
221 if {$value eq $global_config($name)} {
222 catch {exec git repo-config --unset $name}
224 regsub -all "\
[{}\
]" $value {"} value
225 exec git repo-config
$name $value
227 set repo_config
($name) $value
232 proc error_popup
{msg
} {
234 if {[reponame
] ne
{}} {
235 append title
" ([reponame])"
237 set cmd
[list tk_messageBox \
240 -title "$title: error" \
242 if {[winfo ismapped .
]} {
243 lappend cmd
-parent .
248 proc warn_popup
{msg
} {
250 if {[reponame
] ne
{}} {
251 append title
" ([reponame])"
253 set cmd
[list tk_messageBox \
256 -title "$title: warning" \
258 if {[winfo ismapped .
]} {
259 lappend cmd
-parent .
264 proc info_popup
{msg
{parent .
}} {
266 if {[reponame
] ne
{}} {
267 append title
" ([reponame])"
277 proc ask_popup
{msg
} {
279 if {[reponame
] ne
{}} {
280 append title
" ([reponame])"
282 return [tk_messageBox \
290 ######################################################################
294 if { [catch
{set _gitdir
$env(GIT_DIR
)}]
295 && [catch
{set _gitdir
[exec git rev-parse
--git-dir]} err
]} {
296 catch
{wm withdraw .
}
297 error_popup
"Cannot find the git directory:\n\n$err"
300 if {![file isdirectory
$_gitdir] && [is_Cygwin
]} {
301 catch
{set _gitdir
[exec cygpath
--unix $_gitdir]}
303 if {![file isdirectory
$_gitdir]} {
304 catch
{wm withdraw .
}
305 error_popup
"Git directory not found:\n\n$_gitdir"
308 if {[lindex
[file split $_gitdir] end
] ne
{.git
}} {
309 catch
{wm withdraw .
}
310 error_popup
"Cannot use funny .git directory:\n\n$_gitdir"
313 if {[catch
{cd [file dirname $_gitdir]} err
]} {
314 catch
{wm withdraw .
}
315 error_popup
"No working directory [file dirname $_gitdir]:\n\n$err"
318 set _reponame
[lindex
[file split \
319 [file normalize
[file dirname $_gitdir]]] \
322 enable_option multicommit
323 if {[appname
] eq
{git-citool
}} {
324 disable_option multicommit
327 ######################################################################
335 set disable_on_lock
[list
]
336 set index_lock_type none
338 proc lock_index
{type} {
339 global index_lock_type disable_on_lock
341 if {$index_lock_type eq
{none
}} {
342 set index_lock_type
$type
343 foreach w
$disable_on_lock {
344 uplevel
#0 $w disabled
347 } elseif
{$index_lock_type eq
"begin-$type"} {
348 set index_lock_type
$type
354 proc unlock_index
{} {
355 global index_lock_type disable_on_lock
357 set index_lock_type none
358 foreach w
$disable_on_lock {
363 ######################################################################
367 proc repository_state
{ctvar hdvar mhvar
} {
368 global current_branch
369 upvar
$ctvar ct
$hdvar hd
$mhvar mh
373 if {[catch
{set current_branch
[exec git symbolic-ref HEAD
]}]} {
374 set current_branch
{}
376 regsub ^refs
/((heads|tags|remotes
)/)? \
382 if {[catch
{set hd
[exec git rev-parse
--verify HEAD
]}]} {
388 set merge_head
[gitdir MERGE_HEAD
]
389 if {[file exists
$merge_head]} {
391 set fd_mh
[open
$merge_head r
]
392 while {[gets
$fd_mh line
] >= 0} {
403 global PARENT empty_tree
405 set p
[lindex
$PARENT 0]
409 if {$empty_tree eq
{}} {
410 set empty_tree
[exec git mktree
<< {}]
415 proc rescan
{after
{honor_trustmtime
1}} {
416 global HEAD PARENT MERGE_HEAD commit_type
417 global ui_index ui_workdir ui_status_value ui_comm
418 global rescan_active file_states
421 if {$rescan_active > 0 ||
![lock_index
read]} return
423 repository_state newType newHEAD newMERGE_HEAD
424 if {[string match amend
* $commit_type]
425 && $newType eq
{normal
}
426 && $newHEAD eq
$HEAD} {
430 set MERGE_HEAD
$newMERGE_HEAD
431 set commit_type
$newType
434 array
unset file_states
436 if {![$ui_comm edit modified
]
437 ||
[string trim
[$ui_comm get
0.0 end
]] eq
{}} {
438 if {[load_message GITGUI_MSG
]} {
439 } elseif
{[load_message MERGE_MSG
]} {
440 } elseif
{[load_message SQUASH_MSG
]} {
443 $ui_comm edit modified false
446 if {[is_enabled multicommit
]} {
451 if {$honor_trustmtime && $repo_config(gui.trustmtime
) eq
{true
}} {
452 rescan_stage2
{} $after
455 set ui_status_value
{Refreshing
file status...
}
456 set cmd
[list git update-index
]
458 lappend cmd
--unmerged
459 lappend cmd
--ignore-missing
460 lappend cmd
--refresh
461 set fd_rf
[open
"| $cmd" r
]
462 fconfigure
$fd_rf -blocking 0 -translation binary
463 fileevent
$fd_rf readable \
464 [list rescan_stage2
$fd_rf $after]
468 proc rescan_stage2
{fd after
} {
469 global ui_status_value
470 global rescan_active buf_rdi buf_rdf buf_rlo
474 if {![eof
$fd]} return
478 set ls_others
[list | git ls-files
--others -z \
479 --exclude-per-directory=.gitignore
]
480 set info_exclude
[gitdir info exclude
]
481 if {[file readable
$info_exclude]} {
482 lappend ls_others
"--exclude-from=$info_exclude"
490 set ui_status_value
{Scanning
for modified files ...
}
491 set fd_di
[open
"| git diff-index --cached -z [PARENT]" r
]
492 set fd_df
[open
"| git diff-files -z" r
]
493 set fd_lo
[open
$ls_others r
]
495 fconfigure
$fd_di -blocking 0 -translation binary
-encoding binary
496 fconfigure
$fd_df -blocking 0 -translation binary
-encoding binary
497 fconfigure
$fd_lo -blocking 0 -translation binary
-encoding binary
498 fileevent
$fd_di readable
[list read_diff_index
$fd_di $after]
499 fileevent
$fd_df readable
[list read_diff_files
$fd_df $after]
500 fileevent
$fd_lo readable
[list read_ls_others
$fd_lo $after]
503 proc load_message
{file} {
507 if {[file isfile
$f]} {
508 if {[catch
{set fd
[open
$f r
]}]} {
511 set content
[string trim
[read $fd]]
513 regsub
-all -line {[ \r\t]+$
} $content {} content
514 $ui_comm delete
0.0 end
515 $ui_comm insert end
$content
521 proc read_diff_index
{fd after
} {
524 append buf_rdi
[read $fd]
526 set n
[string length
$buf_rdi]
528 set z1
[string first
"\0" $buf_rdi $c]
531 set z2
[string first
"\0" $buf_rdi $z1]
535 set i
[split [string range
$buf_rdi $c [expr {$z1 - 2}]] { }]
536 set p
[string range
$buf_rdi $z1 [expr {$z2 - 1}]]
538 [encoding convertfrom
$p] \
540 [list
[lindex
$i 0] [lindex
$i 2]] \
546 set buf_rdi
[string range
$buf_rdi $c end
]
551 rescan_done
$fd buf_rdi
$after
554 proc read_diff_files
{fd after
} {
557 append buf_rdf
[read $fd]
559 set n
[string length
$buf_rdf]
561 set z1
[string first
"\0" $buf_rdf $c]
564 set z2
[string first
"\0" $buf_rdf $z1]
568 set i
[split [string range
$buf_rdf $c [expr {$z1 - 2}]] { }]
569 set p
[string range
$buf_rdf $z1 [expr {$z2 - 1}]]
571 [encoding convertfrom
$p] \
574 [list
[lindex
$i 0] [lindex
$i 2]]
579 set buf_rdf
[string range
$buf_rdf $c end
]
584 rescan_done
$fd buf_rdf
$after
587 proc read_ls_others
{fd after
} {
590 append buf_rlo
[read $fd]
591 set pck
[split $buf_rlo "\0"]
592 set buf_rlo
[lindex
$pck end
]
593 foreach p
[lrange
$pck 0 end-1
] {
594 merge_state
[encoding convertfrom
$p] ?O
596 rescan_done
$fd buf_rlo
$after
599 proc rescan_done
{fd buf after
} {
601 global file_states repo_config
604 if {![eof
$fd]} return
607 if {[incr rescan_active
-1] > 0} return
616 proc prune_selection
{} {
617 global file_states selected_paths
619 foreach path
[array names selected_paths
] {
620 if {[catch
{set still_here
$file_states($path)}]} {
621 unset selected_paths
($path)
626 ######################################################################
631 global ui_diff current_diff_path current_diff_header
632 global ui_index ui_workdir
634 $ui_diff conf
-state normal
635 $ui_diff delete
0.0 end
636 $ui_diff conf
-state disabled
638 set current_diff_path
{}
639 set current_diff_header
{}
641 $ui_index tag remove in_diff
0.0 end
642 $ui_workdir tag remove in_diff
0.0 end
645 proc reshow_diff
{} {
646 global ui_status_value file_states file_lists
647 global current_diff_path current_diff_side
649 set p
$current_diff_path
651 ||
$current_diff_side eq
{}
652 ||
[catch
{set s
$file_states($p)}]
653 ||
[lsearch
-sorted -exact $file_lists($current_diff_side) $p] == -1} {
656 show_diff
$p $current_diff_side
660 proc handle_empty_diff
{} {
661 global current_diff_path file_states file_lists
663 set path
$current_diff_path
664 set s
$file_states($path)
665 if {[lindex
$s 0] ne
{_M
}} return
667 info_popup
"No differences detected.
669 [short_path $path] has no changes.
671 The modification date of this file was updated
672 by another application, but the content within
673 the file was not changed.
675 A rescan will be automatically started to find
676 other files which may have the same state."
679 display_file
$path __
680 rescan
{set ui_status_value
{Ready.
}} 0
683 proc show_diff
{path w
{lno
{}}} {
684 global file_states file_lists
685 global is_3way_diff diff_active repo_config
686 global ui_diff ui_status_value ui_index ui_workdir
687 global current_diff_path current_diff_side current_diff_header
689 if {$diff_active ||
![lock_index
read]} return
693 set lno
[lsearch
-sorted -exact $file_lists($w) $path]
699 $w tag add in_diff
$lno.0 [expr {$lno + 1}].0
702 set s
$file_states($path)
706 set current_diff_path
$path
707 set current_diff_side
$w
708 set current_diff_header
{}
709 set ui_status_value
"Loading diff of [escape_path $path]..."
711 # - Git won't give us the diff, there's nothing to compare to!
714 set max_sz
[expr {128 * 1024}]
716 set fd
[open
$path r
]
717 set content
[read $fd $max_sz]
719 set sz
[file size
$path]
723 set ui_status_value
"Unable to display [escape_path $path]"
724 error_popup
"Error loading file:\n\n$err"
727 $ui_diff conf
-state normal
728 if {![catch
{set type [exec file $path]}]} {
729 set n
[string length
$path]
730 if {[string equal
-length $n $path $type]} {
731 set type [string range
$type $n end
]
732 regsub
{^
:?\s
*} $type {} type
734 $ui_diff insert end
"* $type\n" d_@
736 if {[string first
"\0" $content] != -1} {
737 $ui_diff insert end \
738 "* Binary file (not showing content)." \
742 $ui_diff insert end \
743 "* Untracked file is $sz bytes.
744 * Showing only first $max_sz bytes.
747 $ui_diff insert end
$content
749 $ui_diff insert end
"
750 * Untracked file clipped here by [appname].
751 * To see the entire file, use an external editor.
755 $ui_diff conf
-state disabled
758 set ui_status_value
{Ready.
}
763 if {$w eq
$ui_index} {
764 lappend cmd diff-index
766 } elseif
{$w eq
$ui_workdir} {
767 if {[string index
$m 0] eq
{U
}} {
770 lappend cmd diff-files
775 lappend cmd
--no-color
776 if {$repo_config(gui.diffcontext
) > 0} {
777 lappend cmd
"-U$repo_config(gui.diffcontext)"
779 if {$w eq
$ui_index} {
785 if {[catch
{set fd
[open
$cmd r
]} err
]} {
788 set ui_status_value
"Unable to display [escape_path $path]"
789 error_popup
"Error loading diff:\n\n$err"
797 fileevent
$fd readable
[list read_diff
$fd]
800 proc read_diff
{fd
} {
801 global ui_diff ui_status_value diff_active
802 global is_3way_diff current_diff_header
804 $ui_diff conf
-state normal
805 while {[gets
$fd line
] >= 0} {
806 # -- Cleanup uninteresting diff header lines.
808 if { [string match
{diff --git *} $line]
809 ||
[string match
{diff --cc *} $line]
810 ||
[string match
{diff --combined *} $line]
811 ||
[string match
{--- *} $line]
812 ||
[string match
{+++ *} $line]} {
813 append current_diff_header
$line "\n"
816 if {[string match
{index
*} $line]} continue
817 if {$line eq
{deleted
file mode
120000}} {
818 set line
"deleted symlink"
821 # -- Automatically detect if this is a 3 way diff.
823 if {[string match
{@@@
*} $line]} {set is_3way_diff
1}
825 if {[string match
{mode
*} $line]
826 ||
[string match
{new
file *} $line]
827 ||
[string match
{deleted
file *} $line]
828 ||
[string match
{Binary files
* and
* differ
} $line]
829 ||
$line eq
{\ No newline
at end of
file}
830 ||
[regexp
{^\
* Unmerged path
} $line]} {
832 } elseif
{$is_3way_diff} {
833 set op
[string range
$line 0 1]
843 if {[regexp
{^\
+\
+([<>]{7} |
={7})} $line _g op
]} {
844 set line
[string replace
$line 0 1 { }]
851 puts
"error: Unhandled 3 way diff marker: {$op}"
856 set op
[string index
$line 0]
862 if {[regexp
{^\
+([<>]{7} |
={7})} $line _g op
]} {
863 set line
[string replace
$line 0 0 { }]
870 puts
"error: Unhandled 2 way diff marker: {$op}"
875 $ui_diff insert end
$line $tags
876 if {[string index
$line end
] eq
"\r"} {
877 $ui_diff tag add d_cr
{end
- 2c
}
879 $ui_diff insert end
"\n" $tags
881 $ui_diff conf
-state disabled
887 set ui_status_value
{Ready.
}
889 if {[$ui_diff index end
] eq
{2.0}} {
895 proc apply_hunk
{x y
} {
896 global current_diff_path current_diff_header current_diff_side
897 global ui_diff ui_index file_states
899 if {$current_diff_path eq
{} ||
$current_diff_header eq
{}} return
900 if {![lock_index apply_hunk
]} return
902 set apply_cmd
{git apply
--cached --whitespace=nowarn
}
903 set mi
[lindex
$file_states($current_diff_path) 0]
904 if {$current_diff_side eq
$ui_index} {
906 lappend apply_cmd
--reverse
907 if {[string index
$mi 0] ne
{M
}} {
913 if {[string index
$mi 1] ne
{M
}} {
919 set s_lno
[lindex
[split [$ui_diff index @
$x,$y] .
] 0]
920 set s_lno
[$ui_diff search
-backwards -regexp ^@@
$s_lno.0 0.0]
926 set e_lno
[$ui_diff search
-forwards -regexp ^@@
"$s_lno + 1 lines" end
]
932 set p
[open
"| $apply_cmd" w
]
933 fconfigure
$p -translation binary
-encoding binary
934 puts
-nonewline $p $current_diff_header
935 puts
-nonewline $p [$ui_diff get
$s_lno $e_lno]
937 error_popup
"Failed to $mode selected hunk.\n\n$err"
942 $ui_diff conf
-state normal
943 $ui_diff delete
$s_lno $e_lno
944 $ui_diff conf
-state disabled
946 if {[$ui_diff get
1.0 end
] eq
"\n"} {
952 if {$current_diff_side eq
$ui_index} {
954 } elseif
{[string index
$mi 0] eq
{_
}} {
960 display_file
$current_diff_path $mi
966 ######################################################################
970 proc load_last_commit
{} {
971 global HEAD PARENT MERGE_HEAD commit_type ui_comm
974 if {[llength
$PARENT] == 0} {
975 error_popup
{There is nothing to amend.
977 You are about to create the initial commit.
978 There is no commit before this to amend.
983 repository_state curType curHEAD curMERGE_HEAD
984 if {$curType eq
{merge
}} {
985 error_popup
{Cannot amend
while merging.
987 You are currently
in the middle of a merge that
988 has not been fully completed. You cannot amend
989 the prior commit unless you first abort the
990 current merge activity.
998 set fd
[open
"| git cat-file commit $curHEAD" r
]
999 fconfigure
$fd -encoding binary
-translation lf
1000 if {[catch
{set enc
$repo_config(i18n.commitencoding
)}]} {
1003 while {[gets
$fd line
] > 0} {
1004 if {[string match
{parent
*} $line]} {
1005 lappend parents
[string range
$line 7 end
]
1006 } elseif
{[string match
{encoding
*} $line]} {
1007 set enc
[string tolower
[string range
$line 9 end
]]
1010 fconfigure
$fd -encoding $enc
1011 set msg
[string trim
[read $fd]]
1014 error_popup
"Error loading commit data for amend:\n\n$err"
1020 set MERGE_HEAD
[list
]
1021 switch
-- [llength
$parents] {
1022 0 {set commit_type amend-initial
}
1023 1 {set commit_type amend
}
1024 default
{set commit_type amend-merge
}
1027 $ui_comm delete
0.0 end
1028 $ui_comm insert end
$msg
1030 $ui_comm edit modified false
1031 rescan
{set ui_status_value
{Ready.
}}
1034 proc create_new_commit
{} {
1035 global commit_type ui_comm
1037 set commit_type normal
1038 $ui_comm delete
0.0 end
1040 $ui_comm edit modified false
1041 rescan
{set ui_status_value
{Ready.
}}
1044 set GIT_COMMITTER_IDENT
{}
1046 proc committer_ident
{} {
1047 global GIT_COMMITTER_IDENT
1049 if {$GIT_COMMITTER_IDENT eq
{}} {
1050 if {[catch
{set me
[exec git var GIT_COMMITTER_IDENT
]} err
]} {
1051 error_popup
"Unable to obtain your identity:\n\n$err"
1054 if {![regexp
{^
(.
*) [0-9]+ [-+0-9]+$
} \
1055 $me me GIT_COMMITTER_IDENT
]} {
1056 error_popup
"Invalid GIT_COMMITTER_IDENT:\n\n$me"
1061 return $GIT_COMMITTER_IDENT
1064 proc commit_tree
{} {
1065 global HEAD commit_type file_states ui_comm repo_config
1066 global ui_status_value pch_error
1068 if {[committer_ident
] eq
{}} return
1069 if {![lock_index update
]} return
1071 # -- Our in memory state should match the repository.
1073 repository_state curType curHEAD curMERGE_HEAD
1074 if {[string match amend
* $commit_type]
1075 && $curType eq
{normal
}
1076 && $curHEAD eq
$HEAD} {
1077 } elseif
{$commit_type ne
$curType ||
$HEAD ne
$curHEAD} {
1078 info_popup
{Last scanned state does not match repository state.
1080 Another Git program has modified this repository
1081 since the last scan. A rescan must be performed
1082 before another commit can be created.
1084 The rescan will be automatically started now.
1087 rescan
{set ui_status_value
{Ready.
}}
1091 # -- At least one file should differ in the index.
1094 foreach path
[array names file_states
] {
1095 switch
-glob -- [lindex
$file_states($path) 0] {
1099 M?
{set files_ready
1}
1101 error_popup
"Unmerged files cannot be committed.
1103 File [short_path $path] has merge conflicts.
1104 You must resolve them and add the file before committing.
1110 error_popup
"Unknown file state [lindex $s 0] detected.
1112 File [short_path $path] cannot be committed by this program.
1117 if {!$files_ready} {
1118 info_popup
{No changes to commit.
1120 You must add
at least
1 file before you can commit.
1126 # -- A message is required.
1128 set msg
[string trim
[$ui_comm get
1.0 end
]]
1129 regsub
-all -line {[ \t\r]+$
} $msg {} msg
1131 error_popup
{Please supply a commit message.
1133 A good commit message has the following format
:
1135 - First line
: Describe
in one sentance what you did.
1136 - Second line
: Blank
1137 - Remaining lines
: Describe why this change is good.
1143 # -- Run the pre-commit hook.
1145 set pchook
[gitdir hooks pre-commit
]
1147 # On Cygwin [file executable] might lie so we need to ask
1148 # the shell if the hook is executable. Yes that's annoying.
1150 if {[is_Cygwin
] && [file isfile
$pchook]} {
1151 set pchook
[list sh
-c [concat \
1152 "if test -x \"$pchook\";" \
1153 "then exec \"$pchook\" 2>&1;" \
1155 } elseif
{[file executable
$pchook]} {
1156 set pchook
[list
$pchook |
& cat]
1158 commit_writetree
$curHEAD $msg
1162 set ui_status_value
{Calling pre-commit hook...
}
1164 set fd_ph
[open
"| $pchook" r
]
1165 fconfigure
$fd_ph -blocking 0 -translation binary
1166 fileevent
$fd_ph readable \
1167 [list commit_prehook_wait
$fd_ph $curHEAD $msg]
1170 proc commit_prehook_wait
{fd_ph curHEAD msg
} {
1171 global pch_error ui_status_value
1173 append pch_error
[read $fd_ph]
1174 fconfigure
$fd_ph -blocking 1
1176 if {[catch
{close
$fd_ph}]} {
1177 set ui_status_value
{Commit declined by pre-commit hook.
}
1178 hook_failed_popup pre-commit
$pch_error
1181 commit_writetree
$curHEAD $msg
1186 fconfigure
$fd_ph -blocking 0
1189 proc commit_writetree
{curHEAD msg
} {
1190 global ui_status_value
1192 set ui_status_value
{Committing changes...
}
1193 set fd_wt
[open
"| git write-tree" r
]
1194 fileevent
$fd_wt readable \
1195 [list commit_committree
$fd_wt $curHEAD $msg]
1198 proc commit_committree
{fd_wt curHEAD msg
} {
1199 global HEAD PARENT MERGE_HEAD commit_type
1200 global all_heads current_branch
1201 global ui_status_value ui_comm selected_commit_type
1202 global file_states selected_paths rescan_active
1206 if {$tree_id eq
{} ||
[catch
{close
$fd_wt} err
]} {
1207 error_popup
"write-tree failed:\n\n$err"
1208 set ui_status_value
{Commit failed.
}
1213 # -- Build the message.
1215 set msg_p
[gitdir COMMIT_EDITMSG
]
1216 set msg_wt
[open
$msg_p w
]
1217 if {[catch
{set enc
$repo_config(i18n.commitencoding
)}]} {
1220 fconfigure
$msg_wt -encoding $enc -translation binary
1221 puts
-nonewline $msg_wt $msg
1224 # -- Create the commit.
1226 set cmd
[list git commit-tree
$tree_id]
1227 set parents
[concat
$PARENT $MERGE_HEAD]
1228 if {[llength
$parents] > 0} {
1229 foreach p
$parents {
1233 # git commit-tree writes to stderr during initial commit.
1234 lappend cmd
2>/dev
/null
1237 if {[catch
{set cmt_id
[eval exec $cmd]} err
]} {
1238 error_popup
"commit-tree failed:\n\n$err"
1239 set ui_status_value
{Commit failed.
}
1244 # -- Update the HEAD ref.
1247 if {$commit_type ne
{normal
}} {
1248 append reflogm
" ($commit_type)"
1250 set i
[string first
"\n" $msg]
1252 append reflogm
{: } [string range
$msg 0 [expr {$i - 1}]]
1254 append reflogm
{: } $msg
1256 set cmd
[list git update-ref
-m $reflogm HEAD
$cmt_id $curHEAD]
1257 if {[catch
{eval exec $cmd} err
]} {
1258 error_popup
"update-ref failed:\n\n$err"
1259 set ui_status_value
{Commit failed.
}
1264 # -- Make sure our current branch exists.
1266 if {$commit_type eq
{initial
}} {
1267 lappend all_heads
$current_branch
1268 set all_heads
[lsort
-unique $all_heads]
1269 populate_branch_menu
1272 # -- Cleanup after ourselves.
1274 catch
{file delete
$msg_p}
1275 catch
{file delete
[gitdir MERGE_HEAD
]}
1276 catch
{file delete
[gitdir MERGE_MSG
]}
1277 catch
{file delete
[gitdir SQUASH_MSG
]}
1278 catch
{file delete
[gitdir GITGUI_MSG
]}
1280 # -- Let rerere do its thing.
1282 if {[file isdirectory
[gitdir rr-cache
]]} {
1283 catch
{exec git rerere
}
1286 # -- Run the post-commit hook.
1288 set pchook
[gitdir hooks post-commit
]
1289 if {[is_Cygwin
] && [file isfile
$pchook]} {
1290 set pchook
[list sh
-c [concat \
1291 "if test -x \"$pchook\";" \
1292 "then exec \"$pchook\";" \
1294 } elseif
{![file executable
$pchook]} {
1297 if {$pchook ne
{}} {
1298 catch
{exec $pchook &}
1301 $ui_comm delete
0.0 end
1303 $ui_comm edit modified false
1305 if {![is_enabled multicommit
]} do_quit
1307 # -- Update in memory status
1309 set selected_commit_type new
1310 set commit_type normal
1313 set MERGE_HEAD
[list
]
1315 foreach path
[array names file_states
] {
1316 set s
$file_states($path)
1318 switch
-glob -- $m {
1326 unset file_states
($path)
1327 catch
{unset selected_paths
($path)}
1330 set file_states
($path) [list _O
[lindex
$s 1] {} {}]
1336 set file_states
($path) [list \
1337 _
[string index
$m 1] \
1348 set ui_status_value \
1349 "Changes committed as [string range $cmt_id 0 7]."
1352 ######################################################################
1356 proc fetch_from
{remote
} {
1357 set w
[new_console \
1359 "Fetching new changes from $remote"]
1360 set cmd
[list git fetch
]
1362 console_exec
$w $cmd console_done
1365 proc push_to
{remote
} {
1366 set w
[new_console \
1368 "Pushing changes to $remote"]
1369 set cmd
[list git push
]
1372 console_exec
$w $cmd console_done
1375 ######################################################################
1379 proc mapicon
{w state path
} {
1382 if {[catch
{set r
$all_icons($state$w)}]} {
1383 puts
"error: no icon for $w state={$state} $path"
1389 proc mapdesc
{state path
} {
1392 if {[catch
{set r
$all_descs($state)}]} {
1393 puts
"error: no desc for state={$state} $path"
1399 proc escape_path
{path
} {
1400 regsub
-all {\\} $path "\\\\" path
1401 regsub
-all "\n" $path "\\n" path
1405 proc short_path
{path
} {
1406 return [escape_path
[lindex
[file split $path] end
]]
1410 set null_sha1
[string repeat
0 40]
1412 proc merge_state
{path new_state
{head_info
{}} {index_info
{}}} {
1413 global file_states next_icon_id null_sha1
1415 set s0
[string index
$new_state 0]
1416 set s1
[string index
$new_state 1]
1418 if {[catch
{set info
$file_states($path)}]} {
1420 set icon n
[incr next_icon_id
]
1422 set state
[lindex
$info 0]
1423 set icon
[lindex
$info 1]
1424 if {$head_info eq
{}} {set head_info
[lindex
$info 2]}
1425 if {$index_info eq
{}} {set index_info
[lindex
$info 3]}
1428 if {$s0 eq
{?
}} {set s0
[string index
$state 0]} \
1429 elseif
{$s0 eq
{_
}} {set s0 _
}
1431 if {$s1 eq
{?
}} {set s1
[string index
$state 1]} \
1432 elseif
{$s1 eq
{_
}} {set s1 _
}
1434 if {$s0 eq
{A
} && $s1 eq
{_
} && $head_info eq
{}} {
1435 set head_info
[list
0 $null_sha1]
1436 } elseif
{$s0 ne
{_
} && [string index
$state 0] eq
{_
}
1437 && $head_info eq
{}} {
1438 set head_info
$index_info
1441 set file_states
($path) [list
$s0$s1 $icon \
1442 $head_info $index_info \
1447 proc display_file_helper
{w path icon_name old_m new_m
} {
1450 if {$new_m eq
{_
}} {
1451 set lno
[lsearch
-sorted -exact $file_lists($w) $path]
1453 set file_lists
($w) [lreplace
$file_lists($w) $lno $lno]
1455 $w conf
-state normal
1456 $w delete
$lno.0 [expr {$lno + 1}].0
1457 $w conf
-state disabled
1459 } elseif
{$old_m eq
{_
} && $new_m ne
{_
}} {
1460 lappend file_lists
($w) $path
1461 set file_lists
($w) [lsort
-unique $file_lists($w)]
1462 set lno
[lsearch
-sorted -exact $file_lists($w) $path]
1464 $w conf
-state normal
1465 $w image create
$lno.0 \
1466 -align center
-padx 5 -pady 1 \
1468 -image [mapicon
$w $new_m $path]
1469 $w insert
$lno.1 "[escape_path $path]\n"
1470 $w conf
-state disabled
1471 } elseif
{$old_m ne
$new_m} {
1472 $w conf
-state normal
1473 $w image conf
$icon_name -image [mapicon
$w $new_m $path]
1474 $w conf
-state disabled
1478 proc display_file
{path state
} {
1479 global file_states selected_paths
1480 global ui_index ui_workdir
1482 set old_m
[merge_state
$path $state]
1483 set s
$file_states($path)
1484 set new_m
[lindex
$s 0]
1485 set icon_name
[lindex
$s 1]
1487 set o
[string index
$old_m 0]
1488 set n
[string index
$new_m 0]
1495 display_file_helper
$ui_index $path $icon_name $o $n
1497 if {[string index
$old_m 0] eq
{U
}} {
1500 set o
[string index
$old_m 1]
1502 if {[string index
$new_m 0] eq
{U
}} {
1505 set n
[string index
$new_m 1]
1507 display_file_helper
$ui_workdir $path $icon_name $o $n
1509 if {$new_m eq
{__
}} {
1510 unset file_states
($path)
1511 catch
{unset selected_paths
($path)}
1515 proc display_all_files_helper
{w path icon_name m
} {
1518 lappend file_lists
($w) $path
1519 set lno
[expr {[lindex
[split [$w index end
] .
] 0] - 1}]
1520 $w image create end \
1521 -align center
-padx 5 -pady 1 \
1523 -image [mapicon
$w $m $path]
1524 $w insert end
"[escape_path $path]\n"
1527 proc display_all_files
{} {
1528 global ui_index ui_workdir
1529 global file_states file_lists
1532 $ui_index conf
-state normal
1533 $ui_workdir conf
-state normal
1535 $ui_index delete
0.0 end
1536 $ui_workdir delete
0.0 end
1539 set file_lists
($ui_index) [list
]
1540 set file_lists
($ui_workdir) [list
]
1542 foreach path
[lsort
[array names file_states
]] {
1543 set s
$file_states($path)
1545 set icon_name
[lindex
$s 1]
1547 set s
[string index
$m 0]
1548 if {$s ne
{U
} && $s ne
{_
}} {
1549 display_all_files_helper
$ui_index $path \
1553 if {[string index
$m 0] eq
{U
}} {
1556 set s
[string index
$m 1]
1559 display_all_files_helper
$ui_workdir $path \
1564 $ui_index conf
-state disabled
1565 $ui_workdir conf
-state disabled
1568 proc update_indexinfo
{msg pathList after
} {
1569 global update_index_cp ui_status_value
1571 if {![lock_index update
]} return
1573 set update_index_cp
0
1574 set pathList
[lsort
$pathList]
1575 set totalCnt
[llength
$pathList]
1576 set batch [expr {int
($totalCnt * .01) + 1}]
1577 if {$batch > 25} {set batch 25}
1579 set ui_status_value
[format \
1580 "$msg... %i/%i files (%.2f%%)" \
1584 set fd
[open
"| git update-index -z --index-info" w
]
1591 fileevent
$fd writable
[list \
1592 write_update_indexinfo \
1602 proc write_update_indexinfo
{fd pathList totalCnt
batch msg after
} {
1603 global update_index_cp ui_status_value
1604 global file_states current_diff_path
1606 if {$update_index_cp >= $totalCnt} {
1613 for {set i
$batch} \
1614 {$update_index_cp < $totalCnt && $i > 0} \
1616 set path
[lindex
$pathList $update_index_cp]
1617 incr update_index_cp
1619 set s
$file_states($path)
1620 switch
-glob -- [lindex
$s 0] {
1627 set info
[lindex
$s 2]
1628 if {$info eq
{}} continue
1630 puts
-nonewline $fd "$info\t[encoding convertto $path]\0"
1631 display_file
$path $new
1634 set ui_status_value
[format \
1635 "$msg... %i/%i files (%.2f%%)" \
1638 [expr {100.0 * $update_index_cp / $totalCnt}]]
1641 proc update_index
{msg pathList after
} {
1642 global update_index_cp ui_status_value
1644 if {![lock_index update
]} return
1646 set update_index_cp
0
1647 set pathList
[lsort
$pathList]
1648 set totalCnt
[llength
$pathList]
1649 set batch [expr {int
($totalCnt * .01) + 1}]
1650 if {$batch > 25} {set batch 25}
1652 set ui_status_value
[format \
1653 "$msg... %i/%i files (%.2f%%)" \
1657 set fd
[open
"| git update-index --add --remove -z --stdin" w
]
1664 fileevent
$fd writable
[list \
1665 write_update_index \
1675 proc write_update_index
{fd pathList totalCnt
batch msg after
} {
1676 global update_index_cp ui_status_value
1677 global file_states current_diff_path
1679 if {$update_index_cp >= $totalCnt} {
1686 for {set i
$batch} \
1687 {$update_index_cp < $totalCnt && $i > 0} \
1689 set path
[lindex
$pathList $update_index_cp]
1690 incr update_index_cp
1692 switch
-glob -- [lindex
$file_states($path) 0] {
1698 if {[file exists
$path]} {
1707 puts
-nonewline $fd "[encoding convertto $path]\0"
1708 display_file
$path $new
1711 set ui_status_value
[format \
1712 "$msg... %i/%i files (%.2f%%)" \
1715 [expr {100.0 * $update_index_cp / $totalCnt}]]
1718 proc checkout_index
{msg pathList after
} {
1719 global update_index_cp ui_status_value
1721 if {![lock_index update
]} return
1723 set update_index_cp
0
1724 set pathList
[lsort
$pathList]
1725 set totalCnt
[llength
$pathList]
1726 set batch [expr {int
($totalCnt * .01) + 1}]
1727 if {$batch > 25} {set batch 25}
1729 set ui_status_value
[format \
1730 "$msg... %i/%i files (%.2f%%)" \
1734 set cmd
[list git checkout-index
]
1740 set fd
[open
"| $cmd " w
]
1747 fileevent
$fd writable
[list \
1748 write_checkout_index \
1758 proc write_checkout_index
{fd pathList totalCnt
batch msg after
} {
1759 global update_index_cp ui_status_value
1760 global file_states current_diff_path
1762 if {$update_index_cp >= $totalCnt} {
1769 for {set i
$batch} \
1770 {$update_index_cp < $totalCnt && $i > 0} \
1772 set path
[lindex
$pathList $update_index_cp]
1773 incr update_index_cp
1774 switch
-glob -- [lindex
$file_states($path) 0] {
1778 puts
-nonewline $fd "[encoding convertto $path]\0"
1779 display_file
$path ?_
1784 set ui_status_value
[format \
1785 "$msg... %i/%i files (%.2f%%)" \
1788 [expr {100.0 * $update_index_cp / $totalCnt}]]
1791 ######################################################################
1793 ## branch management
1795 proc is_tracking_branch
{name
} {
1796 global tracking_branches
1798 if {![catch
{set info
$tracking_branches($name)}]} {
1801 foreach t
[array names tracking_branches
] {
1802 if {[string match
{*/\
*} $t] && [string match
$t $name]} {
1809 proc load_all_heads
{} {
1812 set all_heads
[list
]
1813 set fd
[open
"| git for-each-ref --format=%(refname) refs/heads" r
]
1814 while {[gets
$fd line
] > 0} {
1815 if {[is_tracking_branch
$line]} continue
1816 if {![regsub ^refs
/heads
/ $line {} name
]} continue
1817 lappend all_heads
$name
1821 set all_heads
[lsort
$all_heads]
1824 proc populate_branch_menu
{} {
1825 global all_heads disable_on_lock
1828 set last
[$m index last
]
1829 for {set i
0} {$i <= $last} {incr i
} {
1830 if {[$m type $i] eq
{separator
}} {
1833 foreach a
$disable_on_lock {
1834 if {[lindex
$a 0] ne
$m ||
[lindex
$a 2] < $i} {
1838 set disable_on_lock
$new_dol
1843 if {$all_heads ne
{}} {
1846 foreach b
$all_heads {
1847 $m add radiobutton \
1849 -command [list switch_branch
$b] \
1850 -variable current_branch \
1853 lappend disable_on_lock \
1854 [list
$m entryconf
[$m index last
] -state]
1858 proc all_tracking_branches
{} {
1859 global tracking_branches
1861 set all_trackings
{}
1863 foreach name
[array names tracking_branches
] {
1864 if {[regsub
{/\
*$
} $name {} name
]} {
1867 regsub ^refs
/(heads|remotes
)/ $name {} name
1868 lappend all_trackings
$name
1873 set fd
[open
"| git for-each-ref --format=%(refname) $cmd" r
]
1874 while {[gets
$fd name
] > 0} {
1875 regsub ^refs
/(heads|remotes
)/ $name {} name
1876 lappend all_trackings
$name
1881 return [lsort
-unique $all_trackings]
1884 proc do_create_branch_action
{w
} {
1885 global all_heads null_sha1 repo_config
1886 global create_branch_checkout create_branch_revtype
1887 global create_branch_head create_branch_trackinghead
1888 global create_branch_name create_branch_revexp
1890 set newbranch
$create_branch_name
1891 if {$newbranch eq
{}
1892 ||
$newbranch eq
$repo_config(gui.newbranchtemplate
)} {
1896 -title [wm title
$w] \
1898 -message "Please supply a branch name."
1899 focus
$w.desc.name_t
1902 if {![catch
{exec git show-ref
--verify -- "refs/heads/$newbranch"}]} {
1906 -title [wm title
$w] \
1908 -message "Branch '$newbranch' already exists."
1909 focus
$w.desc.name_t
1912 if {[catch
{exec git check-ref-format
"heads/$newbranch"}]} {
1916 -title [wm title
$w] \
1918 -message "We do not like '$newbranch' as a branch name."
1919 focus
$w.desc.name_t
1924 switch
-- $create_branch_revtype {
1925 head {set rev $create_branch_head}
1926 tracking
{set rev $create_branch_trackinghead}
1927 expression
{set rev $create_branch_revexp}
1929 if {[catch
{set cmt
[exec git rev-parse
--verify "${rev}^0"]}]} {
1933 -title [wm title
$w] \
1935 -message "Invalid starting revision: $rev"
1938 set cmd
[list git update-ref
]
1940 lappend cmd
"branch: Created from $rev"
1941 lappend cmd
"refs/heads/$newbranch"
1943 lappend cmd
$null_sha1
1944 if {[catch
{eval exec $cmd} err
]} {
1948 -title [wm title
$w] \
1950 -message "Failed to create '$newbranch'.\n\n$err"
1954 lappend all_heads
$newbranch
1955 set all_heads
[lsort
$all_heads]
1956 populate_branch_menu
1958 if {$create_branch_checkout} {
1959 switch_branch
$newbranch
1963 proc radio_selector
{varname value args
} {
1964 upvar
#0 $varname var
1968 trace add variable create_branch_head
write \
1969 [list radio_selector create_branch_revtype
head]
1970 trace add variable create_branch_trackinghead
write \
1971 [list radio_selector create_branch_revtype tracking
]
1973 trace add variable delete_branch_head
write \
1974 [list radio_selector delete_branch_checktype
head]
1975 trace add variable delete_branch_trackinghead
write \
1976 [list radio_selector delete_branch_checktype tracking
]
1978 proc do_create_branch
{} {
1979 global all_heads current_branch repo_config
1980 global create_branch_checkout create_branch_revtype
1981 global create_branch_head create_branch_trackinghead
1982 global create_branch_name create_branch_revexp
1984 set w .branch_editor
1986 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
1988 label
$w.header
-text {Create New Branch
} \
1990 pack
$w.header
-side top
-fill x
1993 button
$w.buttons.create
-text Create \
1996 -command [list do_create_branch_action
$w]
1997 pack
$w.buttons.create
-side right
1998 button
$w.buttons.cancel
-text {Cancel
} \
2000 -command [list destroy
$w]
2001 pack
$w.buttons.cancel
-side right
-padx 5
2002 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
2004 labelframe
$w.desc \
2005 -text {Branch Description
} \
2007 label
$w.desc.name_l
-text {Name
:} -font font_ui
2008 entry
$w.desc.name_t \
2012 -textvariable create_branch_name \
2016 if {%d
== 1 && [regexp
{[~^
:?
*\
[\
0- ]} %S
]} {return 0}
2019 grid
$w.desc.name_l
$w.desc.name_t
-sticky we
-padx {0 5}
2020 grid columnconfigure
$w.desc
1 -weight 1
2021 pack
$w.desc
-anchor nw
-fill x
-pady 5 -padx 5
2023 labelframe
$w.from \
2024 -text {Starting Revision
} \
2026 radiobutton
$w.from.head_r \
2027 -text {Local Branch
:} \
2029 -variable create_branch_revtype \
2031 eval tk_optionMenu
$w.from.head_m create_branch_head
$all_heads
2032 grid
$w.from.head_r
$w.from.head_m
-sticky w
2033 set all_trackings
[all_tracking_branches
]
2034 if {$all_trackings ne
{}} {
2035 set create_branch_trackinghead
[lindex
$all_trackings 0]
2036 radiobutton
$w.from.tracking_r \
2037 -text {Tracking Branch
:} \
2039 -variable create_branch_revtype \
2041 eval tk_optionMenu
$w.from.tracking_m \
2042 create_branch_trackinghead \
2044 grid
$w.from.tracking_r
$w.from.tracking_m
-sticky w
2046 radiobutton
$w.from.exp_r \
2047 -text {Revision Expression
:} \
2049 -variable create_branch_revtype \
2051 entry
$w.from.exp_t \
2055 -textvariable create_branch_revexp \
2059 if {%d
== 1 && [regexp
{\s
} %S
]} {return 0}
2060 if {%d
== 1 && [string length
%S
] > 0} {
2061 set create_branch_revtype expression
2065 grid
$w.from.exp_r
$w.from.exp_t
-sticky we
-padx {0 5}
2066 grid columnconfigure
$w.from
1 -weight 1
2067 pack
$w.from
-anchor nw
-fill x
-pady 5 -padx 5
2069 labelframe
$w.postActions \
2070 -text {Post Creation Actions
} \
2072 checkbutton
$w.postActions.checkout \
2073 -text {Checkout after creation
} \
2074 -variable create_branch_checkout \
2076 pack
$w.postActions.checkout
-anchor nw
2077 pack
$w.postActions
-anchor nw
-fill x
-pady 5 -padx 5
2079 set create_branch_checkout
1
2080 set create_branch_head
$current_branch
2081 set create_branch_revtype
head
2082 set create_branch_name
$repo_config(gui.newbranchtemplate
)
2083 set create_branch_revexp
{}
2085 bind $w <Visibility
> "
2087 $w.desc.name_t icursor end
2088 focus $w.desc.name_t
2090 bind $w <Key-Escape
> "destroy $w"
2091 bind $w <Key-Return
> "do_create_branch_action $w;break"
2092 wm title
$w "[appname] ([reponame]): Create Branch"
2096 proc do_delete_branch_action
{w
} {
2098 global delete_branch_checktype delete_branch_head delete_branch_trackinghead
2101 switch
-- $delete_branch_checktype {
2102 head {set check_rev
$delete_branch_head}
2103 tracking
{set check_rev
$delete_branch_trackinghead}
2104 always
{set check_rev
{:none
}}
2106 if {$check_rev eq
{:none
}} {
2108 } elseif
{[catch
{set check_cmt
[exec git rev-parse
--verify "${check_rev}^0"]}]} {
2112 -title [wm title
$w] \
2114 -message "Invalid check revision: $check_rev"
2118 set to_delete
[list
]
2119 set not_merged
[list
]
2120 foreach i
[$w.list.l curselection
] {
2121 set b
[$w.list.l get
$i]
2122 if {[catch
{set o
[exec git rev-parse
--verify $b]}]} continue
2123 if {$check_cmt ne
{}} {
2124 if {$b eq
$check_rev} continue
2125 if {[catch
{set m
[exec git merge-base
$o $check_cmt]}]} continue
2127 lappend not_merged
$b
2131 lappend to_delete
[list
$b $o]
2133 if {$not_merged ne
{}} {
2134 set msg
"The following branches are not completely merged into $check_rev:
2136 - [join $not_merged "\n - "]"
2140 -title [wm title
$w] \
2144 if {$to_delete eq
{}} return
2145 if {$delete_branch_checktype eq
{always
}} {
2146 set msg
{Recovering deleted branches is difficult.
2148 Delete the selected branches?
}
2149 if {[tk_messageBox \
2152 -title [wm title
$w] \
2154 -message $msg] ne
yes} {
2160 foreach i
$to_delete {
2163 if {[catch
{exec git update-ref
-d "refs/heads/$b" $o} err
]} {
2164 append failed
" - $b: $err\n"
2166 set x
[lsearch
-sorted -exact $all_heads $b]
2168 set all_heads
[lreplace
$all_heads $x $x]
2173 if {$failed ne
{}} {
2177 -title [wm title
$w] \
2179 -message "Failed to delete branches:\n$failed"
2182 set all_heads
[lsort
$all_heads]
2183 populate_branch_menu
2187 proc do_delete_branch
{} {
2188 global all_heads tracking_branches current_branch
2189 global delete_branch_checktype delete_branch_head delete_branch_trackinghead
2191 set w .branch_editor
2193 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
2195 label
$w.header
-text {Delete Local Branch
} \
2197 pack
$w.header
-side top
-fill x
2200 button
$w.buttons.create
-text Delete \
2202 -command [list do_delete_branch_action
$w]
2203 pack
$w.buttons.create
-side right
2204 button
$w.buttons.cancel
-text {Cancel
} \
2206 -command [list destroy
$w]
2207 pack
$w.buttons.cancel
-side right
-padx 5
2208 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
2210 labelframe
$w.list \
2211 -text {Local Branches
} \
2216 -selectmode extended \
2217 -yscrollcommand [list
$w.list.sby
set] \
2219 foreach h
$all_heads {
2220 if {$h ne
$current_branch} {
2221 $w.list.l insert end
$h
2224 scrollbar
$w.list.sby
-command [list
$w.list.l yview
]
2225 pack
$w.list.sby
-side right
-fill y
2226 pack
$w.list.l
-side left
-fill both
-expand 1
2227 pack
$w.list
-fill both
-expand 1 -pady 5 -padx 5
2229 labelframe
$w.validate \
2230 -text {Delete Only If
} \
2232 radiobutton
$w.validate.head_r \
2233 -text {Merged Into Local Branch
:} \
2235 -variable delete_branch_checktype \
2237 eval tk_optionMenu
$w.validate.head_m delete_branch_head
$all_heads
2238 grid
$w.validate.head_r
$w.validate.head_m
-sticky w
2239 set all_trackings
[all_tracking_branches
]
2240 if {$all_trackings ne
{}} {
2241 set delete_branch_trackinghead
[lindex
$all_trackings 0]
2242 radiobutton
$w.validate.tracking_r \
2243 -text {Merged Into Tracking Branch
:} \
2245 -variable delete_branch_checktype \
2247 eval tk_optionMenu
$w.validate.tracking_m \
2248 delete_branch_trackinghead \
2250 grid
$w.validate.tracking_r
$w.validate.tracking_m
-sticky w
2252 radiobutton
$w.validate.always_r \
2253 -text {Always
(Do not perform merge checks
)} \
2255 -variable delete_branch_checktype \
2257 grid
$w.validate.always_r
-columnspan 2 -sticky w
2258 grid columnconfigure
$w.validate
1 -weight 1
2259 pack
$w.validate
-anchor nw
-fill x
-pady 5 -padx 5
2261 set delete_branch_head
$current_branch
2262 set delete_branch_checktype
head
2264 bind $w <Visibility
> "grab $w; focus $w"
2265 bind $w <Key-Escape
> "destroy $w"
2266 wm title
$w "[appname] ([reponame]): Delete Branch"
2270 proc switch_branch
{new_branch
} {
2271 global HEAD commit_type current_branch repo_config
2273 if {![lock_index switch
]} return
2275 # -- Our in memory state should match the repository.
2277 repository_state curType curHEAD curMERGE_HEAD
2278 if {[string match amend
* $commit_type]
2279 && $curType eq
{normal
}
2280 && $curHEAD eq
$HEAD} {
2281 } elseif
{$commit_type ne
$curType ||
$HEAD ne
$curHEAD} {
2282 info_popup
{Last scanned state does not match repository state.
2284 Another Git program has modified this repository
2285 since the last scan. A rescan must be performed
2286 before the current branch can be changed.
2288 The rescan will be automatically started now.
2291 rescan
{set ui_status_value
{Ready.
}}
2295 # -- Don't do a pointless switch.
2297 if {$current_branch eq
$new_branch} {
2302 if {$repo_config(gui.trustmtime
) eq
{true
}} {
2303 switch_branch_stage2
{} $new_branch
2305 set ui_status_value
{Refreshing
file status...
}
2306 set cmd
[list git update-index
]
2308 lappend cmd
--unmerged
2309 lappend cmd
--ignore-missing
2310 lappend cmd
--refresh
2311 set fd_rf
[open
"| $cmd" r
]
2312 fconfigure
$fd_rf -blocking 0 -translation binary
2313 fileevent
$fd_rf readable \
2314 [list switch_branch_stage2
$fd_rf $new_branch]
2318 proc switch_branch_stage2
{fd_rf new_branch
} {
2319 global ui_status_value HEAD
2323 if {![eof
$fd_rf]} return
2327 set ui_status_value
"Updating working directory to '$new_branch'..."
2328 set cmd
[list git read-tree
]
2331 lappend cmd
--exclude-per-directory=.gitignore
2333 lappend cmd
$new_branch
2334 set fd_rt
[open
"| $cmd" r
]
2335 fconfigure
$fd_rt -blocking 0 -translation binary
2336 fileevent
$fd_rt readable \
2337 [list switch_branch_readtree_wait
$fd_rt $new_branch]
2340 proc switch_branch_readtree_wait
{fd_rt new_branch
} {
2341 global selected_commit_type commit_type HEAD MERGE_HEAD PARENT
2342 global current_branch
2343 global ui_comm ui_status_value
2345 # -- We never get interesting output on stdout; only stderr.
2348 fconfigure
$fd_rt -blocking 1
2349 if {![eof
$fd_rt]} {
2350 fconfigure
$fd_rt -blocking 0
2354 # -- The working directory wasn't in sync with the index and
2355 # we'd have to overwrite something to make the switch. A
2356 # merge is required.
2358 if {[catch
{close
$fd_rt} err
]} {
2359 regsub
{^fatal
: } $err {} err
2360 warn_popup
"File level merge required.
2364 Staying on branch '$current_branch'."
2365 set ui_status_value
"Aborted checkout of '$new_branch' (file level merging is required)."
2370 # -- Update the symbolic ref. Core git doesn't even check for failure
2371 # here, it Just Works(tm). If it doesn't we are in some really ugly
2372 # state that is difficult to recover from within git-gui.
2374 if {[catch
{exec git symbolic-ref HEAD
"refs/heads/$new_branch"} err
]} {
2375 error_popup
"Failed to set current branch.
2377 This working directory is only partially switched.
2378 We successfully updated your files, but failed to
2379 update an internal Git file.
2381 This should not have occurred. [appname] will now
2389 # -- Update our repository state. If we were previously in amend mode
2390 # we need to toss the current buffer and do a full rescan to update
2391 # our file lists. If we weren't in amend mode our file lists are
2392 # accurate and we can avoid the rescan.
2395 set selected_commit_type new
2396 if {[string match amend
* $commit_type]} {
2397 $ui_comm delete
0.0 end
2399 $ui_comm edit modified false
2400 rescan
{set ui_status_value
"Checked out branch '$current_branch'."}
2402 repository_state commit_type HEAD MERGE_HEAD
2404 set ui_status_value
"Checked out branch '$current_branch'."
2408 ######################################################################
2410 ## remote management
2412 proc load_all_remotes
{} {
2414 global all_remotes tracking_branches
2416 set all_remotes
[list
]
2417 array
unset tracking_branches
2419 set rm_dir
[gitdir remotes
]
2420 if {[file isdirectory
$rm_dir]} {
2421 set all_remotes
[glob \
2425 -directory $rm_dir *]
2427 foreach name
$all_remotes {
2429 set fd
[open
[file join $rm_dir $name] r
]
2430 while {[gets
$fd line
] >= 0} {
2431 if {![regexp
{^Pull
:[ ]*([^
:]+):(.
+)$
} \
2432 $line line src dst
]} continue
2433 if {![regexp ^refs
/ $dst]} {
2434 set dst
"refs/heads/$dst"
2436 set tracking_branches
($dst) [list
$name $src]
2443 foreach line
[array names repo_config remote.
*.url
] {
2444 if {![regexp ^remote\.
(.
*)\.url\$
$line line name
]} continue
2445 lappend all_remotes
$name
2447 if {[catch
{set fl
$repo_config(remote.
$name.fetch
)}]} {
2451 if {![regexp
{^
([^
:]+):(.
+)$
} $line line src dst
]} continue
2452 if {![regexp ^refs
/ $dst]} {
2453 set dst
"refs/heads/$dst"
2455 set tracking_branches
($dst) [list
$name $src]
2459 set all_remotes
[lsort
-unique $all_remotes]
2462 proc populate_fetch_menu
{} {
2463 global all_remotes repo_config
2466 foreach r
$all_remotes {
2468 if {![catch
{set a
$repo_config(remote.
$r.url
)}]} {
2469 if {![catch
{set a
$repo_config(remote.
$r.fetch
)}]} {
2474 set fd
[open
[gitdir remotes
$r] r
]
2475 while {[gets
$fd n
] >= 0} {
2476 if {[regexp
{^Pull
:[ \t]*([^
:]+):} $n]} {
2487 -label "Fetch from $r..." \
2488 -command [list fetch_from
$r] \
2494 proc populate_push_menu
{} {
2495 global all_remotes repo_config
2499 foreach r
$all_remotes {
2501 if {![catch
{set a
$repo_config(remote.
$r.url
)}]} {
2502 if {![catch
{set a
$repo_config(remote.
$r.push
)}]} {
2507 set fd
[open
[gitdir remotes
$r] r
]
2508 while {[gets
$fd n
] >= 0} {
2509 if {[regexp
{^Push
:[ \t]*([^
:]+):} $n]} {
2523 -label "Push to $r..." \
2524 -command [list push_to
$r] \
2531 proc start_push_anywhere_action
{w
} {
2532 global push_urltype push_remote push_url push_thin push_tags
2535 switch
-- $push_urltype {
2536 remote
{set r_url
$push_remote}
2537 url
{set r_url
$push_url}
2539 if {$r_url eq
{}} return
2541 set cmd
[list git push
]
2551 foreach i
[$w.
source.l curselection
] {
2552 set b
[$w.
source.l get
$i]
2553 lappend cmd
"refs/heads/$b:refs/heads/$b"
2558 } elseif
{$cnt == 1} {
2564 set cons
[new_console
"push $r_url" "Pushing $cnt $unit to $r_url"]
2565 console_exec
$cons $cmd console_done
2569 trace add variable push_remote
write \
2570 [list radio_selector push_urltype remote
]
2572 proc do_push_anywhere
{} {
2573 global all_heads all_remotes current_branch
2574 global push_urltype push_remote push_url push_thin push_tags
2578 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
2580 label
$w.header
-text {Push Branches
} -font font_uibold
2581 pack
$w.header
-side top
-fill x
2584 button
$w.buttons.create
-text Push \
2586 -command [list start_push_anywhere_action
$w]
2587 pack
$w.buttons.create
-side right
2588 button
$w.buttons.cancel
-text {Cancel
} \
2590 -command [list destroy
$w]
2591 pack
$w.buttons.cancel
-side right
-padx 5
2592 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
2594 labelframe
$w.
source \
2595 -text {Source Branches
} \
2597 listbox
$w.
source.l \
2600 -selectmode extended \
2601 -yscrollcommand [list
$w.
source.sby
set] \
2603 foreach h
$all_heads {
2604 $w.
source.l insert end
$h
2605 if {$h eq
$current_branch} {
2606 $w.
source.l
select set end
2609 scrollbar
$w.
source.sby
-command [list
$w.
source.l yview
]
2610 pack
$w.
source.sby
-side right
-fill y
2611 pack
$w.
source.l
-side left
-fill both
-expand 1
2612 pack
$w.
source -fill both
-expand 1 -pady 5 -padx 5
2614 labelframe
$w.dest \
2615 -text {Destination Repository
} \
2617 if {$all_remotes ne
{}} {
2618 radiobutton
$w.dest.remote_r \
2621 -variable push_urltype \
2623 eval tk_optionMenu
$w.dest.remote_m push_remote
$all_remotes
2624 grid
$w.dest.remote_r
$w.dest.remote_m
-sticky w
2625 if {[lsearch
-sorted -exact $all_remotes origin
] != -1} {
2626 set push_remote origin
2628 set push_remote
[lindex
$all_remotes 0]
2630 set push_urltype remote
2632 set push_urltype url
2634 radiobutton
$w.dest.url_r \
2635 -text {Arbitrary URL
:} \
2637 -variable push_urltype \
2639 entry
$w.dest.url_t \
2643 -textvariable push_url \
2647 if {%d
== 1 && [regexp
{\s
} %S
]} {return 0}
2648 if {%d
== 1 && [string length
%S
] > 0} {
2649 set push_urltype url
2653 grid
$w.dest.url_r
$w.dest.url_t
-sticky we
-padx {0 5}
2654 grid columnconfigure
$w.dest
1 -weight 1
2655 pack
$w.dest
-anchor nw
-fill x
-pady 5 -padx 5
2657 labelframe
$w.options \
2658 -text {Transfer Options
} \
2660 checkbutton
$w.options.thin \
2661 -text {Use thin pack
(for slow network connections
)} \
2662 -variable push_thin \
2664 grid
$w.options.thin
-columnspan 2 -sticky w
2665 checkbutton
$w.options.tags \
2666 -text {Include tags
} \
2667 -variable push_tags \
2669 grid
$w.options.tags
-columnspan 2 -sticky w
2670 grid columnconfigure
$w.options
1 -weight 1
2671 pack
$w.options
-anchor nw
-fill x
-pady 5 -padx 5
2677 bind $w <Visibility
> "grab $w"
2678 bind $w <Key-Escape
> "destroy $w"
2679 wm title
$w "[appname] ([reponame]): Push"
2683 ######################################################################
2688 global HEAD commit_type file_states
2690 if {[string match amend
* $commit_type]} {
2691 info_popup
{Cannot merge
while amending.
2693 You must finish amending this commit before
2694 starting any
type of merge.
2699 if {[committer_ident
] eq
{}} {return 0}
2700 if {![lock_index merge
]} {return 0}
2702 # -- Our in memory state should match the repository.
2704 repository_state curType curHEAD curMERGE_HEAD
2705 if {$commit_type ne
$curType ||
$HEAD ne
$curHEAD} {
2706 info_popup
{Last scanned state does not match repository state.
2708 Another Git program has modified this repository
2709 since the last scan. A rescan must be performed
2710 before a merge can be performed.
2712 The rescan will be automatically started now.
2715 rescan
{set ui_status_value
{Ready.
}}
2719 foreach path
[array names file_states
] {
2720 switch
-glob -- [lindex
$file_states($path) 0] {
2722 continue; # and pray it works!
2725 error_popup
"You are in the middle of a conflicted merge.
2727 File [short_path $path] has merge conflicts.
2729 You must resolve them, add the file, and commit to
2730 complete the current merge. Only then can you
2731 begin another merge.
2737 error_popup
"You are in the middle of a change.
2739 File [short_path $path] is modified.
2741 You should complete the current commit before
2742 starting a merge. Doing so will help you abort
2743 a failed merge, should the need arise.
2754 proc visualize_local_merge
{w
} {
2756 foreach i
[$w.
source.l curselection
] {
2757 lappend revs
[$w.
source.l get
$i]
2759 if {$revs eq
{}} return
2760 lappend revs
--not HEAD
2764 proc start_local_merge_action
{w
} {
2765 global HEAD ui_status_value current_branch
2767 set cmd
[list git merge
]
2770 foreach i
[$w.
source.l curselection
] {
2771 set b
[$w.
source.l get
$i]
2779 } elseif
{$revcnt == 1} {
2781 } elseif
{$revcnt <= 15} {
2787 -title [wm title
$w] \
2789 -message "Too many branches selected.
2791 You have requested to merge $revcnt branches
2792 in an octopus merge. This exceeds Git's
2793 internal limit of 15 branches per merge.
2795 Please select fewer branches. To merge more
2796 than 15 branches, merge the branches in batches.
2801 set msg
"Merging $current_branch, [join $names {, }]"
2802 set ui_status_value
"$msg..."
2803 set cons
[new_console
"Merge" $msg]
2804 console_exec
$cons $cmd [list finish_merge
$revcnt]
2805 bind $w <Destroy
> {}
2809 proc finish_merge
{revcnt w ok
} {
2812 set msg
{Merge completed successfully.
}
2815 info_popup
"Octopus merge failed.
2817 Your merge of $revcnt branches has failed.
2819 There are file-level conflicts between the
2820 branches which must be resolved manually.
2822 The working directory will now be reset.
2824 You can attempt this merge again
2825 by merging only one branch at a time." $w
2827 set fd
[open
"| git read-tree --reset -u HEAD" r
]
2828 fconfigure
$fd -blocking 0 -translation binary
2829 fileevent
$fd readable
[list reset_hard_wait
$fd]
2830 set ui_status_value
{Aborting... please
wait...
}
2834 set msg
{Merge failed. Conflict resolution is required.
}
2837 rescan
[list
set ui_status_value
$msg]
2840 proc do_local_merge
{} {
2841 global current_branch
2843 if {![can_merge
]} return
2847 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
2850 -text "Merge Into $current_branch" \
2852 pack
$w.header
-side top
-fill x
2855 button
$w.buttons.visualize
-text Visualize \
2857 -command [list visualize_local_merge
$w]
2858 pack
$w.buttons.visualize
-side left
2859 button
$w.buttons.create
-text Merge \
2861 -command [list start_local_merge_action
$w]
2862 pack
$w.buttons.create
-side right
2863 button
$w.buttons.cancel
-text {Cancel
} \
2865 -command [list destroy
$w]
2866 pack
$w.buttons.cancel
-side right
-padx 5
2867 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
2869 labelframe
$w.
source \
2870 -text {Source Branches
} \
2872 listbox
$w.
source.l \
2875 -selectmode extended \
2876 -yscrollcommand [list
$w.
source.sby
set] \
2878 scrollbar
$w.
source.sby
-command [list
$w.
source.l yview
]
2879 pack
$w.
source.sby
-side right
-fill y
2880 pack
$w.
source.l
-side left
-fill both
-expand 1
2881 pack
$w.
source -fill both
-expand 1 -pady 5 -padx 5
2883 set cmd
[list git for-each-ref
]
2884 lappend cmd
{--format=%(objectname
) %(refname
)}
2885 lappend cmd refs
/heads
2886 lappend cmd refs
/remotes
2887 set fr_fd
[open
"| $cmd" r
]
2888 fconfigure
$fr_fd -translation binary
2889 while {[gets
$fr_fd line
] > 0} {
2890 set line
[split $line { }]
2891 set sha1
([lindex
$line 0]) [lindex
$line 1]
2896 set fr_fd
[open
"| git rev-list --all --not HEAD"]
2897 while {[gets
$fr_fd line
] > 0} {
2898 if {[catch
{set ref
$sha1($line)}]} continue
2899 regsub ^refs
/(heads|remotes
)/ $ref {} ref
2900 lappend to_show
$ref
2904 foreach ref
[lsort
-unique $to_show] {
2905 $w.
source.l insert end
$ref
2908 bind $w <Visibility
> "grab $w"
2909 bind $w <Key-Escape
> "unlock_index;destroy $w"
2910 bind $w <Destroy
> unlock_index
2911 wm title
$w "[appname] ([reponame]): Merge"
2915 proc do_reset_hard
{} {
2916 global HEAD commit_type file_states
2918 if {[string match amend
* $commit_type]} {
2919 info_popup
{Cannot abort
while amending.
2921 You must finish amending this commit.
2926 if {![lock_index abort
]} return
2928 if {[string match
*merge
* $commit_type]} {
2934 if {[ask_popup
"Abort $op?
2936 Aborting the current $op will cause
2937 *ALL* uncommitted changes to be lost.
2939 Continue with aborting the current $op?"] eq
{yes}} {
2940 set fd
[open
"| git read-tree --reset -u HEAD" r
]
2941 fconfigure
$fd -blocking 0 -translation binary
2942 fileevent
$fd readable
[list reset_hard_wait
$fd]
2943 set ui_status_value
{Aborting... please
wait...
}
2949 proc reset_hard_wait
{fd
} {
2957 $ui_comm delete
0.0 end
2958 $ui_comm edit modified false
2960 catch
{file delete
[gitdir MERGE_HEAD
]}
2961 catch
{file delete
[gitdir rr-cache MERGE_RR
]}
2962 catch
{file delete
[gitdir SQUASH_MSG
]}
2963 catch
{file delete
[gitdir MERGE_MSG
]}
2964 catch
{file delete
[gitdir GITGUI_MSG
]}
2966 rescan
{set ui_status_value
{Abort completed. Ready.
}}
2970 ######################################################################
2974 set next_browser_id
0
2976 proc new_browser
{commit
} {
2977 global next_browser_id cursor_ptr M1B
2978 global browser_commit browser_status browser_stack browser_path browser_busy
2980 set w .browser
[incr next_browser_id
]
2981 set w_list
$w.list.l
2982 set browser_commit
($w_list) $commit
2983 set browser_status
($w_list) {Starting...
}
2984 set browser_stack
($w_list) {}
2985 set browser_path
($w_list) $browser_commit($w_list):
2986 set browser_busy
($w_list) 1
2989 label
$w.path
-textvariable browser_path
($w_list) \
2995 pack
$w.path
-anchor w
-side top
-fill x
2998 text
$w_list -background white
-borderwidth 0 \
2999 -cursor $cursor_ptr \
3004 -xscrollcommand [list
$w.list.sbx
set] \
3005 -yscrollcommand [list
$w.list.sby
set] \
3007 $w_list tag conf in_sel \
3008 -background [$w_list cget
-foreground] \
3009 -foreground [$w_list cget
-background]
3010 scrollbar
$w.list.sbx
-orient h
-command [list
$w_list xview
]
3011 scrollbar
$w.list.sby
-orient v
-command [list
$w_list yview
]
3012 pack
$w.list.sbx
-side bottom
-fill x
3013 pack
$w.list.sby
-side right
-fill y
3014 pack
$w_list -side left
-fill both
-expand 1
3015 pack
$w.list
-side top
-fill both
-expand 1
3017 label
$w.status
-textvariable browser_status
($w_list) \
3023 pack
$w.status
-anchor w
-side bottom
-fill x
3025 bind $w_list <Button-1
> "browser_click 0 $w_list @%x,%y;break"
3026 bind $w_list <Double-Button-1
> "browser_click 1 $w_list @%x,%y;break"
3027 bind $w_list <$M1B-Up> "browser_parent $w_list;break"
3028 bind $w_list <$M1B-Left> "browser_parent $w_list;break"
3029 bind $w_list <Up
> "browser_move -1 $w_list;break"
3030 bind $w_list <Down
> "browser_move 1 $w_list;break"
3031 bind $w_list <$M1B-Right> "browser_enter $w_list;break"
3032 bind $w_list <Return
> "browser_enter $w_list;break"
3033 bind $w_list <Prior
> "browser_page -1 $w_list;break"
3034 bind $w_list <Next
> "browser_page 1 $w_list;break"
3035 bind $w_list <Left
> break
3036 bind $w_list <Right
> break
3038 bind $w <Visibility
> "focus $w"
3040 array unset browser_buffer $w_list
3041 array unset browser_files $w_list
3042 array unset browser_status $w_list
3043 array unset browser_stack $w_list
3044 array unset browser_path $w_list
3045 array unset browser_commit $w_list
3046 array unset browser_busy $w_list
3048 wm title
$w "[appname] ([reponame]): File Browser"
3049 ls_tree
$w_list $browser_commit($w_list) {}
3052 proc browser_move
{dir w
} {
3053 global browser_files browser_busy
3055 if {$browser_busy($w)} return
3056 set lno
[lindex
[split [$w index in_sel.first
] .
] 0]
3058 if {[lindex
$browser_files($w) [expr {$lno - 1}]] ne
{}} {
3059 $w tag remove in_sel
0.0 end
3060 $w tag add in_sel
$lno.0 [expr {$lno + 1}].0
3065 proc browser_page
{dir w
} {
3066 global browser_files browser_busy
3068 if {$browser_busy($w)} return
3069 $w yview scroll
$dir pages
3071 [lindex
[$w yview
] 0]
3072 * [llength
$browser_files($w)]
3074 if {[lindex
$browser_files($w) [expr {$lno - 1}]] ne
{}} {
3075 $w tag remove in_sel
0.0 end
3076 $w tag add in_sel
$lno.0 [expr {$lno + 1}].0
3081 proc browser_parent
{w
} {
3082 global browser_files browser_status browser_path
3083 global browser_stack browser_busy
3085 if {$browser_busy($w)} return
3086 set info
[lindex
$browser_files($w) 0]
3087 if {[lindex
$info 0] eq
{parent
}} {
3088 set parent
[lindex
$browser_stack($w) end-1
]
3089 set browser_stack
($w) [lrange
$browser_stack($w) 0 end-2
]
3090 if {$browser_stack($w) eq
{}} {
3091 regsub
{:.
*$
} $browser_path($w) {:} browser_path
($w)
3093 regsub
{/[^
/]+$
} $browser_path($w) {} browser_path
($w)
3095 set browser_status
($w) "Loading $browser_path($w)..."
3096 ls_tree
$w [lindex
$parent 0] [lindex
$parent 1]
3100 proc browser_enter
{w
} {
3101 global browser_files browser_status browser_path
3102 global browser_commit browser_stack browser_busy
3104 if {$browser_busy($w)} return
3105 set lno
[lindex
[split [$w index in_sel.first
] .
] 0]
3106 set info
[lindex
$browser_files($w) [expr {$lno - 1}]]
3108 switch
-- [lindex
$info 0] {
3113 set name
[lindex
$info 2]
3114 set escn
[escape_path
$name]
3115 set browser_status
($w) "Loading $escn..."
3116 append browser_path
($w) $escn
3117 ls_tree
$w [lindex
$info 1] $name
3120 set name
[lindex
$info 2]
3122 foreach n
$browser_stack($w) {
3123 append p
[lindex
$n 1]
3126 show_blame
$browser_commit($w) $p
3132 proc browser_click
{was_double_click w pos
} {
3133 global browser_files browser_busy
3135 if {$browser_busy($w)} return
3136 set lno
[lindex
[split [$w index
$pos] .
] 0]
3139 if {[lindex
$browser_files($w) [expr {$lno - 1}]] ne
{}} {
3140 $w tag remove in_sel
0.0 end
3141 $w tag add in_sel
$lno.0 [expr {$lno + 1}].0
3142 if {$was_double_click} {
3148 proc ls_tree
{w tree_id name
} {
3149 global browser_buffer browser_files browser_stack browser_busy
3151 set browser_buffer
($w) {}
3152 set browser_files
($w) {}
3153 set browser_busy
($w) 1
3155 $w conf
-state normal
3156 $w tag remove in_sel
0.0 end
3158 if {$browser_stack($w) ne
{}} {
3159 $w image create end \
3160 -align center
-padx 5 -pady 1 \
3163 $w insert end
{[Up To Parent
]}
3164 lappend browser_files
($w) parent
3166 lappend browser_stack
($w) [list
$tree_id $name]
3167 $w conf
-state disabled
3169 set cmd
[list git ls-tree
-z $tree_id]
3170 set fd
[open
"| $cmd" r
]
3171 fconfigure
$fd -blocking 0 -translation binary
-encoding binary
3172 fileevent
$fd readable
[list read_ls_tree
$fd $w]
3175 proc read_ls_tree
{fd w
} {
3176 global browser_buffer browser_files browser_status browser_busy
3178 if {![winfo exists
$w]} {
3183 append browser_buffer
($w) [read $fd]
3184 set pck
[split $browser_buffer($w) "\0"]
3185 set browser_buffer
($w) [lindex
$pck end
]
3187 set n
[llength
$browser_files($w)]
3188 $w conf
-state normal
3189 foreach p
[lrange
$pck 0 end-1
] {
3190 set info
[split $p "\t"]
3191 set path
[lindex
$info 1]
3192 set info
[split [lindex
$info 0] { }]
3193 set type [lindex
$info 1]
3194 set object
[lindex
$info 2]
3205 set image file_question
3209 if {$n > 0} {$w insert end
"\n"}
3210 $w image create end \
3211 -align center
-padx 5 -pady 1 \
3212 -name icon
[incr n
] \
3214 $w insert end
[escape_path
$path]
3215 lappend browser_files
($w) [list
$type $object $path]
3217 $w conf
-state disabled
3221 set browser_status
($w) Ready.
3222 set browser_busy
($w) 0
3223 array
unset browser_buffer
$w
3225 $w tag add in_sel
1.0 2.0
3231 proc show_blame
{commit path
} {
3232 global next_browser_id blame_status blame_data
3234 set w .browser
[incr next_browser_id
]
3235 set blame_status
($w) {Loading current
file content...
}
3240 label
$w.path
-text "$commit:$path" \
3246 pack
$w.path
-side top
-fill x
3250 label
$w.out.commit_l
-text Commit \
3255 text
$w.out.commit_t \
3256 -background white
-borderwidth 0 \
3262 lappend texts
$w.out.commit_t
3264 label
$w.out.author_l
-text Author \
3269 text
$w.out.author_t \
3270 -background white
-borderwidth 0 \
3276 lappend texts
$w.out.author_t
3278 label
$w.out.date_l
-text Date \
3283 text
$w.out.date_t \
3284 -background white
-borderwidth 0 \
3288 -width [string length
"yyyy-mm-dd hh:mm:ss"] \
3290 lappend texts
$w.out.date_t
3292 label
$w.out.filename_l
-text Filename \
3297 text
$w.out.filename_t \
3298 -background white
-borderwidth 0 \
3304 lappend texts
$w.out.filename_t
3306 label
$w.out.origlinenumber_l
-text {Orig Line
} \
3311 text
$w.out.origlinenumber_t \
3312 -background white
-borderwidth 0 \
3318 $w.out.origlinenumber_t tag conf linenumber
-justify right
3319 lappend texts
$w.out.origlinenumber_t
3321 label
$w.out.linenumber_l
-text {Curr Line
} \
3326 text
$w.out.linenumber_t \
3327 -background white
-borderwidth 0 \
3333 $w.out.linenumber_t tag conf linenumber
-justify right
3334 lappend texts
$w.out.linenumber_t
3336 label
$w.out.file_l
-text {File Content
} \
3341 text
$w.out.file_t \
3342 -background white
-borderwidth 0 \
3347 -xscrollcommand [list
$w.out.sbx
set] \
3349 lappend texts
$w.out.file_t
3351 scrollbar
$w.out.sbx
-orient h
-command [list
$w.out.file_t xview
]
3352 scrollbar
$w.out.sby
-orient v \
3353 -command [list scrollbar2many
$texts yview
]
3356 regsub
{_t$
} $i _l l
3359 set file_col
[expr {[llength
$texts] - 1}]
3360 eval grid
$labels -sticky we
3361 eval grid
$texts $w.out.sby
-sticky nsew
3362 grid conf
$w.out.sbx
-column $file_col -sticky we
3363 grid columnconfigure
$w.out
$file_col -weight 1
3364 grid rowconfigure
$w.out
1 -weight 1
3365 pack
$w.out
-fill both
-expand 1
3367 label
$w.status
-textvariable blame_status
($w) \
3373 pack
$w.status
-side bottom
-fill x
3375 menu
$w.ctxm
-tearoff 0
3376 $w.ctxm add
command -label "Copy Commit" \
3378 -command "blame_copycommit $w \$cursorW @\$cursorX,\$cursorY"
3381 $i tag conf in_sel \
3382 -background [$i cget
-foreground] \
3383 -foreground [$i cget
-background]
3384 $i conf
-yscrollcommand \
3385 [list many2scrollbar
$texts yview
$w.out.sby
]
3386 bind $i <Button-1
> "blame_highlight $i @%x,%y $texts;break"
3391 tk_popup $w.ctxm %X %Y
3395 set blame_data
($w,colors
) {}
3397 bind $w <Visibility
> "focus $w"
3399 array unset blame_status $w
3400 array unset blame_data $w,*
3402 wm title
$w "[appname] ([reponame]): File Viewer"
3404 set blame_data
($w,total_lines
) 0
3405 set cmd
[list git cat-file blob
"$commit:$path"]
3406 set fd
[open
"| $cmd" r
]
3407 fconfigure
$fd -blocking 0 -translation lf
-encoding binary
3408 fileevent
$fd readable
[list read_blame_catfile \
3409 $fd $w $commit $path \
3410 $texts $w.out.linenumber_t
$w.out.file_t
]
3413 proc read_blame_catfile
{fd w commit path texts w_lno w_file
} {
3414 global blame_status blame_data
3416 if {![winfo exists
$w_file]} {
3421 set n
$blame_data($w,total_lines
)
3422 foreach i
$texts {$i conf
-state normal
}
3423 while {[gets
$fd line
] >= 0} {
3424 regsub
"\r\$" $line {} line
3426 $w_lno insert end
$n linenumber
3427 $w_file insert end
$line
3428 foreach i
$texts {$i insert end
"\n"}
3430 foreach i
$texts {$i conf
-state disabled
}
3431 set blame_data
($w,total_lines
) $n
3435 set blame_status
($w) {Loading annotations...
}
3436 set cmd
[list git blame
-M -C --incremental]
3437 lappend cmd
$commit -- $path
3438 set fd
[open
"| $cmd" r
]
3439 fconfigure
$fd -blocking 0 -translation lf
-encoding binary
3440 fileevent
$fd readable
"read_blame_incremental $fd $w $texts"
3444 proc read_blame_incremental
{fd w
3445 w_commit w_author w_date w_filename w_olno
3447 global blame_status blame_data
3449 if {![winfo exists
$w_commit]} {
3463 $w_commit conf
-state normal
3464 $w_author conf
-state normal
3465 $w_date conf
-state normal
3466 $w_filename conf
-state normal
3467 $w_olno conf
-state normal
3469 while {[gets
$fd line
] >= 0} {
3470 if {[regexp
{^
([a-z0-9
]{40}) (\d
+) (\d
+) (\d
+)$
} $line line \
3471 cmit original_line final_line line_count
]} {
3472 set blame_data
($w,commit
) $cmit
3473 set blame_data
($w,original_line
) $original_line
3474 set blame_data
($w,final_line
) $final_line
3475 set blame_data
($w,line_count
) $line_count
3477 if {[catch
{set g
$blame_data($w,$cmit,seen
)}]} {
3478 if {$blame_data($w,colors
) eq
{}} {
3479 set blame_data
($w,colors
) {
3488 set c
[lindex
$blame_data($w,colors
) 0]
3489 set blame_data
($w,colors
) \
3490 [lrange
$blame_data($w,colors
) 1 end
]
3492 $t tag conf g
$cmit -background $c
3495 set blame_data
($w,$cmit,seen
) 1
3497 } elseif
{[string match
{filename
*} $line]} {
3498 set n
$blame_data($w,line_count
)
3499 set lno
$blame_data($w,final_line
)
3500 set ol
$blame_data($w,original_line
)
3501 set file [string range
$line 9 end
]
3502 set cmit
$blame_data($w,commit
)
3503 set abbrev
[string range
$cmit 0 8]
3505 if {[catch
{set author
$blame_data($w,$cmit,author
)} err
]} {
3509 if {[catch
{set atime
$blame_data($w,$cmit,author-time
)}]} {
3512 set atime
[clock format
$atime -format {%Y-
%m-
%d
%T
}]
3516 if {![catch
{set g g
$blame_data($w,line
$lno,commit
)}]} {
3518 $t tag remove
$g $lno.0 "$lno.0 lineend + 1c"
3528 $t delete
$lno.0 "$lno.0 lineend"
3531 $w_commit insert
$lno.0 $abbrev
3532 $w_author insert
$lno.0 $author
3533 $w_date insert
$lno.0 $atime
3534 $w_filename insert
$lno.0 $file
3535 $w_olno insert
$lno.0 $ol linenumber
3539 $t tag add
$g $lno.0 "$lno.0 lineend + 1c"
3542 set blame_data
($w,line
$lno,commit
) $cmit
3548 } elseif
{[regexp
{^
([a-z-
]+) (.
*)$
} $line line header data
]} {
3549 set blame_data
($w,$blame_data($w,commit
),$header) $data
3553 $w_commit conf
-state disabled
3554 $w_author conf
-state disabled
3555 $w_date conf
-state disabled
3556 $w_filename conf
-state disabled
3557 $w_olno conf
-state disabled
3561 set blame_status
($w) {Annotation complete.
}
3565 proc blame_highlight
{w pos args
} {
3566 set lno
[lindex
[split [$w index
$pos] .
] 0]
3568 $i tag remove in_sel
0.0 end
3570 if {$lno eq
{}} return
3572 $i tag add in_sel
$lno.0 "$lno.0 + 1 line"
3576 proc blame_copycommit
{w i pos
} {
3578 set lno
[lindex
[split [$i index
$pos] .
] 0]
3579 if {![catch
{set commit
$blame_data($w,line
$lno,commit
)}]} {
3588 ######################################################################
3593 #define mask_width 14
3594 #define mask_height 15
3595 static unsigned char mask_bits
[] = {
3596 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
3597 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
3598 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
3601 image create bitmap file_plain
-background white
-foreground black
-data {
3602 #define plain_width 14
3603 #define plain_height 15
3604 static unsigned char plain_bits
[] = {
3605 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
3606 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
3607 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
3608 } -maskdata $filemask
3610 image create bitmap file_mod
-background white
-foreground blue
-data {
3611 #define mod_width 14
3612 #define mod_height 15
3613 static unsigned char mod_bits
[] = {
3614 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
3615 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
3616 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
3617 } -maskdata $filemask
3619 image create bitmap file_fulltick
-background white
-foreground "#007000" -data {
3620 #define file_fulltick_width 14
3621 #define file_fulltick_height 15
3622 static unsigned char file_fulltick_bits
[] = {
3623 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
3624 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
3625 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
3626 } -maskdata $filemask
3628 image create bitmap file_parttick
-background white
-foreground "#005050" -data {
3629 #define parttick_width 14
3630 #define parttick_height 15
3631 static unsigned char parttick_bits
[] = {
3632 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
3633 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
3634 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
3635 } -maskdata $filemask
3637 image create bitmap file_question
-background white
-foreground black
-data {
3638 #define file_question_width 14
3639 #define file_question_height 15
3640 static unsigned char file_question_bits
[] = {
3641 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
3642 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
3643 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
3644 } -maskdata $filemask
3646 image create bitmap file_removed
-background white
-foreground red
-data {
3647 #define file_removed_width 14
3648 #define file_removed_height 15
3649 static unsigned char file_removed_bits
[] = {
3650 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
3651 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
3652 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
3653 } -maskdata $filemask
3655 image create bitmap file_merge
-background white
-foreground blue
-data {
3656 #define file_merge_width 14
3657 #define file_merge_height 15
3658 static unsigned char file_merge_bits
[] = {
3659 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
3660 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
3661 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
3662 } -maskdata $filemask
3665 #define file_width 18
3666 #define file_height 18
3667 static unsigned char file_bits
[] = {
3668 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
3669 0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
3670 0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
3671 0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
3672 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
3674 image create bitmap file_dir
-background white
-foreground blue \
3675 -data $file_dir_data -maskdata $file_dir_data
3678 set file_uplevel_data
{
3680 #define up_height 15
3681 static unsigned char up_bits
[] = {
3682 0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
3683 0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
3684 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
3686 image create bitmap file_uplevel
-background white
-foreground red \
3687 -data $file_uplevel_data -maskdata $file_uplevel_data
3688 unset file_uplevel_data
3690 set ui_index .vpane.files.index.list
3691 set ui_workdir .vpane.files.workdir.list
3693 set all_icons
(_
$ui_index) file_plain
3694 set all_icons
(A
$ui_index) file_fulltick
3695 set all_icons
(M
$ui_index) file_fulltick
3696 set all_icons
(D
$ui_index) file_removed
3697 set all_icons
(U
$ui_index) file_merge
3699 set all_icons
(_
$ui_workdir) file_plain
3700 set all_icons
(M
$ui_workdir) file_mod
3701 set all_icons
(D
$ui_workdir) file_question
3702 set all_icons
(U
$ui_workdir) file_merge
3703 set all_icons
(O
$ui_workdir) file_plain
3705 set max_status_desc
0
3709 {_M
"Modified, not staged"}
3710 {M_
"Staged for commit"}
3711 {MM
"Portions staged for commit"}
3712 {MD
"Staged for commit, missing"}
3714 {_O
"Untracked, not staged"}
3715 {A_
"Staged for commit"}
3716 {AM
"Portions staged for commit"}
3717 {AD
"Staged for commit, missing"}
3720 {D_
"Staged for removal"}
3721 {DO
"Staged for removal, still present"}
3723 {U_
"Requires merge resolution"}
3724 {UU
"Requires merge resolution"}
3725 {UM
"Requires merge resolution"}
3726 {UD
"Requires merge resolution"}
3728 if {$max_status_desc < [string length
[lindex
$i 1]]} {
3729 set max_status_desc
[string length
[lindex
$i 1]]
3731 set all_descs
([lindex
$i 0]) [lindex
$i 1]
3735 ######################################################################
3739 proc bind_button3
{w cmd
} {
3740 bind $w <Any-Button-3
> $cmd
3742 bind $w <Control-Button-1
> $cmd
3746 proc scrollbar2many
{list mode args
} {
3747 foreach w
$list {eval $w $mode $args}
3750 proc many2scrollbar
{list mode sb top bottom
} {
3751 $sb set $top $bottom
3752 foreach w
$list {$w $mode moveto
$top}
3755 proc incr_font_size
{font
{amt
1}} {
3756 set sz
[font configure
$font -size]
3758 font configure
$font -size $sz
3759 font configure
${font}bold
-size $sz
3762 proc hook_failed_popup
{hook msg
} {
3767 label
$w.m.l1
-text "$hook hook failed:" \
3772 -background white
-borderwidth 1 \
3774 -width 80 -height 10 \
3776 -yscrollcommand [list
$w.m.sby
set]
3778 -text {You must correct the above errors before committing.
} \
3782 scrollbar
$w.m.sby
-command [list
$w.m.t yview
]
3783 pack
$w.m.l1
-side top
-fill x
3784 pack
$w.m.l2
-side bottom
-fill x
3785 pack
$w.m.sby
-side right
-fill y
3786 pack
$w.m.t
-side left
-fill both
-expand 1
3787 pack
$w.m
-side top
-fill both
-expand 1 -padx 5 -pady 10
3789 $w.m.t insert
1.0 $msg
3790 $w.m.t conf
-state disabled
3792 button
$w.ok
-text OK \
3795 -command "destroy $w"
3796 pack
$w.ok
-side bottom
-anchor e
-pady 10 -padx 10
3798 bind $w <Visibility
> "grab $w; focus $w"
3799 bind $w <Key-Return
> "destroy $w"
3800 wm title
$w "[appname] ([reponame]): error"
3804 set next_console_id
0
3806 proc new_console
{short_title long_title
} {
3807 global next_console_id console_data
3808 set w .console
[incr next_console_id
]
3809 set console_data
($w) [list
$short_title $long_title]
3810 return [console_init
$w]
3813 proc console_init
{w
} {
3814 global console_cr console_data M1B
3816 set console_cr
($w) 1.0
3819 label
$w.m.l1
-text "[lindex $console_data($w) 1]:" \
3824 -background white
-borderwidth 1 \
3826 -width 80 -height 10 \
3829 -yscrollcommand [list
$w.m.sby
set]
3830 label
$w.m.s
-text {Working... please
wait...
} \
3834 scrollbar
$w.m.sby
-command [list
$w.m.t yview
]
3835 pack
$w.m.l1
-side top
-fill x
3836 pack
$w.m.s
-side bottom
-fill x
3837 pack
$w.m.sby
-side right
-fill y
3838 pack
$w.m.t
-side left
-fill both
-expand 1
3839 pack
$w.m
-side top
-fill both
-expand 1 -padx 5 -pady 10
3841 menu
$w.ctxm
-tearoff 0
3842 $w.ctxm add
command -label "Copy" \
3844 -command "tk_textCopy $w.m.t"
3845 $w.ctxm add
command -label "Select All" \
3847 -command "focus $w.m.t;$w.m.t tag add sel 0.0 end"
3848 $w.ctxm add
command -label "Copy All" \
3851 $w.m.t tag add sel 0.0 end
3853 $w.m.t tag remove sel 0.0 end
3856 button
$w.ok
-text {Close
} \
3859 -command "destroy $w"
3860 pack
$w.ok
-side bottom
-anchor e
-pady 10 -padx 10
3862 bind_button3
$w.m.t
"tk_popup $w.ctxm %X %Y"
3863 bind $w.m.t
<$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break"
3864 bind $w.m.t
<$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break"
3865 bind $w <Visibility
> "focus $w"
3866 wm title
$w "[appname] ([reponame]): [lindex $console_data($w) 0]"
3870 proc console_exec
{w cmd after
} {
3871 # -- Cygwin's Tcl tosses the enviroment when we exec our child.
3872 # But most users need that so we have to relogin. :-(
3875 set cmd
[list sh
--login -c "cd \"[pwd]\" && [join $cmd { }]"]
3878 # -- Tcl won't let us redirect both stdout and stderr to
3879 # the same pipe. So pass it through cat...
3881 set cmd
[concat |
$cmd |
& cat]
3883 set fd_f
[open
$cmd r
]
3884 fconfigure
$fd_f -blocking 0 -translation binary
3885 fileevent
$fd_f readable
[list console_read
$w $fd_f $after]
3888 proc console_read
{w fd after
} {
3893 if {![winfo exists
$w]} {console_init
$w}
3894 $w.m.t conf
-state normal
3896 set n
[string length
$buf]
3898 set cr
[string first
"\r" $buf $c]
3899 set lf
[string first
"\n" $buf $c]
3900 if {$cr < 0} {set cr
[expr {$n + 1}]}
3901 if {$lf < 0} {set lf
[expr {$n + 1}]}
3904 $w.m.t insert end
[string range
$buf $c $lf]
3905 set console_cr
($w) [$w.m.t index
{end
-1c}]
3909 $w.m.t delete
$console_cr($w) end
3910 $w.m.t insert end
"\n"
3911 $w.m.t insert end
[string range
$buf $c $cr]
3916 $w.m.t conf
-state disabled
3920 fconfigure
$fd -blocking 1
3922 if {[catch
{close
$fd}]} {
3927 uplevel
#0 $after $w $ok
3930 fconfigure
$fd -blocking 0
3933 proc console_chain
{cmdlist w
{ok
1}} {
3935 if {[llength
$cmdlist] == 0} {
3940 set cmd
[lindex
$cmdlist 0]
3941 set cmdlist
[lrange
$cmdlist 1 end
]
3943 if {[lindex
$cmd 0] eq
{console_exec
}} {
3946 [list console_chain
$cmdlist]
3948 uplevel
#0 $cmd $cmdlist $w $ok
3955 proc console_done
{args
} {
3956 global console_cr console_data
3958 switch
-- [llength
$args] {
3960 set w
[lindex
$args 0]
3961 set ok
[lindex
$args 1]
3964 set w
[lindex
$args 1]
3965 set ok
[lindex
$args 2]
3968 error
"wrong number of args: console_done ?ignored? w ok"
3973 if {[winfo exists
$w]} {
3974 $w.m.s conf
-background green
-text {Success
}
3975 $w.ok conf
-state normal
3978 if {![winfo exists
$w]} {
3981 $w.m.s conf
-background red
-text {Error
: Command Failed
}
3982 $w.ok conf
-state normal
3985 array
unset console_cr
$w
3986 array
unset console_data
$w
3989 ######################################################################
3993 set starting_gitk_msg
{Starting gitk... please
wait...
}
3995 proc do_gitk
{revs
} {
3996 global env ui_status_value starting_gitk_msg
3998 # -- On Windows gitk is severly broken, and right now it seems like
3999 # nobody cares about fixing it. The only known workaround is to
4000 # always delete ~/.gitk before starting the program.
4003 catch
{file delete
[file join $env(HOME
) .gitk
]}
4006 # -- Always start gitk through whatever we were loaded with. This
4007 # lets us bypass using shell process on Windows systems.
4009 set cmd
[info nameofexecutable
]
4010 lappend cmd
[gitexec gitk
]
4016 if {[catch
{eval exec $cmd &} err
]} {
4017 error_popup
"Failed to start gitk:\n\n$err"
4019 set ui_status_value
$starting_gitk_msg
4021 if {$ui_status_value eq
$starting_gitk_msg} {
4022 set ui_status_value
{Ready.
}
4029 set fd
[open
"| git count-objects -v" r
]
4030 while {[gets
$fd line
] > 0} {
4031 if {[regexp
{^
([^
:]+): (\d
+)$
} $line _ name value
]} {
4032 set stats
($name) $value
4038 foreach p
[glob
-directory [gitdir objects pack
] \
4041 incr packed_sz
[file size
$p]
4043 if {$packed_sz > 0} {
4044 set stats
(size-pack
) [expr {$packed_sz / 1024}]
4049 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
4051 label
$w.header
-text {Database Statistics
} \
4053 pack
$w.header
-side top
-fill x
4055 frame
$w.buttons
-border 1
4056 button
$w.buttons.close
-text Close \
4058 -command [list destroy
$w]
4059 button
$w.buttons.gc
-text {Compress Database
} \
4061 -command "destroy $w;do_gc"
4062 pack
$w.buttons.close
-side right
4063 pack
$w.buttons.gc
-side left
4064 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
4066 frame
$w.stat
-borderwidth 1 -relief solid
4068 {count
{Number of loose objects
}}
4069 {size
{Disk space used by loose objects
} { KiB
}}
4070 {in-pack
{Number of packed objects
}}
4071 {packs
{Number of packs
}}
4072 {size-pack
{Disk space used by packed objects
} { KiB
}}
4073 {prune-packable
{Packed objects waiting
for pruning
}}
4074 {garbage
{Garbage files
}}
4076 set name
[lindex
$s 0]
4077 set label
[lindex
$s 1]
4078 if {[catch
{set value
$stats($name)}]} continue
4079 if {[llength
$s] > 2} {
4080 set value
"$value[lindex $s 2]"
4083 label
$w.stat.l_
$name -text "$label:" -anchor w
-font font_ui
4084 label
$w.stat.v_
$name -text $value -anchor w
-font font_ui
4085 grid
$w.stat.l_
$name $w.stat.v_
$name -sticky we
-padx {0 5}
4087 pack
$w.stat
-pady 10 -padx 10
4089 bind $w <Visibility
> "grab $w; focus $w"
4090 bind $w <Key-Escape
> [list destroy
$w]
4091 bind $w <Key-Return
> [list destroy
$w]
4092 wm title
$w "[appname] ([reponame]): Database Statistics"
4097 set w
[new_console
{gc
} {Compressing the object database
}]
4099 {console_exec
{git pack-refs
--prune}}
4100 {console_exec
{git reflog expire
--all}}
4101 {console_exec
{git repack
-a -d -l}}
4102 {console_exec
{git rerere gc
}}
4106 proc do_fsck_objects
{} {
4107 set w
[new_console
{fsck-objects
} \
4108 {Verifying the object database with fsck-objects
}]
4109 set cmd
[list git fsck-objects
]
4112 lappend cmd
--strict
4113 console_exec
$w $cmd console_done
4119 global ui_comm is_quitting repo_config commit_type
4121 if {$is_quitting} return
4124 # -- Stash our current commit buffer.
4126 set save
[gitdir GITGUI_MSG
]
4127 set msg
[string trim
[$ui_comm get
0.0 end
]]
4128 regsub
-all -line {[ \r\t]+$
} $msg {} msg
4129 if {(![string match amend
* $commit_type]
4130 ||
[$ui_comm edit modified
])
4133 set fd
[open
$save w
]
4134 puts
-nonewline $fd $msg
4138 catch
{file delete
$save}
4141 # -- Stash our current window geometry into this repository.
4143 set cfg_geometry
[list
]
4144 lappend cfg_geometry
[wm geometry .
]
4145 lappend cfg_geometry
[lindex
[.vpane sash coord
0] 1]
4146 lappend cfg_geometry
[lindex
[.vpane.files sash coord
0] 0]
4147 if {[catch
{set rc_geometry
$repo_config(gui.geometry
)}]} {
4150 if {$cfg_geometry ne
$rc_geometry} {
4151 catch
{exec git repo-config gui.geometry
$cfg_geometry}
4158 rescan
{set ui_status_value
{Ready.
}}
4161 proc unstage_helper
{txt paths
} {
4162 global file_states current_diff_path
4164 if {![lock_index begin-update
]} return
4168 foreach path
$paths {
4169 switch
-glob -- [lindex
$file_states($path) 0] {
4173 lappend pathList
$path
4174 if {$path eq
$current_diff_path} {
4175 set after
{reshow_diff
;}
4180 if {$pathList eq
{}} {
4186 [concat
$after {set ui_status_value
{Ready.
}}]
4190 proc do_unstage_selection
{} {
4191 global current_diff_path selected_paths
4193 if {[array size selected_paths
] > 0} {
4195 {Unstaging selected files from commit
} \
4196 [array names selected_paths
]
4197 } elseif
{$current_diff_path ne
{}} {
4199 "Unstaging [short_path $current_diff_path] from commit" \
4200 [list
$current_diff_path]
4204 proc add_helper
{txt paths
} {
4205 global file_states current_diff_path
4207 if {![lock_index begin-update
]} return
4211 foreach path
$paths {
4212 switch
-glob -- [lindex
$file_states($path) 0] {
4217 lappend pathList
$path
4218 if {$path eq
$current_diff_path} {
4219 set after
{reshow_diff
;}
4224 if {$pathList eq
{}} {
4230 [concat
$after {set ui_status_value
{Ready to commit.
}}]
4234 proc do_add_selection
{} {
4235 global current_diff_path selected_paths
4237 if {[array size selected_paths
] > 0} {
4239 {Adding selected files
} \
4240 [array names selected_paths
]
4241 } elseif
{$current_diff_path ne
{}} {
4243 "Adding [short_path $current_diff_path]" \
4244 [list
$current_diff_path]
4248 proc do_add_all
{} {
4252 foreach path
[array names file_states
] {
4253 switch
-glob -- [lindex
$file_states($path) 0] {
4256 ?D
{lappend paths
$path}
4259 add_helper
{Adding all changed files
} $paths
4262 proc revert_helper
{txt paths
} {
4263 global file_states current_diff_path
4265 if {![lock_index begin-update
]} return
4269 foreach path
$paths {
4270 switch
-glob -- [lindex
$file_states($path) 0] {
4274 lappend pathList
$path
4275 if {$path eq
$current_diff_path} {
4276 set after
{reshow_diff
;}
4282 set n
[llength
$pathList]
4286 } elseif
{$n == 1} {
4287 set s
"[short_path [lindex $pathList]]"
4289 set s
"these $n files"
4292 set reply
[tk_dialog \
4294 "[appname] ([reponame])" \
4295 "Revert changes in $s?
4297 Any unadded changes will be permanently lost by the revert." \
4307 [concat
$after {set ui_status_value
{Ready.
}}]
4313 proc do_revert_selection
{} {
4314 global current_diff_path selected_paths
4316 if {[array size selected_paths
] > 0} {
4318 {Reverting selected files
} \
4319 [array names selected_paths
]
4320 } elseif
{$current_diff_path ne
{}} {
4322 "Reverting [short_path $current_diff_path]" \
4323 [list
$current_diff_path]
4327 proc do_signoff
{} {
4330 set me
[committer_ident
]
4331 if {$me eq
{}} return
4333 set sob
"Signed-off-by: $me"
4334 set last
[$ui_comm get
{end
-1c linestart
} {end
-1c}]
4335 if {$last ne
$sob} {
4336 $ui_comm edit separator
4338 && ![regexp
{^
[A-Z
][A-Za-z
]*-[A-Za-z-
]+: *} $last]} {
4339 $ui_comm insert end
"\n"
4341 $ui_comm insert end
"\n$sob"
4342 $ui_comm edit separator
4347 proc do_select_commit_type
{} {
4348 global commit_type selected_commit_type
4350 if {$selected_commit_type eq
{new
}
4351 && [string match amend
* $commit_type]} {
4353 } elseif
{$selected_commit_type eq
{amend
}
4354 && ![string match amend
* $commit_type]} {
4357 # The amend request was rejected...
4359 if {![string match amend
* $commit_type]} {
4360 set selected_commit_type new
4370 global appvers copyright
4371 global tcl_patchLevel tk_patchLevel
4375 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
4377 label
$w.header
-text "About [appname]" \
4379 pack
$w.header
-side top
-fill x
4382 button
$w.buttons.close
-text {Close
} \
4384 -command [list destroy
$w]
4385 pack
$w.buttons.close
-side right
4386 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
4389 -text "[appname] - a commit creation tool for Git.
4397 pack
$w.desc
-side top
-fill x
-padx 5 -pady 5
4400 append v
"[appname] version $appvers\n"
4401 append v
"[exec git version]\n"
4403 if {$tcl_patchLevel eq
$tk_patchLevel} {
4404 append v
"Tcl/Tk version $tcl_patchLevel"
4406 append v
"Tcl version $tcl_patchLevel"
4407 append v
", Tk version $tk_patchLevel"
4418 pack
$w.vers
-side top
-fill x
-padx 5 -pady 5
4420 menu
$w.ctxm
-tearoff 0
4421 $w.ctxm add
command \
4426 clipboard append -format STRING -type STRING -- \[$w.vers cget -text\]
4429 bind $w <Visibility
> "grab $w; focus $w"
4430 bind $w <Key-Escape
> "destroy $w"
4431 bind_button3
$w.vers
"tk_popup $w.ctxm %X %Y; grab $w; focus $w"
4432 wm title
$w "About [appname]"
4436 proc do_options
{} {
4437 global repo_config global_config font_descs
4438 global repo_config_new global_config_new
4440 array
unset repo_config_new
4441 array
unset global_config_new
4442 foreach name
[array names repo_config
] {
4443 set repo_config_new
($name) $repo_config($name)
4446 foreach name
[array names repo_config
] {
4448 gui.diffcontext
{continue}
4450 set repo_config_new
($name) $repo_config($name)
4452 foreach name
[array names global_config
] {
4453 set global_config_new
($name) $global_config($name)
4456 set w .options_editor
4458 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
4460 label
$w.header
-text "[appname] Options" \
4462 pack
$w.header
-side top
-fill x
4465 button
$w.buttons.restore
-text {Restore Defaults
} \
4467 -command do_restore_defaults
4468 pack
$w.buttons.restore
-side left
4469 button
$w.buttons.save
-text Save \
4471 -command [list do_save_config
$w]
4472 pack
$w.buttons.save
-side right
4473 button
$w.buttons.cancel
-text {Cancel
} \
4475 -command [list destroy
$w]
4476 pack
$w.buttons.cancel
-side right
-padx 5
4477 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
4479 labelframe
$w.repo
-text "[reponame] Repository" \
4481 labelframe
$w.global
-text {Global
(All Repositories
)} \
4483 pack
$w.repo
-side left
-fill both
-expand 1 -pady 5 -padx 5
4484 pack
$w.global
-side right
-fill both
-expand 1 -pady 5 -padx 5
4488 {t user.name
{User Name
}}
4489 {t user.email
{Email Address
}}
4491 {b merge.summary
{Summarize Merge Commits
}}
4492 {i-1.
.5 merge.verbosity
{Merge Verbosity
}}
4494 {b gui.trustmtime
{Trust File Modification Timestamps
}}
4495 {i-1.
.99 gui.diffcontext
{Number of Diff Context Lines
}}
4496 {t gui.newbranchtemplate
{New Branch Name Template
}}
4498 set type [lindex
$option 0]
4499 set name
[lindex
$option 1]
4500 set text
[lindex
$option 2]
4502 foreach f
{repo global
} {
4503 switch
-glob -- $type {
4505 checkbutton
$w.
$f.
$optid -text $text \
4506 -variable ${f}_config_new
($name) \
4510 pack
$w.
$f.
$optid -side top
-anchor w
4513 regexp
-- {-(\d
+)\.\.
(\d
+)$
} $type _junk min max
4515 label
$w.
$f.
$optid.l
-text "$text:" -font font_ui
4516 pack
$w.
$f.
$optid.l
-side left
-anchor w
-fill x
4517 spinbox
$w.
$f.
$optid.v \
4518 -textvariable ${f}_config_new
($name) \
4522 -width [expr {1 + [string length
$max]}] \
4524 bind $w.
$f.
$optid.v
<FocusIn
> {%W selection range
0 end
}
4525 pack
$w.
$f.
$optid.v
-side right
-anchor e
-padx 5
4526 pack
$w.
$f.
$optid -side top
-anchor w
-fill x
4530 label
$w.
$f.
$optid.l
-text "$text:" -font font_ui
4531 entry
$w.
$f.
$optid.v \
4535 -textvariable ${f}_config_new
($name) \
4537 pack
$w.
$f.
$optid.l
-side left
-anchor w
4538 pack
$w.
$f.
$optid.v
-side left
-anchor w \
4541 pack
$w.
$f.
$optid -side top
-anchor w
-fill x
4547 set all_fonts
[lsort
[font families
]]
4548 foreach option
$font_descs {
4549 set name
[lindex
$option 0]
4550 set font
[lindex
$option 1]
4551 set text
[lindex
$option 2]
4553 set global_config_new
(gui.
$font^^family
) \
4554 [font configure
$font -family]
4555 set global_config_new
(gui.
$font^^size
) \
4556 [font configure
$font -size]
4558 frame
$w.global.
$name
4559 label
$w.global.
$name.l
-text "$text:" -font font_ui
4560 pack
$w.global.
$name.l
-side left
-anchor w
-fill x
4561 eval tk_optionMenu
$w.global.
$name.family \
4562 global_config_new
(gui.
$font^^family
) \
4564 spinbox
$w.global.
$name.size \
4565 -textvariable global_config_new
(gui.
$font^^size
) \
4566 -from 2 -to 80 -increment 1 \
4569 bind $w.global.
$name.size
<FocusIn
> {%W selection range
0 end
}
4570 pack
$w.global.
$name.size
-side right
-anchor e
4571 pack
$w.global.
$name.family
-side right
-anchor e
4572 pack
$w.global.
$name -side top
-anchor w
-fill x
4575 bind $w <Visibility
> "grab $w; focus $w"
4576 bind $w <Key-Escape
> "destroy $w"
4577 wm title
$w "[appname] ([reponame]): Options"
4581 proc do_restore_defaults
{} {
4582 global font_descs default_config repo_config
4583 global repo_config_new global_config_new
4585 foreach name
[array names default_config
] {
4586 set repo_config_new
($name) $default_config($name)
4587 set global_config_new
($name) $default_config($name)
4590 foreach option
$font_descs {
4591 set name
[lindex
$option 0]
4592 set repo_config
(gui.
$name) $default_config(gui.
$name)
4596 foreach option
$font_descs {
4597 set name
[lindex
$option 0]
4598 set font
[lindex
$option 1]
4599 set global_config_new
(gui.
$font^^family
) \
4600 [font configure
$font -family]
4601 set global_config_new
(gui.
$font^^size
) \
4602 [font configure
$font -size]
4606 proc do_save_config
{w
} {
4607 if {[catch
{save_config
} err
]} {
4608 error_popup
"Failed to completely save options:\n\n$err"
4614 proc do_windows_shortcut
{} {
4617 set fn
[tk_getSaveFile \
4619 -title "[appname] ([reponame]): Create Desktop Icon" \
4620 -initialfile "Git [reponame].bat"]
4624 puts
$fd "@ECHO Entering [reponame]"
4625 puts
$fd "@ECHO Starting git-gui... please wait..."
4626 puts
$fd "@SET PATH=[file normalize [gitexec]];%PATH%"
4627 puts
$fd "@SET GIT_DIR=[file normalize [gitdir]]"
4628 puts
-nonewline $fd "@\"[info nameofexecutable]\""
4629 puts
$fd " \"[file normalize $argv0]\""
4632 error_popup
"Cannot write script:\n\n$err"
4637 proc do_cygwin_shortcut
{} {
4641 set desktop
[exec cygpath \
4649 set fn
[tk_getSaveFile \
4651 -title "[appname] ([reponame]): Create Desktop Icon" \
4652 -initialdir $desktop \
4653 -initialfile "Git [reponame].bat"]
4657 set sh
[exec cygpath \
4661 set me
[exec cygpath \
4665 set gd
[exec cygpath \
4669 set gw
[exec cygpath \
4672 [file dirname [gitdir
]]]
4673 regsub
-all ' $me "'\\''" me
4674 regsub -all ' $gd "'\\''" gd
4675 puts $fd "@ECHO Entering $gw"
4676 puts $fd "@ECHO Starting git-gui... please wait..."
4677 puts -nonewline $fd "@\"$sh\" --login -c \""
4678 puts -nonewline $fd "GIT_DIR='$gd'"
4679 puts -nonewline $fd " '$me'"
4683 error_popup "Cannot write script:\n\n$err"
4688 proc do_macosx_app {} {
4691 set fn [tk_getSaveFile \
4693 -title "[appname] ([reponame]): Create Desktop Icon" \
4694 -initialdir [file join $env(HOME) Desktop] \
4695 -initialfile "Git [reponame].app"]
4698 set Contents [file join $fn Contents]
4699 set MacOS [file join $Contents MacOS]
4700 set exe [file join $MacOS git-gui]
4704 set fd [open [file join $Contents Info.plist] w]
4705 puts $fd {<?xml version="1.0" encoding="UTF-8"?>
4706 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4707 <plist version="1.0">
4709 <key>CFBundleDevelopmentRegion</key>
4710 <string>English</string>
4711 <key>CFBundleExecutable</key>
4712 <string>git-gui</string>
4713 <key>CFBundleIdentifier</key>
4714 <string>org.spearce.git-gui</string>
4715 <key>CFBundleInfoDictionaryVersion</key>
4716 <string>6.0</string>
4717 <key>CFBundlePackageType</key>
4718 <string>APPL</string>
4719 <key>CFBundleSignature</key>
4720 <string>????</string>
4721 <key>CFBundleVersion</key>
4722 <string>1.0</string>
4723 <key>NSPrincipalClass</key>
4724 <string>NSApplication</string>
4729 set fd [open $exe w]
4730 set gd [file normalize [gitdir]]
4731 set ep [file normalize [gitexec]]
4732 regsub -all ' $gd "'\\''" gd
4733 regsub
-all ' $ep "'\\''" ep
4734 puts $fd "#!/bin/sh"
4735 foreach name
[array names env
] {
4736 if {[string match GIT_
* $name]} {
4737 regsub
-all ' $env($name) "'\\''" v
4738 puts $fd "export $name='$v'"
4741 puts $fd "export PATH
='$ep':\
$PATH"
4742 puts $fd "export GIT_DIR
='$gd'"
4743 puts $fd "exec [file normalize
$argv0]"
4746 file attributes $exe -permissions u+x,g+x,o+x
4748 error_popup "Cannot
write icon
:\n\n$err"
4753 proc toggle_or_diff {w x y} {
4754 global file_states file_lists current_diff_path ui_index ui_workdir
4755 global last_clicked selected_paths
4757 set pos [split [$w index @$x,$y] .]
4758 set lno [lindex $pos 0]
4759 set col [lindex $pos 1]
4760 set path [lindex $file_lists($w) [expr {$lno - 1}]]
4766 set last_clicked [list $w $lno]
4767 array unset selected_paths
4768 $ui_index tag remove in_sel 0.0 end
4769 $ui_workdir tag remove in_sel 0.0 end
4772 if {$current_diff_path eq $path} {
4773 set after {reshow_diff;}
4777 if {$w eq $ui_index} {
4779 "Unstaging
[short_path
$path] from commit
" \
4781 [concat $after {set ui_status_value {Ready.}}]
4782 } elseif {$w eq $ui_workdir} {
4784 "Adding
[short_path
$path]" \
4786 [concat $after {set ui_status_value {Ready.}}]
4789 show_diff $path $w $lno
4793 proc add_one_to_selection {w x y} {
4794 global file_lists last_clicked selected_paths
4796 set lno [lindex [split [$w index @$x,$y] .] 0]
4797 set path [lindex $file_lists($w) [expr {$lno - 1}]]
4803 if {$last_clicked ne {}
4804 && [lindex $last_clicked 0] ne $w} {
4805 array unset selected_paths
4806 [lindex $last_clicked 0] tag remove in_sel 0.0 end
4809 set last_clicked [list $w $lno]
4810 if {[catch {set in_sel $selected_paths($path)}]} {
4814 unset selected_paths($path)
4815 $w tag remove in_sel $lno.0 [expr {$lno + 1}].0
4817 set selected_paths($path) 1
4818 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
4822 proc add_range_to_selection {w x y} {
4823 global file_lists last_clicked selected_paths
4825 if {[lindex $last_clicked 0] ne $w} {
4826 toggle_or_diff $w $x $y
4830 set lno [lindex [split [$w index @$x,$y] .] 0]
4831 set lc [lindex $last_clicked 1]
4840 foreach path [lrange $file_lists($w) \
4841 [expr {$begin - 1}] \
4842 [expr {$end - 1}]] {
4843 set selected_paths($path) 1
4845 $w tag add in_sel $begin.0 [expr {$end + 1}].0
4848 ######################################################################
4852 set cursor_ptr arrow
4853 font create font_diff -family Courier -size 10
4857 eval font configure font_ui [font actual [.dummy cget -font]]
4861 font create font_uibold
4862 font create font_diffbold
4867 } elseif {[is_MacOSX]} {
4875 proc apply_config {} {
4876 global repo_config font_descs
4878 foreach option $font_descs {
4879 set name [lindex $option 0]
4880 set font [lindex $option 1]
4882 foreach {cn cv} $repo_config(gui.$name) {
4883 font configure $font $cn $cv
4886 error_popup "Invalid font specified
in gui.
$name:\n\n$err"
4888 foreach {cn cv} [font configure $font] {
4889 font configure ${font}bold $cn $cv
4891 font configure ${font}bold -weight bold
4895 set default_config(merge.summary) false
4896 set default_config(merge.verbosity) 2
4897 set default_config(user.name) {}
4898 set default_config(user.email) {}
4900 set default_config(gui.trustmtime) false
4901 set default_config(gui.diffcontext) 5
4902 set default_config(gui.newbranchtemplate) {}
4903 set default_config(gui.fontui) [font configure font_ui]
4904 set default_config(gui.fontdiff) [font configure font_diff]
4906 {fontui font_ui {Main Font}}
4907 {fontdiff font_diff {Diff/Console Font}}
4912 ######################################################################
4918 menu .mbar -tearoff 0
4919 .mbar add cascade -label Repository -menu .mbar.repository
4920 .mbar add cascade -label Edit -menu .mbar.edit
4921 if {[is_enabled multicommit]} {
4922 .mbar add cascade -label Branch -menu .mbar.branch
4924 .mbar add cascade -label Commit -menu .mbar.commit
4925 if {[is_enabled multicommit]} {
4926 .mbar add cascade -label Merge -menu .mbar.merge
4927 .mbar add cascade -label Fetch -menu .mbar.fetch
4928 .mbar add cascade -label Push -menu .mbar.push
4930 . configure -menu .mbar
4932 # -- Repository Menu
4934 menu .mbar.repository
4936 .mbar.repository add command \
4937 -label {Browse Current Branch} \
4938 -command {new_browser $current_branch} \
4940 .mbar.repository add separator
4942 .mbar.repository add command \
4943 -label {Visualize Current Branch} \
4944 -command {do_gitk {}} \
4946 .mbar.repository add command \
4947 -label {Visualize All Branches} \
4948 -command {do_gitk {--all}} \
4950 .mbar.repository add separator
4952 if {[is_enabled multicommit]} {
4953 .mbar.repository add command -label {Database Statistics} \
4957 .mbar.repository add command -label {Compress Database} \
4961 .mbar.repository add command -label {Verify Database} \
4962 -command do_fsck_objects \
4965 .mbar.repository add separator
4968 .mbar.repository add command \
4969 -label {Create Desktop Icon} \
4970 -command do_cygwin_shortcut \
4972 } elseif {[is_Windows]} {
4973 .mbar.repository add command \
4974 -label {Create Desktop Icon} \
4975 -command do_windows_shortcut \
4977 } elseif {[is_MacOSX]} {
4978 .mbar.repository add command \
4979 -label {Create Desktop Icon} \
4980 -command do_macosx_app \
4985 .mbar.repository add command -label Quit \
4987 -accelerator $M1T-Q \
4993 .mbar.edit add command -label Undo \
4994 -command {catch {[focus] edit undo}} \
4995 -accelerator $M1T-Z \
4997 .mbar.edit add command -label Redo \
4998 -command {catch {[focus] edit redo}} \
4999 -accelerator $M1T-Y \
5001 .mbar.edit add separator
5002 .mbar.edit add command -label Cut \
5003 -command {catch {tk_textCut [focus]}} \
5004 -accelerator $M1T-X \
5006 .mbar.edit add command -label Copy \
5007 -command {catch {tk_textCopy [focus]}} \
5008 -accelerator $M1T-C \
5010 .mbar.edit add command -label Paste \
5011 -command {catch {tk_textPaste [focus]; [focus] see insert}} \
5012 -accelerator $M1T-V \
5014 .mbar.edit add command -label Delete \
5015 -command {catch {[focus] delete sel.first sel.last}} \
5018 .mbar.edit add separator
5019 .mbar.edit add command -label {Select All} \
5020 -command {catch {[focus] tag add sel 0.0 end}} \
5021 -accelerator $M1T-A \
5026 if {[is_enabled multicommit]} {
5029 .mbar.branch add command -label {Create...} \
5030 -command do_create_branch \
5031 -accelerator $M1T-N \
5033 lappend disable_on_lock [list .mbar.branch entryconf \
5034 [.mbar.branch index last] -state]
5036 .mbar.branch add command -label {Delete...} \
5037 -command do_delete_branch \
5039 lappend disable_on_lock [list .mbar.branch entryconf \
5040 [.mbar.branch index last] -state]
5047 .mbar.commit add radiobutton \
5048 -label {New Commit} \
5049 -command do_select_commit_type \
5050 -variable selected_commit_type \
5053 lappend disable_on_lock \
5054 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5056 .mbar.commit add radiobutton \
5057 -label {Amend Last Commit} \
5058 -command do_select_commit_type \
5059 -variable selected_commit_type \
5062 lappend disable_on_lock \
5063 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5065 .mbar.commit add separator
5067 .mbar.commit add command -label Rescan \
5068 -command do_rescan \
5071 lappend disable_on_lock \
5072 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5074 .mbar.commit add command -label {Add To Commit} \
5075 -command do_add_selection \
5077 lappend disable_on_lock \
5078 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5080 .mbar.commit add command -label {Add All To Commit} \
5081 -command do_add_all \
5082 -accelerator $M1T-I \
5084 lappend disable_on_lock \
5085 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5087 .mbar.commit add command -label {Unstage From Commit} \
5088 -command do_unstage_selection \
5090 lappend disable_on_lock \
5091 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5093 .mbar.commit add command -label {Revert Changes} \
5094 -command do_revert_selection \
5096 lappend disable_on_lock \
5097 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5099 .mbar.commit add separator
5101 .mbar.commit add command -label {Sign Off} \
5102 -command do_signoff \
5103 -accelerator $M1T-S \
5106 .mbar.commit add command -label Commit \
5107 -command do_commit \
5108 -accelerator $M1T-Return \
5110 lappend disable_on_lock \
5111 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5114 # -- Apple Menu (Mac OS X only)
5116 .mbar add cascade -label Apple -menu .mbar.apple
5119 .mbar.apple add command -label "About
[appname
]" \
5122 .mbar.apple add command -label "[appname
] Options...
" \
5123 -command do_options \
5128 .mbar.edit add separator
5129 .mbar.edit add command -label {Options...} \
5130 -command do_options \
5135 if {[file exists /usr/local/miga/lib/gui-miga]
5136 && [file exists .pvcsrc]} {
5138 global ui_status_value
5139 if {![lock_index update]} return
5140 set cmd [list sh --login -c "/usr
/local
/miga
/lib
/gui-miga
\"[pwd]\""]
5141 set miga_fd [open "|
$cmd" r]
5142 fconfigure $miga_fd -blocking 0
5143 fileevent $miga_fd readable [list miga_done $miga_fd]
5144 set ui_status_value {Running miga...}
5146 proc miga_done {fd} {
5151 rescan [list set ui_status_value {Ready.}]
5154 .mbar add cascade -label Tools -menu .mbar.tools
5156 .mbar.tools add command -label "Migrate
" \
5159 lappend disable_on_lock \
5160 [list .mbar.tools entryconf [.mbar.tools index last] -state]
5166 .mbar add cascade -label Help -menu .mbar.help
5170 .mbar.help add command -label "About
[appname
]" \
5176 catch {set browser $repo_config(instaweb.browser)}
5177 set doc_path [file dirname [gitexec]]
5178 set doc_path [file join $doc_path Documentation index.html]
5181 set doc_path [exec cygpath --windows $doc_path]
5184 if {$browser eq {}} {
5187 } elseif {[is_Cygwin]} {
5188 set program_files [file dirname [exec cygpath --windir]]
5189 set program_files [file join $program_files {Program Files}]
5190 set firefox [file join $program_files {Mozilla Firefox} firefox.exe]
5191 set ie [file join $program_files {Internet Explorer} IEXPLORE.EXE]
5192 if {[file exists $firefox]} {
5193 set browser $firefox
5194 } elseif {[file exists $ie]} {
5197 unset program_files firefox ie
5201 if {[file isfile $doc_path]} {
5202 set doc_url "file:$doc_path"
5204 set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
5207 if {$browser ne {}} {
5208 .mbar.help add command -label {Online Documentation} \
5209 -command [list exec $browser $doc_url &] \
5212 unset browser doc_path doc_url
5220 -text {Current Branch:} \
5225 -textvariable current_branch \
5229 pack .branch.l1 -side left
5230 pack .branch.cb -side left -fill x
5231 pack .branch -side top -fill x
5233 if {[is_enabled multicommit]} {
5235 .mbar.merge add command -label {Local Merge...} \
5236 -command do_local_merge \
5238 lappend disable_on_lock \
5239 [list .mbar.merge entryconf [.mbar.merge index last] -state]
5240 .mbar.merge add command -label {Abort Merge...} \
5241 -command do_reset_hard \
5243 lappend disable_on_lock \
5244 [list .mbar.merge entryconf [.mbar.merge index last] -state]
5250 .mbar.push add command -label {Push...} \
5251 -command do_push_anywhere \
5255 # -- Main Window Layout
5257 panedwindow .vpane -orient vertical
5258 panedwindow .vpane.files -orient horizontal
5259 .vpane add .vpane.files -sticky nsew -height 100 -width 200
5260 pack .vpane -anchor n -side top -fill both -expand 1
5262 # -- Index File List
5264 frame .vpane.files.index -height 100 -width 200
5265 label .vpane.files.index.title -text {Changes To Be Committed} \
5268 text $ui_index -background white -borderwidth 0 \
5269 -width 20 -height 10 \
5272 -cursor $cursor_ptr \
5273 -xscrollcommand {.vpane.files.index.sx set} \
5274 -yscrollcommand {.vpane.files.index.sy set} \
5276 scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview]
5277 scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview]
5278 pack .vpane.files.index.title -side top -fill x
5279 pack .vpane.files.index.sx -side bottom -fill x
5280 pack .vpane.files.index.sy -side right -fill y
5281 pack $ui_index -side left -fill both -expand 1
5282 .vpane.files add .vpane.files.index -sticky nsew
5284 # -- Working Directory File List
5286 frame .vpane.files.workdir -height 100 -width 200
5287 label .vpane.files.workdir.title -text {Changed But Not Updated} \
5290 text $ui_workdir -background white -borderwidth 0 \
5291 -width 20 -height 10 \
5294 -cursor $cursor_ptr \
5295 -xscrollcommand {.vpane.files.workdir.sx set} \
5296 -yscrollcommand {.vpane.files.workdir.sy set} \
5298 scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
5299 scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
5300 pack .vpane.files.workdir.title -side top -fill x
5301 pack .vpane.files.workdir.sx -side bottom -fill x
5302 pack .vpane.files.workdir.sy -side right -fill y
5303 pack $ui_workdir -side left -fill both -expand 1
5304 .vpane.files add .vpane.files.workdir -sticky nsew
5306 foreach i [list $ui_index $ui_workdir] {
5307 $i tag conf in_diff -font font_uibold
5308 $i tag conf in_sel \
5309 -background [$i cget -foreground] \
5310 -foreground [$i cget -background]
5314 # -- Diff and Commit Area
5316 frame .vpane.lower -height 300 -width 400
5317 frame .vpane.lower.commarea
5318 frame .vpane.lower.diff -relief sunken -borderwidth 1
5319 pack .vpane.lower.commarea -side top -fill x
5320 pack .vpane.lower.diff -side bottom -fill both -expand 1
5321 .vpane add .vpane.lower -sticky nsew
5323 # -- Commit Area Buttons
5325 frame .vpane.lower.commarea.buttons
5326 label .vpane.lower.commarea.buttons.l -text {} \
5330 pack .vpane.lower.commarea.buttons.l -side top -fill x
5331 pack .vpane.lower.commarea.buttons -side left -fill y
5333 button .vpane.lower.commarea.buttons.rescan -text {Rescan} \
5334 -command do_rescan \
5336 pack .vpane.lower.commarea.buttons.rescan -side top -fill x
5337 lappend disable_on_lock \
5338 {.vpane.lower.commarea.buttons.rescan conf -state}
5340 button .vpane.lower.commarea.buttons.incall -text {Add All} \
5341 -command do_add_all \
5343 pack .vpane.lower.commarea.buttons.incall -side top -fill x
5344 lappend disable_on_lock \
5345 {.vpane.lower.commarea.buttons.incall conf -state}
5347 button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \
5348 -command do_signoff \
5350 pack .vpane.lower.commarea.buttons.signoff -side top -fill x
5352 button .vpane.lower.commarea.buttons.commit -text {Commit} \
5353 -command do_commit \
5355 pack .vpane.lower.commarea.buttons.commit -side top -fill x
5356 lappend disable_on_lock \
5357 {.vpane.lower.commarea.buttons.commit conf -state}
5359 # -- Commit Message Buffer
5361 frame .vpane.lower.commarea.buffer
5362 frame .vpane.lower.commarea.buffer.header
5363 set ui_comm .vpane.lower.commarea.buffer.t
5364 set ui_coml .vpane.lower.commarea.buffer.header.l
5365 radiobutton .vpane.lower.commarea.buffer.header.new \
5366 -text {New Commit} \
5367 -command do_select_commit_type \
5368 -variable selected_commit_type \
5371 lappend disable_on_lock \
5372 [list .vpane.lower.commarea.buffer.header.new conf -state]
5373 radiobutton .vpane.lower.commarea.buffer.header.amend \
5374 -text {Amend Last Commit} \
5375 -command do_select_commit_type \
5376 -variable selected_commit_type \
5379 lappend disable_on_lock \
5380 [list .vpane.lower.commarea.buffer.header.amend conf -state]
5385 proc trace_commit_type {varname args} {
5386 global ui_coml commit_type
5387 switch -glob -- $commit_type {
5388 initial {set txt {Initial Commit Message:}}
5389 amend {set txt {Amended Commit Message:}}
5390 amend-initial {set txt {Amended Initial Commit Message:}}
5391 amend-merge {set txt {Amended Merge Commit Message:}}
5392 merge {set txt {Merge Commit Message:}}
5393 * {set txt {Commit Message:}}
5395 $ui_coml conf -text $txt
5397 trace add variable commit_type write trace_commit_type
5398 pack $ui_coml -side left -fill x
5399 pack .vpane.lower.commarea.buffer.header.amend -side right
5400 pack .vpane.lower.commarea.buffer.header.new -side right
5402 text $ui_comm -background white -borderwidth 1 \
5405 -autoseparators true \
5407 -width 75 -height 9 -wrap none \
5409 -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
5410 scrollbar .vpane.lower.commarea.buffer.sby \
5411 -command [list $ui_comm yview]
5412 pack .vpane.lower.commarea.buffer.header -side top -fill x
5413 pack .vpane.lower.commarea.buffer.sby -side right -fill y
5414 pack $ui_comm -side left -fill y
5415 pack .vpane.lower.commarea.buffer -side left -fill y
5417 # -- Commit Message Buffer Context Menu
5419 set ctxm .vpane.lower.commarea.buffer.ctxm
5420 menu $ctxm -tearoff 0
5424 -command {tk_textCut $ui_comm}
5428 -command {tk_textCopy $ui_comm}
5432 -command {tk_textPaste $ui_comm}
5436 -command {$ui_comm delete sel.first sel.last}
5439 -label {Select All} \
5441 -command {focus $ui_comm;$ui_comm tag add sel 0.0 end}
5446 $ui_comm tag add sel 0.0 end
5447 tk_textCopy $ui_comm
5448 $ui_comm tag remove sel 0.0 end
5455 bind_button3 $ui_comm "tk_popup
$ctxm %X
%Y
"
5459 set current_diff_path {}
5460 set current_diff_side {}
5461 set diff_actions [list]
5462 proc trace_current_diff_path {varname args} {
5463 global current_diff_path diff_actions file_states
5464 if {$current_diff_path eq {}} {
5470 set p $current_diff_path
5471 set s [mapdesc [lindex $file_states($p) 0] $p]
5473 set p [escape_path $p]
5477 .vpane.lower.diff.header.status configure -text $s
5478 .vpane.lower.diff.header.file configure -text $f
5479 .vpane.lower.diff.header.path configure -text $p
5480 foreach w $diff_actions {
5484 trace add variable current_diff_path write trace_current_diff_path
5486 frame .vpane.lower.diff.header -background orange
5487 label .vpane.lower.diff.header.status \
5488 -background orange \
5489 -width $max_status_desc \
5493 label .vpane.lower.diff.header.file \
5494 -background orange \
5498 label .vpane.lower.diff.header.path \
5499 -background orange \
5503 pack .vpane.lower.diff.header.status -side left
5504 pack .vpane.lower.diff.header.file -side left
5505 pack .vpane.lower.diff.header.path -fill x
5506 set ctxm .vpane.lower.diff.header.ctxm
5507 menu $ctxm -tearoff 0
5516 -- $current_diff_path
5518 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5519 bind_button3 .vpane.lower.diff.header.path "tk_popup
$ctxm %X
%Y
"
5523 frame .vpane.lower.diff.body
5524 set ui_diff .vpane.lower.diff.body.t
5525 text $ui_diff -background white -borderwidth 0 \
5526 -width 80 -height 15 -wrap none \
5528 -xscrollcommand {.vpane.lower.diff.body.sbx set} \
5529 -yscrollcommand {.vpane.lower.diff.body.sby set} \
5531 scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
5532 -command [list $ui_diff xview]
5533 scrollbar .vpane.lower.diff.body.sby -orient vertical \
5534 -command [list $ui_diff yview]
5535 pack .vpane.lower.diff.body.sbx -side bottom -fill x
5536 pack .vpane.lower.diff.body.sby -side right -fill y
5537 pack $ui_diff -side left -fill both -expand 1
5538 pack .vpane.lower.diff.header -side top -fill x
5539 pack .vpane.lower.diff.body -side bottom -fill both -expand 1
5541 $ui_diff tag conf d_cr -elide true
5542 $ui_diff tag conf d_@ -foreground blue -font font_diffbold
5543 $ui_diff tag conf d_+ -foreground {#00a000}
5544 $ui_diff tag conf d_- -foreground red
5546 $ui_diff tag conf d_++ -foreground {#00a000}
5547 $ui_diff tag conf d_-- -foreground red
5548 $ui_diff tag conf d_+s \
5549 -foreground {#00a000} \
5550 -background {#e2effa}
5551 $ui_diff tag conf d_-s \
5553 -background {#e2effa}
5554 $ui_diff tag conf d_s+ \
5555 -foreground {#00a000} \
5557 $ui_diff tag conf d_s- \
5561 $ui_diff tag conf d<<<<<<< \
5562 -foreground orange \
5564 $ui_diff tag conf d======= \
5565 -foreground orange \
5567 $ui_diff tag conf d>>>>>>> \
5568 -foreground orange \
5571 $ui_diff tag raise sel
5573 # -- Diff Body Context Menu
5575 set ctxm .vpane.lower.diff.body.ctxm
5576 menu $ctxm -tearoff 0
5580 -command reshow_diff
5581 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5585 -command {tk_textCopy $ui_diff}
5586 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5588 -label {Select All} \
5590 -command {focus $ui_diff;$ui_diff tag add sel 0.0 end}
5591 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5596 $ui_diff tag add sel 0.0 end
5597 tk_textCopy $ui_diff
5598 $ui_diff tag remove sel 0.0 end
5600 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5603 -label {Apply/Reverse Hunk} \
5605 -command {apply_hunk $cursorX $cursorY}
5606 set ui_diff_applyhunk [$ctxm index last]
5607 lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
5610 -label {Decrease Font Size} \
5612 -command {incr_font_size font_diff -1}
5613 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5615 -label {Increase Font Size} \
5617 -command {incr_font_size font_diff 1}
5618 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5621 -label {Show Less Context} \
5623 -command {if {$repo_config(gui.diffcontext) >= 2} {
5624 incr repo_config(gui.diffcontext) -1
5627 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5629 -label {Show More Context} \
5632 incr repo_config(gui.diffcontext)
5635 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5637 $ctxm add command -label {Options...} \
5640 bind_button3 $ui_diff "
5643 if {\
$ui_index eq \
$current_diff_side} {
5644 $ctxm entryconf
$ui_diff_applyhunk -label {Unstage Hunk From Commit
}
5646 $ctxm entryconf
$ui_diff_applyhunk -label {Stage Hunk For Commit
}
5648 tk_popup
$ctxm %X
%Y
5650 unset ui_diff_applyhunk
5654 set ui_status_value {Initializing...}
5655 label .status -textvariable ui_status_value \
5661 pack .status -anchor w -side bottom -fill x
5666 set gm $repo_config(gui.geometry)
5667 wm geometry . [lindex $gm 0]
5668 .vpane sash place 0 \
5669 [lindex [.vpane sash coord 0] 0] \
5671 .vpane.files sash place 0 \
5673 [lindex [.vpane.files sash coord 0] 1]
5679 bind $ui_comm <$M1B-Key-Return> {do_commit;break}
5680 bind $ui_comm <$M1B-Key-i> {do_add_all;break}
5681 bind $ui_comm <$M1B-Key-I> {do_add_all;break}
5682 bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
5683 bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
5684 bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
5685 bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
5686 bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
5687 bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
5688 bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
5689 bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
5691 bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
5692 bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
5693 bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
5694 bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
5695 bind $ui_diff <$M1B-Key-v> {break}
5696 bind $ui_diff <$M1B-Key-V> {break}
5697 bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
5698 bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
5699 bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
5700 bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
5701 bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
5702 bind $ui_diff <Key-Right> {catch {%W xview scroll 1 units};break}
5703 bind $ui_diff <Button-1> {focus %W}
5705 if {[is_enabled multicommit]} {
5706 bind . <$M1B-Key-n> do_create_branch
5707 bind . <$M1B-Key-N> do_create_branch
5710 bind . <Destroy> do_quit
5711 bind all <Key-F5> do_rescan
5712 bind all <$M1B-Key-r> do_rescan
5713 bind all <$M1B-Key-R> do_rescan
5714 bind . <$M1B-Key-s> do_signoff
5715 bind . <$M1B-Key-S> do_signoff
5716 bind . <$M1B-Key-i> do_add_all
5717 bind . <$M1B-Key-I> do_add_all
5718 bind . <$M1B-Key-Return> do_commit
5719 bind all <$M1B-Key-q> do_quit
5720 bind all <$M1B-Key-Q> do_quit
5721 bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
5722 bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
5723 foreach i [list $ui_index $ui_workdir] {
5724 bind $i <Button-1> "toggle_or_diff
$i %x
%y
; break"
5725 bind $i <$M1B-Button-1> "add_one_to_selection
$i %x
%y
; break"
5726 bind $i <Shift-Button-1> "add_range_to_selection
$i %x
%y
; break"
5730 set file_lists($ui_index) [list]
5731 set file_lists($ui_workdir) [list]
5735 set MERGE_HEAD [list]
5738 set current_branch {}
5739 set current_diff_path {}
5740 set selected_commit_type new
5742 wm title . "[appname
] ([file normalize
[file dirname [gitdir
]]])"
5743 focus -force $ui_comm
5745 # -- Warn the user about environmental problems. Cygwin's Tcl
5746 # does *not* pass its env array onto any processes it spawns.
5747 # This means that git processes get none of our environment.
5752 set msg "Possible environment issues exist.
5754 The following environment variables are probably
5755 going to be ignored by any Git subprocess run
5759 foreach name [array names env] {
5760 switch -regexp -- $name {
5761 {^GIT_INDEX_FILE$} -
5762 {^GIT_OBJECT_DIRECTORY$} -
5763 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} -
5765 {^GIT_EXTERNAL_DIFF$} -
5769 {^GIT_CONFIG_LOCAL$} -
5770 {^GIT_(AUTHOR|COMMITTER)_DATE$} {
5771 append msg " - $name\n"
5774 {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} {
5775 append msg " - $name\n"
5777 set suggest_user $name
5781 if {$ignored_env > 0} {
5783 This is due to a known issue with the
5784 Tcl binary distributed by Cygwin.
"
5786 if {$suggest_user ne {}} {
5789 A good replacement
for $suggest_user
5790 is placing values
for the user.name and
5791 user.email settings into your personal
5797 unset ignored_env msg suggest_user name
5800 # -- Only initialize complex UI if we are going to stay running.
5802 if {[is_enabled multicommit]} {
5806 populate_branch_menu
5811 # -- Only suggest a gc run if we are going to stay running.
5813 if {[is_enabled multicommit]} {
5814 set object_limit 2000
5815 if {[is_Windows]} {set object_limit 200}
5816 regexp {^([0-9]+) objects,} [exec git count-objects] _junk objects_current
5817 if {$objects_current >= $object_limit} {
5819 "This repository currently has
$objects_current loose objects.
5821 To maintain optimal performance it is strongly
5822 recommended that you
compress the database
5823 when
more than
$object_limit loose objects exist.
5825 Compress the database now?
"] eq yes} {
5829 unset object_limit _junk objects_current
5832 lock_index begin-read