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 ######################################################################
100 proc is_many_config
{name
} {
101 switch
-glob -- $name {
110 proc is_config_true
{name
} {
112 if {[catch
{set v
$repo_config($name)}]} {
114 } elseif
{$v eq
{true
} ||
$v eq
{1} ||
$v eq
{yes}} {
121 proc load_config
{include_global
} {
122 global repo_config global_config default_config
124 array
unset global_config
125 if {$include_global} {
127 set fd_rc
[open
"| git repo-config --global --list" r
]
128 while {[gets
$fd_rc line
] >= 0} {
129 if {[regexp
{^
([^
=]+)=(.
*)$
} $line line name value
]} {
130 if {[is_many_config
$name]} {
131 lappend global_config
($name) $value
133 set global_config
($name) $value
141 array
unset repo_config
143 set fd_rc
[open
"| git repo-config --list" r
]
144 while {[gets
$fd_rc line
] >= 0} {
145 if {[regexp
{^
([^
=]+)=(.
*)$
} $line line name value
]} {
146 if {[is_many_config
$name]} {
147 lappend repo_config
($name) $value
149 set repo_config
($name) $value
156 foreach name
[array names default_config
] {
157 if {[catch
{set v
$global_config($name)}]} {
158 set global_config
($name) $default_config($name)
160 if {[catch
{set v
$repo_config($name)}]} {
161 set repo_config
($name) $default_config($name)
166 proc save_config
{} {
167 global default_config font_descs
168 global repo_config global_config
169 global repo_config_new global_config_new
171 foreach option
$font_descs {
172 set name
[lindex
$option 0]
173 set font
[lindex
$option 1]
174 font configure
$font \
175 -family $global_config_new(gui.
$font^^family
) \
176 -size $global_config_new(gui.
$font^^size
)
177 font configure
${font}bold \
178 -family $global_config_new(gui.
$font^^family
) \
179 -size $global_config_new(gui.
$font^^size
)
180 set global_config_new
(gui.
$name) [font configure
$font]
181 unset global_config_new
(gui.
$font^^family
)
182 unset global_config_new
(gui.
$font^^size
)
185 foreach name
[array names default_config
] {
186 set value
$global_config_new($name)
187 if {$value ne
$global_config($name)} {
188 if {$value eq
$default_config($name)} {
189 catch
{exec git repo-config
--global --unset $name}
191 regsub
-all "\[{}\]" $value {"} value
192 exec git repo-config --global $name $value
194 set global_config($name) $value
195 if {$value eq $repo_config($name)} {
196 catch {exec git repo-config --unset $name}
197 set repo_config($name) $value
202 foreach name [array names default_config] {
203 set value $repo_config_new($name)
204 if {$value ne $repo_config($name)} {
205 if {$value eq $global_config($name)} {
206 catch {exec git repo-config --unset $name}
208 regsub -all "\
[{}\
]" $value {"} value
209 exec git repo-config
$name $value
211 set repo_config
($name) $value
216 proc error_popup
{msg
} {
218 if {[reponame
] ne
{}} {
219 append title
" ([reponame])"
221 set cmd
[list tk_messageBox \
224 -title "$title: error" \
226 if {[winfo ismapped .
]} {
227 lappend cmd
-parent .
232 proc warn_popup
{msg
} {
234 if {[reponame
] ne
{}} {
235 append title
" ([reponame])"
237 set cmd
[list tk_messageBox \
240 -title "$title: warning" \
242 if {[winfo ismapped .
]} {
243 lappend cmd
-parent .
248 proc info_popup
{msg
{parent .
}} {
250 if {[reponame
] ne
{}} {
251 append title
" ([reponame])"
261 proc ask_popup
{msg
} {
263 if {[reponame
] ne
{}} {
264 append title
" ([reponame])"
266 return [tk_messageBox \
274 ######################################################################
278 if { [catch
{set _gitdir
$env(GIT_DIR
)}]
279 && [catch
{set _gitdir
[exec git rev-parse
--git-dir]} err
]} {
280 catch
{wm withdraw .
}
281 error_popup
"Cannot find the git directory:\n\n$err"
284 if {![file isdirectory
$_gitdir] && [is_Cygwin
]} {
285 catch
{set _gitdir
[exec cygpath
--unix $_gitdir]}
287 if {![file isdirectory
$_gitdir]} {
288 catch
{wm withdraw .
}
289 error_popup
"Git directory not found:\n\n$_gitdir"
292 if {[lindex
[file split $_gitdir] end
] ne
{.git
}} {
293 catch
{wm withdraw .
}
294 error_popup
"Cannot use funny .git directory:\n\n$_gitdir"
297 if {[catch
{cd [file dirname $_gitdir]} err
]} {
298 catch
{wm withdraw .
}
299 error_popup
"No working directory [file dirname $_gitdir]:\n\n$err"
302 set _reponame
[lindex
[file split \
303 [file normalize
[file dirname $_gitdir]]] \
307 if {[appname
] eq
{git-citool
}} {
311 ######################################################################
319 set disable_on_lock
[list
]
320 set index_lock_type none
322 proc lock_index
{type} {
323 global index_lock_type disable_on_lock
325 if {$index_lock_type eq
{none
}} {
326 set index_lock_type
$type
327 foreach w
$disable_on_lock {
328 uplevel
#0 $w disabled
331 } elseif
{$index_lock_type eq
"begin-$type"} {
332 set index_lock_type
$type
338 proc unlock_index
{} {
339 global index_lock_type disable_on_lock
341 set index_lock_type none
342 foreach w
$disable_on_lock {
347 ######################################################################
351 proc repository_state
{ctvar hdvar mhvar
} {
352 global current_branch
353 upvar
$ctvar ct
$hdvar hd
$mhvar mh
357 if {[catch
{set current_branch
[exec git symbolic-ref HEAD
]}]} {
358 set current_branch
{}
360 regsub ^refs
/((heads|tags|remotes
)/)? \
366 if {[catch
{set hd
[exec git rev-parse
--verify HEAD
]}]} {
372 set merge_head
[gitdir MERGE_HEAD
]
373 if {[file exists
$merge_head]} {
375 set fd_mh
[open
$merge_head r
]
376 while {[gets
$fd_mh line
] >= 0} {
387 global PARENT empty_tree
389 set p
[lindex
$PARENT 0]
393 if {$empty_tree eq
{}} {
394 set empty_tree
[exec git mktree
<< {}]
399 proc rescan
{after
{honor_trustmtime
1}} {
400 global HEAD PARENT MERGE_HEAD commit_type
401 global ui_index ui_workdir ui_status_value ui_comm
402 global rescan_active file_states
403 global repo_config single_commit
405 if {$rescan_active > 0 ||
![lock_index
read]} return
407 repository_state newType newHEAD newMERGE_HEAD
408 if {[string match amend
* $commit_type]
409 && $newType eq
{normal
}
410 && $newHEAD eq
$HEAD} {
414 set MERGE_HEAD
$newMERGE_HEAD
415 set commit_type
$newType
418 array
unset file_states
420 if {![$ui_comm edit modified
]
421 ||
[string trim
[$ui_comm get
0.0 end
]] eq
{}} {
422 if {[load_message GITGUI_MSG
]} {
423 } elseif
{[load_message MERGE_MSG
]} {
424 } elseif
{[load_message SQUASH_MSG
]} {
427 $ui_comm edit modified false
430 if {!$single_commit} {
435 if {$honor_trustmtime && $repo_config(gui.trustmtime
) eq
{true
}} {
436 rescan_stage2
{} $after
439 set ui_status_value
{Refreshing
file status...
}
440 set cmd
[list git update-index
]
442 lappend cmd
--unmerged
443 lappend cmd
--ignore-missing
444 lappend cmd
--refresh
445 set fd_rf
[open
"| $cmd" r
]
446 fconfigure
$fd_rf -blocking 0 -translation binary
447 fileevent
$fd_rf readable \
448 [list rescan_stage2
$fd_rf $after]
452 proc rescan_stage2
{fd after
} {
453 global ui_status_value
454 global rescan_active buf_rdi buf_rdf buf_rlo
458 if {![eof
$fd]} return
462 set ls_others
[list | git ls-files
--others -z \
463 --exclude-per-directory=.gitignore
]
464 set info_exclude
[gitdir info exclude
]
465 if {[file readable
$info_exclude]} {
466 lappend ls_others
"--exclude-from=$info_exclude"
474 set ui_status_value
{Scanning
for modified files ...
}
475 set fd_di
[open
"| git diff-index --cached -z [PARENT]" r
]
476 set fd_df
[open
"| git diff-files -z" r
]
477 set fd_lo
[open
$ls_others r
]
479 fconfigure
$fd_di -blocking 0 -translation binary
-encoding binary
480 fconfigure
$fd_df -blocking 0 -translation binary
-encoding binary
481 fconfigure
$fd_lo -blocking 0 -translation binary
-encoding binary
482 fileevent
$fd_di readable
[list read_diff_index
$fd_di $after]
483 fileevent
$fd_df readable
[list read_diff_files
$fd_df $after]
484 fileevent
$fd_lo readable
[list read_ls_others
$fd_lo $after]
487 proc load_message
{file} {
491 if {[file isfile
$f]} {
492 if {[catch
{set fd
[open
$f r
]}]} {
495 set content
[string trim
[read $fd]]
497 regsub
-all -line {[ \r\t]+$
} $content {} content
498 $ui_comm delete
0.0 end
499 $ui_comm insert end
$content
505 proc read_diff_index
{fd after
} {
508 append buf_rdi
[read $fd]
510 set n
[string length
$buf_rdi]
512 set z1
[string first
"\0" $buf_rdi $c]
515 set z2
[string first
"\0" $buf_rdi $z1]
519 set i
[split [string range
$buf_rdi $c [expr {$z1 - 2}]] { }]
520 set p
[string range
$buf_rdi $z1 [expr {$z2 - 1}]]
522 [encoding convertfrom
$p] \
524 [list
[lindex
$i 0] [lindex
$i 2]] \
530 set buf_rdi
[string range
$buf_rdi $c end
]
535 rescan_done
$fd buf_rdi
$after
538 proc read_diff_files
{fd after
} {
541 append buf_rdf
[read $fd]
543 set n
[string length
$buf_rdf]
545 set z1
[string first
"\0" $buf_rdf $c]
548 set z2
[string first
"\0" $buf_rdf $z1]
552 set i
[split [string range
$buf_rdf $c [expr {$z1 - 2}]] { }]
553 set p
[string range
$buf_rdf $z1 [expr {$z2 - 1}]]
555 [encoding convertfrom
$p] \
558 [list
[lindex
$i 0] [lindex
$i 2]]
563 set buf_rdf
[string range
$buf_rdf $c end
]
568 rescan_done
$fd buf_rdf
$after
571 proc read_ls_others
{fd after
} {
574 append buf_rlo
[read $fd]
575 set pck
[split $buf_rlo "\0"]
576 set buf_rlo
[lindex
$pck end
]
577 foreach p
[lrange
$pck 0 end-1
] {
578 merge_state
[encoding convertfrom
$p] ?O
580 rescan_done
$fd buf_rlo
$after
583 proc rescan_done
{fd buf after
} {
585 global file_states repo_config
588 if {![eof
$fd]} return
591 if {[incr rescan_active
-1] > 0} return
600 proc prune_selection
{} {
601 global file_states selected_paths
603 foreach path
[array names selected_paths
] {
604 if {[catch
{set still_here
$file_states($path)}]} {
605 unset selected_paths
($path)
610 ######################################################################
615 global ui_diff current_diff_path current_diff_header
616 global ui_index ui_workdir
618 $ui_diff conf
-state normal
619 $ui_diff delete
0.0 end
620 $ui_diff conf
-state disabled
622 set current_diff_path
{}
623 set current_diff_header
{}
625 $ui_index tag remove in_diff
0.0 end
626 $ui_workdir tag remove in_diff
0.0 end
629 proc reshow_diff
{} {
630 global ui_status_value file_states file_lists
631 global current_diff_path current_diff_side
633 set p
$current_diff_path
635 ||
$current_diff_side eq
{}
636 ||
[catch
{set s
$file_states($p)}]
637 ||
[lsearch
-sorted -exact $file_lists($current_diff_side) $p] == -1} {
640 show_diff
$p $current_diff_side
644 proc handle_empty_diff
{} {
645 global current_diff_path file_states file_lists
647 set path
$current_diff_path
648 set s
$file_states($path)
649 if {[lindex
$s 0] ne
{_M
}} return
651 info_popup
"No differences detected.
653 [short_path $path] has no changes.
655 The modification date of this file was updated
656 by another application, but the content within
657 the file was not changed.
659 A rescan will be automatically started to find
660 other files which may have the same state."
663 display_file
$path __
664 rescan
{set ui_status_value
{Ready.
}} 0
667 proc show_diff
{path w
{lno
{}}} {
668 global file_states file_lists
669 global is_3way_diff diff_active repo_config
670 global ui_diff ui_status_value ui_index ui_workdir
671 global current_diff_path current_diff_side current_diff_header
673 if {$diff_active ||
![lock_index
read]} return
677 set lno
[lsearch
-sorted -exact $file_lists($w) $path]
683 $w tag add in_diff
$lno.0 [expr {$lno + 1}].0
686 set s
$file_states($path)
690 set current_diff_path
$path
691 set current_diff_side
$w
692 set current_diff_header
{}
693 set ui_status_value
"Loading diff of [escape_path $path]..."
695 # - Git won't give us the diff, there's nothing to compare to!
698 set max_sz
[expr {128 * 1024}]
700 set fd
[open
$path r
]
701 set content
[read $fd $max_sz]
703 set sz
[file size
$path]
707 set ui_status_value
"Unable to display [escape_path $path]"
708 error_popup
"Error loading file:\n\n$err"
711 $ui_diff conf
-state normal
712 if {![catch
{set type [exec file $path]}]} {
713 set n
[string length
$path]
714 if {[string equal
-length $n $path $type]} {
715 set type [string range
$type $n end
]
716 regsub
{^
:?\s
*} $type {} type
718 $ui_diff insert end
"* $type\n" d_@
720 if {[string first
"\0" $content] != -1} {
721 $ui_diff insert end \
722 "* Binary file (not showing content)." \
726 $ui_diff insert end \
727 "* Untracked file is $sz bytes.
728 * Showing only first $max_sz bytes.
731 $ui_diff insert end
$content
733 $ui_diff insert end
"
734 * Untracked file clipped here by [appname].
735 * To see the entire file, use an external editor.
739 $ui_diff conf
-state disabled
742 set ui_status_value
{Ready.
}
747 if {$w eq
$ui_index} {
748 lappend cmd diff-index
750 } elseif
{$w eq
$ui_workdir} {
751 if {[string index
$m 0] eq
{U
}} {
754 lappend cmd diff-files
759 lappend cmd
--no-color
760 if {$repo_config(gui.diffcontext
) > 0} {
761 lappend cmd
"-U$repo_config(gui.diffcontext)"
763 if {$w eq
$ui_index} {
769 if {[catch
{set fd
[open
$cmd r
]} err
]} {
772 set ui_status_value
"Unable to display [escape_path $path]"
773 error_popup
"Error loading diff:\n\n$err"
781 fileevent
$fd readable
[list read_diff
$fd]
784 proc read_diff
{fd
} {
785 global ui_diff ui_status_value diff_active
786 global is_3way_diff current_diff_header
788 $ui_diff conf
-state normal
789 while {[gets
$fd line
] >= 0} {
790 # -- Cleanup uninteresting diff header lines.
792 if { [string match
{diff --git *} $line]
793 ||
[string match
{diff --cc *} $line]
794 ||
[string match
{diff --combined *} $line]
795 ||
[string match
{--- *} $line]
796 ||
[string match
{+++ *} $line]} {
797 append current_diff_header
$line "\n"
800 if {[string match
{index
*} $line]} continue
801 if {$line eq
{deleted
file mode
120000}} {
802 set line
"deleted symlink"
805 # -- Automatically detect if this is a 3 way diff.
807 if {[string match
{@@@
*} $line]} {set is_3way_diff
1}
809 if {[string match
{mode
*} $line]
810 ||
[string match
{new
file *} $line]
811 ||
[string match
{deleted
file *} $line]
812 ||
[string match
{Binary files
* and
* differ
} $line]
813 ||
$line eq
{\ No newline
at end of
file}
814 ||
[regexp
{^\
* Unmerged path
} $line]} {
816 } elseif
{$is_3way_diff} {
817 set op
[string range
$line 0 1]
827 if {[regexp
{^\
+\
+([<>]{7} |
={7})} $line _g op
]} {
828 set line
[string replace
$line 0 1 { }]
835 puts
"error: Unhandled 3 way diff marker: {$op}"
840 set op
[string index
$line 0]
846 if {[regexp
{^\
+([<>]{7} |
={7})} $line _g op
]} {
847 set line
[string replace
$line 0 0 { }]
854 puts
"error: Unhandled 2 way diff marker: {$op}"
859 $ui_diff insert end
$line $tags
860 if {[string index
$line end
] eq
"\r"} {
861 $ui_diff tag add d_cr
{end
- 2c
}
863 $ui_diff insert end
"\n" $tags
865 $ui_diff conf
-state disabled
871 set ui_status_value
{Ready.
}
873 if {[$ui_diff index end
] eq
{2.0}} {
879 proc apply_hunk
{x y
} {
880 global current_diff_path current_diff_header current_diff_side
881 global ui_diff ui_index file_states
883 if {$current_diff_path eq
{} ||
$current_diff_header eq
{}} return
884 if {![lock_index apply_hunk
]} return
886 set apply_cmd
{git apply
--cached --whitespace=nowarn
}
887 set mi
[lindex
$file_states($current_diff_path) 0]
888 if {$current_diff_side eq
$ui_index} {
890 lappend apply_cmd
--reverse
891 if {[string index
$mi 0] ne
{M
}} {
897 if {[string index
$mi 1] ne
{M
}} {
903 set s_lno
[lindex
[split [$ui_diff index @
$x,$y] .
] 0]
904 set s_lno
[$ui_diff search
-backwards -regexp ^@@
$s_lno.0 0.0]
910 set e_lno
[$ui_diff search
-forwards -regexp ^@@
"$s_lno + 1 lines" end
]
916 set p
[open
"| $apply_cmd" w
]
917 fconfigure
$p -translation binary
-encoding binary
918 puts
-nonewline $p $current_diff_header
919 puts
-nonewline $p [$ui_diff get
$s_lno $e_lno]
921 error_popup
"Failed to $mode selected hunk.\n\n$err"
926 $ui_diff conf
-state normal
927 $ui_diff delete
$s_lno $e_lno
928 $ui_diff conf
-state disabled
930 if {[$ui_diff get
1.0 end
] eq
"\n"} {
936 if {$current_diff_side eq
$ui_index} {
938 } elseif
{[string index
$mi 0] eq
{_
}} {
944 display_file
$current_diff_path $mi
950 ######################################################################
954 proc load_last_commit
{} {
955 global HEAD PARENT MERGE_HEAD commit_type ui_comm
958 if {[llength
$PARENT] == 0} {
959 error_popup
{There is nothing to amend.
961 You are about to create the initial commit.
962 There is no commit before this to amend.
967 repository_state curType curHEAD curMERGE_HEAD
968 if {$curType eq
{merge
}} {
969 error_popup
{Cannot amend
while merging.
971 You are currently
in the middle of a merge that
972 has not been fully completed. You cannot amend
973 the prior commit unless you first abort the
974 current merge activity.
982 set fd
[open
"| git cat-file commit $curHEAD" r
]
983 fconfigure
$fd -encoding binary
-translation lf
984 if {[catch
{set enc
$repo_config(i18n.commitencoding
)}]} {
987 while {[gets
$fd line
] > 0} {
988 if {[string match
{parent
*} $line]} {
989 lappend parents
[string range
$line 7 end
]
990 } elseif
{[string match
{encoding
*} $line]} {
991 set enc
[string tolower
[string range
$line 9 end
]]
994 fconfigure
$fd -encoding $enc
995 set msg
[string trim
[read $fd]]
998 error_popup
"Error loading commit data for amend:\n\n$err"
1004 set MERGE_HEAD
[list
]
1005 switch
-- [llength
$parents] {
1006 0 {set commit_type amend-initial
}
1007 1 {set commit_type amend
}
1008 default
{set commit_type amend-merge
}
1011 $ui_comm delete
0.0 end
1012 $ui_comm insert end
$msg
1014 $ui_comm edit modified false
1015 rescan
{set ui_status_value
{Ready.
}}
1018 proc create_new_commit
{} {
1019 global commit_type ui_comm
1021 set commit_type normal
1022 $ui_comm delete
0.0 end
1024 $ui_comm edit modified false
1025 rescan
{set ui_status_value
{Ready.
}}
1028 set GIT_COMMITTER_IDENT
{}
1030 proc committer_ident
{} {
1031 global GIT_COMMITTER_IDENT
1033 if {$GIT_COMMITTER_IDENT eq
{}} {
1034 if {[catch
{set me
[exec git var GIT_COMMITTER_IDENT
]} err
]} {
1035 error_popup
"Unable to obtain your identity:\n\n$err"
1038 if {![regexp
{^
(.
*) [0-9]+ [-+0-9]+$
} \
1039 $me me GIT_COMMITTER_IDENT
]} {
1040 error_popup
"Invalid GIT_COMMITTER_IDENT:\n\n$me"
1045 return $GIT_COMMITTER_IDENT
1048 proc commit_tree
{} {
1049 global HEAD commit_type file_states ui_comm repo_config
1050 global ui_status_value pch_error
1052 if {[committer_ident
] eq
{}} return
1053 if {![lock_index update
]} return
1055 # -- Our in memory state should match the repository.
1057 repository_state curType curHEAD curMERGE_HEAD
1058 if {[string match amend
* $commit_type]
1059 && $curType eq
{normal
}
1060 && $curHEAD eq
$HEAD} {
1061 } elseif
{$commit_type ne
$curType ||
$HEAD ne
$curHEAD} {
1062 info_popup
{Last scanned state does not match repository state.
1064 Another Git program has modified this repository
1065 since the last scan. A rescan must be performed
1066 before another commit can be created.
1068 The rescan will be automatically started now.
1071 rescan
{set ui_status_value
{Ready.
}}
1075 # -- At least one file should differ in the index.
1078 foreach path
[array names file_states
] {
1079 switch
-glob -- [lindex
$file_states($path) 0] {
1083 M?
{set files_ready
1}
1085 error_popup
"Unmerged files cannot be committed.
1087 File [short_path $path] has merge conflicts.
1088 You must resolve them and add the file before committing.
1094 error_popup
"Unknown file state [lindex $s 0] detected.
1096 File [short_path $path] cannot be committed by this program.
1101 if {!$files_ready} {
1102 info_popup
{No changes to commit.
1104 You must add
at least
1 file before you can commit.
1110 # -- A message is required.
1112 set msg
[string trim
[$ui_comm get
1.0 end
]]
1113 regsub
-all -line {[ \t\r]+$
} $msg {} msg
1115 error_popup
{Please supply a commit message.
1117 A good commit message has the following format
:
1119 - First line
: Describe
in one sentance what you did.
1120 - Second line
: Blank
1121 - Remaining lines
: Describe why this change is good.
1127 # -- Run the pre-commit hook.
1129 set pchook
[gitdir hooks pre-commit
]
1131 # On Cygwin [file executable] might lie so we need to ask
1132 # the shell if the hook is executable. Yes that's annoying.
1134 if {[is_Cygwin
] && [file isfile
$pchook]} {
1135 set pchook
[list sh
-c [concat \
1136 "if test -x \"$pchook\";" \
1137 "then exec \"$pchook\" 2>&1;" \
1139 } elseif
{[file executable
$pchook]} {
1140 set pchook
[list
$pchook |
& cat]
1142 commit_writetree
$curHEAD $msg
1146 set ui_status_value
{Calling pre-commit hook...
}
1148 set fd_ph
[open
"| $pchook" r
]
1149 fconfigure
$fd_ph -blocking 0 -translation binary
1150 fileevent
$fd_ph readable \
1151 [list commit_prehook_wait
$fd_ph $curHEAD $msg]
1154 proc commit_prehook_wait
{fd_ph curHEAD msg
} {
1155 global pch_error ui_status_value
1157 append pch_error
[read $fd_ph]
1158 fconfigure
$fd_ph -blocking 1
1160 if {[catch
{close
$fd_ph}]} {
1161 set ui_status_value
{Commit declined by pre-commit hook.
}
1162 hook_failed_popup pre-commit
$pch_error
1165 commit_writetree
$curHEAD $msg
1170 fconfigure
$fd_ph -blocking 0
1173 proc commit_writetree
{curHEAD msg
} {
1174 global ui_status_value
1176 set ui_status_value
{Committing changes...
}
1177 set fd_wt
[open
"| git write-tree" r
]
1178 fileevent
$fd_wt readable \
1179 [list commit_committree
$fd_wt $curHEAD $msg]
1182 proc commit_committree
{fd_wt curHEAD msg
} {
1183 global HEAD PARENT MERGE_HEAD commit_type
1184 global single_commit all_heads current_branch
1185 global ui_status_value ui_comm selected_commit_type
1186 global file_states selected_paths rescan_active
1190 if {$tree_id eq
{} ||
[catch
{close
$fd_wt} err
]} {
1191 error_popup
"write-tree failed:\n\n$err"
1192 set ui_status_value
{Commit failed.
}
1197 # -- Build the message.
1199 set msg_p
[gitdir COMMIT_EDITMSG
]
1200 set msg_wt
[open
$msg_p w
]
1201 if {[catch
{set enc
$repo_config(i18n.commitencoding
)}]} {
1204 fconfigure
$msg_wt -encoding $enc -translation binary
1205 puts
-nonewline $msg_wt $msg
1208 # -- Create the commit.
1210 set cmd
[list git commit-tree
$tree_id]
1211 set parents
[concat
$PARENT $MERGE_HEAD]
1212 if {[llength
$parents] > 0} {
1213 foreach p
$parents {
1217 # git commit-tree writes to stderr during initial commit.
1218 lappend cmd
2>/dev
/null
1221 if {[catch
{set cmt_id
[eval exec $cmd]} err
]} {
1222 error_popup
"commit-tree failed:\n\n$err"
1223 set ui_status_value
{Commit failed.
}
1228 # -- Update the HEAD ref.
1231 if {$commit_type ne
{normal
}} {
1232 append reflogm
" ($commit_type)"
1234 set i
[string first
"\n" $msg]
1236 append reflogm
{: } [string range
$msg 0 [expr {$i - 1}]]
1238 append reflogm
{: } $msg
1240 set cmd
[list git update-ref
-m $reflogm HEAD
$cmt_id $curHEAD]
1241 if {[catch
{eval exec $cmd} err
]} {
1242 error_popup
"update-ref failed:\n\n$err"
1243 set ui_status_value
{Commit failed.
}
1248 # -- Make sure our current branch exists.
1250 if {$commit_type eq
{initial
}} {
1251 lappend all_heads
$current_branch
1252 set all_heads
[lsort
-unique $all_heads]
1253 populate_branch_menu
1256 # -- Cleanup after ourselves.
1258 catch
{file delete
$msg_p}
1259 catch
{file delete
[gitdir MERGE_HEAD
]}
1260 catch
{file delete
[gitdir MERGE_MSG
]}
1261 catch
{file delete
[gitdir SQUASH_MSG
]}
1262 catch
{file delete
[gitdir GITGUI_MSG
]}
1264 # -- Let rerere do its thing.
1266 if {[file isdirectory
[gitdir rr-cache
]]} {
1267 catch
{exec git rerere
}
1270 # -- Run the post-commit hook.
1272 set pchook
[gitdir hooks post-commit
]
1273 if {[is_Cygwin
] && [file isfile
$pchook]} {
1274 set pchook
[list sh
-c [concat \
1275 "if test -x \"$pchook\";" \
1276 "then exec \"$pchook\";" \
1278 } elseif
{![file executable
$pchook]} {
1281 if {$pchook ne
{}} {
1282 catch
{exec $pchook &}
1285 $ui_comm delete
0.0 end
1287 $ui_comm edit modified false
1289 if {$single_commit} do_quit
1291 # -- Update in memory status
1293 set selected_commit_type new
1294 set commit_type normal
1297 set MERGE_HEAD
[list
]
1299 foreach path
[array names file_states
] {
1300 set s
$file_states($path)
1302 switch
-glob -- $m {
1310 unset file_states
($path)
1311 catch
{unset selected_paths
($path)}
1314 set file_states
($path) [list _O
[lindex
$s 1] {} {}]
1320 set file_states
($path) [list \
1321 _
[string index
$m 1] \
1332 set ui_status_value \
1333 "Changes committed as [string range $cmt_id 0 7]."
1336 ######################################################################
1340 proc fetch_from
{remote
} {
1341 set w
[new_console \
1343 "Fetching new changes from $remote"]
1344 set cmd
[list git fetch
]
1346 console_exec
$w $cmd console_done
1349 proc push_to
{remote
} {
1350 set w
[new_console \
1352 "Pushing changes to $remote"]
1353 set cmd
[list git push
]
1356 console_exec
$w $cmd console_done
1359 ######################################################################
1363 proc mapicon
{w state path
} {
1366 if {[catch
{set r
$all_icons($state$w)}]} {
1367 puts
"error: no icon for $w state={$state} $path"
1373 proc mapdesc
{state path
} {
1376 if {[catch
{set r
$all_descs($state)}]} {
1377 puts
"error: no desc for state={$state} $path"
1383 proc escape_path
{path
} {
1384 regsub
-all "\n" $path "\\n" path
1388 proc short_path
{path
} {
1389 return [escape_path
[lindex
[file split $path] end
]]
1393 set null_sha1
[string repeat
0 40]
1395 proc merge_state
{path new_state
{head_info
{}} {index_info
{}}} {
1396 global file_states next_icon_id null_sha1
1398 set s0
[string index
$new_state 0]
1399 set s1
[string index
$new_state 1]
1401 if {[catch
{set info
$file_states($path)}]} {
1403 set icon n
[incr next_icon_id
]
1405 set state
[lindex
$info 0]
1406 set icon
[lindex
$info 1]
1407 if {$head_info eq
{}} {set head_info
[lindex
$info 2]}
1408 if {$index_info eq
{}} {set index_info
[lindex
$info 3]}
1411 if {$s0 eq
{?
}} {set s0
[string index
$state 0]} \
1412 elseif
{$s0 eq
{_
}} {set s0 _
}
1414 if {$s1 eq
{?
}} {set s1
[string index
$state 1]} \
1415 elseif
{$s1 eq
{_
}} {set s1 _
}
1417 if {$s0 eq
{A
} && $s1 eq
{_
} && $head_info eq
{}} {
1418 set head_info
[list
0 $null_sha1]
1419 } elseif
{$s0 ne
{_
} && [string index
$state 0] eq
{_
}
1420 && $head_info eq
{}} {
1421 set head_info
$index_info
1424 set file_states
($path) [list
$s0$s1 $icon \
1425 $head_info $index_info \
1430 proc display_file_helper
{w path icon_name old_m new_m
} {
1433 if {$new_m eq
{_
}} {
1434 set lno
[lsearch
-sorted -exact $file_lists($w) $path]
1436 set file_lists
($w) [lreplace
$file_lists($w) $lno $lno]
1438 $w conf
-state normal
1439 $w delete
$lno.0 [expr {$lno + 1}].0
1440 $w conf
-state disabled
1442 } elseif
{$old_m eq
{_
} && $new_m ne
{_
}} {
1443 lappend file_lists
($w) $path
1444 set file_lists
($w) [lsort
-unique $file_lists($w)]
1445 set lno
[lsearch
-sorted -exact $file_lists($w) $path]
1447 $w conf
-state normal
1448 $w image create
$lno.0 \
1449 -align center
-padx 5 -pady 1 \
1451 -image [mapicon
$w $new_m $path]
1452 $w insert
$lno.1 "[escape_path $path]\n"
1453 $w conf
-state disabled
1454 } elseif
{$old_m ne
$new_m} {
1455 $w conf
-state normal
1456 $w image conf
$icon_name -image [mapicon
$w $new_m $path]
1457 $w conf
-state disabled
1461 proc display_file
{path state
} {
1462 global file_states selected_paths
1463 global ui_index ui_workdir
1465 set old_m
[merge_state
$path $state]
1466 set s
$file_states($path)
1467 set new_m
[lindex
$s 0]
1468 set icon_name
[lindex
$s 1]
1470 set o
[string index
$old_m 0]
1471 set n
[string index
$new_m 0]
1478 display_file_helper
$ui_index $path $icon_name $o $n
1480 if {[string index
$old_m 0] eq
{U
}} {
1483 set o
[string index
$old_m 1]
1485 if {[string index
$new_m 0] eq
{U
}} {
1488 set n
[string index
$new_m 1]
1490 display_file_helper
$ui_workdir $path $icon_name $o $n
1492 if {$new_m eq
{__
}} {
1493 unset file_states
($path)
1494 catch
{unset selected_paths
($path)}
1498 proc display_all_files_helper
{w path icon_name m
} {
1501 lappend file_lists
($w) $path
1502 set lno
[expr {[lindex
[split [$w index end
] .
] 0] - 1}]
1503 $w image create end \
1504 -align center
-padx 5 -pady 1 \
1506 -image [mapicon
$w $m $path]
1507 $w insert end
"[escape_path $path]\n"
1510 proc display_all_files
{} {
1511 global ui_index ui_workdir
1512 global file_states file_lists
1515 $ui_index conf
-state normal
1516 $ui_workdir conf
-state normal
1518 $ui_index delete
0.0 end
1519 $ui_workdir delete
0.0 end
1522 set file_lists
($ui_index) [list
]
1523 set file_lists
($ui_workdir) [list
]
1525 foreach path
[lsort
[array names file_states
]] {
1526 set s
$file_states($path)
1528 set icon_name
[lindex
$s 1]
1530 set s
[string index
$m 0]
1531 if {$s ne
{U
} && $s ne
{_
}} {
1532 display_all_files_helper
$ui_index $path \
1536 if {[string index
$m 0] eq
{U
}} {
1539 set s
[string index
$m 1]
1542 display_all_files_helper
$ui_workdir $path \
1547 $ui_index conf
-state disabled
1548 $ui_workdir conf
-state disabled
1551 proc update_indexinfo
{msg pathList after
} {
1552 global update_index_cp ui_status_value
1554 if {![lock_index update
]} return
1556 set update_index_cp
0
1557 set pathList
[lsort
$pathList]
1558 set totalCnt
[llength
$pathList]
1559 set batch [expr {int
($totalCnt * .01) + 1}]
1560 if {$batch > 25} {set batch 25}
1562 set ui_status_value
[format \
1563 "$msg... %i/%i files (%.2f%%)" \
1567 set fd
[open
"| git update-index -z --index-info" w
]
1574 fileevent
$fd writable
[list \
1575 write_update_indexinfo \
1585 proc write_update_indexinfo
{fd pathList totalCnt
batch msg after
} {
1586 global update_index_cp ui_status_value
1587 global file_states current_diff_path
1589 if {$update_index_cp >= $totalCnt} {
1596 for {set i
$batch} \
1597 {$update_index_cp < $totalCnt && $i > 0} \
1599 set path
[lindex
$pathList $update_index_cp]
1600 incr update_index_cp
1602 set s
$file_states($path)
1603 switch
-glob -- [lindex
$s 0] {
1610 set info
[lindex
$s 2]
1611 if {$info eq
{}} continue
1613 puts
-nonewline $fd "$info\t[encoding convertto $path]\0"
1614 display_file
$path $new
1617 set ui_status_value
[format \
1618 "$msg... %i/%i files (%.2f%%)" \
1621 [expr {100.0 * $update_index_cp / $totalCnt}]]
1624 proc update_index
{msg pathList after
} {
1625 global update_index_cp ui_status_value
1627 if {![lock_index update
]} return
1629 set update_index_cp
0
1630 set pathList
[lsort
$pathList]
1631 set totalCnt
[llength
$pathList]
1632 set batch [expr {int
($totalCnt * .01) + 1}]
1633 if {$batch > 25} {set batch 25}
1635 set ui_status_value
[format \
1636 "$msg... %i/%i files (%.2f%%)" \
1640 set fd
[open
"| git update-index --add --remove -z --stdin" w
]
1647 fileevent
$fd writable
[list \
1648 write_update_index \
1658 proc write_update_index
{fd pathList totalCnt
batch msg after
} {
1659 global update_index_cp ui_status_value
1660 global file_states current_diff_path
1662 if {$update_index_cp >= $totalCnt} {
1669 for {set i
$batch} \
1670 {$update_index_cp < $totalCnt && $i > 0} \
1672 set path
[lindex
$pathList $update_index_cp]
1673 incr update_index_cp
1675 switch
-glob -- [lindex
$file_states($path) 0] {
1681 if {[file exists
$path]} {
1690 puts
-nonewline $fd "[encoding convertto $path]\0"
1691 display_file
$path $new
1694 set ui_status_value
[format \
1695 "$msg... %i/%i files (%.2f%%)" \
1698 [expr {100.0 * $update_index_cp / $totalCnt}]]
1701 proc checkout_index
{msg pathList after
} {
1702 global update_index_cp ui_status_value
1704 if {![lock_index update
]} return
1706 set update_index_cp
0
1707 set pathList
[lsort
$pathList]
1708 set totalCnt
[llength
$pathList]
1709 set batch [expr {int
($totalCnt * .01) + 1}]
1710 if {$batch > 25} {set batch 25}
1712 set ui_status_value
[format \
1713 "$msg... %i/%i files (%.2f%%)" \
1717 set cmd
[list git checkout-index
]
1723 set fd
[open
"| $cmd " w
]
1730 fileevent
$fd writable
[list \
1731 write_checkout_index \
1741 proc write_checkout_index
{fd pathList totalCnt
batch msg after
} {
1742 global update_index_cp ui_status_value
1743 global file_states current_diff_path
1745 if {$update_index_cp >= $totalCnt} {
1752 for {set i
$batch} \
1753 {$update_index_cp < $totalCnt && $i > 0} \
1755 set path
[lindex
$pathList $update_index_cp]
1756 incr update_index_cp
1757 switch
-glob -- [lindex
$file_states($path) 0] {
1761 puts
-nonewline $fd "[encoding convertto $path]\0"
1762 display_file
$path ?_
1767 set ui_status_value
[format \
1768 "$msg... %i/%i files (%.2f%%)" \
1771 [expr {100.0 * $update_index_cp / $totalCnt}]]
1774 ######################################################################
1776 ## branch management
1778 proc is_tracking_branch
{name
} {
1779 global tracking_branches
1781 if {![catch
{set info
$tracking_branches($name)}]} {
1784 foreach t
[array names tracking_branches
] {
1785 if {[string match
{*/\
*} $t] && [string match
$t $name]} {
1792 proc load_all_heads
{} {
1795 set all_heads
[list
]
1796 set fd
[open
"| git for-each-ref --format=%(refname) refs/heads" r
]
1797 while {[gets
$fd line
] > 0} {
1798 if {[is_tracking_branch
$line]} continue
1799 if {![regsub ^refs
/heads
/ $line {} name
]} continue
1800 lappend all_heads
$name
1804 set all_heads
[lsort
$all_heads]
1807 proc populate_branch_menu
{} {
1808 global all_heads disable_on_lock
1811 set last
[$m index last
]
1812 for {set i
0} {$i <= $last} {incr i
} {
1813 if {[$m type $i] eq
{separator
}} {
1816 foreach a
$disable_on_lock {
1817 if {[lindex
$a 0] ne
$m ||
[lindex
$a 2] < $i} {
1821 set disable_on_lock
$new_dol
1826 if {$all_heads ne
{}} {
1829 foreach b
$all_heads {
1830 $m add radiobutton \
1832 -command [list switch_branch
$b] \
1833 -variable current_branch \
1836 lappend disable_on_lock \
1837 [list
$m entryconf
[$m index last
] -state]
1841 proc all_tracking_branches
{} {
1842 global tracking_branches
1844 set all_trackings
{}
1846 foreach name
[array names tracking_branches
] {
1847 if {[regsub
{/\
*$
} $name {} name
]} {
1850 regsub ^refs
/(heads|remotes
)/ $name {} name
1851 lappend all_trackings
$name
1856 set fd
[open
"| git for-each-ref --format=%(refname) $cmd" r
]
1857 while {[gets
$fd name
] > 0} {
1858 regsub ^refs
/(heads|remotes
)/ $name {} name
1859 lappend all_trackings
$name
1864 return [lsort
-unique $all_trackings]
1867 proc do_create_branch_action
{w
} {
1868 global all_heads null_sha1 repo_config
1869 global create_branch_checkout create_branch_revtype
1870 global create_branch_head create_branch_trackinghead
1871 global create_branch_name create_branch_revexp
1873 set newbranch
$create_branch_name
1874 if {$newbranch eq
{}
1875 ||
$newbranch eq
$repo_config(gui.newbranchtemplate
)} {
1879 -title [wm title
$w] \
1881 -message "Please supply a branch name."
1882 focus
$w.desc.name_t
1885 if {![catch
{exec git show-ref
--verify -- "refs/heads/$newbranch"}]} {
1889 -title [wm title
$w] \
1891 -message "Branch '$newbranch' already exists."
1892 focus
$w.desc.name_t
1895 if {[catch
{exec git check-ref-format
"heads/$newbranch"}]} {
1899 -title [wm title
$w] \
1901 -message "We do not like '$newbranch' as a branch name."
1902 focus
$w.desc.name_t
1907 switch
-- $create_branch_revtype {
1908 head {set rev $create_branch_head}
1909 tracking
{set rev $create_branch_trackinghead}
1910 expression
{set rev $create_branch_revexp}
1912 if {[catch
{set cmt
[exec git rev-parse
--verify "${rev}^0"]}]} {
1916 -title [wm title
$w] \
1918 -message "Invalid starting revision: $rev"
1921 set cmd
[list git update-ref
]
1923 lappend cmd
"branch: Created from $rev"
1924 lappend cmd
"refs/heads/$newbranch"
1926 lappend cmd
$null_sha1
1927 if {[catch
{eval exec $cmd} err
]} {
1931 -title [wm title
$w] \
1933 -message "Failed to create '$newbranch'.\n\n$err"
1937 lappend all_heads
$newbranch
1938 set all_heads
[lsort
$all_heads]
1939 populate_branch_menu
1941 if {$create_branch_checkout} {
1942 switch_branch
$newbranch
1946 proc radio_selector
{varname value args
} {
1947 upvar
#0 $varname var
1951 trace add variable create_branch_head
write \
1952 [list radio_selector create_branch_revtype
head]
1953 trace add variable create_branch_trackinghead
write \
1954 [list radio_selector create_branch_revtype tracking
]
1956 trace add variable delete_branch_head
write \
1957 [list radio_selector delete_branch_checktype
head]
1958 trace add variable delete_branch_trackinghead
write \
1959 [list radio_selector delete_branch_checktype tracking
]
1961 proc do_create_branch
{} {
1962 global all_heads current_branch repo_config
1963 global create_branch_checkout create_branch_revtype
1964 global create_branch_head create_branch_trackinghead
1965 global create_branch_name create_branch_revexp
1967 set w .branch_editor
1969 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
1971 label
$w.header
-text {Create New Branch
} \
1973 pack
$w.header
-side top
-fill x
1976 button
$w.buttons.create
-text Create \
1979 -command [list do_create_branch_action
$w]
1980 pack
$w.buttons.create
-side right
1981 button
$w.buttons.cancel
-text {Cancel
} \
1983 -command [list destroy
$w]
1984 pack
$w.buttons.cancel
-side right
-padx 5
1985 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
1987 labelframe
$w.desc \
1988 -text {Branch Description
} \
1990 label
$w.desc.name_l
-text {Name
:} -font font_ui
1991 entry
$w.desc.name_t \
1995 -textvariable create_branch_name \
1999 if {%d
== 1 && [regexp
{[~^
:?
*\
[\
0- ]} %S
]} {return 0}
2002 grid
$w.desc.name_l
$w.desc.name_t
-sticky we
-padx {0 5}
2003 grid columnconfigure
$w.desc
1 -weight 1
2004 pack
$w.desc
-anchor nw
-fill x
-pady 5 -padx 5
2006 labelframe
$w.from \
2007 -text {Starting Revision
} \
2009 radiobutton
$w.from.head_r \
2010 -text {Local Branch
:} \
2012 -variable create_branch_revtype \
2014 eval tk_optionMenu
$w.from.head_m create_branch_head
$all_heads
2015 grid
$w.from.head_r
$w.from.head_m
-sticky w
2016 set all_trackings
[all_tracking_branches
]
2017 if {$all_trackings ne
{}} {
2018 set create_branch_trackinghead
[lindex
$all_trackings 0]
2019 radiobutton
$w.from.tracking_r \
2020 -text {Tracking Branch
:} \
2022 -variable create_branch_revtype \
2024 eval tk_optionMenu
$w.from.tracking_m \
2025 create_branch_trackinghead \
2027 grid
$w.from.tracking_r
$w.from.tracking_m
-sticky w
2029 radiobutton
$w.from.exp_r \
2030 -text {Revision Expression
:} \
2032 -variable create_branch_revtype \
2034 entry
$w.from.exp_t \
2038 -textvariable create_branch_revexp \
2042 if {%d
== 1 && [regexp
{\s
} %S
]} {return 0}
2043 if {%d
== 1 && [string length
%S
] > 0} {
2044 set create_branch_revtype expression
2048 grid
$w.from.exp_r
$w.from.exp_t
-sticky we
-padx {0 5}
2049 grid columnconfigure
$w.from
1 -weight 1
2050 pack
$w.from
-anchor nw
-fill x
-pady 5 -padx 5
2052 labelframe
$w.postActions \
2053 -text {Post Creation Actions
} \
2055 checkbutton
$w.postActions.checkout \
2056 -text {Checkout after creation
} \
2057 -variable create_branch_checkout \
2059 pack
$w.postActions.checkout
-anchor nw
2060 pack
$w.postActions
-anchor nw
-fill x
-pady 5 -padx 5
2062 set create_branch_checkout
1
2063 set create_branch_head
$current_branch
2064 set create_branch_revtype
head
2065 set create_branch_name
$repo_config(gui.newbranchtemplate
)
2066 set create_branch_revexp
{}
2068 bind $w <Visibility
> "
2070 $w.desc.name_t icursor end
2071 focus $w.desc.name_t
2073 bind $w <Key-Escape
> "destroy $w"
2074 bind $w <Key-Return
> "do_create_branch_action $w;break"
2075 wm title
$w "[appname] ([reponame]): Create Branch"
2079 proc do_delete_branch_action
{w
} {
2081 global delete_branch_checktype delete_branch_head delete_branch_trackinghead
2084 switch
-- $delete_branch_checktype {
2085 head {set check_rev
$delete_branch_head}
2086 tracking
{set check_rev
$delete_branch_trackinghead}
2087 always
{set check_rev
{:none
}}
2089 if {$check_rev eq
{:none
}} {
2091 } elseif
{[catch
{set check_cmt
[exec git rev-parse
--verify "${check_rev}^0"]}]} {
2095 -title [wm title
$w] \
2097 -message "Invalid check revision: $check_rev"
2101 set to_delete
[list
]
2102 set not_merged
[list
]
2103 foreach i
[$w.list.l curselection
] {
2104 set b
[$w.list.l get
$i]
2105 if {[catch
{set o
[exec git rev-parse
--verify $b]}]} continue
2106 if {$check_cmt ne
{}} {
2107 if {$b eq
$check_rev} continue
2108 if {[catch
{set m
[exec git merge-base
$o $check_cmt]}]} continue
2110 lappend not_merged
$b
2114 lappend to_delete
[list
$b $o]
2116 if {$not_merged ne
{}} {
2117 set msg
"The following branches are not completely merged into $check_rev:
2119 - [join $not_merged "\n - "]"
2123 -title [wm title
$w] \
2127 if {$to_delete eq
{}} return
2128 if {$delete_branch_checktype eq
{always
}} {
2129 set msg
{Recovering deleted branches is difficult.
2131 Delete the selected branches?
}
2132 if {[tk_messageBox \
2135 -title [wm title
$w] \
2137 -message $msg] ne
yes} {
2143 foreach i
$to_delete {
2146 if {[catch
{exec git update-ref
-d "refs/heads/$b" $o} err
]} {
2147 append failed
" - $b: $err\n"
2149 set x
[lsearch
-sorted -exact $all_heads $b]
2151 set all_heads
[lreplace
$all_heads $x $x]
2156 if {$failed ne
{}} {
2160 -title [wm title
$w] \
2162 -message "Failed to delete branches:\n$failed"
2165 set all_heads
[lsort
$all_heads]
2166 populate_branch_menu
2170 proc do_delete_branch
{} {
2171 global all_heads tracking_branches current_branch
2172 global delete_branch_checktype delete_branch_head delete_branch_trackinghead
2174 set w .branch_editor
2176 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
2178 label
$w.header
-text {Delete Local Branch
} \
2180 pack
$w.header
-side top
-fill x
2183 button
$w.buttons.create
-text Delete \
2185 -command [list do_delete_branch_action
$w]
2186 pack
$w.buttons.create
-side right
2187 button
$w.buttons.cancel
-text {Cancel
} \
2189 -command [list destroy
$w]
2190 pack
$w.buttons.cancel
-side right
-padx 5
2191 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
2193 labelframe
$w.list \
2194 -text {Local Branches
} \
2199 -selectmode extended \
2200 -yscrollcommand [list
$w.list.sby
set] \
2202 foreach h
$all_heads {
2203 if {$h ne
$current_branch} {
2204 $w.list.l insert end
$h
2207 scrollbar
$w.list.sby
-command [list
$w.list.l yview
]
2208 pack
$w.list.sby
-side right
-fill y
2209 pack
$w.list.l
-side left
-fill both
-expand 1
2210 pack
$w.list
-fill both
-expand 1 -pady 5 -padx 5
2212 labelframe
$w.validate \
2213 -text {Delete Only If
} \
2215 radiobutton
$w.validate.head_r \
2216 -text {Merged Into Local Branch
:} \
2218 -variable delete_branch_checktype \
2220 eval tk_optionMenu
$w.validate.head_m delete_branch_head
$all_heads
2221 grid
$w.validate.head_r
$w.validate.head_m
-sticky w
2222 set all_trackings
[all_tracking_branches
]
2223 if {$all_trackings ne
{}} {
2224 set delete_branch_trackinghead
[lindex
$all_trackings 0]
2225 radiobutton
$w.validate.tracking_r \
2226 -text {Merged Into Tracking Branch
:} \
2228 -variable delete_branch_checktype \
2230 eval tk_optionMenu
$w.validate.tracking_m \
2231 delete_branch_trackinghead \
2233 grid
$w.validate.tracking_r
$w.validate.tracking_m
-sticky w
2235 radiobutton
$w.validate.always_r \
2236 -text {Always
(Do not perform merge checks
)} \
2238 -variable delete_branch_checktype \
2240 grid
$w.validate.always_r
-columnspan 2 -sticky w
2241 grid columnconfigure
$w.validate
1 -weight 1
2242 pack
$w.validate
-anchor nw
-fill x
-pady 5 -padx 5
2244 set delete_branch_head
$current_branch
2245 set delete_branch_checktype
head
2247 bind $w <Visibility
> "grab $w; focus $w"
2248 bind $w <Key-Escape
> "destroy $w"
2249 wm title
$w "[appname] ([reponame]): Delete Branch"
2253 proc switch_branch
{new_branch
} {
2254 global HEAD commit_type current_branch repo_config
2256 if {![lock_index switch
]} return
2258 # -- Our in memory state should match the repository.
2260 repository_state curType curHEAD curMERGE_HEAD
2261 if {[string match amend
* $commit_type]
2262 && $curType eq
{normal
}
2263 && $curHEAD eq
$HEAD} {
2264 } elseif
{$commit_type ne
$curType ||
$HEAD ne
$curHEAD} {
2265 info_popup
{Last scanned state does not match repository state.
2267 Another Git program has modified this repository
2268 since the last scan. A rescan must be performed
2269 before the current branch can be changed.
2271 The rescan will be automatically started now.
2274 rescan
{set ui_status_value
{Ready.
}}
2278 # -- Don't do a pointless switch.
2280 if {$current_branch eq
$new_branch} {
2285 if {$repo_config(gui.trustmtime
) eq
{true
}} {
2286 switch_branch_stage2
{} $new_branch
2288 set ui_status_value
{Refreshing
file status...
}
2289 set cmd
[list git update-index
]
2291 lappend cmd
--unmerged
2292 lappend cmd
--ignore-missing
2293 lappend cmd
--refresh
2294 set fd_rf
[open
"| $cmd" r
]
2295 fconfigure
$fd_rf -blocking 0 -translation binary
2296 fileevent
$fd_rf readable \
2297 [list switch_branch_stage2
$fd_rf $new_branch]
2301 proc switch_branch_stage2
{fd_rf new_branch
} {
2302 global ui_status_value HEAD
2306 if {![eof
$fd_rf]} return
2310 set ui_status_value
"Updating working directory to '$new_branch'..."
2311 set cmd
[list git read-tree
]
2314 lappend cmd
--exclude-per-directory=.gitignore
2316 lappend cmd
$new_branch
2317 set fd_rt
[open
"| $cmd" r
]
2318 fconfigure
$fd_rt -blocking 0 -translation binary
2319 fileevent
$fd_rt readable \
2320 [list switch_branch_readtree_wait
$fd_rt $new_branch]
2323 proc switch_branch_readtree_wait
{fd_rt new_branch
} {
2324 global selected_commit_type commit_type HEAD MERGE_HEAD PARENT
2325 global current_branch
2326 global ui_comm ui_status_value
2328 # -- We never get interesting output on stdout; only stderr.
2331 fconfigure
$fd_rt -blocking 1
2332 if {![eof
$fd_rt]} {
2333 fconfigure
$fd_rt -blocking 0
2337 # -- The working directory wasn't in sync with the index and
2338 # we'd have to overwrite something to make the switch. A
2339 # merge is required.
2341 if {[catch
{close
$fd_rt} err
]} {
2342 regsub
{^fatal
: } $err {} err
2343 warn_popup
"File level merge required.
2347 Staying on branch '$current_branch'."
2348 set ui_status_value
"Aborted checkout of '$new_branch' (file level merging is required)."
2353 # -- Update the symbolic ref. Core git doesn't even check for failure
2354 # here, it Just Works(tm). If it doesn't we are in some really ugly
2355 # state that is difficult to recover from within git-gui.
2357 if {[catch
{exec git symbolic-ref HEAD
"refs/heads/$new_branch"} err
]} {
2358 error_popup
"Failed to set current branch.
2360 This working directory is only partially switched.
2361 We successfully updated your files, but failed to
2362 update an internal Git file.
2364 This should not have occurred. [appname] will now
2372 # -- Update our repository state. If we were previously in amend mode
2373 # we need to toss the current buffer and do a full rescan to update
2374 # our file lists. If we weren't in amend mode our file lists are
2375 # accurate and we can avoid the rescan.
2378 set selected_commit_type new
2379 if {[string match amend
* $commit_type]} {
2380 $ui_comm delete
0.0 end
2382 $ui_comm edit modified false
2383 rescan
{set ui_status_value
"Checked out branch '$current_branch'."}
2385 repository_state commit_type HEAD MERGE_HEAD
2387 set ui_status_value
"Checked out branch '$current_branch'."
2391 ######################################################################
2393 ## remote management
2395 proc load_all_remotes
{} {
2397 global all_remotes tracking_branches
2399 set all_remotes
[list
]
2400 array
unset tracking_branches
2402 set rm_dir
[gitdir remotes
]
2403 if {[file isdirectory
$rm_dir]} {
2404 set all_remotes
[glob \
2408 -directory $rm_dir *]
2410 foreach name
$all_remotes {
2412 set fd
[open
[file join $rm_dir $name] r
]
2413 while {[gets
$fd line
] >= 0} {
2414 if {![regexp
{^Pull
:[ ]*([^
:]+):(.
+)$
} \
2415 $line line src dst
]} continue
2416 if {![regexp ^refs
/ $dst]} {
2417 set dst
"refs/heads/$dst"
2419 set tracking_branches
($dst) [list
$name $src]
2426 foreach line
[array names repo_config remote.
*.url
] {
2427 if {![regexp ^remote\.
(.
*)\.url\$
$line line name
]} continue
2428 lappend all_remotes
$name
2430 if {[catch
{set fl
$repo_config(remote.
$name.fetch
)}]} {
2434 if {![regexp
{^
([^
:]+):(.
+)$
} $line line src dst
]} continue
2435 if {![regexp ^refs
/ $dst]} {
2436 set dst
"refs/heads/$dst"
2438 set tracking_branches
($dst) [list
$name $src]
2442 set all_remotes
[lsort
-unique $all_remotes]
2445 proc populate_fetch_menu
{} {
2446 global all_remotes repo_config
2449 foreach r
$all_remotes {
2451 if {![catch
{set a
$repo_config(remote.
$r.url
)}]} {
2452 if {![catch
{set a
$repo_config(remote.
$r.fetch
)}]} {
2457 set fd
[open
[gitdir remotes
$r] r
]
2458 while {[gets
$fd n
] >= 0} {
2459 if {[regexp
{^Pull
:[ \t]*([^
:]+):} $n]} {
2470 -label "Fetch from $r..." \
2471 -command [list fetch_from
$r] \
2477 proc populate_push_menu
{} {
2478 global all_remotes repo_config
2482 foreach r
$all_remotes {
2484 if {![catch
{set a
$repo_config(remote.
$r.url
)}]} {
2485 if {![catch
{set a
$repo_config(remote.
$r.push
)}]} {
2490 set fd
[open
[gitdir remotes
$r] r
]
2491 while {[gets
$fd n
] >= 0} {
2492 if {[regexp
{^Push
:[ \t]*([^
:]+):} $n]} {
2506 -label "Push to $r..." \
2507 -command [list push_to
$r] \
2514 proc start_push_anywhere_action
{w
} {
2515 global push_urltype push_remote push_url push_thin push_tags
2518 switch
-- $push_urltype {
2519 remote
{set r_url
$push_remote}
2520 url
{set r_url
$push_url}
2522 if {$r_url eq
{}} return
2524 set cmd
[list git push
]
2534 foreach i
[$w.
source.l curselection
] {
2535 set b
[$w.
source.l get
$i]
2536 lappend cmd
"refs/heads/$b:refs/heads/$b"
2541 } elseif
{$cnt == 1} {
2547 set cons
[new_console
"push $r_url" "Pushing $cnt $unit to $r_url"]
2548 console_exec
$cons $cmd console_done
2552 trace add variable push_remote
write \
2553 [list radio_selector push_urltype remote
]
2555 proc do_push_anywhere
{} {
2556 global all_heads all_remotes current_branch
2557 global push_urltype push_remote push_url push_thin push_tags
2561 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
2563 label
$w.header
-text {Push Branches
} -font font_uibold
2564 pack
$w.header
-side top
-fill x
2567 button
$w.buttons.create
-text Push \
2569 -command [list start_push_anywhere_action
$w]
2570 pack
$w.buttons.create
-side right
2571 button
$w.buttons.cancel
-text {Cancel
} \
2573 -command [list destroy
$w]
2574 pack
$w.buttons.cancel
-side right
-padx 5
2575 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
2577 labelframe
$w.
source \
2578 -text {Source Branches
} \
2580 listbox
$w.
source.l \
2583 -selectmode extended \
2584 -yscrollcommand [list
$w.
source.sby
set] \
2586 foreach h
$all_heads {
2587 $w.
source.l insert end
$h
2588 if {$h eq
$current_branch} {
2589 $w.
source.l
select set end
2592 scrollbar
$w.
source.sby
-command [list
$w.
source.l yview
]
2593 pack
$w.
source.sby
-side right
-fill y
2594 pack
$w.
source.l
-side left
-fill both
-expand 1
2595 pack
$w.
source -fill both
-expand 1 -pady 5 -padx 5
2597 labelframe
$w.dest \
2598 -text {Destination Repository
} \
2600 if {$all_remotes ne
{}} {
2601 radiobutton
$w.dest.remote_r \
2604 -variable push_urltype \
2606 eval tk_optionMenu
$w.dest.remote_m push_remote
$all_remotes
2607 grid
$w.dest.remote_r
$w.dest.remote_m
-sticky w
2608 if {[lsearch
-sorted -exact $all_remotes origin
] != -1} {
2609 set push_remote origin
2611 set push_remote
[lindex
$all_remotes 0]
2613 set push_urltype remote
2615 set push_urltype url
2617 radiobutton
$w.dest.url_r \
2618 -text {Arbitrary URL
:} \
2620 -variable push_urltype \
2622 entry
$w.dest.url_t \
2626 -textvariable push_url \
2630 if {%d
== 1 && [regexp
{\s
} %S
]} {return 0}
2631 if {%d
== 1 && [string length
%S
] > 0} {
2632 set push_urltype url
2636 grid
$w.dest.url_r
$w.dest.url_t
-sticky we
-padx {0 5}
2637 grid columnconfigure
$w.dest
1 -weight 1
2638 pack
$w.dest
-anchor nw
-fill x
-pady 5 -padx 5
2640 labelframe
$w.options \
2641 -text {Transfer Options
} \
2643 checkbutton
$w.options.thin \
2644 -text {Use thin pack
(for slow network connections
)} \
2645 -variable push_thin \
2647 grid
$w.options.thin
-columnspan 2 -sticky w
2648 checkbutton
$w.options.tags \
2649 -text {Include tags
} \
2650 -variable push_tags \
2652 grid
$w.options.tags
-columnspan 2 -sticky w
2653 grid columnconfigure
$w.options
1 -weight 1
2654 pack
$w.options
-anchor nw
-fill x
-pady 5 -padx 5
2660 bind $w <Visibility
> "grab $w"
2661 bind $w <Key-Escape
> "destroy $w"
2662 wm title
$w "[appname] ([reponame]): Push"
2666 ######################################################################
2671 global HEAD commit_type file_states
2673 if {[string match amend
* $commit_type]} {
2674 info_popup
{Cannot merge
while amending.
2676 You must finish amending this commit before
2677 starting any
type of merge.
2682 if {[committer_ident
] eq
{}} {return 0}
2683 if {![lock_index merge
]} {return 0}
2685 # -- Our in memory state should match the repository.
2687 repository_state curType curHEAD curMERGE_HEAD
2688 if {$commit_type ne
$curType ||
$HEAD ne
$curHEAD} {
2689 info_popup
{Last scanned state does not match repository state.
2691 Another Git program has modified this repository
2692 since the last scan. A rescan must be performed
2693 before a merge can be performed.
2695 The rescan will be automatically started now.
2698 rescan
{set ui_status_value
{Ready.
}}
2702 foreach path
[array names file_states
] {
2703 switch
-glob -- [lindex
$file_states($path) 0] {
2705 continue; # and pray it works!
2708 error_popup
"You are in the middle of a conflicted merge.
2710 File [short_path $path] has merge conflicts.
2712 You must resolve them, add the file, and commit to
2713 complete the current merge. Only then can you
2714 begin another merge.
2720 error_popup
"You are in the middle of a change.
2722 File [short_path $path] is modified.
2724 You should complete the current commit before
2725 starting a merge. Doing so will help you abort
2726 a failed merge, should the need arise.
2737 proc visualize_local_merge
{w
} {
2739 foreach i
[$w.
source.l curselection
] {
2740 lappend revs
[$w.
source.l get
$i]
2742 if {$revs eq
{}} return
2743 lappend revs
--not HEAD
2747 proc start_local_merge_action
{w
} {
2748 global HEAD ui_status_value current_branch
2750 set cmd
[list git merge
]
2753 foreach i
[$w.
source.l curselection
] {
2754 set b
[$w.
source.l get
$i]
2762 } elseif
{$revcnt == 1} {
2764 } elseif
{$revcnt <= 15} {
2770 -title [wm title
$w] \
2772 -message "Too many branches selected.
2774 You have requested to merge $revcnt branches
2775 in an octopus merge. This exceeds Git's
2776 internal limit of 15 branches per merge.
2778 Please select fewer branches. To merge more
2779 than 15 branches, merge the branches in batches.
2784 set msg
"Merging $current_branch, [join $names {, }]"
2785 set ui_status_value
"$msg..."
2786 set cons
[new_console
"Merge" $msg]
2787 console_exec
$cons $cmd [list finish_merge
$revcnt]
2788 bind $w <Destroy
> {}
2792 proc finish_merge
{revcnt w ok
} {
2795 set msg
{Merge completed successfully.
}
2798 info_popup
"Octopus merge failed.
2800 Your merge of $revcnt branches has failed.
2802 There are file-level conflicts between the
2803 branches which must be resolved manually.
2805 The working directory will now be reset.
2807 You can attempt this merge again
2808 by merging only one branch at a time." $w
2810 set fd
[open
"| git read-tree --reset -u HEAD" r
]
2811 fconfigure
$fd -blocking 0 -translation binary
2812 fileevent
$fd readable
[list reset_hard_wait
$fd]
2813 set ui_status_value
{Aborting... please
wait...
}
2817 set msg
{Merge failed. Conflict resolution is required.
}
2820 rescan
[list
set ui_status_value
$msg]
2823 proc do_local_merge
{} {
2824 global current_branch
2826 if {![can_merge
]} return
2830 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
2833 -text "Merge Into $current_branch" \
2835 pack
$w.header
-side top
-fill x
2838 button
$w.buttons.visualize
-text Visualize \
2840 -command [list visualize_local_merge
$w]
2841 pack
$w.buttons.visualize
-side left
2842 button
$w.buttons.create
-text Merge \
2844 -command [list start_local_merge_action
$w]
2845 pack
$w.buttons.create
-side right
2846 button
$w.buttons.cancel
-text {Cancel
} \
2848 -command [list destroy
$w]
2849 pack
$w.buttons.cancel
-side right
-padx 5
2850 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
2852 labelframe
$w.
source \
2853 -text {Source Branches
} \
2855 listbox
$w.
source.l \
2858 -selectmode extended \
2859 -yscrollcommand [list
$w.
source.sby
set] \
2861 scrollbar
$w.
source.sby
-command [list
$w.
source.l yview
]
2862 pack
$w.
source.sby
-side right
-fill y
2863 pack
$w.
source.l
-side left
-fill both
-expand 1
2864 pack
$w.
source -fill both
-expand 1 -pady 5 -padx 5
2866 set cmd
[list git for-each-ref
]
2867 lappend cmd
{--format=%(objectname
) %(refname
)}
2868 lappend cmd refs
/heads
2869 lappend cmd refs
/remotes
2870 set fr_fd
[open
"| $cmd" r
]
2871 fconfigure
$fr_fd -translation binary
2872 while {[gets
$fr_fd line
] > 0} {
2873 set line
[split $line { }]
2874 set sha1
([lindex
$line 0]) [lindex
$line 1]
2879 set fr_fd
[open
"| git rev-list --all --not HEAD"]
2880 while {[gets
$fr_fd line
] > 0} {
2881 if {[catch
{set ref
$sha1($line)}]} continue
2882 regsub ^refs
/(heads|remotes
)/ $ref {} ref
2883 lappend to_show
$ref
2887 foreach ref
[lsort
-unique $to_show] {
2888 $w.
source.l insert end
$ref
2891 bind $w <Visibility
> "grab $w"
2892 bind $w <Key-Escape
> "unlock_index;destroy $w"
2893 bind $w <Destroy
> unlock_index
2894 wm title
$w "[appname] ([reponame]): Merge"
2898 proc do_reset_hard
{} {
2899 global HEAD commit_type file_states
2901 if {[string match amend
* $commit_type]} {
2902 info_popup
{Cannot abort
while amending.
2904 You must finish amending this commit.
2909 if {![lock_index abort
]} return
2911 if {[string match
*merge
* $commit_type]} {
2917 if {[ask_popup
"Abort $op?
2919 Aborting the current $op will cause
2920 *ALL* uncommitted changes to be lost.
2922 Continue with aborting the current $op?"] eq
{yes}} {
2923 set fd
[open
"| git read-tree --reset -u HEAD" r
]
2924 fconfigure
$fd -blocking 0 -translation binary
2925 fileevent
$fd readable
[list reset_hard_wait
$fd]
2926 set ui_status_value
{Aborting... please
wait...
}
2932 proc reset_hard_wait
{fd
} {
2940 $ui_comm delete
0.0 end
2941 $ui_comm edit modified false
2943 catch
{file delete
[gitdir MERGE_HEAD
]}
2944 catch
{file delete
[gitdir rr-cache MERGE_RR
]}
2945 catch
{file delete
[gitdir SQUASH_MSG
]}
2946 catch
{file delete
[gitdir MERGE_MSG
]}
2947 catch
{file delete
[gitdir GITGUI_MSG
]}
2949 rescan
{set ui_status_value
{Abort completed. Ready.
}}
2953 ######################################################################
2957 set next_browser_id
0
2959 proc new_browser
{commit
} {
2960 global next_browser_id cursor_ptr M1B
2961 global browser_commit browser_status browser_stack browser_path browser_busy
2963 set w .browser
[incr next_browser_id
]
2964 set w_list
$w.list.l
2965 set browser_commit
($w_list) $commit
2966 set browser_status
($w_list) {Starting...
}
2967 set browser_stack
($w_list) {}
2968 set browser_path
($w_list) $browser_commit($w_list):
2969 set browser_busy
($w_list) 1
2972 label
$w.path
-textvariable browser_path
($w_list) \
2978 pack
$w.path
-anchor w
-side top
-fill x
2981 text
$w_list -background white
-borderwidth 0 \
2982 -cursor $cursor_ptr \
2987 -xscrollcommand [list
$w.list.sbx
set] \
2988 -yscrollcommand [list
$w.list.sby
set] \
2990 $w_list tag conf in_sel \
2991 -background [$w_list cget
-foreground] \
2992 -foreground [$w_list cget
-background]
2993 scrollbar
$w.list.sbx
-orient h
-command [list
$w_list xview
]
2994 scrollbar
$w.list.sby
-orient v
-command [list
$w_list yview
]
2995 pack
$w.list.sbx
-side bottom
-fill x
2996 pack
$w.list.sby
-side right
-fill y
2997 pack
$w_list -side left
-fill both
-expand 1
2998 pack
$w.list
-side top
-fill both
-expand 1
3000 label
$w.status
-textvariable browser_status
($w_list) \
3006 pack
$w.status
-anchor w
-side bottom
-fill x
3008 bind $w_list <Button-1
> "browser_click 0 $w_list @%x,%y;break"
3009 bind $w_list <Double-Button-1
> "browser_click 1 $w_list @%x,%y;break"
3010 bind $w_list <$M1B-Up> "browser_parent $w_list;break"
3011 bind $w_list <$M1B-Left> "browser_parent $w_list;break"
3012 bind $w_list <Up
> "browser_move -1 $w_list;break"
3013 bind $w_list <Down
> "browser_move 1 $w_list;break"
3014 bind $w_list <$M1B-Right> "browser_enter $w_list;break"
3015 bind $w_list <Return
> "browser_enter $w_list;break"
3016 bind $w_list <Prior
> "browser_page -1 $w_list;break"
3017 bind $w_list <Next
> "browser_page 1 $w_list;break"
3018 bind $w_list <Left
> break
3019 bind $w_list <Right
> break
3021 bind $w <Visibility
> "focus $w"
3023 array unset browser_buffer $w_list
3024 array unset browser_files $w_list
3025 array unset browser_status $w_list
3026 array unset browser_stack $w_list
3027 array unset browser_path $w_list
3028 array unset browser_commit $w_list
3029 array unset browser_busy $w_list
3031 wm title
$w "[appname] ([reponame]): File Browser"
3032 ls_tree
$w_list $browser_commit($w_list) {}
3035 proc browser_move
{dir w
} {
3036 global browser_files browser_busy
3038 if {$browser_busy($w)} return
3039 set lno
[lindex
[split [$w index in_sel.first
] .
] 0]
3041 if {[lindex
$browser_files($w) [expr {$lno - 1}]] ne
{}} {
3042 $w tag remove in_sel
0.0 end
3043 $w tag add in_sel
$lno.0 [expr {$lno + 1}].0
3048 proc browser_page
{dir w
} {
3049 global browser_files browser_busy
3051 if {$browser_busy($w)} return
3052 $w yview scroll
$dir pages
3054 [lindex
[$w yview
] 0]
3055 * [llength
$browser_files($w)]
3057 if {[lindex
$browser_files($w) [expr {$lno - 1}]] ne
{}} {
3058 $w tag remove in_sel
0.0 end
3059 $w tag add in_sel
$lno.0 [expr {$lno + 1}].0
3064 proc browser_parent
{w
} {
3065 global browser_files browser_status browser_path
3066 global browser_stack browser_busy
3068 if {$browser_busy($w)} return
3069 set info
[lindex
$browser_files($w) 0]
3070 if {[lindex
$info 0] eq
{parent
}} {
3071 set parent
[lindex
$browser_stack($w) end-1
]
3072 set browser_stack
($w) [lrange
$browser_stack($w) 0 end-2
]
3073 if {$browser_stack($w) eq
{}} {
3074 regsub
{:.
*$
} $browser_path($w) {:} browser_path
($w)
3076 regsub
{/[^
/]+$
} $browser_path($w) {} browser_path
($w)
3078 set browser_status
($w) "Loading $browser_path($w)..."
3079 ls_tree
$w [lindex
$parent 0] [lindex
$parent 1]
3083 proc browser_enter
{w
} {
3084 global browser_files browser_status browser_path
3085 global browser_commit browser_stack browser_busy
3087 if {$browser_busy($w)} return
3088 set lno
[lindex
[split [$w index in_sel.first
] .
] 0]
3089 set info
[lindex
$browser_files($w) [expr {$lno - 1}]]
3091 switch
-- [lindex
$info 0] {
3096 set name
[lindex
$info 2]
3097 set escn
[escape_path
$name]
3098 set browser_status
($w) "Loading $escn..."
3099 append browser_path
($w) $escn
3100 ls_tree
$w [lindex
$info 1] $name
3103 set name
[lindex
$info 2]
3105 foreach n
$browser_stack($w) {
3106 append p
[lindex
$n 1]
3109 show_blame
$browser_commit($w) $p
3115 proc browser_click
{was_double_click w pos
} {
3116 global browser_files browser_busy
3118 if {$browser_busy($w)} return
3119 set lno
[lindex
[split [$w index
$pos] .
] 0]
3122 if {[lindex
$browser_files($w) [expr {$lno - 1}]] ne
{}} {
3123 $w tag remove in_sel
0.0 end
3124 $w tag add in_sel
$lno.0 [expr {$lno + 1}].0
3125 if {$was_double_click} {
3131 proc ls_tree
{w tree_id name
} {
3132 global browser_buffer browser_files browser_stack browser_busy
3134 set browser_buffer
($w) {}
3135 set browser_files
($w) {}
3136 set browser_busy
($w) 1
3138 $w conf
-state normal
3139 $w tag remove in_sel
0.0 end
3141 if {$browser_stack($w) ne
{}} {
3142 $w image create end \
3143 -align center
-padx 5 -pady 1 \
3146 $w insert end
{[Up To Parent
]}
3147 lappend browser_files
($w) parent
3149 lappend browser_stack
($w) [list
$tree_id $name]
3150 $w conf
-state disabled
3152 set cmd
[list git ls-tree
-z $tree_id]
3153 set fd
[open
"| $cmd" r
]
3154 fconfigure
$fd -blocking 0 -translation binary
-encoding binary
3155 fileevent
$fd readable
[list read_ls_tree
$fd $w]
3158 proc read_ls_tree
{fd w
} {
3159 global browser_buffer browser_files browser_status browser_busy
3161 if {![winfo exists
$w]} {
3166 append browser_buffer
($w) [read $fd]
3167 set pck
[split $browser_buffer($w) "\0"]
3168 set browser_buffer
($w) [lindex
$pck end
]
3170 set n
[llength
$browser_files($w)]
3171 $w conf
-state normal
3172 foreach p
[lrange
$pck 0 end-1
] {
3173 set info
[split $p "\t"]
3174 set path
[lindex
$info 1]
3175 set info
[split [lindex
$info 0] { }]
3176 set type [lindex
$info 1]
3177 set object
[lindex
$info 2]
3188 set image file_question
3192 if {$n > 0} {$w insert end
"\n"}
3193 $w image create end \
3194 -align center
-padx 5 -pady 1 \
3195 -name icon
[incr n
] \
3197 $w insert end
[escape_path
$path]
3198 lappend browser_files
($w) [list
$type $object $path]
3200 $w conf
-state disabled
3204 set browser_status
($w) Ready.
3205 set browser_busy
($w) 0
3206 array
unset browser_buffer
$w
3208 $w tag add in_sel
1.0 2.0
3214 proc show_blame
{commit path
} {
3215 global next_browser_id blame_status blame_data
3217 set w .browser
[incr next_browser_id
]
3218 set blame_status
($w) {Loading current
file content...
}
3223 label
$w.path
-text "$commit:$path" \
3229 pack
$w.path
-side top
-fill x
3233 label
$w.out.commit_l
-text Commit \
3238 text
$w.out.commit_t \
3239 -background white
-borderwidth 0 \
3245 lappend texts
$w.out.commit_t
3247 label
$w.out.author_l
-text Author \
3252 text
$w.out.author_t \
3253 -background white
-borderwidth 0 \
3259 lappend texts
$w.out.author_t
3261 label
$w.out.date_l
-text Date \
3266 text
$w.out.date_t \
3267 -background white
-borderwidth 0 \
3271 -width [string length
"yyyy-mm-dd hh:mm:ss"] \
3273 lappend texts
$w.out.date_t
3275 label
$w.out.filename_l
-text Filename \
3280 text
$w.out.filename_t \
3281 -background white
-borderwidth 0 \
3287 lappend texts
$w.out.filename_t
3289 label
$w.out.origlinenumber_l
-text {Orig Line
} \
3294 text
$w.out.origlinenumber_t \
3295 -background white
-borderwidth 0 \
3301 $w.out.origlinenumber_t tag conf linenumber
-justify right
3302 lappend texts
$w.out.origlinenumber_t
3304 label
$w.out.linenumber_l
-text {Curr Line
} \
3309 text
$w.out.linenumber_t \
3310 -background white
-borderwidth 0 \
3316 $w.out.linenumber_t tag conf linenumber
-justify right
3317 lappend texts
$w.out.linenumber_t
3319 label
$w.out.file_l
-text {File Content
} \
3324 text
$w.out.file_t \
3325 -background white
-borderwidth 0 \
3330 -xscrollcommand [list
$w.out.sbx
set] \
3332 lappend texts
$w.out.file_t
3334 scrollbar
$w.out.sbx
-orient h
-command [list
$w.out.file_t xview
]
3335 scrollbar
$w.out.sby
-orient v \
3336 -command [list scrollbar2many
$texts yview
]
3339 regsub
{_t$
} $i _l l
3342 set file_col
[expr {[llength
$texts] - 1}]
3343 eval grid
$labels -sticky we
3344 eval grid
$texts $w.out.sby
-sticky nsew
3345 grid conf
$w.out.sbx
-column $file_col -sticky we
3346 grid columnconfigure
$w.out
$file_col -weight 1
3347 grid rowconfigure
$w.out
1 -weight 1
3348 pack
$w.out
-fill both
-expand 1
3350 label
$w.status
-textvariable blame_status
($w) \
3356 pack
$w.status
-side bottom
-fill x
3358 menu
$w.ctxm
-tearoff 0
3359 $w.ctxm add
command -label "Copy Commit" \
3361 -command "blame_copycommit $w \$cursorW @\$cursorX,\$cursorY"
3364 $i tag conf in_sel \
3365 -background [$i cget
-foreground] \
3366 -foreground [$i cget
-background]
3367 $i conf
-yscrollcommand \
3368 [list many2scrollbar
$texts yview
$w.out.sby
]
3369 bind $i <Button-1
> "blame_highlight $i @%x,%y $texts;break"
3374 tk_popup $w.ctxm %X %Y
3378 set blame_data
($w,colors
) {}
3380 bind $w <Visibility
> "focus $w"
3382 array unset blame_status $w
3383 array unset blame_data $w,*
3385 wm title
$w "[appname] ([reponame]): File Viewer"
3387 set blame_data
($w,total_lines
) 0
3388 set cmd
[list git cat-file blob
"$commit:$path"]
3389 set fd
[open
"| $cmd" r
]
3390 fconfigure
$fd -blocking 0 -translation lf
-encoding binary
3391 fileevent
$fd readable
[list read_blame_catfile \
3392 $fd $w $commit $path \
3393 $texts $w.out.linenumber_t
$w.out.file_t
]
3396 proc read_blame_catfile
{fd w commit path texts w_lno w_file
} {
3397 global blame_status blame_data
3399 if {![winfo exists
$w_file]} {
3404 set n
$blame_data($w,total_lines
)
3405 foreach i
$texts {$i conf
-state normal
}
3406 while {[gets
$fd line
] >= 0} {
3407 regsub
"\r\$" $line {} line
3409 $w_lno insert end
$n linenumber
3410 $w_file insert end
$line
3411 foreach i
$texts {$i insert end
"\n"}
3413 foreach i
$texts {$i conf
-state disabled
}
3414 set blame_data
($w,total_lines
) $n
3418 set blame_status
($w) {Loading annotations...
}
3419 set cmd
[list git blame
-M -C --incremental]
3420 lappend cmd
$commit -- $path
3421 set fd
[open
"| $cmd" r
]
3422 fconfigure
$fd -blocking 0 -translation lf
-encoding binary
3423 fileevent
$fd readable
"read_blame_incremental $fd $w $texts"
3427 proc read_blame_incremental
{fd w
3428 w_commit w_author w_date w_filename w_olno
3430 global blame_status blame_data
3432 if {![winfo exists
$w_commit]} {
3446 $w_commit conf
-state normal
3447 $w_author conf
-state normal
3448 $w_date conf
-state normal
3449 $w_filename conf
-state normal
3450 $w_olno conf
-state normal
3452 while {[gets
$fd line
] >= 0} {
3453 if {[regexp
{^
([a-z0-9
]{40}) (\d
+) (\d
+) (\d
+)$
} $line line \
3454 cmit original_line final_line line_count
]} {
3455 set blame_data
($w,commit
) $cmit
3456 set blame_data
($w,original_line
) $original_line
3457 set blame_data
($w,final_line
) $final_line
3458 set blame_data
($w,line_count
) $line_count
3460 if {[catch
{set g
$blame_data($w,$cmit,seen
)}]} {
3461 if {$blame_data($w,colors
) eq
{}} {
3462 set blame_data
($w,colors
) {
3471 set c
[lindex
$blame_data($w,colors
) 0]
3472 set blame_data
($w,colors
) \
3473 [lrange
$blame_data($w,colors
) 1 end
]
3475 $t tag conf g
$cmit -background $c
3478 set blame_data
($w,$cmit,seen
) 1
3480 } elseif
{[string match
{filename
*} $line]} {
3481 set n
$blame_data($w,line_count
)
3482 set lno
$blame_data($w,final_line
)
3483 set ol
$blame_data($w,original_line
)
3484 set file [string range
$line 9 end
]
3485 set cmit
$blame_data($w,commit
)
3486 set abbrev
[string range
$cmit 0 8]
3488 if {[catch
{set author
$blame_data($w,$cmit,author
)} err
]} {
3492 if {[catch
{set atime
$blame_data($w,$cmit,author-time
)}]} {
3495 set atime
[clock format
$atime -format {%Y-
%m-
%d
%T
}]
3499 if {![catch
{set g g
$blame_data($w,line
$lno,commit
)}]} {
3501 $t tag remove
$g $lno.0 "$lno.0 lineend + 1c"
3511 $t delete
$lno.0 "$lno.0 lineend"
3514 $w_commit insert
$lno.0 $abbrev
3515 $w_author insert
$lno.0 $author
3516 $w_date insert
$lno.0 $atime
3517 $w_filename insert
$lno.0 $file
3518 $w_olno insert
$lno.0 $ol linenumber
3522 $t tag add
$g $lno.0 "$lno.0 lineend + 1c"
3525 set blame_data
($w,line
$lno,commit
) $cmit
3531 } elseif
{[regexp
{^
([a-z-
]+) (.
*)$
} $line line header data
]} {
3532 set blame_data
($w,$blame_data($w,commit
),$header) $data
3536 $w_commit conf
-state disabled
3537 $w_author conf
-state disabled
3538 $w_date conf
-state disabled
3539 $w_filename conf
-state disabled
3540 $w_olno conf
-state disabled
3544 set blame_status
($w) {Annotation complete.
}
3548 proc blame_highlight
{w pos args
} {
3549 set lno
[lindex
[split [$w index
$pos] .
] 0]
3551 $i tag remove in_sel
0.0 end
3553 if {$lno eq
{}} return
3555 $i tag add in_sel
$lno.0 "$lno.0 + 1 line"
3559 proc blame_copycommit
{w i pos
} {
3561 set lno
[lindex
[split [$i index
$pos] .
] 0]
3562 if {![catch
{set commit
$blame_data($w,line
$lno,commit
)}]} {
3571 ######################################################################
3576 #define mask_width 14
3577 #define mask_height 15
3578 static unsigned char mask_bits
[] = {
3579 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
3580 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
3581 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
3584 image create bitmap file_plain
-background white
-foreground black
-data {
3585 #define plain_width 14
3586 #define plain_height 15
3587 static unsigned char plain_bits
[] = {
3588 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
3589 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
3590 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
3591 } -maskdata $filemask
3593 image create bitmap file_mod
-background white
-foreground blue
-data {
3594 #define mod_width 14
3595 #define mod_height 15
3596 static unsigned char mod_bits
[] = {
3597 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
3598 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
3599 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
3600 } -maskdata $filemask
3602 image create bitmap file_fulltick
-background white
-foreground "#007000" -data {
3603 #define file_fulltick_width 14
3604 #define file_fulltick_height 15
3605 static unsigned char file_fulltick_bits
[] = {
3606 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
3607 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
3608 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
3609 } -maskdata $filemask
3611 image create bitmap file_parttick
-background white
-foreground "#005050" -data {
3612 #define parttick_width 14
3613 #define parttick_height 15
3614 static unsigned char parttick_bits
[] = {
3615 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
3616 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
3617 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
3618 } -maskdata $filemask
3620 image create bitmap file_question
-background white
-foreground black
-data {
3621 #define file_question_width 14
3622 #define file_question_height 15
3623 static unsigned char file_question_bits
[] = {
3624 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
3625 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
3626 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
3627 } -maskdata $filemask
3629 image create bitmap file_removed
-background white
-foreground red
-data {
3630 #define file_removed_width 14
3631 #define file_removed_height 15
3632 static unsigned char file_removed_bits
[] = {
3633 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
3634 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
3635 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
3636 } -maskdata $filemask
3638 image create bitmap file_merge
-background white
-foreground blue
-data {
3639 #define file_merge_width 14
3640 #define file_merge_height 15
3641 static unsigned char file_merge_bits
[] = {
3642 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
3643 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
3644 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
3645 } -maskdata $filemask
3648 #define file_width 18
3649 #define file_height 18
3650 static unsigned char file_bits
[] = {
3651 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
3652 0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
3653 0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
3654 0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
3655 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
3657 image create bitmap file_dir
-background white
-foreground blue \
3658 -data $file_dir_data -maskdata $file_dir_data
3661 set file_uplevel_data
{
3663 #define up_height 15
3664 static unsigned char up_bits
[] = {
3665 0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
3666 0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
3667 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
3669 image create bitmap file_uplevel
-background white
-foreground red \
3670 -data $file_uplevel_data -maskdata $file_uplevel_data
3671 unset file_uplevel_data
3673 set ui_index .vpane.files.index.list
3674 set ui_workdir .vpane.files.workdir.list
3676 set all_icons
(_
$ui_index) file_plain
3677 set all_icons
(A
$ui_index) file_fulltick
3678 set all_icons
(M
$ui_index) file_fulltick
3679 set all_icons
(D
$ui_index) file_removed
3680 set all_icons
(U
$ui_index) file_merge
3682 set all_icons
(_
$ui_workdir) file_plain
3683 set all_icons
(M
$ui_workdir) file_mod
3684 set all_icons
(D
$ui_workdir) file_question
3685 set all_icons
(U
$ui_workdir) file_merge
3686 set all_icons
(O
$ui_workdir) file_plain
3688 set max_status_desc
0
3692 {_M
"Modified, not staged"}
3693 {M_
"Staged for commit"}
3694 {MM
"Portions staged for commit"}
3695 {MD
"Staged for commit, missing"}
3697 {_O
"Untracked, not staged"}
3698 {A_
"Staged for commit"}
3699 {AM
"Portions staged for commit"}
3700 {AD
"Staged for commit, missing"}
3703 {D_
"Staged for removal"}
3704 {DO
"Staged for removal, still present"}
3706 {U_
"Requires merge resolution"}
3707 {UU
"Requires merge resolution"}
3708 {UM
"Requires merge resolution"}
3709 {UD
"Requires merge resolution"}
3711 if {$max_status_desc < [string length
[lindex
$i 1]]} {
3712 set max_status_desc
[string length
[lindex
$i 1]]
3714 set all_descs
([lindex
$i 0]) [lindex
$i 1]
3718 ######################################################################
3722 proc bind_button3
{w cmd
} {
3723 bind $w <Any-Button-3
> $cmd
3725 bind $w <Control-Button-1
> $cmd
3729 proc scrollbar2many
{list mode args
} {
3730 foreach w
$list {eval $w $mode $args}
3733 proc many2scrollbar
{list mode sb top bottom
} {
3734 $sb set $top $bottom
3735 foreach w
$list {$w $mode moveto
$top}
3738 proc incr_font_size
{font
{amt
1}} {
3739 set sz
[font configure
$font -size]
3741 font configure
$font -size $sz
3742 font configure
${font}bold
-size $sz
3745 proc hook_failed_popup
{hook msg
} {
3750 label
$w.m.l1
-text "$hook hook failed:" \
3755 -background white
-borderwidth 1 \
3757 -width 80 -height 10 \
3759 -yscrollcommand [list
$w.m.sby
set]
3761 -text {You must correct the above errors before committing.
} \
3765 scrollbar
$w.m.sby
-command [list
$w.m.t yview
]
3766 pack
$w.m.l1
-side top
-fill x
3767 pack
$w.m.l2
-side bottom
-fill x
3768 pack
$w.m.sby
-side right
-fill y
3769 pack
$w.m.t
-side left
-fill both
-expand 1
3770 pack
$w.m
-side top
-fill both
-expand 1 -padx 5 -pady 10
3772 $w.m.t insert
1.0 $msg
3773 $w.m.t conf
-state disabled
3775 button
$w.ok
-text OK \
3778 -command "destroy $w"
3779 pack
$w.ok
-side bottom
-anchor e
-pady 10 -padx 10
3781 bind $w <Visibility
> "grab $w; focus $w"
3782 bind $w <Key-Return
> "destroy $w"
3783 wm title
$w "[appname] ([reponame]): error"
3787 set next_console_id
0
3789 proc new_console
{short_title long_title
} {
3790 global next_console_id console_data
3791 set w .console
[incr next_console_id
]
3792 set console_data
($w) [list
$short_title $long_title]
3793 return [console_init
$w]
3796 proc console_init
{w
} {
3797 global console_cr console_data M1B
3799 set console_cr
($w) 1.0
3802 label
$w.m.l1
-text "[lindex $console_data($w) 1]:" \
3807 -background white
-borderwidth 1 \
3809 -width 80 -height 10 \
3812 -yscrollcommand [list
$w.m.sby
set]
3813 label
$w.m.s
-text {Working... please
wait...
} \
3817 scrollbar
$w.m.sby
-command [list
$w.m.t yview
]
3818 pack
$w.m.l1
-side top
-fill x
3819 pack
$w.m.s
-side bottom
-fill x
3820 pack
$w.m.sby
-side right
-fill y
3821 pack
$w.m.t
-side left
-fill both
-expand 1
3822 pack
$w.m
-side top
-fill both
-expand 1 -padx 5 -pady 10
3824 menu
$w.ctxm
-tearoff 0
3825 $w.ctxm add
command -label "Copy" \
3827 -command "tk_textCopy $w.m.t"
3828 $w.ctxm add
command -label "Select All" \
3830 -command "focus $w.m.t;$w.m.t tag add sel 0.0 end"
3831 $w.ctxm add
command -label "Copy All" \
3834 $w.m.t tag add sel 0.0 end
3836 $w.m.t tag remove sel 0.0 end
3839 button
$w.ok
-text {Close
} \
3842 -command "destroy $w"
3843 pack
$w.ok
-side bottom
-anchor e
-pady 10 -padx 10
3845 bind_button3
$w.m.t
"tk_popup $w.ctxm %X %Y"
3846 bind $w.m.t
<$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break"
3847 bind $w.m.t
<$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break"
3848 bind $w <Visibility
> "focus $w"
3849 wm title
$w "[appname] ([reponame]): [lindex $console_data($w) 0]"
3853 proc console_exec
{w cmd after
} {
3854 # -- Cygwin's Tcl tosses the enviroment when we exec our child.
3855 # But most users need that so we have to relogin. :-(
3858 set cmd
[list sh
--login -c "cd \"[pwd]\" && [join $cmd { }]"]
3861 # -- Tcl won't let us redirect both stdout and stderr to
3862 # the same pipe. So pass it through cat...
3864 set cmd
[concat |
$cmd |
& cat]
3866 set fd_f
[open
$cmd r
]
3867 fconfigure
$fd_f -blocking 0 -translation binary
3868 fileevent
$fd_f readable
[list console_read
$w $fd_f $after]
3871 proc console_read
{w fd after
} {
3876 if {![winfo exists
$w]} {console_init
$w}
3877 $w.m.t conf
-state normal
3879 set n
[string length
$buf]
3881 set cr
[string first
"\r" $buf $c]
3882 set lf
[string first
"\n" $buf $c]
3883 if {$cr < 0} {set cr
[expr {$n + 1}]}
3884 if {$lf < 0} {set lf
[expr {$n + 1}]}
3887 $w.m.t insert end
[string range
$buf $c $lf]
3888 set console_cr
($w) [$w.m.t index
{end
-1c}]
3892 $w.m.t delete
$console_cr($w) end
3893 $w.m.t insert end
"\n"
3894 $w.m.t insert end
[string range
$buf $c $cr]
3899 $w.m.t conf
-state disabled
3903 fconfigure
$fd -blocking 1
3905 if {[catch
{close
$fd}]} {
3910 uplevel
#0 $after $w $ok
3913 fconfigure
$fd -blocking 0
3916 proc console_chain
{cmdlist w
{ok
1}} {
3918 if {[llength
$cmdlist] == 0} {
3923 set cmd
[lindex
$cmdlist 0]
3924 set cmdlist
[lrange
$cmdlist 1 end
]
3926 if {[lindex
$cmd 0] eq
{console_exec
}} {
3929 [list console_chain
$cmdlist]
3931 uplevel
#0 $cmd $cmdlist $w $ok
3938 proc console_done
{args
} {
3939 global console_cr console_data
3941 switch
-- [llength
$args] {
3943 set w
[lindex
$args 0]
3944 set ok
[lindex
$args 1]
3947 set w
[lindex
$args 1]
3948 set ok
[lindex
$args 2]
3951 error
"wrong number of args: console_done ?ignored? w ok"
3956 if {[winfo exists
$w]} {
3957 $w.m.s conf
-background green
-text {Success
}
3958 $w.ok conf
-state normal
3961 if {![winfo exists
$w]} {
3964 $w.m.s conf
-background red
-text {Error
: Command Failed
}
3965 $w.ok conf
-state normal
3968 array
unset console_cr
$w
3969 array
unset console_data
$w
3972 ######################################################################
3976 set starting_gitk_msg
{Starting gitk... please
wait...
}
3978 proc do_gitk
{revs
} {
3979 global env ui_status_value starting_gitk_msg
3981 # -- On Windows gitk is severly broken, and right now it seems like
3982 # nobody cares about fixing it. The only known workaround is to
3983 # always delete ~/.gitk before starting the program.
3986 catch
{file delete
[file join $env(HOME
) .gitk
]}
3989 # -- Always start gitk through whatever we were loaded with. This
3990 # lets us bypass using shell process on Windows systems.
3992 set cmd
[info nameofexecutable
]
3993 lappend cmd
[gitexec gitk
]
3999 if {[catch
{eval exec $cmd &} err
]} {
4000 error_popup
"Failed to start gitk:\n\n$err"
4002 set ui_status_value
$starting_gitk_msg
4004 if {$ui_status_value eq
$starting_gitk_msg} {
4005 set ui_status_value
{Ready.
}
4012 set fd
[open
"| git count-objects -v" r
]
4013 while {[gets
$fd line
] > 0} {
4014 if {[regexp
{^
([^
:]+): (\d
+)$
} $line _ name value
]} {
4015 set stats
($name) $value
4021 foreach p
[glob
-directory [gitdir objects pack
] \
4024 incr packed_sz
[file size
$p]
4026 if {$packed_sz > 0} {
4027 set stats
(size-pack
) [expr {$packed_sz / 1024}]
4032 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
4034 label
$w.header
-text {Database Statistics
} \
4036 pack
$w.header
-side top
-fill x
4038 frame
$w.buttons
-border 1
4039 button
$w.buttons.close
-text Close \
4041 -command [list destroy
$w]
4042 button
$w.buttons.gc
-text {Compress Database
} \
4044 -command "destroy $w;do_gc"
4045 pack
$w.buttons.close
-side right
4046 pack
$w.buttons.gc
-side left
4047 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
4049 frame
$w.stat
-borderwidth 1 -relief solid
4051 {count
{Number of loose objects
}}
4052 {size
{Disk space used by loose objects
} { KiB
}}
4053 {in-pack
{Number of packed objects
}}
4054 {packs
{Number of packs
}}
4055 {size-pack
{Disk space used by packed objects
} { KiB
}}
4056 {prune-packable
{Packed objects waiting
for pruning
}}
4057 {garbage
{Garbage files
}}
4059 set name
[lindex
$s 0]
4060 set label
[lindex
$s 1]
4061 if {[catch
{set value
$stats($name)}]} continue
4062 if {[llength
$s] > 2} {
4063 set value
"$value[lindex $s 2]"
4066 label
$w.stat.l_
$name -text "$label:" -anchor w
-font font_ui
4067 label
$w.stat.v_
$name -text $value -anchor w
-font font_ui
4068 grid
$w.stat.l_
$name $w.stat.v_
$name -sticky we
-padx {0 5}
4070 pack
$w.stat
-pady 10 -padx 10
4072 bind $w <Visibility
> "grab $w; focus $w"
4073 bind $w <Key-Escape
> [list destroy
$w]
4074 bind $w <Key-Return
> [list destroy
$w]
4075 wm title
$w "[appname] ([reponame]): Database Statistics"
4080 set w
[new_console
{gc
} {Compressing the object database
}]
4082 {console_exec
{git pack-refs
--prune}}
4083 {console_exec
{git reflog expire
--all}}
4084 {console_exec
{git repack
-a -d -l}}
4085 {console_exec
{git rerere gc
}}
4089 proc do_fsck_objects
{} {
4090 set w
[new_console
{fsck-objects
} \
4091 {Verifying the object database with fsck-objects
}]
4092 set cmd
[list git fsck-objects
]
4095 lappend cmd
--strict
4096 console_exec
$w $cmd console_done
4102 global ui_comm is_quitting repo_config commit_type
4104 if {$is_quitting} return
4107 # -- Stash our current commit buffer.
4109 set save
[gitdir GITGUI_MSG
]
4110 set msg
[string trim
[$ui_comm get
0.0 end
]]
4111 regsub
-all -line {[ \r\t]+$
} $msg {} msg
4112 if {(![string match amend
* $commit_type]
4113 ||
[$ui_comm edit modified
])
4116 set fd
[open
$save w
]
4117 puts
-nonewline $fd $msg
4121 catch
{file delete
$save}
4124 # -- Stash our current window geometry into this repository.
4126 set cfg_geometry
[list
]
4127 lappend cfg_geometry
[wm geometry .
]
4128 lappend cfg_geometry
[lindex
[.vpane sash coord
0] 1]
4129 lappend cfg_geometry
[lindex
[.vpane.files sash coord
0] 0]
4130 if {[catch
{set rc_geometry
$repo_config(gui.geometry
)}]} {
4133 if {$cfg_geometry ne
$rc_geometry} {
4134 catch
{exec git repo-config gui.geometry
$cfg_geometry}
4141 rescan
{set ui_status_value
{Ready.
}}
4144 proc unstage_helper
{txt paths
} {
4145 global file_states current_diff_path
4147 if {![lock_index begin-update
]} return
4151 foreach path
$paths {
4152 switch
-glob -- [lindex
$file_states($path) 0] {
4156 lappend pathList
$path
4157 if {$path eq
$current_diff_path} {
4158 set after
{reshow_diff
;}
4163 if {$pathList eq
{}} {
4169 [concat
$after {set ui_status_value
{Ready.
}}]
4173 proc do_unstage_selection
{} {
4174 global current_diff_path selected_paths
4176 if {[array size selected_paths
] > 0} {
4178 {Unstaging selected files from commit
} \
4179 [array names selected_paths
]
4180 } elseif
{$current_diff_path ne
{}} {
4182 "Unstaging [short_path $current_diff_path] from commit" \
4183 [list
$current_diff_path]
4187 proc add_helper
{txt paths
} {
4188 global file_states current_diff_path
4190 if {![lock_index begin-update
]} return
4194 foreach path
$paths {
4195 switch
-glob -- [lindex
$file_states($path) 0] {
4200 lappend pathList
$path
4201 if {$path eq
$current_diff_path} {
4202 set after
{reshow_diff
;}
4207 if {$pathList eq
{}} {
4213 [concat
$after {set ui_status_value
{Ready to commit.
}}]
4217 proc do_add_selection
{} {
4218 global current_diff_path selected_paths
4220 if {[array size selected_paths
] > 0} {
4222 {Adding selected files
} \
4223 [array names selected_paths
]
4224 } elseif
{$current_diff_path ne
{}} {
4226 "Adding [short_path $current_diff_path]" \
4227 [list
$current_diff_path]
4231 proc do_add_all
{} {
4235 foreach path
[array names file_states
] {
4236 switch
-glob -- [lindex
$file_states($path) 0] {
4239 ?D
{lappend paths
$path}
4242 add_helper
{Adding all changed files
} $paths
4245 proc revert_helper
{txt paths
} {
4246 global file_states current_diff_path
4248 if {![lock_index begin-update
]} return
4252 foreach path
$paths {
4253 switch
-glob -- [lindex
$file_states($path) 0] {
4257 lappend pathList
$path
4258 if {$path eq
$current_diff_path} {
4259 set after
{reshow_diff
;}
4265 set n
[llength
$pathList]
4269 } elseif
{$n == 1} {
4270 set s
"[short_path [lindex $pathList]]"
4272 set s
"these $n files"
4275 set reply
[tk_dialog \
4277 "[appname] ([reponame])" \
4278 "Revert changes in $s?
4280 Any unadded changes will be permanently lost by the revert." \
4290 [concat
$after {set ui_status_value
{Ready.
}}]
4296 proc do_revert_selection
{} {
4297 global current_diff_path selected_paths
4299 if {[array size selected_paths
] > 0} {
4301 {Reverting selected files
} \
4302 [array names selected_paths
]
4303 } elseif
{$current_diff_path ne
{}} {
4305 "Reverting [short_path $current_diff_path]" \
4306 [list
$current_diff_path]
4310 proc do_signoff
{} {
4313 set me
[committer_ident
]
4314 if {$me eq
{}} return
4316 set sob
"Signed-off-by: $me"
4317 set last
[$ui_comm get
{end
-1c linestart
} {end
-1c}]
4318 if {$last ne
$sob} {
4319 $ui_comm edit separator
4321 && ![regexp
{^
[A-Z
][A-Za-z
]*-[A-Za-z-
]+: *} $last]} {
4322 $ui_comm insert end
"\n"
4324 $ui_comm insert end
"\n$sob"
4325 $ui_comm edit separator
4330 proc do_select_commit_type
{} {
4331 global commit_type selected_commit_type
4333 if {$selected_commit_type eq
{new
}
4334 && [string match amend
* $commit_type]} {
4336 } elseif
{$selected_commit_type eq
{amend
}
4337 && ![string match amend
* $commit_type]} {
4340 # The amend request was rejected...
4342 if {![string match amend
* $commit_type]} {
4343 set selected_commit_type new
4353 global appvers copyright
4354 global tcl_patchLevel tk_patchLevel
4358 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
4360 label
$w.header
-text "About [appname]" \
4362 pack
$w.header
-side top
-fill x
4365 button
$w.buttons.close
-text {Close
} \
4367 -command [list destroy
$w]
4368 pack
$w.buttons.close
-side right
4369 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
4372 -text "[appname] - a commit creation tool for Git.
4380 pack
$w.desc
-side top
-fill x
-padx 5 -pady 5
4383 append v
"[appname] version $appvers\n"
4384 append v
"[exec git version]\n"
4386 if {$tcl_patchLevel eq
$tk_patchLevel} {
4387 append v
"Tcl/Tk version $tcl_patchLevel"
4389 append v
"Tcl version $tcl_patchLevel"
4390 append v
", Tk version $tk_patchLevel"
4401 pack
$w.vers
-side top
-fill x
-padx 5 -pady 5
4403 menu
$w.ctxm
-tearoff 0
4404 $w.ctxm add
command \
4409 clipboard append -format STRING -type STRING -- \[$w.vers cget -text\]
4412 bind $w <Visibility
> "grab $w; focus $w"
4413 bind $w <Key-Escape
> "destroy $w"
4414 bind_button3
$w.vers
"tk_popup $w.ctxm %X %Y; grab $w; focus $w"
4415 wm title
$w "About [appname]"
4419 proc do_options
{} {
4420 global repo_config global_config font_descs
4421 global repo_config_new global_config_new
4423 array
unset repo_config_new
4424 array
unset global_config_new
4425 foreach name
[array names repo_config
] {
4426 set repo_config_new
($name) $repo_config($name)
4429 foreach name
[array names repo_config
] {
4431 gui.diffcontext
{continue}
4433 set repo_config_new
($name) $repo_config($name)
4435 foreach name
[array names global_config
] {
4436 set global_config_new
($name) $global_config($name)
4439 set w .options_editor
4441 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
4443 label
$w.header
-text "[appname] Options" \
4445 pack
$w.header
-side top
-fill x
4448 button
$w.buttons.restore
-text {Restore Defaults
} \
4450 -command do_restore_defaults
4451 pack
$w.buttons.restore
-side left
4452 button
$w.buttons.save
-text Save \
4454 -command [list do_save_config
$w]
4455 pack
$w.buttons.save
-side right
4456 button
$w.buttons.cancel
-text {Cancel
} \
4458 -command [list destroy
$w]
4459 pack
$w.buttons.cancel
-side right
-padx 5
4460 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
4462 labelframe
$w.repo
-text "[reponame] Repository" \
4464 labelframe
$w.global
-text {Global
(All Repositories
)} \
4466 pack
$w.repo
-side left
-fill both
-expand 1 -pady 5 -padx 5
4467 pack
$w.global
-side right
-fill both
-expand 1 -pady 5 -padx 5
4471 {t user.name
{User Name
}}
4472 {t user.email
{Email Address
}}
4474 {b merge.summary
{Summarize Merge Commits
}}
4475 {i-1.
.5 merge.verbosity
{Merge Verbosity
}}
4477 {b gui.trustmtime
{Trust File Modification Timestamps
}}
4478 {i-1.
.99 gui.diffcontext
{Number of Diff Context Lines
}}
4479 {t gui.newbranchtemplate
{New Branch Name Template
}}
4481 set type [lindex
$option 0]
4482 set name
[lindex
$option 1]
4483 set text
[lindex
$option 2]
4485 foreach f
{repo global
} {
4486 switch
-glob -- $type {
4488 checkbutton
$w.
$f.
$optid -text $text \
4489 -variable ${f}_config_new
($name) \
4493 pack
$w.
$f.
$optid -side top
-anchor w
4496 regexp
-- {-(\d
+)\.\.
(\d
+)$
} $type _junk min max
4498 label
$w.
$f.
$optid.l
-text "$text:" -font font_ui
4499 pack
$w.
$f.
$optid.l
-side left
-anchor w
-fill x
4500 spinbox
$w.
$f.
$optid.v \
4501 -textvariable ${f}_config_new
($name) \
4505 -width [expr {1 + [string length
$max]}] \
4507 bind $w.
$f.
$optid.v
<FocusIn
> {%W selection range
0 end
}
4508 pack
$w.
$f.
$optid.v
-side right
-anchor e
-padx 5
4509 pack
$w.
$f.
$optid -side top
-anchor w
-fill x
4513 label
$w.
$f.
$optid.l
-text "$text:" -font font_ui
4514 entry
$w.
$f.
$optid.v \
4518 -textvariable ${f}_config_new
($name) \
4520 pack
$w.
$f.
$optid.l
-side left
-anchor w
4521 pack
$w.
$f.
$optid.v
-side left
-anchor w \
4524 pack
$w.
$f.
$optid -side top
-anchor w
-fill x
4530 set all_fonts
[lsort
[font families
]]
4531 foreach option
$font_descs {
4532 set name
[lindex
$option 0]
4533 set font
[lindex
$option 1]
4534 set text
[lindex
$option 2]
4536 set global_config_new
(gui.
$font^^family
) \
4537 [font configure
$font -family]
4538 set global_config_new
(gui.
$font^^size
) \
4539 [font configure
$font -size]
4541 frame
$w.global.
$name
4542 label
$w.global.
$name.l
-text "$text:" -font font_ui
4543 pack
$w.global.
$name.l
-side left
-anchor w
-fill x
4544 eval tk_optionMenu
$w.global.
$name.family \
4545 global_config_new
(gui.
$font^^family
) \
4547 spinbox
$w.global.
$name.size \
4548 -textvariable global_config_new
(gui.
$font^^size
) \
4549 -from 2 -to 80 -increment 1 \
4552 bind $w.global.
$name.size
<FocusIn
> {%W selection range
0 end
}
4553 pack
$w.global.
$name.size
-side right
-anchor e
4554 pack
$w.global.
$name.family
-side right
-anchor e
4555 pack
$w.global.
$name -side top
-anchor w
-fill x
4558 bind $w <Visibility
> "grab $w; focus $w"
4559 bind $w <Key-Escape
> "destroy $w"
4560 wm title
$w "[appname] ([reponame]): Options"
4564 proc do_restore_defaults
{} {
4565 global font_descs default_config repo_config
4566 global repo_config_new global_config_new
4568 foreach name
[array names default_config
] {
4569 set repo_config_new
($name) $default_config($name)
4570 set global_config_new
($name) $default_config($name)
4573 foreach option
$font_descs {
4574 set name
[lindex
$option 0]
4575 set repo_config
(gui.
$name) $default_config(gui.
$name)
4579 foreach option
$font_descs {
4580 set name
[lindex
$option 0]
4581 set font
[lindex
$option 1]
4582 set global_config_new
(gui.
$font^^family
) \
4583 [font configure
$font -family]
4584 set global_config_new
(gui.
$font^^size
) \
4585 [font configure
$font -size]
4589 proc do_save_config
{w
} {
4590 if {[catch
{save_config
} err
]} {
4591 error_popup
"Failed to completely save options:\n\n$err"
4597 proc do_windows_shortcut
{} {
4600 set fn
[tk_getSaveFile \
4602 -title "[appname] ([reponame]): Create Desktop Icon" \
4603 -initialfile "Git [reponame].bat"]
4607 puts
$fd "@ECHO Entering [reponame]"
4608 puts
$fd "@ECHO Starting git-gui... please wait..."
4609 puts
$fd "@SET PATH=[file normalize [gitexec]];%PATH%"
4610 puts
$fd "@SET GIT_DIR=[file normalize [gitdir]]"
4611 puts
-nonewline $fd "@\"[info nameofexecutable]\""
4612 puts
$fd " \"[file normalize $argv0]\""
4615 error_popup
"Cannot write script:\n\n$err"
4620 proc do_cygwin_shortcut
{} {
4624 set desktop
[exec cygpath \
4632 set fn
[tk_getSaveFile \
4634 -title "[appname] ([reponame]): Create Desktop Icon" \
4635 -initialdir $desktop \
4636 -initialfile "Git [reponame].bat"]
4640 set sh
[exec cygpath \
4644 set me
[exec cygpath \
4648 set gd
[exec cygpath \
4652 set gw
[exec cygpath \
4655 [file dirname [gitdir
]]]
4656 regsub
-all ' $me "'\\''" me
4657 regsub -all ' $gd "'\\''" gd
4658 puts $fd "@ECHO Entering $gw"
4659 puts $fd "@ECHO Starting git-gui... please wait..."
4660 puts -nonewline $fd "@\"$sh\" --login -c \""
4661 puts -nonewline $fd "GIT_DIR='$gd'"
4662 puts -nonewline $fd " '$me'"
4666 error_popup "Cannot write script:\n\n$err"
4671 proc do_macosx_app {} {
4674 set fn [tk_getSaveFile \
4676 -title "[appname] ([reponame]): Create Desktop Icon" \
4677 -initialdir [file join $env(HOME) Desktop] \
4678 -initialfile "Git [reponame].app"]
4681 set Contents [file join $fn Contents]
4682 set MacOS [file join $Contents MacOS]
4683 set exe [file join $MacOS git-gui]
4687 set fd [open [file join $Contents Info.plist] w]
4688 puts $fd {<?xml version="1.0" encoding="UTF-8"?>
4689 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4690 <plist version="1.0">
4692 <key>CFBundleDevelopmentRegion</key>
4693 <string>English</string>
4694 <key>CFBundleExecutable</key>
4695 <string>git-gui</string>
4696 <key>CFBundleIdentifier</key>
4697 <string>org.spearce.git-gui</string>
4698 <key>CFBundleInfoDictionaryVersion</key>
4699 <string>6.0</string>
4700 <key>CFBundlePackageType</key>
4701 <string>APPL</string>
4702 <key>CFBundleSignature</key>
4703 <string>????</string>
4704 <key>CFBundleVersion</key>
4705 <string>1.0</string>
4706 <key>NSPrincipalClass</key>
4707 <string>NSApplication</string>
4712 set fd [open $exe w]
4713 set gd [file normalize [gitdir]]
4714 set ep [file normalize [gitexec]]
4715 regsub -all ' $gd "'\\''" gd
4716 regsub
-all ' $ep "'\\''" ep
4717 puts $fd "#!/bin/sh"
4718 foreach name
[array names env
] {
4719 if {[string match GIT_
* $name]} {
4720 regsub
-all ' $env($name) "'\\''" v
4721 puts $fd "export $name='$v'"
4724 puts $fd "export PATH
='$ep':\
$PATH"
4725 puts $fd "export GIT_DIR
='$gd'"
4726 puts $fd "exec [file normalize
$argv0]"
4729 file attributes $exe -permissions u+x,g+x,o+x
4731 error_popup "Cannot
write icon
:\n\n$err"
4736 proc toggle_or_diff {w x y} {
4737 global file_states file_lists current_diff_path ui_index ui_workdir
4738 global last_clicked selected_paths
4740 set pos [split [$w index @$x,$y] .]
4741 set lno [lindex $pos 0]
4742 set col [lindex $pos 1]
4743 set path [lindex $file_lists($w) [expr {$lno - 1}]]
4749 set last_clicked [list $w $lno]
4750 array unset selected_paths
4751 $ui_index tag remove in_sel 0.0 end
4752 $ui_workdir tag remove in_sel 0.0 end
4755 if {$current_diff_path eq $path} {
4756 set after {reshow_diff;}
4760 if {$w eq $ui_index} {
4762 "Unstaging
[short_path
$path] from commit
" \
4764 [concat $after {set ui_status_value {Ready.}}]
4765 } elseif {$w eq $ui_workdir} {
4767 "Adding
[short_path
$path]" \
4769 [concat $after {set ui_status_value {Ready.}}]
4772 show_diff $path $w $lno
4776 proc add_one_to_selection {w x y} {
4777 global file_lists last_clicked selected_paths
4779 set lno [lindex [split [$w index @$x,$y] .] 0]
4780 set path [lindex $file_lists($w) [expr {$lno - 1}]]
4786 if {$last_clicked ne {}
4787 && [lindex $last_clicked 0] ne $w} {
4788 array unset selected_paths
4789 [lindex $last_clicked 0] tag remove in_sel 0.0 end
4792 set last_clicked [list $w $lno]
4793 if {[catch {set in_sel $selected_paths($path)}]} {
4797 unset selected_paths($path)
4798 $w tag remove in_sel $lno.0 [expr {$lno + 1}].0
4800 set selected_paths($path) 1
4801 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
4805 proc add_range_to_selection {w x y} {
4806 global file_lists last_clicked selected_paths
4808 if {[lindex $last_clicked 0] ne $w} {
4809 toggle_or_diff $w $x $y
4813 set lno [lindex [split [$w index @$x,$y] .] 0]
4814 set lc [lindex $last_clicked 1]
4823 foreach path [lrange $file_lists($w) \
4824 [expr {$begin - 1}] \
4825 [expr {$end - 1}]] {
4826 set selected_paths($path) 1
4828 $w tag add in_sel $begin.0 [expr {$end + 1}].0
4831 ######################################################################
4835 set cursor_ptr arrow
4836 font create font_diff -family Courier -size 10
4840 eval font configure font_ui [font actual [.dummy cget -font]]
4844 font create font_uibold
4845 font create font_diffbold
4850 } elseif {[is_MacOSX]} {
4858 proc apply_config {} {
4859 global repo_config font_descs
4861 foreach option $font_descs {
4862 set name [lindex $option 0]
4863 set font [lindex $option 1]
4865 foreach {cn cv} $repo_config(gui.$name) {
4866 font configure $font $cn $cv
4869 error_popup "Invalid font specified
in gui.
$name:\n\n$err"
4871 foreach {cn cv} [font configure $font] {
4872 font configure ${font}bold $cn $cv
4874 font configure ${font}bold -weight bold
4878 set default_config(merge.summary) false
4879 set default_config(merge.verbosity) 2
4880 set default_config(user.name) {}
4881 set default_config(user.email) {}
4883 set default_config(gui.trustmtime) false
4884 set default_config(gui.diffcontext) 5
4885 set default_config(gui.newbranchtemplate) {}
4886 set default_config(gui.fontui) [font configure font_ui]
4887 set default_config(gui.fontdiff) [font configure font_diff]
4889 {fontui font_ui {Main Font}}
4890 {fontdiff font_diff {Diff/Console Font}}
4895 ######################################################################
4901 menu .mbar -tearoff 0
4902 .mbar add cascade -label Repository -menu .mbar.repository
4903 .mbar add cascade -label Edit -menu .mbar.edit
4904 if {!$single_commit} {
4905 .mbar add cascade -label Branch -menu .mbar.branch
4907 .mbar add cascade -label Commit -menu .mbar.commit
4908 if {!$single_commit} {
4909 .mbar add cascade -label Merge -menu .mbar.merge
4910 .mbar add cascade -label Fetch -menu .mbar.fetch
4911 .mbar add cascade -label Push -menu .mbar.push
4913 . configure -menu .mbar
4915 # -- Repository Menu
4917 menu .mbar.repository
4919 .mbar.repository add command \
4920 -label {Browse Current Branch} \
4921 -command {new_browser $current_branch} \
4923 .mbar.repository add separator
4925 .mbar.repository add command \
4926 -label {Visualize Current Branch} \
4927 -command {do_gitk {}} \
4929 .mbar.repository add command \
4930 -label {Visualize All Branches} \
4931 -command {do_gitk {--all}} \
4933 .mbar.repository add separator
4935 if {!$single_commit} {
4936 .mbar.repository add command -label {Database Statistics} \
4940 .mbar.repository add command -label {Compress Database} \
4944 .mbar.repository add command -label {Verify Database} \
4945 -command do_fsck_objects \
4948 .mbar.repository add separator
4951 .mbar.repository add command \
4952 -label {Create Desktop Icon} \
4953 -command do_cygwin_shortcut \
4955 } elseif {[is_Windows]} {
4956 .mbar.repository add command \
4957 -label {Create Desktop Icon} \
4958 -command do_windows_shortcut \
4960 } elseif {[is_MacOSX]} {
4961 .mbar.repository add command \
4962 -label {Create Desktop Icon} \
4963 -command do_macosx_app \
4968 .mbar.repository add command -label Quit \
4970 -accelerator $M1T-Q \
4976 .mbar.edit add command -label Undo \
4977 -command {catch {[focus] edit undo}} \
4978 -accelerator $M1T-Z \
4980 .mbar.edit add command -label Redo \
4981 -command {catch {[focus] edit redo}} \
4982 -accelerator $M1T-Y \
4984 .mbar.edit add separator
4985 .mbar.edit add command -label Cut \
4986 -command {catch {tk_textCut [focus]}} \
4987 -accelerator $M1T-X \
4989 .mbar.edit add command -label Copy \
4990 -command {catch {tk_textCopy [focus]}} \
4991 -accelerator $M1T-C \
4993 .mbar.edit add command -label Paste \
4994 -command {catch {tk_textPaste [focus]; [focus] see insert}} \
4995 -accelerator $M1T-V \
4997 .mbar.edit add command -label Delete \
4998 -command {catch {[focus] delete sel.first sel.last}} \
5001 .mbar.edit add separator
5002 .mbar.edit add command -label {Select All} \
5003 -command {catch {[focus] tag add sel 0.0 end}} \
5004 -accelerator $M1T-A \
5009 if {!$single_commit} {
5012 .mbar.branch add command -label {Create...} \
5013 -command do_create_branch \
5014 -accelerator $M1T-N \
5016 lappend disable_on_lock [list .mbar.branch entryconf \
5017 [.mbar.branch index last] -state]
5019 .mbar.branch add command -label {Delete...} \
5020 -command do_delete_branch \
5022 lappend disable_on_lock [list .mbar.branch entryconf \
5023 [.mbar.branch index last] -state]
5030 .mbar.commit add radiobutton \
5031 -label {New Commit} \
5032 -command do_select_commit_type \
5033 -variable selected_commit_type \
5036 lappend disable_on_lock \
5037 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5039 .mbar.commit add radiobutton \
5040 -label {Amend Last Commit} \
5041 -command do_select_commit_type \
5042 -variable selected_commit_type \
5045 lappend disable_on_lock \
5046 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5048 .mbar.commit add separator
5050 .mbar.commit add command -label Rescan \
5051 -command do_rescan \
5054 lappend disable_on_lock \
5055 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5057 .mbar.commit add command -label {Add To Commit} \
5058 -command do_add_selection \
5060 lappend disable_on_lock \
5061 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5063 .mbar.commit add command -label {Add All To Commit} \
5064 -command do_add_all \
5065 -accelerator $M1T-I \
5067 lappend disable_on_lock \
5068 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5070 .mbar.commit add command -label {Unstage From Commit} \
5071 -command do_unstage_selection \
5073 lappend disable_on_lock \
5074 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5076 .mbar.commit add command -label {Revert Changes} \
5077 -command do_revert_selection \
5079 lappend disable_on_lock \
5080 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5082 .mbar.commit add separator
5084 .mbar.commit add command -label {Sign Off} \
5085 -command do_signoff \
5086 -accelerator $M1T-S \
5089 .mbar.commit add command -label Commit \
5090 -command do_commit \
5091 -accelerator $M1T-Return \
5093 lappend disable_on_lock \
5094 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5097 # -- Apple Menu (Mac OS X only)
5099 .mbar add cascade -label Apple -menu .mbar.apple
5102 .mbar.apple add command -label "About
[appname
]" \
5105 .mbar.apple add command -label "[appname
] Options...
" \
5106 -command do_options \
5111 .mbar.edit add separator
5112 .mbar.edit add command -label {Options...} \
5113 -command do_options \
5118 if {[file exists /usr/local/miga/lib/gui-miga]
5119 && [file exists .pvcsrc]} {
5121 global ui_status_value
5122 if {![lock_index update]} return
5123 set cmd [list sh --login -c "/usr
/local
/miga
/lib
/gui-miga
\"[pwd]\""]
5124 set miga_fd [open "|
$cmd" r]
5125 fconfigure $miga_fd -blocking 0
5126 fileevent $miga_fd readable [list miga_done $miga_fd]
5127 set ui_status_value {Running miga...}
5129 proc miga_done {fd} {
5134 rescan [list set ui_status_value {Ready.}]
5137 .mbar add cascade -label Tools -menu .mbar.tools
5139 .mbar.tools add command -label "Migrate
" \
5142 lappend disable_on_lock \
5143 [list .mbar.tools entryconf [.mbar.tools index last] -state]
5149 .mbar add cascade -label Help -menu .mbar.help
5153 .mbar.help add command -label "About
[appname
]" \
5159 catch {set browser $repo_config(instaweb.browser)}
5160 set doc_path [file dirname [gitexec]]
5161 set doc_path [file join $doc_path Documentation index.html]
5164 set doc_path [exec cygpath --windows $doc_path]
5167 if {$browser eq {}} {
5170 } elseif {[is_Cygwin]} {
5171 set program_files [file dirname [exec cygpath --windir]]
5172 set program_files [file join $program_files {Program Files}]
5173 set firefox [file join $program_files {Mozilla Firefox} firefox.exe]
5174 set ie [file join $program_files {Internet Explorer} IEXPLORE.EXE]
5175 if {[file exists $firefox]} {
5176 set browser $firefox
5177 } elseif {[file exists $ie]} {
5180 unset program_files firefox ie
5184 if {[file isfile $doc_path]} {
5185 set doc_url "file:$doc_path"
5187 set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
5190 if {$browser ne {}} {
5191 .mbar.help add command -label {Online Documentation} \
5192 -command [list exec $browser $doc_url &] \
5195 unset browser doc_path doc_url
5203 -text {Current Branch:} \
5208 -textvariable current_branch \
5212 pack .branch.l1 -side left
5213 pack .branch.cb -side left -fill x
5214 pack .branch -side top -fill x
5216 if {!$single_commit} {
5218 .mbar.merge add command -label {Local Merge...} \
5219 -command do_local_merge \
5221 lappend disable_on_lock \
5222 [list .mbar.merge entryconf [.mbar.merge index last] -state]
5223 .mbar.merge add command -label {Abort Merge...} \
5224 -command do_reset_hard \
5226 lappend disable_on_lock \
5227 [list .mbar.merge entryconf [.mbar.merge index last] -state]
5233 .mbar.push add command -label {Push...} \
5234 -command do_push_anywhere \
5238 # -- Main Window Layout
5240 panedwindow .vpane -orient vertical
5241 panedwindow .vpane.files -orient horizontal
5242 .vpane add .vpane.files -sticky nsew -height 100 -width 200
5243 pack .vpane -anchor n -side top -fill both -expand 1
5245 # -- Index File List
5247 frame .vpane.files.index -height 100 -width 200
5248 label .vpane.files.index.title -text {Changes To Be Committed} \
5251 text $ui_index -background white -borderwidth 0 \
5252 -width 20 -height 10 \
5255 -cursor $cursor_ptr \
5256 -xscrollcommand {.vpane.files.index.sx set} \
5257 -yscrollcommand {.vpane.files.index.sy set} \
5259 scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview]
5260 scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview]
5261 pack .vpane.files.index.title -side top -fill x
5262 pack .vpane.files.index.sx -side bottom -fill x
5263 pack .vpane.files.index.sy -side right -fill y
5264 pack $ui_index -side left -fill both -expand 1
5265 .vpane.files add .vpane.files.index -sticky nsew
5267 # -- Working Directory File List
5269 frame .vpane.files.workdir -height 100 -width 200
5270 label .vpane.files.workdir.title -text {Changed But Not Updated} \
5273 text $ui_workdir -background white -borderwidth 0 \
5274 -width 20 -height 10 \
5277 -cursor $cursor_ptr \
5278 -xscrollcommand {.vpane.files.workdir.sx set} \
5279 -yscrollcommand {.vpane.files.workdir.sy set} \
5281 scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
5282 scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
5283 pack .vpane.files.workdir.title -side top -fill x
5284 pack .vpane.files.workdir.sx -side bottom -fill x
5285 pack .vpane.files.workdir.sy -side right -fill y
5286 pack $ui_workdir -side left -fill both -expand 1
5287 .vpane.files add .vpane.files.workdir -sticky nsew
5289 foreach i [list $ui_index $ui_workdir] {
5290 $i tag conf in_diff -font font_uibold
5291 $i tag conf in_sel \
5292 -background [$i cget -foreground] \
5293 -foreground [$i cget -background]
5297 # -- Diff and Commit Area
5299 frame .vpane.lower -height 300 -width 400
5300 frame .vpane.lower.commarea
5301 frame .vpane.lower.diff -relief sunken -borderwidth 1
5302 pack .vpane.lower.commarea -side top -fill x
5303 pack .vpane.lower.diff -side bottom -fill both -expand 1
5304 .vpane add .vpane.lower -sticky nsew
5306 # -- Commit Area Buttons
5308 frame .vpane.lower.commarea.buttons
5309 label .vpane.lower.commarea.buttons.l -text {} \
5313 pack .vpane.lower.commarea.buttons.l -side top -fill x
5314 pack .vpane.lower.commarea.buttons -side left -fill y
5316 button .vpane.lower.commarea.buttons.rescan -text {Rescan} \
5317 -command do_rescan \
5319 pack .vpane.lower.commarea.buttons.rescan -side top -fill x
5320 lappend disable_on_lock \
5321 {.vpane.lower.commarea.buttons.rescan conf -state}
5323 button .vpane.lower.commarea.buttons.incall -text {Add All} \
5324 -command do_add_all \
5326 pack .vpane.lower.commarea.buttons.incall -side top -fill x
5327 lappend disable_on_lock \
5328 {.vpane.lower.commarea.buttons.incall conf -state}
5330 button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \
5331 -command do_signoff \
5333 pack .vpane.lower.commarea.buttons.signoff -side top -fill x
5335 button .vpane.lower.commarea.buttons.commit -text {Commit} \
5336 -command do_commit \
5338 pack .vpane.lower.commarea.buttons.commit -side top -fill x
5339 lappend disable_on_lock \
5340 {.vpane.lower.commarea.buttons.commit conf -state}
5342 # -- Commit Message Buffer
5344 frame .vpane.lower.commarea.buffer
5345 frame .vpane.lower.commarea.buffer.header
5346 set ui_comm .vpane.lower.commarea.buffer.t
5347 set ui_coml .vpane.lower.commarea.buffer.header.l
5348 radiobutton .vpane.lower.commarea.buffer.header.new \
5349 -text {New Commit} \
5350 -command do_select_commit_type \
5351 -variable selected_commit_type \
5354 lappend disable_on_lock \
5355 [list .vpane.lower.commarea.buffer.header.new conf -state]
5356 radiobutton .vpane.lower.commarea.buffer.header.amend \
5357 -text {Amend Last Commit} \
5358 -command do_select_commit_type \
5359 -variable selected_commit_type \
5362 lappend disable_on_lock \
5363 [list .vpane.lower.commarea.buffer.header.amend conf -state]
5368 proc trace_commit_type {varname args} {
5369 global ui_coml commit_type
5370 switch -glob -- $commit_type {
5371 initial {set txt {Initial Commit Message:}}
5372 amend {set txt {Amended Commit Message:}}
5373 amend-initial {set txt {Amended Initial Commit Message:}}
5374 amend-merge {set txt {Amended Merge Commit Message:}}
5375 merge {set txt {Merge Commit Message:}}
5376 * {set txt {Commit Message:}}
5378 $ui_coml conf -text $txt
5380 trace add variable commit_type write trace_commit_type
5381 pack $ui_coml -side left -fill x
5382 pack .vpane.lower.commarea.buffer.header.amend -side right
5383 pack .vpane.lower.commarea.buffer.header.new -side right
5385 text $ui_comm -background white -borderwidth 1 \
5388 -autoseparators true \
5390 -width 75 -height 9 -wrap none \
5392 -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
5393 scrollbar .vpane.lower.commarea.buffer.sby \
5394 -command [list $ui_comm yview]
5395 pack .vpane.lower.commarea.buffer.header -side top -fill x
5396 pack .vpane.lower.commarea.buffer.sby -side right -fill y
5397 pack $ui_comm -side left -fill y
5398 pack .vpane.lower.commarea.buffer -side left -fill y
5400 # -- Commit Message Buffer Context Menu
5402 set ctxm .vpane.lower.commarea.buffer.ctxm
5403 menu $ctxm -tearoff 0
5407 -command {tk_textCut $ui_comm}
5411 -command {tk_textCopy $ui_comm}
5415 -command {tk_textPaste $ui_comm}
5419 -command {$ui_comm delete sel.first sel.last}
5422 -label {Select All} \
5424 -command {focus $ui_comm;$ui_comm tag add sel 0.0 end}
5429 $ui_comm tag add sel 0.0 end
5430 tk_textCopy $ui_comm
5431 $ui_comm tag remove sel 0.0 end
5438 bind_button3 $ui_comm "tk_popup
$ctxm %X
%Y
"
5442 set current_diff_path {}
5443 set current_diff_side {}
5444 set diff_actions [list]
5445 proc trace_current_diff_path {varname args} {
5446 global current_diff_path diff_actions file_states
5447 if {$current_diff_path eq {}} {
5453 set p $current_diff_path
5454 set s [mapdesc [lindex $file_states($p) 0] $p]
5456 set p [escape_path $p]
5460 .vpane.lower.diff.header.status configure -text $s
5461 .vpane.lower.diff.header.file configure -text $f
5462 .vpane.lower.diff.header.path configure -text $p
5463 foreach w $diff_actions {
5467 trace add variable current_diff_path write trace_current_diff_path
5469 frame .vpane.lower.diff.header -background orange
5470 label .vpane.lower.diff.header.status \
5471 -background orange \
5472 -width $max_status_desc \
5476 label .vpane.lower.diff.header.file \
5477 -background orange \
5481 label .vpane.lower.diff.header.path \
5482 -background orange \
5486 pack .vpane.lower.diff.header.status -side left
5487 pack .vpane.lower.diff.header.file -side left
5488 pack .vpane.lower.diff.header.path -fill x
5489 set ctxm .vpane.lower.diff.header.ctxm
5490 menu $ctxm -tearoff 0
5499 -- $current_diff_path
5501 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5502 bind_button3 .vpane.lower.diff.header.path "tk_popup
$ctxm %X
%Y
"
5506 frame .vpane.lower.diff.body
5507 set ui_diff .vpane.lower.diff.body.t
5508 text $ui_diff -background white -borderwidth 0 \
5509 -width 80 -height 15 -wrap none \
5511 -xscrollcommand {.vpane.lower.diff.body.sbx set} \
5512 -yscrollcommand {.vpane.lower.diff.body.sby set} \
5514 scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
5515 -command [list $ui_diff xview]
5516 scrollbar .vpane.lower.diff.body.sby -orient vertical \
5517 -command [list $ui_diff yview]
5518 pack .vpane.lower.diff.body.sbx -side bottom -fill x
5519 pack .vpane.lower.diff.body.sby -side right -fill y
5520 pack $ui_diff -side left -fill both -expand 1
5521 pack .vpane.lower.diff.header -side top -fill x
5522 pack .vpane.lower.diff.body -side bottom -fill both -expand 1
5524 $ui_diff tag conf d_cr -elide true
5525 $ui_diff tag conf d_@ -foreground blue -font font_diffbold
5526 $ui_diff tag conf d_+ -foreground {#00a000}
5527 $ui_diff tag conf d_- -foreground red
5529 $ui_diff tag conf d_++ -foreground {#00a000}
5530 $ui_diff tag conf d_-- -foreground red
5531 $ui_diff tag conf d_+s \
5532 -foreground {#00a000} \
5533 -background {#e2effa}
5534 $ui_diff tag conf d_-s \
5536 -background {#e2effa}
5537 $ui_diff tag conf d_s+ \
5538 -foreground {#00a000} \
5540 $ui_diff tag conf d_s- \
5544 $ui_diff tag conf d<<<<<<< \
5545 -foreground orange \
5547 $ui_diff tag conf d======= \
5548 -foreground orange \
5550 $ui_diff tag conf d>>>>>>> \
5551 -foreground orange \
5554 $ui_diff tag raise sel
5556 # -- Diff Body Context Menu
5558 set ctxm .vpane.lower.diff.body.ctxm
5559 menu $ctxm -tearoff 0
5563 -command reshow_diff
5564 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5568 -command {tk_textCopy $ui_diff}
5569 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5571 -label {Select All} \
5573 -command {focus $ui_diff;$ui_diff tag add sel 0.0 end}
5574 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5579 $ui_diff tag add sel 0.0 end
5580 tk_textCopy $ui_diff
5581 $ui_diff tag remove sel 0.0 end
5583 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5586 -label {Apply/Reverse Hunk} \
5588 -command {apply_hunk $cursorX $cursorY}
5589 set ui_diff_applyhunk [$ctxm index last]
5590 lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
5593 -label {Decrease Font Size} \
5595 -command {incr_font_size font_diff -1}
5596 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5598 -label {Increase Font Size} \
5600 -command {incr_font_size font_diff 1}
5601 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5604 -label {Show Less Context} \
5606 -command {if {$repo_config(gui.diffcontext) >= 2} {
5607 incr repo_config(gui.diffcontext) -1
5610 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5612 -label {Show More Context} \
5615 incr repo_config(gui.diffcontext)
5618 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5620 $ctxm add command -label {Options...} \
5623 bind_button3 $ui_diff "
5626 if {\
$ui_index eq \
$current_diff_side} {
5627 $ctxm entryconf
$ui_diff_applyhunk -label {Unstage Hunk From Commit
}
5629 $ctxm entryconf
$ui_diff_applyhunk -label {Stage Hunk For Commit
}
5631 tk_popup
$ctxm %X
%Y
5633 unset ui_diff_applyhunk
5637 set ui_status_value {Initializing...}
5638 label .status -textvariable ui_status_value \
5644 pack .status -anchor w -side bottom -fill x
5649 set gm $repo_config(gui.geometry)
5650 wm geometry . [lindex $gm 0]
5651 .vpane sash place 0 \
5652 [lindex [.vpane sash coord 0] 0] \
5654 .vpane.files sash place 0 \
5656 [lindex [.vpane.files sash coord 0] 1]
5662 bind $ui_comm <$M1B-Key-Return> {do_commit;break}
5663 bind $ui_comm <$M1B-Key-i> {do_add_all;break}
5664 bind $ui_comm <$M1B-Key-I> {do_add_all;break}
5665 bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
5666 bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
5667 bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
5668 bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
5669 bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
5670 bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
5671 bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
5672 bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
5674 bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
5675 bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
5676 bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
5677 bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
5678 bind $ui_diff <$M1B-Key-v> {break}
5679 bind $ui_diff <$M1B-Key-V> {break}
5680 bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
5681 bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
5682 bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
5683 bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
5684 bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
5685 bind $ui_diff <Key-Right> {catch {%W xview scroll 1 units};break}
5686 bind $ui_diff <Button-1> {focus %W}
5688 if {!$single_commit} {
5689 bind . <$M1B-Key-n> do_create_branch
5690 bind . <$M1B-Key-N> do_create_branch
5693 bind . <Destroy> do_quit
5694 bind all <Key-F5> do_rescan
5695 bind all <$M1B-Key-r> do_rescan
5696 bind all <$M1B-Key-R> do_rescan
5697 bind . <$M1B-Key-s> do_signoff
5698 bind . <$M1B-Key-S> do_signoff
5699 bind . <$M1B-Key-i> do_add_all
5700 bind . <$M1B-Key-I> do_add_all
5701 bind . <$M1B-Key-Return> do_commit
5702 bind all <$M1B-Key-q> do_quit
5703 bind all <$M1B-Key-Q> do_quit
5704 bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
5705 bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
5706 foreach i [list $ui_index $ui_workdir] {
5707 bind $i <Button-1> "toggle_or_diff
$i %x
%y
; break"
5708 bind $i <$M1B-Button-1> "add_one_to_selection
$i %x
%y
; break"
5709 bind $i <Shift-Button-1> "add_range_to_selection
$i %x
%y
; break"
5713 set file_lists($ui_index) [list]
5714 set file_lists($ui_workdir) [list]
5718 set MERGE_HEAD [list]
5721 set current_branch {}
5722 set current_diff_path {}
5723 set selected_commit_type new
5725 wm title . "[appname
] ([file normalize
[file dirname [gitdir
]]])"
5726 focus -force $ui_comm
5728 # -- Warn the user about environmental problems. Cygwin's Tcl
5729 # does *not* pass its env array onto any processes it spawns.
5730 # This means that git processes get none of our environment.
5735 set msg "Possible environment issues exist.
5737 The following environment variables are probably
5738 going to be ignored by any Git subprocess run
5742 foreach name [array names env] {
5743 switch -regexp -- $name {
5744 {^GIT_INDEX_FILE$} -
5745 {^GIT_OBJECT_DIRECTORY$} -
5746 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} -
5748 {^GIT_EXTERNAL_DIFF$} -
5752 {^GIT_CONFIG_LOCAL$} -
5753 {^GIT_(AUTHOR|COMMITTER)_DATE$} {
5754 append msg " - $name\n"
5757 {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} {
5758 append msg " - $name\n"
5760 set suggest_user $name
5764 if {$ignored_env > 0} {
5766 This is due to a known issue with the
5767 Tcl binary distributed by Cygwin.
"
5769 if {$suggest_user ne {}} {
5772 A good replacement
for $suggest_user
5773 is placing values
for the user.name and
5774 user.email settings into your personal
5780 unset ignored_env msg suggest_user name
5783 # -- Only initialize complex UI if we are going to stay running.
5785 if {!$single_commit} {
5789 populate_branch_menu
5794 # -- Only suggest a gc run if we are going to stay running.
5796 if {!$single_commit} {
5797 set object_limit 2000
5798 if {[is_Windows]} {set object_limit 200}
5799 regexp {^([0-9]+) objects,} [exec git count-objects] _junk objects_current
5800 if {$objects_current >= $object_limit} {
5802 "This repository currently has
$objects_current loose objects.
5804 To maintain optimal performance it is strongly
5805 recommended that you
compress the database
5806 when
more than
$object_limit loose objects exist.
5808 Compress the database now?
"] eq yes} {
5812 unset object_limit _junk objects_current
5815 lock_index begin-read