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
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 {$honor_trustmtime && $repo_config(gui.trustmtime
) eq
{true
}} {
431 rescan_stage2
{} $after
434 set ui_status_value
{Refreshing
file status...
}
435 set cmd
[list git update-index
]
437 lappend cmd
--unmerged
438 lappend cmd
--ignore-missing
439 lappend cmd
--refresh
440 set fd_rf
[open
"| $cmd" r
]
441 fconfigure
$fd_rf -blocking 0 -translation binary
442 fileevent
$fd_rf readable \
443 [list rescan_stage2
$fd_rf $after]
447 proc rescan_stage2
{fd after
} {
448 global ui_status_value
449 global rescan_active buf_rdi buf_rdf buf_rlo
453 if {![eof
$fd]} return
457 set ls_others
[list | git ls-files
--others -z \
458 --exclude-per-directory=.gitignore
]
459 set info_exclude
[gitdir info exclude
]
460 if {[file readable
$info_exclude]} {
461 lappend ls_others
"--exclude-from=$info_exclude"
469 set ui_status_value
{Scanning
for modified files ...
}
470 set fd_di
[open
"| git diff-index --cached -z [PARENT]" r
]
471 set fd_df
[open
"| git diff-files -z" r
]
472 set fd_lo
[open
$ls_others r
]
474 fconfigure
$fd_di -blocking 0 -translation binary
-encoding binary
475 fconfigure
$fd_df -blocking 0 -translation binary
-encoding binary
476 fconfigure
$fd_lo -blocking 0 -translation binary
-encoding binary
477 fileevent
$fd_di readable
[list read_diff_index
$fd_di $after]
478 fileevent
$fd_df readable
[list read_diff_files
$fd_df $after]
479 fileevent
$fd_lo readable
[list read_ls_others
$fd_lo $after]
482 proc load_message
{file} {
486 if {[file isfile
$f]} {
487 if {[catch
{set fd
[open
$f r
]}]} {
490 set content
[string trim
[read $fd]]
492 regsub
-all -line {[ \r\t]+$
} $content {} content
493 $ui_comm delete
0.0 end
494 $ui_comm insert end
$content
500 proc read_diff_index
{fd after
} {
503 append buf_rdi
[read $fd]
505 set n
[string length
$buf_rdi]
507 set z1
[string first
"\0" $buf_rdi $c]
510 set z2
[string first
"\0" $buf_rdi $z1]
514 set i
[split [string range
$buf_rdi $c [expr {$z1 - 2}]] { }]
515 set p
[string range
$buf_rdi $z1 [expr {$z2 - 1}]]
517 [encoding convertfrom
$p] \
519 [list
[lindex
$i 0] [lindex
$i 2]] \
525 set buf_rdi
[string range
$buf_rdi $c end
]
530 rescan_done
$fd buf_rdi
$after
533 proc read_diff_files
{fd after
} {
536 append buf_rdf
[read $fd]
538 set n
[string length
$buf_rdf]
540 set z1
[string first
"\0" $buf_rdf $c]
543 set z2
[string first
"\0" $buf_rdf $z1]
547 set i
[split [string range
$buf_rdf $c [expr {$z1 - 2}]] { }]
548 set p
[string range
$buf_rdf $z1 [expr {$z2 - 1}]]
550 [encoding convertfrom
$p] \
553 [list
[lindex
$i 0] [lindex
$i 2]]
558 set buf_rdf
[string range
$buf_rdf $c end
]
563 rescan_done
$fd buf_rdf
$after
566 proc read_ls_others
{fd after
} {
569 append buf_rlo
[read $fd]
570 set pck
[split $buf_rlo "\0"]
571 set buf_rlo
[lindex
$pck end
]
572 foreach p
[lrange
$pck 0 end-1
] {
573 merge_state
[encoding convertfrom
$p] ?O
575 rescan_done
$fd buf_rlo
$after
578 proc rescan_done
{fd buf after
} {
580 global file_states repo_config
583 if {![eof
$fd]} return
586 if {[incr rescan_active
-1] > 0} return
595 proc prune_selection
{} {
596 global file_states selected_paths
598 foreach path
[array names selected_paths
] {
599 if {[catch
{set still_here
$file_states($path)}]} {
600 unset selected_paths
($path)
605 ######################################################################
610 global ui_diff current_diff_path current_diff_header
611 global ui_index ui_workdir
613 $ui_diff conf
-state normal
614 $ui_diff delete
0.0 end
615 $ui_diff conf
-state disabled
617 set current_diff_path
{}
618 set current_diff_header
{}
620 $ui_index tag remove in_diff
0.0 end
621 $ui_workdir tag remove in_diff
0.0 end
624 proc reshow_diff
{} {
625 global ui_status_value file_states file_lists
626 global current_diff_path current_diff_side
628 set p
$current_diff_path
630 ||
$current_diff_side eq
{}
631 ||
[catch
{set s
$file_states($p)}]
632 ||
[lsearch
-sorted -exact $file_lists($current_diff_side) $p] == -1} {
635 show_diff
$p $current_diff_side
639 proc handle_empty_diff
{} {
640 global current_diff_path file_states file_lists
642 set path
$current_diff_path
643 set s
$file_states($path)
644 if {[lindex
$s 0] ne
{_M
}} return
646 info_popup
"No differences detected.
648 [short_path $path] has no changes.
650 The modification date of this file was updated
651 by another application, but the content within
652 the file was not changed.
654 A rescan will be automatically started to find
655 other files which may have the same state."
658 display_file
$path __
659 rescan
{set ui_status_value
{Ready.
}} 0
662 proc show_diff
{path w
{lno
{}}} {
663 global file_states file_lists
664 global is_3way_diff diff_active repo_config
665 global ui_diff ui_status_value ui_index ui_workdir
666 global current_diff_path current_diff_side current_diff_header
668 if {$diff_active ||
![lock_index
read]} return
672 set lno
[lsearch
-sorted -exact $file_lists($w) $path]
678 $w tag add in_diff
$lno.0 [expr {$lno + 1}].0
681 set s
$file_states($path)
685 set current_diff_path
$path
686 set current_diff_side
$w
687 set current_diff_header
{}
688 set ui_status_value
"Loading diff of [escape_path $path]..."
690 # - Git won't give us the diff, there's nothing to compare to!
693 set max_sz
[expr {128 * 1024}]
695 set fd
[open
$path r
]
696 set content
[read $fd $max_sz]
698 set sz
[file size
$path]
702 set ui_status_value
"Unable to display [escape_path $path]"
703 error_popup
"Error loading file:\n\n$err"
706 $ui_diff conf
-state normal
707 if {![catch
{set type [exec file $path]}]} {
708 set n
[string length
$path]
709 if {[string equal
-length $n $path $type]} {
710 set type [string range
$type $n end
]
711 regsub
{^
:?\s
*} $type {} type
713 $ui_diff insert end
"* $type\n" d_@
715 if {[string first
"\0" $content] != -1} {
716 $ui_diff insert end \
717 "* Binary file (not showing content)." \
721 $ui_diff insert end \
722 "* Untracked file is $sz bytes.
723 * Showing only first $max_sz bytes.
726 $ui_diff insert end
$content
728 $ui_diff insert end
"
729 * Untracked file clipped here by [appname].
730 * To see the entire file, use an external editor.
734 $ui_diff conf
-state disabled
737 set ui_status_value
{Ready.
}
742 if {$w eq
$ui_index} {
743 lappend cmd diff-index
745 } elseif
{$w eq
$ui_workdir} {
746 if {[string index
$m 0] eq
{U
}} {
749 lappend cmd diff-files
754 lappend cmd
--no-color
755 if {$repo_config(gui.diffcontext
) > 0} {
756 lappend cmd
"-U$repo_config(gui.diffcontext)"
758 if {$w eq
$ui_index} {
764 if {[catch
{set fd
[open
$cmd r
]} err
]} {
767 set ui_status_value
"Unable to display [escape_path $path]"
768 error_popup
"Error loading diff:\n\n$err"
776 fileevent
$fd readable
[list read_diff
$fd]
779 proc read_diff
{fd
} {
780 global ui_diff ui_status_value diff_active
781 global is_3way_diff current_diff_header
783 $ui_diff conf
-state normal
784 while {[gets
$fd line
] >= 0} {
785 # -- Cleanup uninteresting diff header lines.
787 if { [string match
{diff --git *} $line]
788 ||
[string match
{diff --cc *} $line]
789 ||
[string match
{diff --combined *} $line]
790 ||
[string match
{--- *} $line]
791 ||
[string match
{+++ *} $line]} {
792 append current_diff_header
$line "\n"
795 if {[string match
{index
*} $line]} continue
796 if {$line eq
{deleted
file mode
120000}} {
797 set line
"deleted symlink"
800 # -- Automatically detect if this is a 3 way diff.
802 if {[string match
{@@@
*} $line]} {set is_3way_diff
1}
804 if {[string match
{mode
*} $line]
805 ||
[string match
{new
file *} $line]
806 ||
[string match
{deleted
file *} $line]
807 ||
[string match
{Binary files
* and
* differ
} $line]
808 ||
$line eq
{\ No newline
at end of
file}
809 ||
[regexp
{^\
* Unmerged path
} $line]} {
811 } elseif
{$is_3way_diff} {
812 set op
[string range
$line 0 1]
822 if {[regexp
{^\
+\
+([<>]{7} |
={7})} $line _g op
]} {
823 set line
[string replace
$line 0 1 { }]
830 puts
"error: Unhandled 3 way diff marker: {$op}"
835 set op
[string index
$line 0]
841 if {[regexp
{^\
+([<>]{7} |
={7})} $line _g op
]} {
842 set line
[string replace
$line 0 0 { }]
849 puts
"error: Unhandled 2 way diff marker: {$op}"
854 $ui_diff insert end
$line $tags
855 if {[string index
$line end
] eq
"\r"} {
856 $ui_diff tag add d_cr
{end
- 2c
}
858 $ui_diff insert end
"\n" $tags
860 $ui_diff conf
-state disabled
866 set ui_status_value
{Ready.
}
868 if {[$ui_diff index end
] eq
{2.0}} {
874 proc apply_hunk
{x y
} {
875 global current_diff_path current_diff_header current_diff_side
876 global ui_diff ui_index file_states
878 if {$current_diff_path eq
{} ||
$current_diff_header eq
{}} return
879 if {![lock_index apply_hunk
]} return
881 set apply_cmd
{git apply
--cached --whitespace=nowarn
}
882 set mi
[lindex
$file_states($current_diff_path) 0]
883 if {$current_diff_side eq
$ui_index} {
885 lappend apply_cmd
--reverse
886 if {[string index
$mi 0] ne
{M
}} {
892 if {[string index
$mi 1] ne
{M
}} {
898 set s_lno
[lindex
[split [$ui_diff index @
$x,$y] .
] 0]
899 set s_lno
[$ui_diff search
-backwards -regexp ^@@
$s_lno.0 0.0]
905 set e_lno
[$ui_diff search
-forwards -regexp ^@@
"$s_lno + 1 lines" end
]
911 set p
[open
"| $apply_cmd" w
]
912 fconfigure
$p -translation binary
-encoding binary
913 puts
-nonewline $p $current_diff_header
914 puts
-nonewline $p [$ui_diff get
$s_lno $e_lno]
916 error_popup
"Failed to $mode selected hunk.\n\n$err"
921 $ui_diff conf
-state normal
922 $ui_diff delete
$s_lno $e_lno
923 $ui_diff conf
-state disabled
925 if {[$ui_diff get
1.0 end
] eq
"\n"} {
931 if {$current_diff_side eq
$ui_index} {
933 } elseif
{[string index
$mi 0] eq
{_
}} {
939 display_file
$current_diff_path $mi
945 ######################################################################
949 proc load_last_commit
{} {
950 global HEAD PARENT MERGE_HEAD commit_type ui_comm
953 if {[llength
$PARENT] == 0} {
954 error_popup
{There is nothing to amend.
956 You are about to create the initial commit.
957 There is no commit before this to amend.
962 repository_state curType curHEAD curMERGE_HEAD
963 if {$curType eq
{merge
}} {
964 error_popup
{Cannot amend
while merging.
966 You are currently
in the middle of a merge that
967 has not been fully completed. You cannot amend
968 the prior commit unless you first abort the
969 current merge activity.
977 set fd
[open
"| git cat-file commit $curHEAD" r
]
978 fconfigure
$fd -encoding binary
-translation lf
979 if {[catch
{set enc
$repo_config(i18n.commitencoding
)}]} {
982 while {[gets
$fd line
] > 0} {
983 if {[string match
{parent
*} $line]} {
984 lappend parents
[string range
$line 7 end
]
985 } elseif
{[string match
{encoding
*} $line]} {
986 set enc
[string tolower
[string range
$line 9 end
]]
989 fconfigure
$fd -encoding $enc
990 set msg
[string trim
[read $fd]]
993 error_popup
"Error loading commit data for amend:\n\n$err"
999 set MERGE_HEAD
[list
]
1000 switch
-- [llength
$parents] {
1001 0 {set commit_type amend-initial
}
1002 1 {set commit_type amend
}
1003 default
{set commit_type amend-merge
}
1006 $ui_comm delete
0.0 end
1007 $ui_comm insert end
$msg
1009 $ui_comm edit modified false
1010 rescan
{set ui_status_value
{Ready.
}}
1013 proc create_new_commit
{} {
1014 global commit_type ui_comm
1016 set commit_type normal
1017 $ui_comm delete
0.0 end
1019 $ui_comm edit modified false
1020 rescan
{set ui_status_value
{Ready.
}}
1023 set GIT_COMMITTER_IDENT
{}
1025 proc committer_ident
{} {
1026 global GIT_COMMITTER_IDENT
1028 if {$GIT_COMMITTER_IDENT eq
{}} {
1029 if {[catch
{set me
[exec git var GIT_COMMITTER_IDENT
]} err
]} {
1030 error_popup
"Unable to obtain your identity:\n\n$err"
1033 if {![regexp
{^
(.
*) [0-9]+ [-+0-9]+$
} \
1034 $me me GIT_COMMITTER_IDENT
]} {
1035 error_popup
"Invalid GIT_COMMITTER_IDENT:\n\n$me"
1040 return $GIT_COMMITTER_IDENT
1043 proc commit_tree
{} {
1044 global HEAD commit_type file_states ui_comm repo_config
1045 global ui_status_value pch_error
1047 if {[committer_ident
] eq
{}} return
1048 if {![lock_index update
]} return
1050 # -- Our in memory state should match the repository.
1052 repository_state curType curHEAD curMERGE_HEAD
1053 if {[string match amend
* $commit_type]
1054 && $curType eq
{normal
}
1055 && $curHEAD eq
$HEAD} {
1056 } elseif
{$commit_type ne
$curType ||
$HEAD ne
$curHEAD} {
1057 info_popup
{Last scanned state does not match repository state.
1059 Another Git program has modified this repository
1060 since the last scan. A rescan must be performed
1061 before another commit can be created.
1063 The rescan will be automatically started now.
1066 rescan
{set ui_status_value
{Ready.
}}
1070 # -- At least one file should differ in the index.
1073 foreach path
[array names file_states
] {
1074 switch
-glob -- [lindex
$file_states($path) 0] {
1078 M?
{set files_ready
1}
1080 error_popup
"Unmerged files cannot be committed.
1082 File [short_path $path] has merge conflicts.
1083 You must resolve them and add the file before committing.
1089 error_popup
"Unknown file state [lindex $s 0] detected.
1091 File [short_path $path] cannot be committed by this program.
1096 if {!$files_ready} {
1097 info_popup
{No changes to commit.
1099 You must add
at least
1 file before you can commit.
1105 # -- A message is required.
1107 set msg
[string trim
[$ui_comm get
1.0 end
]]
1108 regsub
-all -line {[ \t\r]+$
} $msg {} msg
1110 error_popup
{Please supply a commit message.
1112 A good commit message has the following format
:
1114 - First line
: Describe
in one sentance what you did.
1115 - Second line
: Blank
1116 - Remaining lines
: Describe why this change is good.
1122 # -- Run the pre-commit hook.
1124 set pchook
[gitdir hooks pre-commit
]
1126 # On Cygwin [file executable] might lie so we need to ask
1127 # the shell if the hook is executable. Yes that's annoying.
1129 if {[is_Cygwin
] && [file isfile
$pchook]} {
1130 set pchook
[list sh
-c [concat \
1131 "if test -x \"$pchook\";" \
1132 "then exec \"$pchook\" 2>&1;" \
1134 } elseif
{[file executable
$pchook]} {
1135 set pchook
[list
$pchook |
& cat]
1137 commit_writetree
$curHEAD $msg
1141 set ui_status_value
{Calling pre-commit hook...
}
1143 set fd_ph
[open
"| $pchook" r
]
1144 fconfigure
$fd_ph -blocking 0 -translation binary
1145 fileevent
$fd_ph readable \
1146 [list commit_prehook_wait
$fd_ph $curHEAD $msg]
1149 proc commit_prehook_wait
{fd_ph curHEAD msg
} {
1150 global pch_error ui_status_value
1152 append pch_error
[read $fd_ph]
1153 fconfigure
$fd_ph -blocking 1
1155 if {[catch
{close
$fd_ph}]} {
1156 set ui_status_value
{Commit declined by pre-commit hook.
}
1157 hook_failed_popup pre-commit
$pch_error
1160 commit_writetree
$curHEAD $msg
1165 fconfigure
$fd_ph -blocking 0
1168 proc commit_writetree
{curHEAD msg
} {
1169 global ui_status_value
1171 set ui_status_value
{Committing changes...
}
1172 set fd_wt
[open
"| git write-tree" r
]
1173 fileevent
$fd_wt readable \
1174 [list commit_committree
$fd_wt $curHEAD $msg]
1177 proc commit_committree
{fd_wt curHEAD msg
} {
1178 global HEAD PARENT MERGE_HEAD commit_type
1179 global single_commit all_heads current_branch
1180 global ui_status_value ui_comm selected_commit_type
1181 global file_states selected_paths rescan_active
1185 if {$tree_id eq
{} ||
[catch
{close
$fd_wt} err
]} {
1186 error_popup
"write-tree failed:\n\n$err"
1187 set ui_status_value
{Commit failed.
}
1192 # -- Build the message.
1194 set msg_p
[gitdir COMMIT_EDITMSG
]
1195 set msg_wt
[open
$msg_p w
]
1196 if {[catch
{set enc
$repo_config(i18n.commitencoding
)}]} {
1199 fconfigure
$msg_wt -encoding $enc -translation binary
1200 puts
-nonewline $msg_wt $msg
1203 # -- Create the commit.
1205 set cmd
[list git commit-tree
$tree_id]
1206 set parents
[concat
$PARENT $MERGE_HEAD]
1207 if {[llength
$parents] > 0} {
1208 foreach p
$parents {
1212 # git commit-tree writes to stderr during initial commit.
1213 lappend cmd
2>/dev
/null
1216 if {[catch
{set cmt_id
[eval exec $cmd]} err
]} {
1217 error_popup
"commit-tree failed:\n\n$err"
1218 set ui_status_value
{Commit failed.
}
1223 # -- Update the HEAD ref.
1226 if {$commit_type ne
{normal
}} {
1227 append reflogm
" ($commit_type)"
1229 set i
[string first
"\n" $msg]
1231 append reflogm
{: } [string range
$msg 0 [expr {$i - 1}]]
1233 append reflogm
{: } $msg
1235 set cmd
[list git update-ref
-m $reflogm HEAD
$cmt_id $curHEAD]
1236 if {[catch
{eval exec $cmd} err
]} {
1237 error_popup
"update-ref failed:\n\n$err"
1238 set ui_status_value
{Commit failed.
}
1243 # -- Make sure our current branch exists.
1245 if {$commit_type eq
{initial
}} {
1246 lappend all_heads
$current_branch
1247 set all_heads
[lsort
-unique $all_heads]
1248 populate_branch_menu
1251 # -- Cleanup after ourselves.
1253 catch
{file delete
$msg_p}
1254 catch
{file delete
[gitdir MERGE_HEAD
]}
1255 catch
{file delete
[gitdir MERGE_MSG
]}
1256 catch
{file delete
[gitdir SQUASH_MSG
]}
1257 catch
{file delete
[gitdir GITGUI_MSG
]}
1259 # -- Let rerere do its thing.
1261 if {[file isdirectory
[gitdir rr-cache
]]} {
1262 catch
{exec git rerere
}
1265 # -- Run the post-commit hook.
1267 set pchook
[gitdir hooks post-commit
]
1268 if {[is_Cygwin
] && [file isfile
$pchook]} {
1269 set pchook
[list sh
-c [concat \
1270 "if test -x \"$pchook\";" \
1271 "then exec \"$pchook\";" \
1273 } elseif
{![file executable
$pchook]} {
1276 if {$pchook ne
{}} {
1277 catch
{exec $pchook &}
1280 $ui_comm delete
0.0 end
1282 $ui_comm edit modified false
1284 if {$single_commit} do_quit
1286 # -- Update in memory status
1288 set selected_commit_type new
1289 set commit_type normal
1292 set MERGE_HEAD
[list
]
1294 foreach path
[array names file_states
] {
1295 set s
$file_states($path)
1297 switch
-glob -- $m {
1305 unset file_states
($path)
1306 catch
{unset selected_paths
($path)}
1309 set file_states
($path) [list _O
[lindex
$s 1] {} {}]
1315 set file_states
($path) [list \
1316 _
[string index
$m 1] \
1327 set ui_status_value \
1328 "Changes committed as [string range $cmt_id 0 7]."
1331 ######################################################################
1335 proc fetch_from
{remote
} {
1336 set w
[new_console \
1338 "Fetching new changes from $remote"]
1339 set cmd
[list git fetch
]
1341 console_exec
$w $cmd console_done
1344 proc push_to
{remote
} {
1345 set w
[new_console \
1347 "Pushing changes to $remote"]
1348 set cmd
[list git push
]
1351 console_exec
$w $cmd console_done
1354 ######################################################################
1358 proc mapicon
{w state path
} {
1361 if {[catch
{set r
$all_icons($state$w)}]} {
1362 puts
"error: no icon for $w state={$state} $path"
1368 proc mapdesc
{state path
} {
1371 if {[catch
{set r
$all_descs($state)}]} {
1372 puts
"error: no desc for state={$state} $path"
1378 proc escape_path
{path
} {
1379 regsub
-all "\n" $path "\\n" path
1383 proc short_path
{path
} {
1384 return [escape_path
[lindex
[file split $path] end
]]
1388 set null_sha1
[string repeat
0 40]
1390 proc merge_state
{path new_state
{head_info
{}} {index_info
{}}} {
1391 global file_states next_icon_id null_sha1
1393 set s0
[string index
$new_state 0]
1394 set s1
[string index
$new_state 1]
1396 if {[catch
{set info
$file_states($path)}]} {
1398 set icon n
[incr next_icon_id
]
1400 set state
[lindex
$info 0]
1401 set icon
[lindex
$info 1]
1402 if {$head_info eq
{}} {set head_info
[lindex
$info 2]}
1403 if {$index_info eq
{}} {set index_info
[lindex
$info 3]}
1406 if {$s0 eq
{?
}} {set s0
[string index
$state 0]} \
1407 elseif
{$s0 eq
{_
}} {set s0 _
}
1409 if {$s1 eq
{?
}} {set s1
[string index
$state 1]} \
1410 elseif
{$s1 eq
{_
}} {set s1 _
}
1412 if {$s0 eq
{A
} && $s1 eq
{_
} && $head_info eq
{}} {
1413 set head_info
[list
0 $null_sha1]
1414 } elseif
{$s0 ne
{_
} && [string index
$state 0] eq
{_
}
1415 && $head_info eq
{}} {
1416 set head_info
$index_info
1419 set file_states
($path) [list
$s0$s1 $icon \
1420 $head_info $index_info \
1425 proc display_file_helper
{w path icon_name old_m new_m
} {
1428 if {$new_m eq
{_
}} {
1429 set lno
[lsearch
-sorted -exact $file_lists($w) $path]
1431 set file_lists
($w) [lreplace
$file_lists($w) $lno $lno]
1433 $w conf
-state normal
1434 $w delete
$lno.0 [expr {$lno + 1}].0
1435 $w conf
-state disabled
1437 } elseif
{$old_m eq
{_
} && $new_m ne
{_
}} {
1438 lappend file_lists
($w) $path
1439 set file_lists
($w) [lsort
-unique $file_lists($w)]
1440 set lno
[lsearch
-sorted -exact $file_lists($w) $path]
1442 $w conf
-state normal
1443 $w image create
$lno.0 \
1444 -align center
-padx 5 -pady 1 \
1446 -image [mapicon
$w $new_m $path]
1447 $w insert
$lno.1 "[escape_path $path]\n"
1448 $w conf
-state disabled
1449 } elseif
{$old_m ne
$new_m} {
1450 $w conf
-state normal
1451 $w image conf
$icon_name -image [mapicon
$w $new_m $path]
1452 $w conf
-state disabled
1456 proc display_file
{path state
} {
1457 global file_states selected_paths
1458 global ui_index ui_workdir
1460 set old_m
[merge_state
$path $state]
1461 set s
$file_states($path)
1462 set new_m
[lindex
$s 0]
1463 set icon_name
[lindex
$s 1]
1465 set o
[string index
$old_m 0]
1466 set n
[string index
$new_m 0]
1473 display_file_helper
$ui_index $path $icon_name $o $n
1475 if {[string index
$old_m 0] eq
{U
}} {
1478 set o
[string index
$old_m 1]
1480 if {[string index
$new_m 0] eq
{U
}} {
1483 set n
[string index
$new_m 1]
1485 display_file_helper
$ui_workdir $path $icon_name $o $n
1487 if {$new_m eq
{__
}} {
1488 unset file_states
($path)
1489 catch
{unset selected_paths
($path)}
1493 proc display_all_files_helper
{w path icon_name m
} {
1496 lappend file_lists
($w) $path
1497 set lno
[expr {[lindex
[split [$w index end
] .
] 0] - 1}]
1498 $w image create end \
1499 -align center
-padx 5 -pady 1 \
1501 -image [mapicon
$w $m $path]
1502 $w insert end
"[escape_path $path]\n"
1505 proc display_all_files
{} {
1506 global ui_index ui_workdir
1507 global file_states file_lists
1510 $ui_index conf
-state normal
1511 $ui_workdir conf
-state normal
1513 $ui_index delete
0.0 end
1514 $ui_workdir delete
0.0 end
1517 set file_lists
($ui_index) [list
]
1518 set file_lists
($ui_workdir) [list
]
1520 foreach path
[lsort
[array names file_states
]] {
1521 set s
$file_states($path)
1523 set icon_name
[lindex
$s 1]
1525 set s
[string index
$m 0]
1526 if {$s ne
{U
} && $s ne
{_
}} {
1527 display_all_files_helper
$ui_index $path \
1531 if {[string index
$m 0] eq
{U
}} {
1534 set s
[string index
$m 1]
1537 display_all_files_helper
$ui_workdir $path \
1542 $ui_index conf
-state disabled
1543 $ui_workdir conf
-state disabled
1546 proc update_indexinfo
{msg pathList after
} {
1547 global update_index_cp ui_status_value
1549 if {![lock_index update
]} return
1551 set update_index_cp
0
1552 set pathList
[lsort
$pathList]
1553 set totalCnt
[llength
$pathList]
1554 set batch [expr {int
($totalCnt * .01) + 1}]
1555 if {$batch > 25} {set batch 25}
1557 set ui_status_value
[format \
1558 "$msg... %i/%i files (%.2f%%)" \
1562 set fd
[open
"| git update-index -z --index-info" w
]
1569 fileevent
$fd writable
[list \
1570 write_update_indexinfo \
1580 proc write_update_indexinfo
{fd pathList totalCnt
batch msg after
} {
1581 global update_index_cp ui_status_value
1582 global file_states current_diff_path
1584 if {$update_index_cp >= $totalCnt} {
1591 for {set i
$batch} \
1592 {$update_index_cp < $totalCnt && $i > 0} \
1594 set path
[lindex
$pathList $update_index_cp]
1595 incr update_index_cp
1597 set s
$file_states($path)
1598 switch
-glob -- [lindex
$s 0] {
1605 set info
[lindex
$s 2]
1606 if {$info eq
{}} continue
1608 puts
-nonewline $fd "$info\t[encoding convertto $path]\0"
1609 display_file
$path $new
1612 set ui_status_value
[format \
1613 "$msg... %i/%i files (%.2f%%)" \
1616 [expr {100.0 * $update_index_cp / $totalCnt}]]
1619 proc update_index
{msg pathList after
} {
1620 global update_index_cp ui_status_value
1622 if {![lock_index update
]} return
1624 set update_index_cp
0
1625 set pathList
[lsort
$pathList]
1626 set totalCnt
[llength
$pathList]
1627 set batch [expr {int
($totalCnt * .01) + 1}]
1628 if {$batch > 25} {set batch 25}
1630 set ui_status_value
[format \
1631 "$msg... %i/%i files (%.2f%%)" \
1635 set fd
[open
"| git update-index --add --remove -z --stdin" w
]
1642 fileevent
$fd writable
[list \
1643 write_update_index \
1653 proc write_update_index
{fd pathList totalCnt
batch msg after
} {
1654 global update_index_cp ui_status_value
1655 global file_states current_diff_path
1657 if {$update_index_cp >= $totalCnt} {
1664 for {set i
$batch} \
1665 {$update_index_cp < $totalCnt && $i > 0} \
1667 set path
[lindex
$pathList $update_index_cp]
1668 incr update_index_cp
1670 switch
-glob -- [lindex
$file_states($path) 0] {
1676 if {[file exists
$path]} {
1685 puts
-nonewline $fd "[encoding convertto $path]\0"
1686 display_file
$path $new
1689 set ui_status_value
[format \
1690 "$msg... %i/%i files (%.2f%%)" \
1693 [expr {100.0 * $update_index_cp / $totalCnt}]]
1696 proc checkout_index
{msg pathList after
} {
1697 global update_index_cp ui_status_value
1699 if {![lock_index update
]} return
1701 set update_index_cp
0
1702 set pathList
[lsort
$pathList]
1703 set totalCnt
[llength
$pathList]
1704 set batch [expr {int
($totalCnt * .01) + 1}]
1705 if {$batch > 25} {set batch 25}
1707 set ui_status_value
[format \
1708 "$msg... %i/%i files (%.2f%%)" \
1712 set cmd
[list git checkout-index
]
1718 set fd
[open
"| $cmd " w
]
1725 fileevent
$fd writable
[list \
1726 write_checkout_index \
1736 proc write_checkout_index
{fd pathList totalCnt
batch msg after
} {
1737 global update_index_cp ui_status_value
1738 global file_states current_diff_path
1740 if {$update_index_cp >= $totalCnt} {
1747 for {set i
$batch} \
1748 {$update_index_cp < $totalCnt && $i > 0} \
1750 set path
[lindex
$pathList $update_index_cp]
1751 incr update_index_cp
1752 switch
-glob -- [lindex
$file_states($path) 0] {
1756 puts
-nonewline $fd "[encoding convertto $path]\0"
1757 display_file
$path ?_
1762 set ui_status_value
[format \
1763 "$msg... %i/%i files (%.2f%%)" \
1766 [expr {100.0 * $update_index_cp / $totalCnt}]]
1769 ######################################################################
1771 ## branch management
1773 proc is_tracking_branch
{name
} {
1774 global tracking_branches
1776 if {![catch
{set info
$tracking_branches($name)}]} {
1779 foreach t
[array names tracking_branches
] {
1780 if {[string match
{*/\
*} $t] && [string match
$t $name]} {
1787 proc load_all_heads
{} {
1790 set all_heads
[list
]
1791 set fd
[open
"| git for-each-ref --format=%(refname) refs/heads" r
]
1792 while {[gets
$fd line
] > 0} {
1793 if {[is_tracking_branch
$line]} continue
1794 if {![regsub ^refs
/heads
/ $line {} name
]} continue
1795 lappend all_heads
$name
1799 set all_heads
[lsort
$all_heads]
1802 proc populate_branch_menu
{} {
1803 global all_heads disable_on_lock
1806 set last
[$m index last
]
1807 for {set i
0} {$i <= $last} {incr i
} {
1808 if {[$m type $i] eq
{separator
}} {
1811 foreach a
$disable_on_lock {
1812 if {[lindex
$a 0] ne
$m ||
[lindex
$a 2] < $i} {
1816 set disable_on_lock
$new_dol
1821 if {$all_heads ne
{}} {
1824 foreach b
$all_heads {
1825 $m add radiobutton \
1827 -command [list switch_branch
$b] \
1828 -variable current_branch \
1831 lappend disable_on_lock \
1832 [list
$m entryconf
[$m index last
] -state]
1836 proc all_tracking_branches
{} {
1837 global tracking_branches
1839 set all_trackings
{}
1841 foreach name
[array names tracking_branches
] {
1842 if {[regsub
{/\
*$
} $name {} name
]} {
1845 regsub ^refs
/(heads|remotes
)/ $name {} name
1846 lappend all_trackings
$name
1851 set fd
[open
"| git for-each-ref --format=%(refname) $cmd" r
]
1852 while {[gets
$fd name
] > 0} {
1853 regsub ^refs
/(heads|remotes
)/ $name {} name
1854 lappend all_trackings
$name
1859 return [lsort
-unique $all_trackings]
1862 proc do_create_branch_action
{w
} {
1863 global all_heads null_sha1 repo_config
1864 global create_branch_checkout create_branch_revtype
1865 global create_branch_head create_branch_trackinghead
1866 global create_branch_name create_branch_revexp
1868 set newbranch
$create_branch_name
1869 if {$newbranch eq
{}
1870 ||
$newbranch eq
$repo_config(gui.newbranchtemplate
)} {
1874 -title [wm title
$w] \
1876 -message "Please supply a branch name."
1877 focus
$w.desc.name_t
1880 if {![catch
{exec git show-ref
--verify -- "refs/heads/$newbranch"}]} {
1884 -title [wm title
$w] \
1886 -message "Branch '$newbranch' already exists."
1887 focus
$w.desc.name_t
1890 if {[catch
{exec git check-ref-format
"heads/$newbranch"}]} {
1894 -title [wm title
$w] \
1896 -message "We do not like '$newbranch' as a branch name."
1897 focus
$w.desc.name_t
1902 switch
-- $create_branch_revtype {
1903 head {set rev $create_branch_head}
1904 tracking
{set rev $create_branch_trackinghead}
1905 expression
{set rev $create_branch_revexp}
1907 if {[catch
{set cmt
[exec git rev-parse
--verify "${rev}^0"]}]} {
1911 -title [wm title
$w] \
1913 -message "Invalid starting revision: $rev"
1916 set cmd
[list git update-ref
]
1918 lappend cmd
"branch: Created from $rev"
1919 lappend cmd
"refs/heads/$newbranch"
1921 lappend cmd
$null_sha1
1922 if {[catch
{eval exec $cmd} err
]} {
1926 -title [wm title
$w] \
1928 -message "Failed to create '$newbranch'.\n\n$err"
1932 lappend all_heads
$newbranch
1933 set all_heads
[lsort
$all_heads]
1934 populate_branch_menu
1936 if {$create_branch_checkout} {
1937 switch_branch
$newbranch
1941 proc radio_selector
{varname value args
} {
1942 upvar
#0 $varname var
1946 trace add variable create_branch_head
write \
1947 [list radio_selector create_branch_revtype
head]
1948 trace add variable create_branch_trackinghead
write \
1949 [list radio_selector create_branch_revtype tracking
]
1951 trace add variable delete_branch_head
write \
1952 [list radio_selector delete_branch_checktype
head]
1953 trace add variable delete_branch_trackinghead
write \
1954 [list radio_selector delete_branch_checktype tracking
]
1956 proc do_create_branch
{} {
1957 global all_heads current_branch repo_config
1958 global create_branch_checkout create_branch_revtype
1959 global create_branch_head create_branch_trackinghead
1960 global create_branch_name create_branch_revexp
1962 set w .branch_editor
1964 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
1966 label
$w.header
-text {Create New Branch
} \
1968 pack
$w.header
-side top
-fill x
1971 button
$w.buttons.create
-text Create \
1974 -command [list do_create_branch_action
$w]
1975 pack
$w.buttons.create
-side right
1976 button
$w.buttons.cancel
-text {Cancel
} \
1978 -command [list destroy
$w]
1979 pack
$w.buttons.cancel
-side right
-padx 5
1980 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
1982 labelframe
$w.desc \
1983 -text {Branch Description
} \
1985 label
$w.desc.name_l
-text {Name
:} -font font_ui
1986 entry
$w.desc.name_t \
1990 -textvariable create_branch_name \
1994 if {%d
== 1 && [regexp
{[~^
:?
*\
[\
0- ]} %S
]} {return 0}
1997 grid
$w.desc.name_l
$w.desc.name_t
-sticky we
-padx {0 5}
1998 grid columnconfigure
$w.desc
1 -weight 1
1999 pack
$w.desc
-anchor nw
-fill x
-pady 5 -padx 5
2001 labelframe
$w.from \
2002 -text {Starting Revision
} \
2004 radiobutton
$w.from.head_r \
2005 -text {Local Branch
:} \
2007 -variable create_branch_revtype \
2009 eval tk_optionMenu
$w.from.head_m create_branch_head
$all_heads
2010 grid
$w.from.head_r
$w.from.head_m
-sticky w
2011 set all_trackings
[all_tracking_branches
]
2012 if {$all_trackings ne
{}} {
2013 set create_branch_trackinghead
[lindex
$all_trackings 0]
2014 radiobutton
$w.from.tracking_r \
2015 -text {Tracking Branch
:} \
2017 -variable create_branch_revtype \
2019 eval tk_optionMenu
$w.from.tracking_m \
2020 create_branch_trackinghead \
2022 grid
$w.from.tracking_r
$w.from.tracking_m
-sticky w
2024 radiobutton
$w.from.exp_r \
2025 -text {Revision Expression
:} \
2027 -variable create_branch_revtype \
2029 entry
$w.from.exp_t \
2033 -textvariable create_branch_revexp \
2037 if {%d
== 1 && [regexp
{\s
} %S
]} {return 0}
2038 if {%d
== 1 && [string length
%S
] > 0} {
2039 set create_branch_revtype expression
2043 grid
$w.from.exp_r
$w.from.exp_t
-sticky we
-padx {0 5}
2044 grid columnconfigure
$w.from
1 -weight 1
2045 pack
$w.from
-anchor nw
-fill x
-pady 5 -padx 5
2047 labelframe
$w.postActions \
2048 -text {Post Creation Actions
} \
2050 checkbutton
$w.postActions.checkout \
2051 -text {Checkout after creation
} \
2052 -variable create_branch_checkout \
2054 pack
$w.postActions.checkout
-anchor nw
2055 pack
$w.postActions
-anchor nw
-fill x
-pady 5 -padx 5
2057 set create_branch_checkout
1
2058 set create_branch_head
$current_branch
2059 set create_branch_revtype
head
2060 set create_branch_name
$repo_config(gui.newbranchtemplate
)
2061 set create_branch_revexp
{}
2063 bind $w <Visibility
> "
2065 $w.desc.name_t icursor end
2066 focus $w.desc.name_t
2068 bind $w <Key-Escape
> "destroy $w"
2069 bind $w <Key-Return
> "do_create_branch_action $w;break"
2070 wm title
$w "[appname] ([reponame]): Create Branch"
2074 proc do_delete_branch_action
{w
} {
2076 global delete_branch_checktype delete_branch_head delete_branch_trackinghead
2079 switch
-- $delete_branch_checktype {
2080 head {set check_rev
$delete_branch_head}
2081 tracking
{set check_rev
$delete_branch_trackinghead}
2082 always
{set check_rev
{:none
}}
2084 if {$check_rev eq
{:none
}} {
2086 } elseif
{[catch
{set check_cmt
[exec git rev-parse
--verify "${check_rev}^0"]}]} {
2090 -title [wm title
$w] \
2092 -message "Invalid check revision: $check_rev"
2096 set to_delete
[list
]
2097 set not_merged
[list
]
2098 foreach i
[$w.list.l curselection
] {
2099 set b
[$w.list.l get
$i]
2100 if {[catch
{set o
[exec git rev-parse
--verify $b]}]} continue
2101 if {$check_cmt ne
{}} {
2102 if {$b eq
$check_rev} continue
2103 if {[catch
{set m
[exec git merge-base
$o $check_cmt]}]} continue
2105 lappend not_merged
$b
2109 lappend to_delete
[list
$b $o]
2111 if {$not_merged ne
{}} {
2112 set msg
"The following branches are not completely merged into $check_rev:
2114 - [join $not_merged "\n - "]"
2118 -title [wm title
$w] \
2122 if {$to_delete eq
{}} return
2123 if {$delete_branch_checktype eq
{always
}} {
2124 set msg
{Recovering deleted branches is difficult.
2126 Delete the selected branches?
}
2127 if {[tk_messageBox \
2130 -title [wm title
$w] \
2132 -message $msg] ne
yes} {
2138 foreach i
$to_delete {
2141 if {[catch
{exec git update-ref
-d "refs/heads/$b" $o} err
]} {
2142 append failed
" - $b: $err\n"
2144 set x
[lsearch
-sorted -exact $all_heads $b]
2146 set all_heads
[lreplace
$all_heads $x $x]
2151 if {$failed ne
{}} {
2155 -title [wm title
$w] \
2157 -message "Failed to delete branches:\n$failed"
2160 set all_heads
[lsort
$all_heads]
2161 populate_branch_menu
2165 proc do_delete_branch
{} {
2166 global all_heads tracking_branches current_branch
2167 global delete_branch_checktype delete_branch_head delete_branch_trackinghead
2169 set w .branch_editor
2171 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
2173 label
$w.header
-text {Delete Local Branch
} \
2175 pack
$w.header
-side top
-fill x
2178 button
$w.buttons.create
-text Delete \
2180 -command [list do_delete_branch_action
$w]
2181 pack
$w.buttons.create
-side right
2182 button
$w.buttons.cancel
-text {Cancel
} \
2184 -command [list destroy
$w]
2185 pack
$w.buttons.cancel
-side right
-padx 5
2186 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
2188 labelframe
$w.list \
2189 -text {Local Branches
} \
2194 -selectmode extended \
2195 -yscrollcommand [list
$w.list.sby
set] \
2197 foreach h
$all_heads {
2198 if {$h ne
$current_branch} {
2199 $w.list.l insert end
$h
2202 scrollbar
$w.list.sby
-command [list
$w.list.l yview
]
2203 pack
$w.list.sby
-side right
-fill y
2204 pack
$w.list.l
-side left
-fill both
-expand 1
2205 pack
$w.list
-fill both
-expand 1 -pady 5 -padx 5
2207 labelframe
$w.validate \
2208 -text {Delete Only If
} \
2210 radiobutton
$w.validate.head_r \
2211 -text {Merged Into Local Branch
:} \
2213 -variable delete_branch_checktype \
2215 eval tk_optionMenu
$w.validate.head_m delete_branch_head
$all_heads
2216 grid
$w.validate.head_r
$w.validate.head_m
-sticky w
2217 set all_trackings
[all_tracking_branches
]
2218 if {$all_trackings ne
{}} {
2219 set delete_branch_trackinghead
[lindex
$all_trackings 0]
2220 radiobutton
$w.validate.tracking_r \
2221 -text {Merged Into Tracking Branch
:} \
2223 -variable delete_branch_checktype \
2225 eval tk_optionMenu
$w.validate.tracking_m \
2226 delete_branch_trackinghead \
2228 grid
$w.validate.tracking_r
$w.validate.tracking_m
-sticky w
2230 radiobutton
$w.validate.always_r \
2231 -text {Always
(Do not perform merge checks
)} \
2233 -variable delete_branch_checktype \
2235 grid
$w.validate.always_r
-columnspan 2 -sticky w
2236 grid columnconfigure
$w.validate
1 -weight 1
2237 pack
$w.validate
-anchor nw
-fill x
-pady 5 -padx 5
2239 set delete_branch_head
$current_branch
2240 set delete_branch_checktype
head
2242 bind $w <Visibility
> "grab $w; focus $w"
2243 bind $w <Key-Escape
> "destroy $w"
2244 wm title
$w "[appname] ([reponame]): Delete Branch"
2248 proc switch_branch
{new_branch
} {
2249 global HEAD commit_type current_branch repo_config
2251 if {![lock_index switch
]} return
2253 # -- Our in memory state should match the repository.
2255 repository_state curType curHEAD curMERGE_HEAD
2256 if {[string match amend
* $commit_type]
2257 && $curType eq
{normal
}
2258 && $curHEAD eq
$HEAD} {
2259 } elseif
{$commit_type ne
$curType ||
$HEAD ne
$curHEAD} {
2260 info_popup
{Last scanned state does not match repository state.
2262 Another Git program has modified this repository
2263 since the last scan. A rescan must be performed
2264 before the current branch can be changed.
2266 The rescan will be automatically started now.
2269 rescan
{set ui_status_value
{Ready.
}}
2273 # -- Don't do a pointless switch.
2275 if {$current_branch eq
$new_branch} {
2280 if {$repo_config(gui.trustmtime
) eq
{true
}} {
2281 switch_branch_stage2
{} $new_branch
2283 set ui_status_value
{Refreshing
file status...
}
2284 set cmd
[list git update-index
]
2286 lappend cmd
--unmerged
2287 lappend cmd
--ignore-missing
2288 lappend cmd
--refresh
2289 set fd_rf
[open
"| $cmd" r
]
2290 fconfigure
$fd_rf -blocking 0 -translation binary
2291 fileevent
$fd_rf readable \
2292 [list switch_branch_stage2
$fd_rf $new_branch]
2296 proc switch_branch_stage2
{fd_rf new_branch
} {
2297 global ui_status_value HEAD
2301 if {![eof
$fd_rf]} return
2305 set ui_status_value
"Updating working directory to '$new_branch'..."
2306 set cmd
[list git read-tree
]
2309 lappend cmd
--exclude-per-directory=.gitignore
2311 lappend cmd
$new_branch
2312 set fd_rt
[open
"| $cmd" r
]
2313 fconfigure
$fd_rt -blocking 0 -translation binary
2314 fileevent
$fd_rt readable \
2315 [list switch_branch_readtree_wait
$fd_rt $new_branch]
2318 proc switch_branch_readtree_wait
{fd_rt new_branch
} {
2319 global selected_commit_type commit_type HEAD MERGE_HEAD PARENT
2320 global current_branch
2321 global ui_comm ui_status_value
2323 # -- We never get interesting output on stdout; only stderr.
2326 fconfigure
$fd_rt -blocking 1
2327 if {![eof
$fd_rt]} {
2328 fconfigure
$fd_rt -blocking 0
2332 # -- The working directory wasn't in sync with the index and
2333 # we'd have to overwrite something to make the switch. A
2334 # merge is required.
2336 if {[catch
{close
$fd_rt} err
]} {
2337 regsub
{^fatal
: } $err {} err
2338 warn_popup
"File level merge required.
2342 Staying on branch '$current_branch'."
2343 set ui_status_value
"Aborted checkout of '$new_branch' (file level merging is required)."
2348 # -- Update the symbolic ref. Core git doesn't even check for failure
2349 # here, it Just Works(tm). If it doesn't we are in some really ugly
2350 # state that is difficult to recover from within git-gui.
2352 if {[catch
{exec git symbolic-ref HEAD
"refs/heads/$new_branch"} err
]} {
2353 error_popup
"Failed to set current branch.
2355 This working directory is only partially switched.
2356 We successfully updated your files, but failed to
2357 update an internal Git file.
2359 This should not have occurred. [appname] will now
2367 # -- Update our repository state. If we were previously in amend mode
2368 # we need to toss the current buffer and do a full rescan to update
2369 # our file lists. If we weren't in amend mode our file lists are
2370 # accurate and we can avoid the rescan.
2373 set selected_commit_type new
2374 if {[string match amend
* $commit_type]} {
2375 $ui_comm delete
0.0 end
2377 $ui_comm edit modified false
2378 rescan
{set ui_status_value
"Checked out branch '$current_branch'."}
2380 repository_state commit_type HEAD MERGE_HEAD
2382 set ui_status_value
"Checked out branch '$current_branch'."
2386 ######################################################################
2388 ## remote management
2390 proc load_all_remotes
{} {
2392 global all_remotes tracking_branches
2394 set all_remotes
[list
]
2395 array
unset tracking_branches
2397 set rm_dir
[gitdir remotes
]
2398 if {[file isdirectory
$rm_dir]} {
2399 set all_remotes
[glob \
2403 -directory $rm_dir *]
2405 foreach name
$all_remotes {
2407 set fd
[open
[file join $rm_dir $name] r
]
2408 while {[gets
$fd line
] >= 0} {
2409 if {![regexp
{^Pull
:[ ]*([^
:]+):(.
+)$
} \
2410 $line line src dst
]} continue
2411 if {![regexp ^refs
/ $dst]} {
2412 set dst
"refs/heads/$dst"
2414 set tracking_branches
($dst) [list
$name $src]
2421 foreach line
[array names repo_config remote.
*.url
] {
2422 if {![regexp ^remote\.
(.
*)\.url\$
$line line name
]} continue
2423 lappend all_remotes
$name
2425 if {[catch
{set fl
$repo_config(remote.
$name.fetch
)}]} {
2429 if {![regexp
{^
([^
:]+):(.
+)$
} $line line src dst
]} continue
2430 if {![regexp ^refs
/ $dst]} {
2431 set dst
"refs/heads/$dst"
2433 set tracking_branches
($dst) [list
$name $src]
2437 set all_remotes
[lsort
-unique $all_remotes]
2440 proc populate_fetch_menu
{} {
2441 global all_remotes repo_config
2444 foreach r
$all_remotes {
2446 if {![catch
{set a
$repo_config(remote.
$r.url
)}]} {
2447 if {![catch
{set a
$repo_config(remote.
$r.fetch
)}]} {
2452 set fd
[open
[gitdir remotes
$r] r
]
2453 while {[gets
$fd n
] >= 0} {
2454 if {[regexp
{^Pull
:[ \t]*([^
:]+):} $n]} {
2465 -label "Fetch from $r..." \
2466 -command [list fetch_from
$r] \
2472 proc populate_push_menu
{} {
2473 global all_remotes repo_config
2477 foreach r
$all_remotes {
2479 if {![catch
{set a
$repo_config(remote.
$r.url
)}]} {
2480 if {![catch
{set a
$repo_config(remote.
$r.push
)}]} {
2485 set fd
[open
[gitdir remotes
$r] r
]
2486 while {[gets
$fd n
] >= 0} {
2487 if {[regexp
{^Push
:[ \t]*([^
:]+):} $n]} {
2501 -label "Push to $r..." \
2502 -command [list push_to
$r] \
2509 proc start_push_anywhere_action
{w
} {
2510 global push_urltype push_remote push_url push_thin push_tags
2513 switch
-- $push_urltype {
2514 remote
{set r_url
$push_remote}
2515 url
{set r_url
$push_url}
2517 if {$r_url eq
{}} return
2519 set cmd
[list git push
]
2529 foreach i
[$w.
source.l curselection
] {
2530 set b
[$w.
source.l get
$i]
2531 lappend cmd
"refs/heads/$b:refs/heads/$b"
2536 } elseif
{$cnt == 1} {
2542 set cons
[new_console
"push $r_url" "Pushing $cnt $unit to $r_url"]
2543 console_exec
$cons $cmd console_done
2547 trace add variable push_remote
write \
2548 [list radio_selector push_urltype remote
]
2550 proc do_push_anywhere
{} {
2551 global all_heads all_remotes current_branch
2552 global push_urltype push_remote push_url push_thin push_tags
2556 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
2558 label
$w.header
-text {Push Branches
} -font font_uibold
2559 pack
$w.header
-side top
-fill x
2562 button
$w.buttons.create
-text Push \
2564 -command [list start_push_anywhere_action
$w]
2565 pack
$w.buttons.create
-side right
2566 button
$w.buttons.cancel
-text {Cancel
} \
2568 -command [list destroy
$w]
2569 pack
$w.buttons.cancel
-side right
-padx 5
2570 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
2572 labelframe
$w.
source \
2573 -text {Source Branches
} \
2575 listbox
$w.
source.l \
2578 -selectmode extended \
2579 -yscrollcommand [list
$w.
source.sby
set] \
2581 foreach h
$all_heads {
2582 $w.
source.l insert end
$h
2583 if {$h eq
$current_branch} {
2584 $w.
source.l
select set end
2587 scrollbar
$w.
source.sby
-command [list
$w.
source.l yview
]
2588 pack
$w.
source.sby
-side right
-fill y
2589 pack
$w.
source.l
-side left
-fill both
-expand 1
2590 pack
$w.
source -fill both
-expand 1 -pady 5 -padx 5
2592 labelframe
$w.dest \
2593 -text {Destination Repository
} \
2595 if {$all_remotes ne
{}} {
2596 radiobutton
$w.dest.remote_r \
2599 -variable push_urltype \
2601 eval tk_optionMenu
$w.dest.remote_m push_remote
$all_remotes
2602 grid
$w.dest.remote_r
$w.dest.remote_m
-sticky w
2603 if {[lsearch
-sorted -exact $all_remotes origin
] != -1} {
2604 set push_remote origin
2606 set push_remote
[lindex
$all_remotes 0]
2608 set push_urltype remote
2610 set push_urltype url
2612 radiobutton
$w.dest.url_r \
2613 -text {Arbitrary URL
:} \
2615 -variable push_urltype \
2617 entry
$w.dest.url_t \
2621 -textvariable push_url \
2625 if {%d
== 1 && [regexp
{\s
} %S
]} {return 0}
2626 if {%d
== 1 && [string length
%S
] > 0} {
2627 set push_urltype url
2631 grid
$w.dest.url_r
$w.dest.url_t
-sticky we
-padx {0 5}
2632 grid columnconfigure
$w.dest
1 -weight 1
2633 pack
$w.dest
-anchor nw
-fill x
-pady 5 -padx 5
2635 labelframe
$w.options \
2636 -text {Transfer Options
} \
2638 checkbutton
$w.options.thin \
2639 -text {Use thin pack
(for slow network connections
)} \
2640 -variable push_thin \
2642 grid
$w.options.thin
-columnspan 2 -sticky w
2643 checkbutton
$w.options.tags \
2644 -text {Include tags
} \
2645 -variable push_tags \
2647 grid
$w.options.tags
-columnspan 2 -sticky w
2648 grid columnconfigure
$w.options
1 -weight 1
2649 pack
$w.options
-anchor nw
-fill x
-pady 5 -padx 5
2655 bind $w <Visibility
> "grab $w"
2656 bind $w <Key-Escape
> "destroy $w"
2657 wm title
$w "[appname] ([reponame]): Push"
2661 ######################################################################
2666 global HEAD commit_type file_states
2668 if {[string match amend
* $commit_type]} {
2669 info_popup
{Cannot merge
while amending.
2671 You must finish amending this commit before
2672 starting any
type of merge.
2677 if {[committer_ident
] eq
{}} {return 0}
2678 if {![lock_index merge
]} {return 0}
2680 # -- Our in memory state should match the repository.
2682 repository_state curType curHEAD curMERGE_HEAD
2683 if {$commit_type ne
$curType ||
$HEAD ne
$curHEAD} {
2684 info_popup
{Last scanned state does not match repository state.
2686 Another Git program has modified this repository
2687 since the last scan. A rescan must be performed
2688 before a merge can be performed.
2690 The rescan will be automatically started now.
2693 rescan
{set ui_status_value
{Ready.
}}
2697 foreach path
[array names file_states
] {
2698 switch
-glob -- [lindex
$file_states($path) 0] {
2700 continue; # and pray it works!
2703 error_popup
"You are in the middle of a conflicted merge.
2705 File [short_path $path] has merge conflicts.
2707 You must resolve them, add the file, and commit to
2708 complete the current merge. Only then can you
2709 begin another merge.
2715 error_popup
"You are in the middle of a change.
2717 File [short_path $path] is modified.
2719 You should complete the current commit before
2720 starting a merge. Doing so will help you abort
2721 a failed merge, should the need arise.
2732 proc visualize_local_merge
{w
} {
2734 foreach i
[$w.
source.l curselection
] {
2735 lappend revs
[$w.
source.l get
$i]
2737 if {$revs eq
{}} return
2738 lappend revs
--not HEAD
2742 proc start_local_merge_action
{w
} {
2743 global HEAD ui_status_value current_branch
2745 set cmd
[list git merge
]
2748 foreach i
[$w.
source.l curselection
] {
2749 set b
[$w.
source.l get
$i]
2757 } elseif
{$revcnt == 1} {
2759 } elseif
{$revcnt <= 15} {
2765 -title [wm title
$w] \
2767 -message "Too many branches selected.
2769 You have requested to merge $revcnt branches
2770 in an octopus merge. This exceeds Git's
2771 internal limit of 15 branches per merge.
2773 Please select fewer branches. To merge more
2774 than 15 branches, merge the branches in batches.
2779 set msg
"Merging $current_branch, [join $names {, }]"
2780 set ui_status_value
"$msg..."
2781 set cons
[new_console
"Merge" $msg]
2782 console_exec
$cons $cmd [list finish_merge
$revcnt]
2783 bind $w <Destroy
> {}
2787 proc finish_merge
{revcnt w ok
} {
2790 set msg
{Merge completed successfully.
}
2793 info_popup
"Octopus merge failed.
2795 Your merge of $revcnt branches has failed.
2797 There are file-level conflicts between the
2798 branches which must be resolved manually.
2800 The working directory will now be reset.
2802 You can attempt this merge again
2803 by merging only one branch at a time." $w
2805 set fd
[open
"| git read-tree --reset -u HEAD" r
]
2806 fconfigure
$fd -blocking 0 -translation binary
2807 fileevent
$fd readable
[list reset_hard_wait
$fd]
2808 set ui_status_value
{Aborting... please
wait...
}
2812 set msg
{Merge failed. Conflict resolution is required.
}
2815 rescan
[list
set ui_status_value
$msg]
2818 proc do_local_merge
{} {
2819 global current_branch
2821 if {![can_merge
]} return
2825 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
2828 -text "Merge Into $current_branch" \
2830 pack
$w.header
-side top
-fill x
2833 button
$w.buttons.visualize
-text Visualize \
2835 -command [list visualize_local_merge
$w]
2836 pack
$w.buttons.visualize
-side left
2837 button
$w.buttons.create
-text Merge \
2839 -command [list start_local_merge_action
$w]
2840 pack
$w.buttons.create
-side right
2841 button
$w.buttons.cancel
-text {Cancel
} \
2843 -command [list destroy
$w]
2844 pack
$w.buttons.cancel
-side right
-padx 5
2845 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
2847 labelframe
$w.
source \
2848 -text {Source Branches
} \
2850 listbox
$w.
source.l \
2853 -selectmode extended \
2854 -yscrollcommand [list
$w.
source.sby
set] \
2856 scrollbar
$w.
source.sby
-command [list
$w.
source.l yview
]
2857 pack
$w.
source.sby
-side right
-fill y
2858 pack
$w.
source.l
-side left
-fill both
-expand 1
2859 pack
$w.
source -fill both
-expand 1 -pady 5 -padx 5
2861 set cmd
[list git for-each-ref
]
2862 lappend cmd
{--format=%(objectname
) %(refname
)}
2863 lappend cmd refs
/heads
2864 lappend cmd refs
/remotes
2865 set fr_fd
[open
"| $cmd" r
]
2866 fconfigure
$fr_fd -translation binary
2867 while {[gets
$fr_fd line
] > 0} {
2868 set line
[split $line { }]
2869 set sha1
([lindex
$line 0]) [lindex
$line 1]
2874 set fr_fd
[open
"| git rev-list --all --not HEAD"]
2875 while {[gets
$fr_fd line
] > 0} {
2876 if {[catch
{set ref
$sha1($line)}]} continue
2877 regsub ^refs
/(heads|remotes
)/ $ref {} ref
2878 lappend to_show
$ref
2882 foreach ref
[lsort
-unique $to_show] {
2883 $w.
source.l insert end
$ref
2886 bind $w <Visibility
> "grab $w"
2887 bind $w <Key-Escape
> "unlock_index;destroy $w"
2888 bind $w <Destroy
> unlock_index
2889 wm title
$w "[appname] ([reponame]): Merge"
2893 proc do_reset_hard
{} {
2894 global HEAD commit_type file_states
2896 if {[string match amend
* $commit_type]} {
2897 info_popup
{Cannot abort
while amending.
2899 You must finish amending this commit.
2904 if {![lock_index abort
]} return
2906 if {[string match
*merge
* $commit_type]} {
2912 if {[ask_popup
"Abort $op?
2914 Aborting the current $op will cause
2915 *ALL* uncommitted changes to be lost.
2917 Continue with aborting the current $op?"] eq
{yes}} {
2918 set fd
[open
"| git read-tree --reset -u HEAD" r
]
2919 fconfigure
$fd -blocking 0 -translation binary
2920 fileevent
$fd readable
[list reset_hard_wait
$fd]
2921 set ui_status_value
{Aborting... please
wait...
}
2927 proc reset_hard_wait
{fd
} {
2935 $ui_comm delete
0.0 end
2936 $ui_comm edit modified false
2938 catch
{file delete
[gitdir MERGE_HEAD
]}
2939 catch
{file delete
[gitdir rr-cache MERGE_RR
]}
2940 catch
{file delete
[gitdir SQUASH_MSG
]}
2941 catch
{file delete
[gitdir MERGE_MSG
]}
2942 catch
{file delete
[gitdir GITGUI_MSG
]}
2944 rescan
{set ui_status_value
{Abort completed. Ready.
}}
2948 ######################################################################
2952 set next_browser_id
0
2954 proc new_browser
{commit
} {
2955 global next_browser_id cursor_ptr
2956 global browser_commit browser_status browser_stack browser_path browser_busy
2958 set w .browser
[incr next_browser_id
]
2959 set w_list
$w.list.l
2960 set browser_commit
($w_list) $commit
2961 set browser_status
($w_list) {Starting...
}
2962 set browser_stack
($w_list) {}
2963 set browser_path
($w_list) $browser_commit($w_list):
2964 set browser_busy
($w_list) 1
2967 label
$w.path
-textvariable browser_path
($w_list) \
2973 pack
$w.path
-anchor w
-side top
-fill x
2976 text
$w_list -background white
-borderwidth 0 \
2977 -cursor $cursor_ptr \
2982 -xscrollcommand [list
$w.list.sbx
set] \
2983 -yscrollcommand [list
$w.list.sby
set] \
2985 $w_list tag conf in_sel \
2986 -background [$w_list cget
-foreground] \
2987 -foreground [$w_list cget
-background]
2988 scrollbar
$w.list.sbx
-orient h
-command [list
$w_list xview
]
2989 scrollbar
$w.list.sby
-orient v
-command [list
$w_list yview
]
2990 pack
$w.list.sbx
-side bottom
-fill x
2991 pack
$w.list.sby
-side right
-fill y
2992 pack
$w_list -side left
-fill both
-expand 1
2993 pack
$w.list
-side top
-fill both
-expand 1
2995 label
$w.status
-textvariable browser_status
($w_list) \
3001 pack
$w.status
-anchor w
-side bottom
-fill x
3003 bind $w_list <Button-1
> "browser_click 0 $w_list @%x,%y;break"
3004 bind $w_list <Double-Button-1
> "browser_click 1 $w_list @%x,%y;break"
3006 bind $w <Visibility
> "focus $w"
3008 array unset browser_buffer $w_list
3009 array unset browser_files $w_list
3010 array unset browser_status $w_list
3011 array unset browser_stack $w_list
3012 array unset browser_path $w_list
3013 array unset browser_commit $w_list
3014 array unset browser_busy $w_list
3016 wm title
$w "[appname] ([reponame]): File Browser"
3017 ls_tree
$w_list $browser_commit($w_list) {}
3020 proc browser_click
{was_double_click w pos
} {
3021 global browser_files browser_status browser_path
3022 global browser_commit browser_stack browser_busy
3024 if {$browser_busy($w)} return
3025 set lno
[lindex
[split [$w index
$pos] .
] 0]
3026 set info
[lindex
$browser_files($w) [expr {$lno - 1}]]
3028 $w conf
-state normal
3029 $w tag remove sel
0.0 end
3030 $w tag remove in_sel
0.0 end
3032 $w tag add in_sel
$lno.0 [expr {$lno + 1}].0
3033 if {$was_double_click} {
3034 switch
-- [lindex
$info 0] {
3036 set parent
[lindex
$browser_stack($w) end-1
]
3037 set browser_stack
($w) [lrange
$browser_stack($w) 0 end-2
]
3038 if {$browser_stack($w) eq
{}} {
3039 regsub
{:.
*$
} $browser_path($w) {:} browser_path
($w)
3041 regsub
{/[^
/]+$
} $browser_path($w) {} browser_path
($w)
3043 set browser_status
($w) "Loading $browser_path($w)..."
3044 ls_tree
$w [lindex
$parent 0] [lindex
$parent 1]
3047 set name
[lindex
$info 2]
3048 set escn
[escape_path
$name]
3049 set browser_status
($w) "Loading $escn..."
3050 append browser_path
($w) $escn
3051 ls_tree
$w [lindex
$info 1] $name
3054 set name
[lindex
$info 2]
3056 foreach n
$browser_stack($w) {
3057 append p
[lindex
$n 1]
3060 show_blame
$browser_commit($w) $p
3065 $w conf
-state disabled
3068 proc ls_tree
{w tree_id name
} {
3069 global browser_buffer browser_files browser_stack browser_busy
3071 set browser_buffer
($w) {}
3072 set browser_files
($w) {}
3073 set browser_busy
($w) 1
3075 $w conf
-state normal
3076 $w tag remove in_sel
0.0 end
3077 $w tag remove sel
0.0 end
3079 if {$browser_stack($w) ne
{}} {
3080 $w image create end \
3081 -align center
-padx 5 -pady 1 \
3084 $w insert end
{[Up To Parent
]}
3085 lappend browser_files
($w) parent
3087 lappend browser_stack
($w) [list
$tree_id $name]
3088 $w conf
-state disabled
3090 set cmd
[list git ls-tree
-z $tree_id]
3091 set fd
[open
"| $cmd" r
]
3092 fconfigure
$fd -blocking 0 -translation binary
-encoding binary
3093 fileevent
$fd readable
[list read_ls_tree
$fd $w]
3096 proc read_ls_tree
{fd w
} {
3097 global browser_buffer browser_files browser_status browser_busy
3099 if {![winfo exists
$w]} {
3104 append browser_buffer
($w) [read $fd]
3105 set pck
[split $browser_buffer($w) "\0"]
3106 set browser_buffer
($w) [lindex
$pck end
]
3108 set n
[llength
$browser_files($w)]
3109 $w conf
-state normal
3110 foreach p
[lrange
$pck 0 end-1
] {
3111 set info
[split $p "\t"]
3112 set path
[lindex
$info 1]
3113 set info
[split [lindex
$info 0] { }]
3114 set type [lindex
$info 1]
3115 set object
[lindex
$info 2]
3126 set image file_question
3130 if {$n > 0} {$w insert end
"\n"}
3131 $w image create end \
3132 -align center
-padx 5 -pady 1 \
3133 -name icon
[incr n
] \
3135 $w insert end
[escape_path
$path]
3136 lappend browser_files
($w) [list
$type $object $path]
3138 $w conf
-state disabled
3142 set browser_status
($w) Ready.
3143 set browser_busy
($w) 0
3144 array
unset browser_buffer
$w
3148 proc show_blame
{commit path
} {
3149 global next_browser_id blame_status blame_data
3151 set w .browser
[incr next_browser_id
]
3152 set blame_status
($w) {Loading current
file content...
}
3157 label
$w.path
-text "$commit:$path" \
3163 pack
$w.path
-side top
-fill x
3167 label
$w.out.commit_l
-text Commit \
3172 text
$w.out.commit_t \
3173 -background white
-borderwidth 0 \
3179 lappend texts
$w.out.commit_t
3181 label
$w.out.author_l
-text Author \
3186 text
$w.out.author_t \
3187 -background white
-borderwidth 0 \
3193 lappend texts
$w.out.author_t
3195 label
$w.out.date_l
-text Date \
3200 text
$w.out.date_t \
3201 -background white
-borderwidth 0 \
3205 -width [string length
"yyyy-mm-dd hh:mm:ss"] \
3207 lappend texts
$w.out.date_t
3209 label
$w.out.filename_l
-text Filename \
3214 text
$w.out.filename_t \
3215 -background white
-borderwidth 0 \
3221 lappend texts
$w.out.filename_t
3223 label
$w.out.origlinenumber_l
-text {Orig Line
} \
3228 text
$w.out.origlinenumber_t \
3229 -background white
-borderwidth 0 \
3235 $w.out.origlinenumber_t tag conf linenumber
-justify right
3236 lappend texts
$w.out.origlinenumber_t
3238 label
$w.out.linenumber_l
-text {Curr Line
} \
3243 text
$w.out.linenumber_t \
3244 -background white
-borderwidth 0 \
3250 $w.out.linenumber_t tag conf linenumber
-justify right
3251 lappend texts
$w.out.linenumber_t
3253 label
$w.out.file_l
-text {File Content
} \
3258 text
$w.out.file_t \
3259 -background white
-borderwidth 0 \
3264 -xscrollcommand [list
$w.out.sbx
set] \
3266 lappend texts
$w.out.file_t
3268 scrollbar
$w.out.sbx
-orient h
-command [list
$w.out.file_t xview
]
3269 scrollbar
$w.out.sby
-orient v \
3270 -command [list scrollbar2many
$texts yview
]
3273 regsub
{_t$
} $i _l l
3276 set file_col
[expr {[llength
$texts] - 1}]
3277 eval grid
$labels -sticky we
3278 eval grid
$texts $w.out.sby
-sticky nsew
3279 grid conf
$w.out.sbx
-column $file_col -sticky we
3280 grid columnconfigure
$w.out
$file_col -weight 1
3281 grid rowconfigure
$w.out
1 -weight 1
3282 pack
$w.out
-fill both
-expand 1
3284 label
$w.status
-textvariable blame_status
($w) \
3290 pack
$w.status
-side bottom
-fill x
3292 menu
$w.ctxm
-tearoff 0
3293 $w.ctxm add
command -label "Copy Commit" \
3295 -command "blame_copycommit $w \$cursorW @\$cursorX,\$cursorY"
3298 $i tag conf in_sel \
3299 -background [$i cget
-foreground] \
3300 -foreground [$i cget
-background]
3301 $i conf
-yscrollcommand \
3302 [list many2scrollbar
$texts yview
$w.out.sby
]
3303 bind $i <Button-1
> "blame_highlight $i @%x,%y $texts;break"
3308 tk_popup $w.ctxm %X %Y
3312 set blame_data
($w,colors
) {}
3314 bind $w <Visibility
> "focus $w"
3316 array unset blame_status $w
3317 array unset blame_data $w,*
3319 wm title
$w "[appname] ([reponame]): File Viewer"
3321 set blame_data
($w,total_lines
) 0
3322 set cmd
[list git cat-file blob
"$commit:$path"]
3323 set fd
[open
"| $cmd" r
]
3324 fconfigure
$fd -blocking 0 -translation lf
-encoding binary
3325 fileevent
$fd readable
[list read_blame_catfile \
3326 $fd $w $commit $path \
3327 $texts $w.out.linenumber_t
$w.out.file_t
]
3330 proc read_blame_catfile
{fd w commit path texts w_lno w_file
} {
3331 global blame_status blame_data
3333 if {![winfo exists
$w_file]} {
3338 set n
$blame_data($w,total_lines
)
3339 foreach i
$texts {$i conf
-state normal
}
3340 while {[gets
$fd line
] >= 0} {
3341 regsub
"\r\$" $line {} line
3343 $w_lno insert end
$n linenumber
3344 $w_file insert end
$line
3345 foreach i
$texts {$i insert end
"\n"}
3347 foreach i
$texts {$i conf
-state disabled
}
3348 set blame_data
($w,total_lines
) $n
3352 set blame_status
($w) {Loading annotations...
}
3353 set cmd
[list git blame
-M -C --incremental]
3354 lappend cmd
$commit -- $path
3355 set fd
[open
"| $cmd" r
]
3356 fconfigure
$fd -blocking 0 -translation lf
-encoding binary
3357 fileevent
$fd readable
"read_blame_incremental $fd $w $texts"
3361 proc read_blame_incremental
{fd w
3362 w_commit w_author w_date w_filename w_olno
3364 global blame_status blame_data
3366 if {![winfo exists
$w_commit]} {
3380 $w_commit conf
-state normal
3381 $w_author conf
-state normal
3382 $w_date conf
-state normal
3383 $w_filename conf
-state normal
3384 $w_olno conf
-state normal
3386 while {[gets
$fd line
] >= 0} {
3387 if {[regexp
{^
([a-z0-9
]{40}) (\d
+) (\d
+) (\d
+)$
} $line line \
3388 cmit original_line final_line line_count
]} {
3389 set blame_data
($w,commit
) $cmit
3390 set blame_data
($w,original_line
) $original_line
3391 set blame_data
($w,final_line
) $final_line
3392 set blame_data
($w,line_count
) $line_count
3394 if {[catch
{set g
$blame_data($w,$cmit,seen
)}]} {
3395 if {$blame_data($w,colors
) eq
{}} {
3396 set blame_data
($w,colors
) {
3405 set c
[lindex
$blame_data($w,colors
) 0]
3406 set blame_data
($w,colors
) \
3407 [lrange
$blame_data($w,colors
) 1 end
]
3409 $t tag conf g
$cmit -background $c
3412 set blame_data
($w,$cmit,seen
) 1
3414 } elseif
{[string match
{filename
*} $line]} {
3415 set n
$blame_data($w,line_count
)
3416 set lno
$blame_data($w,final_line
)
3417 set ol
$blame_data($w,original_line
)
3418 set file [string range
$line 9 end
]
3419 set cmit
$blame_data($w,commit
)
3420 set abbrev
[string range
$cmit 0 8]
3422 if {[catch
{set author
$blame_data($w,$cmit,author
)} err
]} {
3426 if {[catch
{set atime
$blame_data($w,$cmit,author-time
)}]} {
3429 set atime
[clock format
$atime -format {%Y-
%m-
%d
%T
}]
3433 if {![catch
{set g g
$blame_data($w,line
$lno,commit
)}]} {
3435 $t tag remove
$g $lno.0 "$lno.0 lineend + 1c"
3445 $t delete
$lno.0 "$lno.0 lineend"
3448 $w_commit insert
$lno.0 $abbrev
3449 $w_author insert
$lno.0 $author
3450 $w_date insert
$lno.0 $atime
3451 $w_filename insert
$lno.0 $file
3452 $w_olno insert
$lno.0 $ol linenumber
3456 $t tag add
$g $lno.0 "$lno.0 lineend + 1c"
3459 set blame_data
($w,line
$lno,commit
) $cmit
3465 } elseif
{[regexp
{^
([a-z-
]+) (.
*)$
} $line line header data
]} {
3466 set blame_data
($w,$blame_data($w,commit
),$header) $data
3470 $w_commit conf
-state disabled
3471 $w_author conf
-state disabled
3472 $w_date conf
-state disabled
3473 $w_filename conf
-state disabled
3474 $w_olno conf
-state disabled
3478 set blame_status
($w) {Annotation complete.
}
3482 proc blame_highlight
{w pos args
} {
3483 set lno
[lindex
[split [$w index
$pos] .
] 0]
3485 $i tag remove in_sel
0.0 end
3487 if {$lno eq
{}} return
3489 $i tag add in_sel
$lno.0 "$lno.0 + 1 line"
3493 proc blame_copycommit
{w i pos
} {
3495 set lno
[lindex
[split [$i index
$pos] .
] 0]
3496 if {![catch
{set commit
$blame_data($w,line
$lno,commit
)}]} {
3505 ######################################################################
3510 #define mask_width 14
3511 #define mask_height 15
3512 static unsigned char mask_bits
[] = {
3513 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
3514 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
3515 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
3518 image create bitmap file_plain
-background white
-foreground black
-data {
3519 #define plain_width 14
3520 #define plain_height 15
3521 static unsigned char plain_bits
[] = {
3522 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
3523 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
3524 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
3525 } -maskdata $filemask
3527 image create bitmap file_mod
-background white
-foreground blue
-data {
3528 #define mod_width 14
3529 #define mod_height 15
3530 static unsigned char mod_bits
[] = {
3531 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
3532 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
3533 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
3534 } -maskdata $filemask
3536 image create bitmap file_fulltick
-background white
-foreground "#007000" -data {
3537 #define file_fulltick_width 14
3538 #define file_fulltick_height 15
3539 static unsigned char file_fulltick_bits
[] = {
3540 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
3541 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
3542 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
3543 } -maskdata $filemask
3545 image create bitmap file_parttick
-background white
-foreground "#005050" -data {
3546 #define parttick_width 14
3547 #define parttick_height 15
3548 static unsigned char parttick_bits
[] = {
3549 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
3550 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
3551 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
3552 } -maskdata $filemask
3554 image create bitmap file_question
-background white
-foreground black
-data {
3555 #define file_question_width 14
3556 #define file_question_height 15
3557 static unsigned char file_question_bits
[] = {
3558 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
3559 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
3560 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
3561 } -maskdata $filemask
3563 image create bitmap file_removed
-background white
-foreground red
-data {
3564 #define file_removed_width 14
3565 #define file_removed_height 15
3566 static unsigned char file_removed_bits
[] = {
3567 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
3568 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
3569 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
3570 } -maskdata $filemask
3572 image create bitmap file_merge
-background white
-foreground blue
-data {
3573 #define file_merge_width 14
3574 #define file_merge_height 15
3575 static unsigned char file_merge_bits
[] = {
3576 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
3577 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
3578 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
3579 } -maskdata $filemask
3582 #define file_width 18
3583 #define file_height 18
3584 static unsigned char file_bits
[] = {
3585 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
3586 0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
3587 0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
3588 0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
3589 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
3591 image create bitmap file_dir
-background white
-foreground blue \
3592 -data $file_dir_data -maskdata $file_dir_data
3595 set file_uplevel_data
{
3597 #define up_height 15
3598 static unsigned char up_bits
[] = {
3599 0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
3600 0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
3601 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
3603 image create bitmap file_uplevel
-background white
-foreground red \
3604 -data $file_uplevel_data -maskdata $file_uplevel_data
3605 unset file_uplevel_data
3607 set ui_index .vpane.files.index.list
3608 set ui_workdir .vpane.files.workdir.list
3610 set all_icons
(_
$ui_index) file_plain
3611 set all_icons
(A
$ui_index) file_fulltick
3612 set all_icons
(M
$ui_index) file_fulltick
3613 set all_icons
(D
$ui_index) file_removed
3614 set all_icons
(U
$ui_index) file_merge
3616 set all_icons
(_
$ui_workdir) file_plain
3617 set all_icons
(M
$ui_workdir) file_mod
3618 set all_icons
(D
$ui_workdir) file_question
3619 set all_icons
(U
$ui_workdir) file_merge
3620 set all_icons
(O
$ui_workdir) file_plain
3622 set max_status_desc
0
3626 {_M
"Modified, not staged"}
3627 {M_
"Staged for commit"}
3628 {MM
"Portions staged for commit"}
3629 {MD
"Staged for commit, missing"}
3631 {_O
"Untracked, not staged"}
3632 {A_
"Staged for commit"}
3633 {AM
"Portions staged for commit"}
3634 {AD
"Staged for commit, missing"}
3637 {D_
"Staged for removal"}
3638 {DO
"Staged for removal, still present"}
3640 {U_
"Requires merge resolution"}
3641 {UU
"Requires merge resolution"}
3642 {UM
"Requires merge resolution"}
3643 {UD
"Requires merge resolution"}
3645 if {$max_status_desc < [string length
[lindex
$i 1]]} {
3646 set max_status_desc
[string length
[lindex
$i 1]]
3648 set all_descs
([lindex
$i 0]) [lindex
$i 1]
3652 ######################################################################
3656 proc bind_button3
{w cmd
} {
3657 bind $w <Any-Button-3
> $cmd
3659 bind $w <Control-Button-1
> $cmd
3663 proc scrollbar2many
{list mode args
} {
3664 foreach w
$list {eval $w $mode $args}
3667 proc many2scrollbar
{list mode sb top bottom
} {
3668 $sb set $top $bottom
3669 foreach w
$list {$w $mode moveto
$top}
3672 proc incr_font_size
{font
{amt
1}} {
3673 set sz
[font configure
$font -size]
3675 font configure
$font -size $sz
3676 font configure
${font}bold
-size $sz
3679 proc hook_failed_popup
{hook msg
} {
3684 label
$w.m.l1
-text "$hook hook failed:" \
3689 -background white
-borderwidth 1 \
3691 -width 80 -height 10 \
3693 -yscrollcommand [list
$w.m.sby
set]
3695 -text {You must correct the above errors before committing.
} \
3699 scrollbar
$w.m.sby
-command [list
$w.m.t yview
]
3700 pack
$w.m.l1
-side top
-fill x
3701 pack
$w.m.l2
-side bottom
-fill x
3702 pack
$w.m.sby
-side right
-fill y
3703 pack
$w.m.t
-side left
-fill both
-expand 1
3704 pack
$w.m
-side top
-fill both
-expand 1 -padx 5 -pady 10
3706 $w.m.t insert
1.0 $msg
3707 $w.m.t conf
-state disabled
3709 button
$w.ok
-text OK \
3712 -command "destroy $w"
3713 pack
$w.ok
-side bottom
-anchor e
-pady 10 -padx 10
3715 bind $w <Visibility
> "grab $w; focus $w"
3716 bind $w <Key-Return
> "destroy $w"
3717 wm title
$w "[appname] ([reponame]): error"
3721 set next_console_id
0
3723 proc new_console
{short_title long_title
} {
3724 global next_console_id console_data
3725 set w .console
[incr next_console_id
]
3726 set console_data
($w) [list
$short_title $long_title]
3727 return [console_init
$w]
3730 proc console_init
{w
} {
3731 global console_cr console_data M1B
3733 set console_cr
($w) 1.0
3736 label
$w.m.l1
-text "[lindex $console_data($w) 1]:" \
3741 -background white
-borderwidth 1 \
3743 -width 80 -height 10 \
3746 -yscrollcommand [list
$w.m.sby
set]
3747 label
$w.m.s
-text {Working... please
wait...
} \
3751 scrollbar
$w.m.sby
-command [list
$w.m.t yview
]
3752 pack
$w.m.l1
-side top
-fill x
3753 pack
$w.m.s
-side bottom
-fill x
3754 pack
$w.m.sby
-side right
-fill y
3755 pack
$w.m.t
-side left
-fill both
-expand 1
3756 pack
$w.m
-side top
-fill both
-expand 1 -padx 5 -pady 10
3758 menu
$w.ctxm
-tearoff 0
3759 $w.ctxm add
command -label "Copy" \
3761 -command "tk_textCopy $w.m.t"
3762 $w.ctxm add
command -label "Select All" \
3764 -command "focus $w.m.t;$w.m.t tag add sel 0.0 end"
3765 $w.ctxm add
command -label "Copy All" \
3768 $w.m.t tag add sel 0.0 end
3770 $w.m.t tag remove sel 0.0 end
3773 button
$w.ok
-text {Close
} \
3776 -command "destroy $w"
3777 pack
$w.ok
-side bottom
-anchor e
-pady 10 -padx 10
3779 bind_button3
$w.m.t
"tk_popup $w.ctxm %X %Y"
3780 bind $w.m.t
<$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break"
3781 bind $w.m.t
<$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break"
3782 bind $w <Visibility
> "focus $w"
3783 wm title
$w "[appname] ([reponame]): [lindex $console_data($w) 0]"
3787 proc console_exec
{w cmd after
} {
3788 # -- Cygwin's Tcl tosses the enviroment when we exec our child.
3789 # But most users need that so we have to relogin. :-(
3792 set cmd
[list sh
--login -c "cd \"[pwd]\" && [join $cmd { }]"]
3795 # -- Tcl won't let us redirect both stdout and stderr to
3796 # the same pipe. So pass it through cat...
3798 set cmd
[concat |
$cmd |
& cat]
3800 set fd_f
[open
$cmd r
]
3801 fconfigure
$fd_f -blocking 0 -translation binary
3802 fileevent
$fd_f readable
[list console_read
$w $fd_f $after]
3805 proc console_read
{w fd after
} {
3810 if {![winfo exists
$w]} {console_init
$w}
3811 $w.m.t conf
-state normal
3813 set n
[string length
$buf]
3815 set cr
[string first
"\r" $buf $c]
3816 set lf
[string first
"\n" $buf $c]
3817 if {$cr < 0} {set cr
[expr {$n + 1}]}
3818 if {$lf < 0} {set lf
[expr {$n + 1}]}
3821 $w.m.t insert end
[string range
$buf $c $lf]
3822 set console_cr
($w) [$w.m.t index
{end
-1c}]
3826 $w.m.t delete
$console_cr($w) end
3827 $w.m.t insert end
"\n"
3828 $w.m.t insert end
[string range
$buf $c $cr]
3833 $w.m.t conf
-state disabled
3837 fconfigure
$fd -blocking 1
3839 if {[catch
{close
$fd}]} {
3844 uplevel
#0 $after $w $ok
3847 fconfigure
$fd -blocking 0
3850 proc console_chain
{cmdlist w
{ok
1}} {
3852 if {[llength
$cmdlist] == 0} {
3857 set cmd
[lindex
$cmdlist 0]
3858 set cmdlist
[lrange
$cmdlist 1 end
]
3860 if {[lindex
$cmd 0] eq
{console_exec
}} {
3863 [list console_chain
$cmdlist]
3865 uplevel
#0 $cmd $cmdlist $w $ok
3872 proc console_done
{args
} {
3873 global console_cr console_data
3875 switch
-- [llength
$args] {
3877 set w
[lindex
$args 0]
3878 set ok
[lindex
$args 1]
3881 set w
[lindex
$args 1]
3882 set ok
[lindex
$args 2]
3885 error
"wrong number of args: console_done ?ignored? w ok"
3890 if {[winfo exists
$w]} {
3891 $w.m.s conf
-background green
-text {Success
}
3892 $w.ok conf
-state normal
3895 if {![winfo exists
$w]} {
3898 $w.m.s conf
-background red
-text {Error
: Command Failed
}
3899 $w.ok conf
-state normal
3902 array
unset console_cr
$w
3903 array
unset console_data
$w
3906 ######################################################################
3910 set starting_gitk_msg
{Starting gitk... please
wait...
}
3912 proc do_gitk
{revs
} {
3913 global env ui_status_value starting_gitk_msg
3915 # -- On Windows gitk is severly broken, and right now it seems like
3916 # nobody cares about fixing it. The only known workaround is to
3917 # always delete ~/.gitk before starting the program.
3920 catch
{file delete
[file join $env(HOME
) .gitk
]}
3923 # -- Always start gitk through whatever we were loaded with. This
3924 # lets us bypass using shell process on Windows systems.
3926 set cmd
[info nameofexecutable
]
3927 lappend cmd
[gitexec gitk
]
3933 if {[catch
{eval exec $cmd &} err
]} {
3934 error_popup
"Failed to start gitk:\n\n$err"
3936 set ui_status_value
$starting_gitk_msg
3938 if {$ui_status_value eq
$starting_gitk_msg} {
3939 set ui_status_value
{Ready.
}
3946 set fd
[open
"| git count-objects -v" r
]
3947 while {[gets
$fd line
] > 0} {
3948 if {[regexp
{^
([^
:]+): (\d
+)$
} $line _ name value
]} {
3949 set stats
($name) $value
3955 foreach p
[glob
-directory [gitdir objects pack
] \
3958 incr packed_sz
[file size
$p]
3960 if {$packed_sz > 0} {
3961 set stats
(size-pack
) [expr {$packed_sz / 1024}]
3966 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
3968 label
$w.header
-text {Database Statistics
} \
3970 pack
$w.header
-side top
-fill x
3972 frame
$w.buttons
-border 1
3973 button
$w.buttons.close
-text Close \
3975 -command [list destroy
$w]
3976 button
$w.buttons.gc
-text {Compress Database
} \
3978 -command "destroy $w;do_gc"
3979 pack
$w.buttons.close
-side right
3980 pack
$w.buttons.gc
-side left
3981 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
3983 frame
$w.stat
-borderwidth 1 -relief solid
3985 {count
{Number of loose objects
}}
3986 {size
{Disk space used by loose objects
} { KiB
}}
3987 {in-pack
{Number of packed objects
}}
3988 {packs
{Number of packs
}}
3989 {size-pack
{Disk space used by packed objects
} { KiB
}}
3990 {prune-packable
{Packed objects waiting
for pruning
}}
3991 {garbage
{Garbage files
}}
3993 set name
[lindex
$s 0]
3994 set label
[lindex
$s 1]
3995 if {[catch
{set value
$stats($name)}]} continue
3996 if {[llength
$s] > 2} {
3997 set value
"$value[lindex $s 2]"
4000 label
$w.stat.l_
$name -text "$label:" -anchor w
-font font_ui
4001 label
$w.stat.v_
$name -text $value -anchor w
-font font_ui
4002 grid
$w.stat.l_
$name $w.stat.v_
$name -sticky we
-padx {0 5}
4004 pack
$w.stat
-pady 10 -padx 10
4006 bind $w <Visibility
> "grab $w; focus $w"
4007 bind $w <Key-Escape
> [list destroy
$w]
4008 bind $w <Key-Return
> [list destroy
$w]
4009 wm title
$w "[appname] ([reponame]): Database Statistics"
4014 set w
[new_console
{gc
} {Compressing the object database
}]
4016 {console_exec
{git pack-refs
--prune}}
4017 {console_exec
{git reflog expire
--all}}
4018 {console_exec
{git repack
-a -d -l}}
4019 {console_exec
{git rerere gc
}}
4023 proc do_fsck_objects
{} {
4024 set w
[new_console
{fsck-objects
} \
4025 {Verifying the object database with fsck-objects
}]
4026 set cmd
[list git fsck-objects
]
4029 lappend cmd
--strict
4030 console_exec
$w $cmd console_done
4036 global ui_comm is_quitting repo_config commit_type
4038 if {$is_quitting} return
4041 # -- Stash our current commit buffer.
4043 set save
[gitdir GITGUI_MSG
]
4044 set msg
[string trim
[$ui_comm get
0.0 end
]]
4045 regsub
-all -line {[ \r\t]+$
} $msg {} msg
4046 if {(![string match amend
* $commit_type]
4047 ||
[$ui_comm edit modified
])
4050 set fd
[open
$save w
]
4051 puts
-nonewline $fd $msg
4055 catch
{file delete
$save}
4058 # -- Stash our current window geometry into this repository.
4060 set cfg_geometry
[list
]
4061 lappend cfg_geometry
[wm geometry .
]
4062 lappend cfg_geometry
[lindex
[.vpane sash coord
0] 1]
4063 lappend cfg_geometry
[lindex
[.vpane.files sash coord
0] 0]
4064 if {[catch
{set rc_geometry
$repo_config(gui.geometry
)}]} {
4067 if {$cfg_geometry ne
$rc_geometry} {
4068 catch
{exec git repo-config gui.geometry
$cfg_geometry}
4075 rescan
{set ui_status_value
{Ready.
}}
4078 proc unstage_helper
{txt paths
} {
4079 global file_states current_diff_path
4081 if {![lock_index begin-update
]} return
4085 foreach path
$paths {
4086 switch
-glob -- [lindex
$file_states($path) 0] {
4090 lappend pathList
$path
4091 if {$path eq
$current_diff_path} {
4092 set after
{reshow_diff
;}
4097 if {$pathList eq
{}} {
4103 [concat
$after {set ui_status_value
{Ready.
}}]
4107 proc do_unstage_selection
{} {
4108 global current_diff_path selected_paths
4110 if {[array size selected_paths
] > 0} {
4112 {Unstaging selected files from commit
} \
4113 [array names selected_paths
]
4114 } elseif
{$current_diff_path ne
{}} {
4116 "Unstaging [short_path $current_diff_path] from commit" \
4117 [list
$current_diff_path]
4121 proc add_helper
{txt paths
} {
4122 global file_states current_diff_path
4124 if {![lock_index begin-update
]} return
4128 foreach path
$paths {
4129 switch
-glob -- [lindex
$file_states($path) 0] {
4134 lappend pathList
$path
4135 if {$path eq
$current_diff_path} {
4136 set after
{reshow_diff
;}
4141 if {$pathList eq
{}} {
4147 [concat
$after {set ui_status_value
{Ready to commit.
}}]
4151 proc do_add_selection
{} {
4152 global current_diff_path selected_paths
4154 if {[array size selected_paths
] > 0} {
4156 {Adding selected files
} \
4157 [array names selected_paths
]
4158 } elseif
{$current_diff_path ne
{}} {
4160 "Adding [short_path $current_diff_path]" \
4161 [list
$current_diff_path]
4165 proc do_add_all
{} {
4169 foreach path
[array names file_states
] {
4170 switch
-glob -- [lindex
$file_states($path) 0] {
4173 ?D
{lappend paths
$path}
4176 add_helper
{Adding all changed files
} $paths
4179 proc revert_helper
{txt paths
} {
4180 global file_states current_diff_path
4182 if {![lock_index begin-update
]} return
4186 foreach path
$paths {
4187 switch
-glob -- [lindex
$file_states($path) 0] {
4191 lappend pathList
$path
4192 if {$path eq
$current_diff_path} {
4193 set after
{reshow_diff
;}
4199 set n
[llength
$pathList]
4203 } elseif
{$n == 1} {
4204 set s
"[short_path [lindex $pathList]]"
4206 set s
"these $n files"
4209 set reply
[tk_dialog \
4211 "[appname] ([reponame])" \
4212 "Revert changes in $s?
4214 Any unadded changes will be permanently lost by the revert." \
4224 [concat
$after {set ui_status_value
{Ready.
}}]
4230 proc do_revert_selection
{} {
4231 global current_diff_path selected_paths
4233 if {[array size selected_paths
] > 0} {
4235 {Reverting selected files
} \
4236 [array names selected_paths
]
4237 } elseif
{$current_diff_path ne
{}} {
4239 "Reverting [short_path $current_diff_path]" \
4240 [list
$current_diff_path]
4244 proc do_signoff
{} {
4247 set me
[committer_ident
]
4248 if {$me eq
{}} return
4250 set sob
"Signed-off-by: $me"
4251 set last
[$ui_comm get
{end
-1c linestart
} {end
-1c}]
4252 if {$last ne
$sob} {
4253 $ui_comm edit separator
4255 && ![regexp
{^
[A-Z
][A-Za-z
]*-[A-Za-z-
]+: *} $last]} {
4256 $ui_comm insert end
"\n"
4258 $ui_comm insert end
"\n$sob"
4259 $ui_comm edit separator
4264 proc do_select_commit_type
{} {
4265 global commit_type selected_commit_type
4267 if {$selected_commit_type eq
{new
}
4268 && [string match amend
* $commit_type]} {
4270 } elseif
{$selected_commit_type eq
{amend
}
4271 && ![string match amend
* $commit_type]} {
4274 # The amend request was rejected...
4276 if {![string match amend
* $commit_type]} {
4277 set selected_commit_type new
4287 global appvers copyright
4288 global tcl_patchLevel tk_patchLevel
4292 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
4294 label
$w.header
-text "About [appname]" \
4296 pack
$w.header
-side top
-fill x
4299 button
$w.buttons.close
-text {Close
} \
4301 -command [list destroy
$w]
4302 pack
$w.buttons.close
-side right
4303 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
4306 -text "[appname] - a commit creation tool for Git.
4314 pack
$w.desc
-side top
-fill x
-padx 5 -pady 5
4317 append v
"[appname] version $appvers\n"
4318 append v
"[exec git version]\n"
4320 if {$tcl_patchLevel eq
$tk_patchLevel} {
4321 append v
"Tcl/Tk version $tcl_patchLevel"
4323 append v
"Tcl version $tcl_patchLevel"
4324 append v
", Tk version $tk_patchLevel"
4335 pack
$w.vers
-side top
-fill x
-padx 5 -pady 5
4337 menu
$w.ctxm
-tearoff 0
4338 $w.ctxm add
command \
4343 clipboard append -format STRING -type STRING -- \[$w.vers cget -text\]
4346 bind $w <Visibility
> "grab $w; focus $w"
4347 bind $w <Key-Escape
> "destroy $w"
4348 bind_button3
$w.vers
"tk_popup $w.ctxm %X %Y; grab $w; focus $w"
4349 wm title
$w "About [appname]"
4353 proc do_options
{} {
4354 global repo_config global_config font_descs
4355 global repo_config_new global_config_new
4357 array
unset repo_config_new
4358 array
unset global_config_new
4359 foreach name
[array names repo_config
] {
4360 set repo_config_new
($name) $repo_config($name)
4363 foreach name
[array names repo_config
] {
4365 gui.diffcontext
{continue}
4367 set repo_config_new
($name) $repo_config($name)
4369 foreach name
[array names global_config
] {
4370 set global_config_new
($name) $global_config($name)
4373 set w .options_editor
4375 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
4377 label
$w.header
-text "[appname] Options" \
4379 pack
$w.header
-side top
-fill x
4382 button
$w.buttons.restore
-text {Restore Defaults
} \
4384 -command do_restore_defaults
4385 pack
$w.buttons.restore
-side left
4386 button
$w.buttons.save
-text Save \
4388 -command [list do_save_config
$w]
4389 pack
$w.buttons.save
-side right
4390 button
$w.buttons.cancel
-text {Cancel
} \
4392 -command [list destroy
$w]
4393 pack
$w.buttons.cancel
-side right
-padx 5
4394 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
4396 labelframe
$w.repo
-text "[reponame] Repository" \
4398 labelframe
$w.global
-text {Global
(All Repositories
)} \
4400 pack
$w.repo
-side left
-fill both
-expand 1 -pady 5 -padx 5
4401 pack
$w.global
-side right
-fill both
-expand 1 -pady 5 -padx 5
4405 {t user.name
{User Name
}}
4406 {t user.email
{Email Address
}}
4408 {b merge.summary
{Summarize Merge Commits
}}
4409 {i-1.
.5 merge.verbosity
{Merge Verbosity
}}
4411 {b gui.trustmtime
{Trust File Modification Timestamps
}}
4412 {i-1.
.99 gui.diffcontext
{Number of Diff Context Lines
}}
4413 {t gui.newbranchtemplate
{New Branch Name Template
}}
4415 set type [lindex
$option 0]
4416 set name
[lindex
$option 1]
4417 set text
[lindex
$option 2]
4419 foreach f
{repo global
} {
4420 switch
-glob -- $type {
4422 checkbutton
$w.
$f.
$optid -text $text \
4423 -variable ${f}_config_new
($name) \
4427 pack
$w.
$f.
$optid -side top
-anchor w
4430 regexp
-- {-(\d
+)\.\.
(\d
+)$
} $type _junk min max
4432 label
$w.
$f.
$optid.l
-text "$text:" -font font_ui
4433 pack
$w.
$f.
$optid.l
-side left
-anchor w
-fill x
4434 spinbox
$w.
$f.
$optid.v \
4435 -textvariable ${f}_config_new
($name) \
4439 -width [expr {1 + [string length
$max]}] \
4441 bind $w.
$f.
$optid.v
<FocusIn
> {%W selection range
0 end
}
4442 pack
$w.
$f.
$optid.v
-side right
-anchor e
-padx 5
4443 pack
$w.
$f.
$optid -side top
-anchor w
-fill x
4447 label
$w.
$f.
$optid.l
-text "$text:" -font font_ui
4448 entry
$w.
$f.
$optid.v \
4452 -textvariable ${f}_config_new
($name) \
4454 pack
$w.
$f.
$optid.l
-side left
-anchor w
4455 pack
$w.
$f.
$optid.v
-side left
-anchor w \
4458 pack
$w.
$f.
$optid -side top
-anchor w
-fill x
4464 set all_fonts
[lsort
[font families
]]
4465 foreach option
$font_descs {
4466 set name
[lindex
$option 0]
4467 set font
[lindex
$option 1]
4468 set text
[lindex
$option 2]
4470 set global_config_new
(gui.
$font^^family
) \
4471 [font configure
$font -family]
4472 set global_config_new
(gui.
$font^^size
) \
4473 [font configure
$font -size]
4475 frame
$w.global.
$name
4476 label
$w.global.
$name.l
-text "$text:" -font font_ui
4477 pack
$w.global.
$name.l
-side left
-anchor w
-fill x
4478 eval tk_optionMenu
$w.global.
$name.family \
4479 global_config_new
(gui.
$font^^family
) \
4481 spinbox
$w.global.
$name.size \
4482 -textvariable global_config_new
(gui.
$font^^size
) \
4483 -from 2 -to 80 -increment 1 \
4486 bind $w.global.
$name.size
<FocusIn
> {%W selection range
0 end
}
4487 pack
$w.global.
$name.size
-side right
-anchor e
4488 pack
$w.global.
$name.family
-side right
-anchor e
4489 pack
$w.global.
$name -side top
-anchor w
-fill x
4492 bind $w <Visibility
> "grab $w; focus $w"
4493 bind $w <Key-Escape
> "destroy $w"
4494 wm title
$w "[appname] ([reponame]): Options"
4498 proc do_restore_defaults
{} {
4499 global font_descs default_config repo_config
4500 global repo_config_new global_config_new
4502 foreach name
[array names default_config
] {
4503 set repo_config_new
($name) $default_config($name)
4504 set global_config_new
($name) $default_config($name)
4507 foreach option
$font_descs {
4508 set name
[lindex
$option 0]
4509 set repo_config
(gui.
$name) $default_config(gui.
$name)
4513 foreach option
$font_descs {
4514 set name
[lindex
$option 0]
4515 set font
[lindex
$option 1]
4516 set global_config_new
(gui.
$font^^family
) \
4517 [font configure
$font -family]
4518 set global_config_new
(gui.
$font^^size
) \
4519 [font configure
$font -size]
4523 proc do_save_config
{w
} {
4524 if {[catch
{save_config
} err
]} {
4525 error_popup
"Failed to completely save options:\n\n$err"
4531 proc do_windows_shortcut
{} {
4534 set fn
[tk_getSaveFile \
4536 -title "[appname] ([reponame]): Create Desktop Icon" \
4537 -initialfile "Git [reponame].bat"]
4541 puts
$fd "@ECHO Entering [reponame]"
4542 puts
$fd "@ECHO Starting git-gui... please wait..."
4543 puts
$fd "@SET PATH=[file normalize [gitexec]];%PATH%"
4544 puts
$fd "@SET GIT_DIR=[file normalize [gitdir]]"
4545 puts
-nonewline $fd "@\"[info nameofexecutable]\""
4546 puts
$fd " \"[file normalize $argv0]\""
4549 error_popup
"Cannot write script:\n\n$err"
4554 proc do_cygwin_shortcut
{} {
4558 set desktop
[exec cygpath \
4566 set fn
[tk_getSaveFile \
4568 -title "[appname] ([reponame]): Create Desktop Icon" \
4569 -initialdir $desktop \
4570 -initialfile "Git [reponame].bat"]
4574 set sh
[exec cygpath \
4578 set me
[exec cygpath \
4582 set gd
[exec cygpath \
4586 set gw
[exec cygpath \
4589 [file dirname [gitdir
]]]
4590 regsub
-all ' $me "'\\''" me
4591 regsub -all ' $gd "'\\''" gd
4592 puts $fd "@ECHO Entering $gw"
4593 puts $fd "@ECHO Starting git-gui... please wait..."
4594 puts -nonewline $fd "@\"$sh\" --login -c \""
4595 puts -nonewline $fd "GIT_DIR='$gd'"
4596 puts -nonewline $fd " '$me'"
4600 error_popup "Cannot write script:\n\n$err"
4605 proc do_macosx_app {} {
4608 set fn [tk_getSaveFile \
4610 -title "[appname] ([reponame]): Create Desktop Icon" \
4611 -initialdir [file join $env(HOME) Desktop] \
4612 -initialfile "Git [reponame].app"]
4615 set Contents [file join $fn Contents]
4616 set MacOS [file join $Contents MacOS]
4617 set exe [file join $MacOS git-gui]
4621 set fd [open [file join $Contents Info.plist] w]
4622 puts $fd {<?xml version="1.0" encoding="UTF-8"?>
4623 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4624 <plist version="1.0">
4626 <key>CFBundleDevelopmentRegion</key>
4627 <string>English</string>
4628 <key>CFBundleExecutable</key>
4629 <string>git-gui</string>
4630 <key>CFBundleIdentifier</key>
4631 <string>org.spearce.git-gui</string>
4632 <key>CFBundleInfoDictionaryVersion</key>
4633 <string>6.0</string>
4634 <key>CFBundlePackageType</key>
4635 <string>APPL</string>
4636 <key>CFBundleSignature</key>
4637 <string>????</string>
4638 <key>CFBundleVersion</key>
4639 <string>1.0</string>
4640 <key>NSPrincipalClass</key>
4641 <string>NSApplication</string>
4646 set fd [open $exe w]
4647 set gd [file normalize [gitdir]]
4648 set ep [file normalize [gitexec]]
4649 regsub -all ' $gd "'\\''" gd
4650 regsub
-all ' $ep "'\\''" ep
4651 puts $fd "#!/bin/sh"
4652 foreach name
[array names env
] {
4653 if {[string match GIT_
* $name]} {
4654 regsub
-all ' $env($name) "'\\''" v
4655 puts $fd "export $name='$v'"
4658 puts $fd "export PATH
='$ep':\
$PATH"
4659 puts $fd "export GIT_DIR
='$gd'"
4660 puts $fd "exec [file normalize
$argv0]"
4663 file attributes $exe -permissions u+x,g+x,o+x
4665 error_popup "Cannot
write icon
:\n\n$err"
4670 proc toggle_or_diff {w x y} {
4671 global file_states file_lists current_diff_path ui_index ui_workdir
4672 global last_clicked selected_paths
4674 set pos [split [$w index @$x,$y] .]
4675 set lno [lindex $pos 0]
4676 set col [lindex $pos 1]
4677 set path [lindex $file_lists($w) [expr {$lno - 1}]]
4683 set last_clicked [list $w $lno]
4684 array unset selected_paths
4685 $ui_index tag remove in_sel 0.0 end
4686 $ui_workdir tag remove in_sel 0.0 end
4689 if {$current_diff_path eq $path} {
4690 set after {reshow_diff;}
4694 if {$w eq $ui_index} {
4696 "Unstaging
[short_path
$path] from commit
" \
4698 [concat $after {set ui_status_value {Ready.}}]
4699 } elseif {$w eq $ui_workdir} {
4701 "Adding
[short_path
$path]" \
4703 [concat $after {set ui_status_value {Ready.}}]
4706 show_diff $path $w $lno
4710 proc add_one_to_selection {w x y} {
4711 global file_lists last_clicked selected_paths
4713 set lno [lindex [split [$w index @$x,$y] .] 0]
4714 set path [lindex $file_lists($w) [expr {$lno - 1}]]
4720 if {$last_clicked ne {}
4721 && [lindex $last_clicked 0] ne $w} {
4722 array unset selected_paths
4723 [lindex $last_clicked 0] tag remove in_sel 0.0 end
4726 set last_clicked [list $w $lno]
4727 if {[catch {set in_sel $selected_paths($path)}]} {
4731 unset selected_paths($path)
4732 $w tag remove in_sel $lno.0 [expr {$lno + 1}].0
4734 set selected_paths($path) 1
4735 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
4739 proc add_range_to_selection {w x y} {
4740 global file_lists last_clicked selected_paths
4742 if {[lindex $last_clicked 0] ne $w} {
4743 toggle_or_diff $w $x $y
4747 set lno [lindex [split [$w index @$x,$y] .] 0]
4748 set lc [lindex $last_clicked 1]
4757 foreach path [lrange $file_lists($w) \
4758 [expr {$begin - 1}] \
4759 [expr {$end - 1}]] {
4760 set selected_paths($path) 1
4762 $w tag add in_sel $begin.0 [expr {$end + 1}].0
4765 ######################################################################
4769 set cursor_ptr arrow
4770 font create font_diff -family Courier -size 10
4774 eval font configure font_ui [font actual [.dummy cget -font]]
4778 font create font_uibold
4779 font create font_diffbold
4784 } elseif {[is_MacOSX]} {
4792 proc apply_config {} {
4793 global repo_config font_descs
4795 foreach option $font_descs {
4796 set name [lindex $option 0]
4797 set font [lindex $option 1]
4799 foreach {cn cv} $repo_config(gui.$name) {
4800 font configure $font $cn $cv
4803 error_popup "Invalid font specified
in gui.
$name:\n\n$err"
4805 foreach {cn cv} [font configure $font] {
4806 font configure ${font}bold $cn $cv
4808 font configure ${font}bold -weight bold
4812 set default_config(merge.summary) false
4813 set default_config(merge.verbosity) 2
4814 set default_config(user.name) {}
4815 set default_config(user.email) {}
4817 set default_config(gui.trustmtime) false
4818 set default_config(gui.diffcontext) 5
4819 set default_config(gui.newbranchtemplate) {}
4820 set default_config(gui.fontui) [font configure font_ui]
4821 set default_config(gui.fontdiff) [font configure font_diff]
4823 {fontui font_ui {Main Font}}
4824 {fontdiff font_diff {Diff/Console Font}}
4829 ######################################################################
4835 menu .mbar -tearoff 0
4836 .mbar add cascade -label Repository -menu .mbar.repository
4837 .mbar add cascade -label Edit -menu .mbar.edit
4838 if {!$single_commit} {
4839 .mbar add cascade -label Branch -menu .mbar.branch
4841 .mbar add cascade -label Commit -menu .mbar.commit
4842 if {!$single_commit} {
4843 .mbar add cascade -label Merge -menu .mbar.merge
4844 .mbar add cascade -label Fetch -menu .mbar.fetch
4845 .mbar add cascade -label Push -menu .mbar.push
4847 . configure -menu .mbar
4849 # -- Repository Menu
4851 menu .mbar.repository
4853 .mbar.repository add command \
4854 -label {Browse Current Branch} \
4855 -command {new_browser $current_branch} \
4857 .mbar.repository add separator
4859 .mbar.repository add command \
4860 -label {Visualize Current Branch} \
4861 -command {do_gitk {}} \
4863 .mbar.repository add command \
4864 -label {Visualize All Branches} \
4865 -command {do_gitk {--all}} \
4867 .mbar.repository add separator
4869 if {!$single_commit} {
4870 .mbar.repository add command -label {Database Statistics} \
4874 .mbar.repository add command -label {Compress Database} \
4878 .mbar.repository add command -label {Verify Database} \
4879 -command do_fsck_objects \
4882 .mbar.repository add separator
4885 .mbar.repository add command \
4886 -label {Create Desktop Icon} \
4887 -command do_cygwin_shortcut \
4889 } elseif {[is_Windows]} {
4890 .mbar.repository add command \
4891 -label {Create Desktop Icon} \
4892 -command do_windows_shortcut \
4894 } elseif {[is_MacOSX]} {
4895 .mbar.repository add command \
4896 -label {Create Desktop Icon} \
4897 -command do_macosx_app \
4902 .mbar.repository add command -label Quit \
4904 -accelerator $M1T-Q \
4910 .mbar.edit add command -label Undo \
4911 -command {catch {[focus] edit undo}} \
4912 -accelerator $M1T-Z \
4914 .mbar.edit add command -label Redo \
4915 -command {catch {[focus] edit redo}} \
4916 -accelerator $M1T-Y \
4918 .mbar.edit add separator
4919 .mbar.edit add command -label Cut \
4920 -command {catch {tk_textCut [focus]}} \
4921 -accelerator $M1T-X \
4923 .mbar.edit add command -label Copy \
4924 -command {catch {tk_textCopy [focus]}} \
4925 -accelerator $M1T-C \
4927 .mbar.edit add command -label Paste \
4928 -command {catch {tk_textPaste [focus]; [focus] see insert}} \
4929 -accelerator $M1T-V \
4931 .mbar.edit add command -label Delete \
4932 -command {catch {[focus] delete sel.first sel.last}} \
4935 .mbar.edit add separator
4936 .mbar.edit add command -label {Select All} \
4937 -command {catch {[focus] tag add sel 0.0 end}} \
4938 -accelerator $M1T-A \
4943 if {!$single_commit} {
4946 .mbar.branch add command -label {Create...} \
4947 -command do_create_branch \
4948 -accelerator $M1T-N \
4950 lappend disable_on_lock [list .mbar.branch entryconf \
4951 [.mbar.branch index last] -state]
4953 .mbar.branch add command -label {Delete...} \
4954 -command do_delete_branch \
4956 lappend disable_on_lock [list .mbar.branch entryconf \
4957 [.mbar.branch index last] -state]
4964 .mbar.commit add radiobutton \
4965 -label {New Commit} \
4966 -command do_select_commit_type \
4967 -variable selected_commit_type \
4970 lappend disable_on_lock \
4971 [list .mbar.commit entryconf [.mbar.commit index last] -state]
4973 .mbar.commit add radiobutton \
4974 -label {Amend Last Commit} \
4975 -command do_select_commit_type \
4976 -variable selected_commit_type \
4979 lappend disable_on_lock \
4980 [list .mbar.commit entryconf [.mbar.commit index last] -state]
4982 .mbar.commit add separator
4984 .mbar.commit add command -label Rescan \
4985 -command do_rescan \
4988 lappend disable_on_lock \
4989 [list .mbar.commit entryconf [.mbar.commit index last] -state]
4991 .mbar.commit add command -label {Add To Commit} \
4992 -command do_add_selection \
4994 lappend disable_on_lock \
4995 [list .mbar.commit entryconf [.mbar.commit index last] -state]
4997 .mbar.commit add command -label {Add All To Commit} \
4998 -command do_add_all \
4999 -accelerator $M1T-I \
5001 lappend disable_on_lock \
5002 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5004 .mbar.commit add command -label {Unstage From Commit} \
5005 -command do_unstage_selection \
5007 lappend disable_on_lock \
5008 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5010 .mbar.commit add command -label {Revert Changes} \
5011 -command do_revert_selection \
5013 lappend disable_on_lock \
5014 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5016 .mbar.commit add separator
5018 .mbar.commit add command -label {Sign Off} \
5019 -command do_signoff \
5020 -accelerator $M1T-S \
5023 .mbar.commit add command -label Commit \
5024 -command do_commit \
5025 -accelerator $M1T-Return \
5027 lappend disable_on_lock \
5028 [list .mbar.commit entryconf [.mbar.commit index last] -state]
5031 # -- Apple Menu (Mac OS X only)
5033 .mbar add cascade -label Apple -menu .mbar.apple
5036 .mbar.apple add command -label "About
[appname
]" \
5039 .mbar.apple add command -label "[appname
] Options...
" \
5040 -command do_options \
5045 .mbar.edit add separator
5046 .mbar.edit add command -label {Options...} \
5047 -command do_options \
5052 if {[file exists /usr/local/miga/lib/gui-miga]
5053 && [file exists .pvcsrc]} {
5055 global ui_status_value
5056 if {![lock_index update]} return
5057 set cmd [list sh --login -c "/usr
/local
/miga
/lib
/gui-miga
\"[pwd]\""]
5058 set miga_fd [open "|
$cmd" r]
5059 fconfigure $miga_fd -blocking 0
5060 fileevent $miga_fd readable [list miga_done $miga_fd]
5061 set ui_status_value {Running miga...}
5063 proc miga_done {fd} {
5068 rescan [list set ui_status_value {Ready.}]
5071 .mbar add cascade -label Tools -menu .mbar.tools
5073 .mbar.tools add command -label "Migrate
" \
5076 lappend disable_on_lock \
5077 [list .mbar.tools entryconf [.mbar.tools index last] -state]
5083 .mbar add cascade -label Help -menu .mbar.help
5087 .mbar.help add command -label "About
[appname
]" \
5093 catch {set browser $repo_config(instaweb.browser)}
5094 set doc_path [file dirname [gitexec]]
5095 set doc_path [file join $doc_path Documentation index.html]
5098 set doc_path [exec cygpath --windows $doc_path]
5101 if {$browser eq {}} {
5104 } elseif {[is_Cygwin]} {
5105 set program_files [file dirname [exec cygpath --windir]]
5106 set program_files [file join $program_files {Program Files}]
5107 set firefox [file join $program_files {Mozilla Firefox} firefox.exe]
5108 set ie [file join $program_files {Internet Explorer} IEXPLORE.EXE]
5109 if {[file exists $firefox]} {
5110 set browser $firefox
5111 } elseif {[file exists $ie]} {
5114 unset program_files firefox ie
5118 if {[file isfile $doc_path]} {
5119 set doc_url "file:$doc_path"
5121 set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
5124 if {$browser ne {}} {
5125 .mbar.help add command -label {Online Documentation} \
5126 -command [list exec $browser $doc_url &] \
5129 unset browser doc_path doc_url
5137 -text {Current Branch:} \
5142 -textvariable current_branch \
5146 pack .branch.l1 -side left
5147 pack .branch.cb -side left -fill x
5148 pack .branch -side top -fill x
5150 if {!$single_commit} {
5152 .mbar.merge add command -label {Local Merge...} \
5153 -command do_local_merge \
5155 lappend disable_on_lock \
5156 [list .mbar.merge entryconf [.mbar.merge index last] -state]
5157 .mbar.merge add command -label {Abort Merge...} \
5158 -command do_reset_hard \
5160 lappend disable_on_lock \
5161 [list .mbar.merge entryconf [.mbar.merge index last] -state]
5167 .mbar.push add command -label {Push...} \
5168 -command do_push_anywhere \
5172 # -- Main Window Layout
5174 panedwindow .vpane -orient vertical
5175 panedwindow .vpane.files -orient horizontal
5176 .vpane add .vpane.files -sticky nsew -height 100 -width 200
5177 pack .vpane -anchor n -side top -fill both -expand 1
5179 # -- Index File List
5181 frame .vpane.files.index -height 100 -width 200
5182 label .vpane.files.index.title -text {Changes To Be Committed} \
5185 text $ui_index -background white -borderwidth 0 \
5186 -width 20 -height 10 \
5189 -cursor $cursor_ptr \
5190 -xscrollcommand {.vpane.files.index.sx set} \
5191 -yscrollcommand {.vpane.files.index.sy set} \
5193 scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview]
5194 scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview]
5195 pack .vpane.files.index.title -side top -fill x
5196 pack .vpane.files.index.sx -side bottom -fill x
5197 pack .vpane.files.index.sy -side right -fill y
5198 pack $ui_index -side left -fill both -expand 1
5199 .vpane.files add .vpane.files.index -sticky nsew
5201 # -- Working Directory File List
5203 frame .vpane.files.workdir -height 100 -width 200
5204 label .vpane.files.workdir.title -text {Changed But Not Updated} \
5207 text $ui_workdir -background white -borderwidth 0 \
5208 -width 20 -height 10 \
5211 -cursor $cursor_ptr \
5212 -xscrollcommand {.vpane.files.workdir.sx set} \
5213 -yscrollcommand {.vpane.files.workdir.sy set} \
5215 scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
5216 scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
5217 pack .vpane.files.workdir.title -side top -fill x
5218 pack .vpane.files.workdir.sx -side bottom -fill x
5219 pack .vpane.files.workdir.sy -side right -fill y
5220 pack $ui_workdir -side left -fill both -expand 1
5221 .vpane.files add .vpane.files.workdir -sticky nsew
5223 foreach i [list $ui_index $ui_workdir] {
5224 $i tag conf in_diff -font font_uibold
5225 $i tag conf in_sel \
5226 -background [$i cget -foreground] \
5227 -foreground [$i cget -background]
5231 # -- Diff and Commit Area
5233 frame .vpane.lower -height 300 -width 400
5234 frame .vpane.lower.commarea
5235 frame .vpane.lower.diff -relief sunken -borderwidth 1
5236 pack .vpane.lower.commarea -side top -fill x
5237 pack .vpane.lower.diff -side bottom -fill both -expand 1
5238 .vpane add .vpane.lower -sticky nsew
5240 # -- Commit Area Buttons
5242 frame .vpane.lower.commarea.buttons
5243 label .vpane.lower.commarea.buttons.l -text {} \
5247 pack .vpane.lower.commarea.buttons.l -side top -fill x
5248 pack .vpane.lower.commarea.buttons -side left -fill y
5250 button .vpane.lower.commarea.buttons.rescan -text {Rescan} \
5251 -command do_rescan \
5253 pack .vpane.lower.commarea.buttons.rescan -side top -fill x
5254 lappend disable_on_lock \
5255 {.vpane.lower.commarea.buttons.rescan conf -state}
5257 button .vpane.lower.commarea.buttons.incall -text {Add All} \
5258 -command do_add_all \
5260 pack .vpane.lower.commarea.buttons.incall -side top -fill x
5261 lappend disable_on_lock \
5262 {.vpane.lower.commarea.buttons.incall conf -state}
5264 button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \
5265 -command do_signoff \
5267 pack .vpane.lower.commarea.buttons.signoff -side top -fill x
5269 button .vpane.lower.commarea.buttons.commit -text {Commit} \
5270 -command do_commit \
5272 pack .vpane.lower.commarea.buttons.commit -side top -fill x
5273 lappend disable_on_lock \
5274 {.vpane.lower.commarea.buttons.commit conf -state}
5276 # -- Commit Message Buffer
5278 frame .vpane.lower.commarea.buffer
5279 frame .vpane.lower.commarea.buffer.header
5280 set ui_comm .vpane.lower.commarea.buffer.t
5281 set ui_coml .vpane.lower.commarea.buffer.header.l
5282 radiobutton .vpane.lower.commarea.buffer.header.new \
5283 -text {New Commit} \
5284 -command do_select_commit_type \
5285 -variable selected_commit_type \
5288 lappend disable_on_lock \
5289 [list .vpane.lower.commarea.buffer.header.new conf -state]
5290 radiobutton .vpane.lower.commarea.buffer.header.amend \
5291 -text {Amend Last Commit} \
5292 -command do_select_commit_type \
5293 -variable selected_commit_type \
5296 lappend disable_on_lock \
5297 [list .vpane.lower.commarea.buffer.header.amend conf -state]
5302 proc trace_commit_type {varname args} {
5303 global ui_coml commit_type
5304 switch -glob -- $commit_type {
5305 initial {set txt {Initial Commit Message:}}
5306 amend {set txt {Amended Commit Message:}}
5307 amend-initial {set txt {Amended Initial Commit Message:}}
5308 amend-merge {set txt {Amended Merge Commit Message:}}
5309 merge {set txt {Merge Commit Message:}}
5310 * {set txt {Commit Message:}}
5312 $ui_coml conf -text $txt
5314 trace add variable commit_type write trace_commit_type
5315 pack $ui_coml -side left -fill x
5316 pack .vpane.lower.commarea.buffer.header.amend -side right
5317 pack .vpane.lower.commarea.buffer.header.new -side right
5319 text $ui_comm -background white -borderwidth 1 \
5322 -autoseparators true \
5324 -width 75 -height 9 -wrap none \
5326 -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
5327 scrollbar .vpane.lower.commarea.buffer.sby \
5328 -command [list $ui_comm yview]
5329 pack .vpane.lower.commarea.buffer.header -side top -fill x
5330 pack .vpane.lower.commarea.buffer.sby -side right -fill y
5331 pack $ui_comm -side left -fill y
5332 pack .vpane.lower.commarea.buffer -side left -fill y
5334 # -- Commit Message Buffer Context Menu
5336 set ctxm .vpane.lower.commarea.buffer.ctxm
5337 menu $ctxm -tearoff 0
5341 -command {tk_textCut $ui_comm}
5345 -command {tk_textCopy $ui_comm}
5349 -command {tk_textPaste $ui_comm}
5353 -command {$ui_comm delete sel.first sel.last}
5356 -label {Select All} \
5358 -command {focus $ui_comm;$ui_comm tag add sel 0.0 end}
5363 $ui_comm tag add sel 0.0 end
5364 tk_textCopy $ui_comm
5365 $ui_comm tag remove sel 0.0 end
5372 bind_button3 $ui_comm "tk_popup
$ctxm %X
%Y
"
5376 set current_diff_path {}
5377 set current_diff_side {}
5378 set diff_actions [list]
5379 proc trace_current_diff_path {varname args} {
5380 global current_diff_path diff_actions file_states
5381 if {$current_diff_path eq {}} {
5387 set p $current_diff_path
5388 set s [mapdesc [lindex $file_states($p) 0] $p]
5390 set p [escape_path $p]
5394 .vpane.lower.diff.header.status configure -text $s
5395 .vpane.lower.diff.header.file configure -text $f
5396 .vpane.lower.diff.header.path configure -text $p
5397 foreach w $diff_actions {
5401 trace add variable current_diff_path write trace_current_diff_path
5403 frame .vpane.lower.diff.header -background orange
5404 label .vpane.lower.diff.header.status \
5405 -background orange \
5406 -width $max_status_desc \
5410 label .vpane.lower.diff.header.file \
5411 -background orange \
5415 label .vpane.lower.diff.header.path \
5416 -background orange \
5420 pack .vpane.lower.diff.header.status -side left
5421 pack .vpane.lower.diff.header.file -side left
5422 pack .vpane.lower.diff.header.path -fill x
5423 set ctxm .vpane.lower.diff.header.ctxm
5424 menu $ctxm -tearoff 0
5433 -- $current_diff_path
5435 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5436 bind_button3 .vpane.lower.diff.header.path "tk_popup
$ctxm %X
%Y
"
5440 frame .vpane.lower.diff.body
5441 set ui_diff .vpane.lower.diff.body.t
5442 text $ui_diff -background white -borderwidth 0 \
5443 -width 80 -height 15 -wrap none \
5445 -xscrollcommand {.vpane.lower.diff.body.sbx set} \
5446 -yscrollcommand {.vpane.lower.diff.body.sby set} \
5448 scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
5449 -command [list $ui_diff xview]
5450 scrollbar .vpane.lower.diff.body.sby -orient vertical \
5451 -command [list $ui_diff yview]
5452 pack .vpane.lower.diff.body.sbx -side bottom -fill x
5453 pack .vpane.lower.diff.body.sby -side right -fill y
5454 pack $ui_diff -side left -fill both -expand 1
5455 pack .vpane.lower.diff.header -side top -fill x
5456 pack .vpane.lower.diff.body -side bottom -fill both -expand 1
5458 $ui_diff tag conf d_cr -elide true
5459 $ui_diff tag conf d_@ -foreground blue -font font_diffbold
5460 $ui_diff tag conf d_+ -foreground {#00a000}
5461 $ui_diff tag conf d_- -foreground red
5463 $ui_diff tag conf d_++ -foreground {#00a000}
5464 $ui_diff tag conf d_-- -foreground red
5465 $ui_diff tag conf d_+s \
5466 -foreground {#00a000} \
5467 -background {#e2effa}
5468 $ui_diff tag conf d_-s \
5470 -background {#e2effa}
5471 $ui_diff tag conf d_s+ \
5472 -foreground {#00a000} \
5474 $ui_diff tag conf d_s- \
5478 $ui_diff tag conf d<<<<<<< \
5479 -foreground orange \
5481 $ui_diff tag conf d======= \
5482 -foreground orange \
5484 $ui_diff tag conf d>>>>>>> \
5485 -foreground orange \
5488 $ui_diff tag raise sel
5490 # -- Diff Body Context Menu
5492 set ctxm .vpane.lower.diff.body.ctxm
5493 menu $ctxm -tearoff 0
5497 -command reshow_diff
5498 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5502 -command {tk_textCopy $ui_diff}
5503 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5505 -label {Select All} \
5507 -command {focus $ui_diff;$ui_diff tag add sel 0.0 end}
5508 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5513 $ui_diff tag add sel 0.0 end
5514 tk_textCopy $ui_diff
5515 $ui_diff tag remove sel 0.0 end
5517 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5520 -label {Apply/Reverse Hunk} \
5522 -command {apply_hunk $cursorX $cursorY}
5523 set ui_diff_applyhunk [$ctxm index last]
5524 lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
5527 -label {Decrease Font Size} \
5529 -command {incr_font_size font_diff -1}
5530 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5532 -label {Increase Font Size} \
5534 -command {incr_font_size font_diff 1}
5535 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5538 -label {Show Less Context} \
5540 -command {if {$repo_config(gui.diffcontext) >= 2} {
5541 incr repo_config(gui.diffcontext) -1
5544 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5546 -label {Show More Context} \
5549 incr repo_config(gui.diffcontext)
5552 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
5554 $ctxm add command -label {Options...} \
5557 bind_button3 $ui_diff "
5560 if {\
$ui_index eq \
$current_diff_side} {
5561 $ctxm entryconf
$ui_diff_applyhunk -label {Unstage Hunk From Commit
}
5563 $ctxm entryconf
$ui_diff_applyhunk -label {Stage Hunk For Commit
}
5565 tk_popup
$ctxm %X
%Y
5567 unset ui_diff_applyhunk
5571 set ui_status_value {Initializing...}
5572 label .status -textvariable ui_status_value \
5578 pack .status -anchor w -side bottom -fill x
5583 set gm $repo_config(gui.geometry)
5584 wm geometry . [lindex $gm 0]
5585 .vpane sash place 0 \
5586 [lindex [.vpane sash coord 0] 0] \
5588 .vpane.files sash place 0 \
5590 [lindex [.vpane.files sash coord 0] 1]
5596 bind $ui_comm <$M1B-Key-Return> {do_commit;break}
5597 bind $ui_comm <$M1B-Key-i> {do_add_all;break}
5598 bind $ui_comm <$M1B-Key-I> {do_add_all;break}
5599 bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
5600 bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
5601 bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
5602 bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
5603 bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
5604 bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
5605 bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
5606 bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
5608 bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
5609 bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
5610 bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
5611 bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
5612 bind $ui_diff <$M1B-Key-v> {break}
5613 bind $ui_diff <$M1B-Key-V> {break}
5614 bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
5615 bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
5616 bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
5617 bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
5618 bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
5619 bind $ui_diff <Key-Right> {catch {%W xview scroll 1 units};break}
5620 bind $ui_diff <Button-1> {focus %W}
5622 if {!$single_commit} {
5623 bind . <$M1B-Key-n> do_create_branch
5624 bind . <$M1B-Key-N> do_create_branch
5627 bind . <Destroy> do_quit
5628 bind all <Key-F5> do_rescan
5629 bind all <$M1B-Key-r> do_rescan
5630 bind all <$M1B-Key-R> do_rescan
5631 bind . <$M1B-Key-s> do_signoff
5632 bind . <$M1B-Key-S> do_signoff
5633 bind . <$M1B-Key-i> do_add_all
5634 bind . <$M1B-Key-I> do_add_all
5635 bind . <$M1B-Key-Return> do_commit
5636 bind all <$M1B-Key-q> do_quit
5637 bind all <$M1B-Key-Q> do_quit
5638 bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
5639 bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
5640 foreach i [list $ui_index $ui_workdir] {
5641 bind $i <Button-1> "toggle_or_diff
$i %x
%y
; break"
5642 bind $i <$M1B-Button-1> "add_one_to_selection
$i %x
%y
; break"
5643 bind $i <Shift-Button-1> "add_range_to_selection
$i %x
%y
; break"
5647 set file_lists($ui_index) [list]
5648 set file_lists($ui_workdir) [list]
5652 set MERGE_HEAD [list]
5655 set current_branch {}
5656 set current_diff_path {}
5657 set selected_commit_type new
5659 wm title . "[appname
] ([file normalize
[file dirname [gitdir
]]])"
5660 focus -force $ui_comm
5662 # -- Warn the user about environmental problems. Cygwin's Tcl
5663 # does *not* pass its env array onto any processes it spawns.
5664 # This means that git processes get none of our environment.
5669 set msg "Possible environment issues exist.
5671 The following environment variables are probably
5672 going to be ignored by any Git subprocess run
5676 foreach name [array names env] {
5677 switch -regexp -- $name {
5678 {^GIT_INDEX_FILE$} -
5679 {^GIT_OBJECT_DIRECTORY$} -
5680 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} -
5682 {^GIT_EXTERNAL_DIFF$} -
5686 {^GIT_CONFIG_LOCAL$} -
5687 {^GIT_(AUTHOR|COMMITTER)_DATE$} {
5688 append msg " - $name\n"
5691 {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} {
5692 append msg " - $name\n"
5694 set suggest_user $name
5698 if {$ignored_env > 0} {
5700 This is due to a known issue with the
5701 Tcl binary distributed by Cygwin.
"
5703 if {$suggest_user ne {}} {
5706 A good replacement
for $suggest_user
5707 is placing values
for the user.name and
5708 user.email settings into your personal
5714 unset ignored_env msg suggest_user name
5717 # -- Only initialize complex UI if we are going to stay running.
5719 if {!$single_commit} {
5723 populate_branch_menu
5728 # -- Only suggest a gc run if we are going to stay running.
5730 if {!$single_commit} {
5731 set object_limit 2000
5732 if {[is_Windows]} {set object_limit 200}
5733 regexp {^([0-9]+) objects,} [exec git count-objects] _junk objects_current
5734 if {$objects_current >= $object_limit} {
5736 "This repository currently has
$objects_current loose objects.
5738 To maintain optimal performance it is strongly
5739 recommended that you
compress the database
5740 when
more than
$object_limit loose objects exist.
5742 Compress the database now?
"] eq yes} {
5746 unset object_limit _junk objects_current
5749 lock_index begin-read