2 # Tcl ignores the next line -*- tcl -*- \
6 Copyright ©
2006 Shawn Pearce
, Paul Mackerras.
10 This program is free software
; it may be used
, copied
, modified
11 and distributed under the terms of the GNU General Public Licence
,
12 either version
2, or
(at your option
) any later version.
15 set appname
[lindex
[file split $argv0] end
]
18 ######################################################################
22 proc is_many_config
{name
} {
23 switch
-glob -- $name {
32 proc load_config
{include_global
} {
33 global repo_config global_config default_config
35 array
unset global_config
36 if {$include_global} {
38 set fd_rc
[open
"| git repo-config --global --list" r
]
39 while {[gets
$fd_rc line
] >= 0} {
40 if {[regexp
{^
([^
=]+)=(.
*)$
} $line line name value
]} {
41 if {[is_many_config
$name]} {
42 lappend global_config
($name) $value
44 set global_config
($name) $value
52 array
unset repo_config
54 set fd_rc
[open
"| git repo-config --list" r
]
55 while {[gets
$fd_rc line
] >= 0} {
56 if {[regexp
{^
([^
=]+)=(.
*)$
} $line line name value
]} {
57 if {[is_many_config
$name]} {
58 lappend repo_config
($name) $value
60 set repo_config
($name) $value
67 foreach name
[array names default_config
] {
68 if {[catch
{set v
$global_config($name)}]} {
69 set global_config
($name) $default_config($name)
71 if {[catch
{set v
$repo_config($name)}]} {
72 set repo_config
($name) $default_config($name)
78 global default_config font_descs
79 global repo_config global_config
80 global repo_config_new global_config_new
82 foreach option
$font_descs {
83 set name
[lindex
$option 0]
84 set font
[lindex
$option 1]
85 font configure
$font \
86 -family $global_config_new(gui.
$font^^family
) \
87 -size $global_config_new(gui.
$font^^size
)
88 font configure
${font}bold \
89 -family $global_config_new(gui.
$font^^family
) \
90 -size $global_config_new(gui.
$font^^size
)
91 set global_config_new
(gui.
$name) [font configure
$font]
92 unset global_config_new
(gui.
$font^^family
)
93 unset global_config_new
(gui.
$font^^size
)
96 foreach name
[array names default_config
] {
97 set value
$global_config_new($name)
98 if {$value ne
$global_config($name)} {
99 if {$value eq
$default_config($name)} {
100 catch
{exec git repo-config
--global --unset $name}
102 regsub
-all "\[{}\]" $value {"} value
103 exec git repo-config --global $name $value
105 set global_config($name) $value
106 if {$value eq $repo_config($name)} {
107 catch {exec git repo-config --unset $name}
108 set repo_config($name) $value
113 foreach name [array names default_config] {
114 set value $repo_config_new($name)
115 if {$value ne $repo_config($name)} {
116 if {$value eq $global_config($name)} {
117 catch {exec git repo-config --unset $name}
119 regsub -all "\
[{}\
]" $value {"} value
120 exec git repo-config
$name $value
122 set repo_config
($name) $value
127 proc error_popup
{msg
} {
128 global gitdir appname
133 append title
[lindex \
134 [file split [file normalize
[file dirname $gitdir]]] \
138 set cmd
[list tk_messageBox \
141 -title "$title: error" \
143 if {[winfo ismapped .
]} {
144 lappend cmd
-parent .
149 proc info_popup
{msg
} {
150 global gitdir appname
155 append title
[lindex \
156 [file split [file normalize
[file dirname $gitdir]]] \
168 ######################################################################
172 if { [catch
{set gitdir
$env(GIT_DIR
)}]
173 && [catch
{set gitdir
[exec git rev-parse
--git-dir]} err
]} {
174 catch
{wm withdraw .
}
175 error_popup
"Cannot find the git directory:\n\n$err"
178 if {![file isdirectory
$gitdir]} {
179 catch
{wm withdraw .
}
180 error_popup
"Git directory not found:\n\n$gitdir"
183 if {[lindex
[file split $gitdir] end
] ne
{.git
}} {
184 catch
{wm withdraw .
}
185 error_popup
"Cannot use funny .git directory:\n\n$gitdir"
188 if {[catch
{cd [file dirname $gitdir]} err
]} {
189 catch
{wm withdraw .
}
190 error_popup
"No working directory [file dirname $gitdir]:\n\n$err"
195 if {$appname eq
{git-citool
}} {
199 ######################################################################
207 set disable_on_lock
[list
]
208 set index_lock_type none
210 proc lock_index
{type} {
211 global index_lock_type disable_on_lock
213 if {$index_lock_type eq
{none
}} {
214 set index_lock_type
$type
215 foreach w
$disable_on_lock {
216 uplevel
#0 $w disabled
219 } elseif
{$index_lock_type eq
"begin-$type"} {
220 set index_lock_type
$type
226 proc unlock_index
{} {
227 global index_lock_type disable_on_lock
229 set index_lock_type none
230 foreach w
$disable_on_lock {
235 ######################################################################
239 proc repository_state
{ctvar hdvar mhvar
} {
241 upvar
$ctvar ct
$hdvar hd
$mhvar mh
245 if {[catch
{set hd
[exec git rev-parse
--verify HEAD
]}]} {
251 set merge_head
[file join $gitdir MERGE_HEAD
]
252 if {[file exists
$merge_head]} {
254 set fd_mh
[open
$merge_head r
]
255 while {[gets
$fd_mh line
] >= 0} {
266 global PARENT empty_tree
268 set p
[lindex
$PARENT 0]
272 if {$empty_tree eq
{}} {
273 set empty_tree
[exec git mktree
<< {}]
278 proc rescan
{after
} {
279 global HEAD PARENT MERGE_HEAD commit_type
280 global ui_index ui_other ui_status_value ui_comm
281 global rescan_active file_states
284 if {$rescan_active > 0 ||
![lock_index
read]} return
286 repository_state newType newHEAD newMERGE_HEAD
287 if {[string match amend
* $commit_type]
288 && $newType eq
{normal
}
289 && $newHEAD eq
$HEAD} {
293 set MERGE_HEAD
$newMERGE_HEAD
294 set commit_type
$newType
297 array
unset file_states
299 if {![$ui_comm edit modified
]
300 ||
[string trim
[$ui_comm get
0.0 end
]] eq
{}} {
301 if {[load_message GITGUI_MSG
]} {
302 } elseif
{[load_message MERGE_MSG
]} {
303 } elseif
{[load_message SQUASH_MSG
]} {
306 $ui_comm edit modified false
309 if {$repo_config(gui.trustmtime
) eq
{true
}} {
310 rescan_stage2
{} $after
313 set ui_status_value
{Refreshing
file status...
}
314 set cmd
[list git update-index
]
316 lappend cmd
--unmerged
317 lappend cmd
--ignore-missing
318 lappend cmd
--refresh
319 set fd_rf
[open
"| $cmd" r
]
320 fconfigure
$fd_rf -blocking 0 -translation binary
321 fileevent
$fd_rf readable \
322 [list rescan_stage2
$fd_rf $after]
326 proc rescan_stage2
{fd after
} {
327 global gitdir ui_status_value
328 global rescan_active buf_rdi buf_rdf buf_rlo
332 if {![eof
$fd]} return
336 set ls_others
[list | git ls-files
--others -z \
337 --exclude-per-directory=.gitignore
]
338 set info_exclude
[file join $gitdir info exclude
]
339 if {[file readable
$info_exclude]} {
340 lappend ls_others
"--exclude-from=$info_exclude"
348 set ui_status_value
{Scanning
for modified files ...
}
349 set fd_di
[open
"| git diff-index --cached -z [PARENT]" r
]
350 set fd_df
[open
"| git diff-files -z" r
]
351 set fd_lo
[open
$ls_others r
]
353 fconfigure
$fd_di -blocking 0 -translation binary
354 fconfigure
$fd_df -blocking 0 -translation binary
355 fconfigure
$fd_lo -blocking 0 -translation binary
356 fileevent
$fd_di readable
[list read_diff_index
$fd_di $after]
357 fileevent
$fd_df readable
[list read_diff_files
$fd_df $after]
358 fileevent
$fd_lo readable
[list read_ls_others
$fd_lo $after]
361 proc load_message
{file} {
362 global gitdir ui_comm
364 set f
[file join $gitdir $file]
365 if {[file isfile
$f]} {
366 if {[catch
{set fd
[open
$f r
]}]} {
369 set content
[string trim
[read $fd]]
371 $ui_comm delete
0.0 end
372 $ui_comm insert end
$content
378 proc read_diff_index
{fd after
} {
381 append buf_rdi
[read $fd]
383 set n
[string length
$buf_rdi]
385 set z1
[string first
"\0" $buf_rdi $c]
388 set z2
[string first
"\0" $buf_rdi $z1]
392 set i
[split [string range
$buf_rdi $c [expr {$z1 - 2}]] { }]
394 [string range
$buf_rdi $z1 [expr {$z2 - 1}]] \
396 [list
[lindex
$i 0] [lindex
$i 2]] \
402 set buf_rdi
[string range
$buf_rdi $c end
]
407 rescan_done
$fd buf_rdi
$after
410 proc read_diff_files
{fd after
} {
413 append buf_rdf
[read $fd]
415 set n
[string length
$buf_rdf]
417 set z1
[string first
"\0" $buf_rdf $c]
420 set z2
[string first
"\0" $buf_rdf $z1]
424 set i
[split [string range
$buf_rdf $c [expr {$z1 - 2}]] { }]
426 [string range
$buf_rdf $z1 [expr {$z2 - 1}]] \
429 [list
[lindex
$i 0] [lindex
$i 2]]
434 set buf_rdf
[string range
$buf_rdf $c end
]
439 rescan_done
$fd buf_rdf
$after
442 proc read_ls_others
{fd after
} {
445 append buf_rlo
[read $fd]
446 set pck
[split $buf_rlo "\0"]
447 set buf_rlo
[lindex
$pck end
]
448 foreach p
[lrange
$pck 0 end-1
] {
451 rescan_done
$fd buf_rlo
$after
454 proc rescan_done
{fd buf after
} {
456 global file_states repo_config
459 if {![eof
$fd]} return
462 if {[incr rescan_active
-1] > 0} return
468 if {$repo_config(gui.partialinclude
) ne
{true
}} {
470 foreach path
[array names file_states
] {
471 switch
-- [lindex
$file_states($path) 0] {
473 MM
{lappend pathList
$path}
476 if {$pathList ne
{}} {
478 "Updating included files" \
480 [concat
{reshow_diff
;} $after]
489 proc prune_selection
{} {
490 global file_states selected_paths
492 foreach path
[array names selected_paths
] {
493 if {[catch
{set still_here
$file_states($path)}]} {
494 unset selected_paths
($path)
499 ######################################################################
504 global ui_diff current_diff ui_index ui_other
506 $ui_diff conf
-state normal
507 $ui_diff delete
0.0 end
508 $ui_diff conf
-state disabled
512 $ui_index tag remove in_diff
0.0 end
513 $ui_other tag remove in_diff
0.0 end
516 proc reshow_diff
{} {
517 global current_diff ui_status_value file_states
519 if {$current_diff eq
{}
520 ||
[catch
{set s
$file_states($current_diff)}]} {
523 show_diff
$current_diff
527 proc handle_empty_diff
{} {
528 global current_diff file_states file_lists
530 set path
$current_diff
531 set s
$file_states($path)
532 if {[lindex
$s 0] ne
{_M
}} return
534 info_popup
"No differences detected.
536 [short_path $path] has no changes.
538 The modification date of this file was updated
539 by another application and you currently have
540 the Trust File Modification Timestamps option
541 enabled, so Git did not automatically detect
542 that there are no content differences in this
545 This file will now be removed from the modified
546 files list, to prevent possible confusion.
548 if {[catch
{exec git update-index
-- $path} err
]} {
549 error_popup
"Failed to refresh index:\n\n$err"
553 set old_w
[mapcol
[lindex
$file_states($path) 0] $path]
554 set lno
[lsearch
-sorted $file_lists($old_w) $path]
556 set file_lists
($old_w) \
557 [lreplace
$file_lists($old_w) $lno $lno]
559 $old_w conf
-state normal
560 $old_w delete
$lno.0 [expr {$lno + 1}].0
561 $old_w conf
-state disabled
565 proc show_diff
{path
{w
{}} {lno
{}}} {
566 global file_states file_lists
567 global is_3way_diff diff_active repo_config
568 global ui_diff current_diff ui_status_value
570 if {$diff_active ||
![lock_index
read]} return
573 if {$w eq
{} ||
$lno == {}} {
574 foreach w
[array names file_lists
] {
575 set lno
[lsearch
-sorted $file_lists($w) $path]
582 if {$w ne
{} && $lno >= 1} {
583 $w tag add in_diff
$lno.0 [expr {$lno + 1}].0
586 set s
$file_states($path)
590 set current_diff
$path
591 set ui_status_value
"Loading diff of [escape_path $path]..."
593 set cmd
[list | git diff-index
]
594 lappend cmd
--no-color
595 if {$repo_config(gui.diffcontext
) > 0} {
596 lappend cmd
"-U$repo_config(gui.diffcontext)"
606 set fd
[open
$path r
]
607 set content
[read $fd]
612 set ui_status_value
"Unable to display [escape_path $path]"
613 error_popup
"Error loading file:\n\n$err"
616 $ui_diff conf
-state normal
617 $ui_diff insert end
$content
618 $ui_diff conf
-state disabled
621 set ui_status_value
{Ready.
}
630 if {[catch
{set fd
[open
$cmd r
]} err
]} {
633 set ui_status_value
"Unable to display [escape_path $path]"
634 error_popup
"Error loading diff:\n\n$err"
638 fconfigure
$fd -blocking 0 -translation auto
639 fileevent
$fd readable
[list read_diff
$fd]
642 proc read_diff
{fd
} {
643 global ui_diff ui_status_value is_3way_diff diff_active
646 $ui_diff conf
-state normal
647 while {[gets
$fd line
] >= 0} {
648 # -- Cleanup uninteresting diff header lines.
650 if {[string match
{diff --git *} $line]} continue
651 if {[string match
{diff --combined *} $line]} continue
652 if {[string match
{--- *} $line]} continue
653 if {[string match
{+++ *} $line]} continue
654 if {$line eq
{deleted
file mode
120000}} {
655 set line
"deleted symlink"
658 # -- Automatically detect if this is a 3 way diff.
660 if {[string match
{@@@
*} $line]} {set is_3way_diff
1}
662 # -- Reformat a 3 way diff, 'cause its too weird.
665 set op
[string range
$line 0 1]
668 {++} {set tags d_
+ ; set op
{ +}}
669 {--} {set tags d_-
; set op
{ -}}
670 { +} {set tags d_
++; set op
{++}}
671 { -} {set tags d_--
; set op
{--}}
672 {+ } {set tags d_-
+; set op
{-+}}
673 {- } {set tags d_
+-; set op
{+-}}
674 default
{set tags
{}}
676 set line
[string replace
$line 0 1 $op]
678 switch
-- [string index
$line 0] {
682 default
{set tags
{}}
685 $ui_diff insert end
$line $tags
686 $ui_diff insert end
"\n" $tags
688 $ui_diff conf
-state disabled
694 set ui_status_value
{Ready.
}
696 if {$repo_config(gui.trustmtime
) eq
{true
}
697 && [$ui_diff index end
] eq
{2.0}} {
703 ######################################################################
707 proc load_last_commit
{} {
708 global HEAD PARENT MERGE_HEAD commit_type ui_comm
710 if {[llength
$PARENT] == 0} {
711 error_popup
{There is nothing to amend.
713 You are about to create the initial commit.
714 There is no commit before this to amend.
719 repository_state curType curHEAD curMERGE_HEAD
720 if {$curType eq
{merge
}} {
721 error_popup
{Cannot amend
while merging.
723 You are currently
in the middle of a merge that
724 has not been fully completed. You cannot amend
725 the prior commit unless you first abort the
726 current merge activity.
734 set fd
[open
"| git cat-file commit $curHEAD" r
]
735 while {[gets
$fd line
] > 0} {
736 if {[string match
{parent
*} $line]} {
737 lappend parents
[string range
$line 7 end
]
740 set msg
[string trim
[read $fd]]
743 error_popup
"Error loading commit data for amend:\n\n$err"
749 set MERGE_HEAD
[list
]
750 switch
-- [llength
$parents] {
751 0 {set commit_type amend-initial
}
752 1 {set commit_type amend
}
753 default
{set commit_type amend-merge
}
756 $ui_comm delete
0.0 end
757 $ui_comm insert end
$msg
759 $ui_comm edit modified false
760 rescan
{set ui_status_value
{Ready.
}}
763 proc create_new_commit
{} {
764 global commit_type ui_comm
766 set commit_type normal
767 $ui_comm delete
0.0 end
769 $ui_comm edit modified false
770 rescan
{set ui_status_value
{Ready.
}}
773 set GIT_COMMITTER_IDENT
{}
775 proc committer_ident
{} {
776 global GIT_COMMITTER_IDENT
778 if {$GIT_COMMITTER_IDENT eq
{}} {
779 if {[catch
{set me
[exec git var GIT_COMMITTER_IDENT
]} err
]} {
780 error_popup
"Unable to obtain your identity:\n\n$err"
783 if {![regexp
{^
(.
*) [0-9]+ [-+0-9]+$
} \
784 $me me GIT_COMMITTER_IDENT
]} {
785 error_popup
"Invalid GIT_COMMITTER_IDENT:\n\n$me"
790 return $GIT_COMMITTER_IDENT
793 proc commit_tree
{} {
794 global HEAD commit_type file_states ui_comm repo_config
796 if {![lock_index update
]} return
797 if {[committer_ident
] eq
{}} return
799 # -- Our in memory state should match the repository.
801 repository_state curType curHEAD curMERGE_HEAD
802 if {[string match amend
* $commit_type]
803 && $curType eq
{normal
}
804 && $curHEAD eq
$HEAD} {
805 } elseif
{$commit_type ne
$curType ||
$HEAD ne
$curHEAD} {
806 info_popup
{Last scanned state does not match repository state.
808 Another Git program has modified this repository
809 since the last scan. A rescan must be performed
810 before another commit can be created.
812 The rescan will be automatically started now.
815 rescan
{set ui_status_value
{Ready.
}}
819 # -- At least one file should differ in the index.
822 foreach path
[array names file_states
] {
823 switch
-glob -- [lindex
$file_states($path) 0] {
827 M?
{set files_ready
1; break}
829 error_popup
"Unmerged files cannot be committed.
831 File [short_path $path] has merge conflicts.
832 You must resolve them and include the file before committing.
838 error_popup
"Unknown file state [lindex $s 0] detected.
840 File [short_path $path] cannot be committed by this program.
846 error_popup
{No included files to commit.
848 You must include
at least
1 file before you can commit.
854 # -- A message is required.
856 set msg
[string trim
[$ui_comm get
1.0 end
]]
858 error_popup
{Please supply a commit message.
860 A good commit message has the following format
:
862 - First line
: Describe
in one sentance what you did.
864 - Remaining lines
: Describe why this change is good.
870 # -- Update included files if partialincludes are off.
872 if {$repo_config(gui.partialinclude
) ne
{true
}} {
874 foreach path
[array names file_states
] {
875 switch
-glob -- [lindex
$file_states($path) 0] {
877 M?
{lappend pathList
$path}
880 if {$pathList ne
{}} {
883 "Updating included files" \
885 [concat
{lock_index update
;} \
886 [list commit_prehook
$curHEAD $msg]]
891 commit_prehook
$curHEAD $msg
894 proc commit_prehook
{curHEAD msg
} {
895 global tcl_platform gitdir ui_status_value pch_error
897 # On Cygwin [file executable] might lie so we need to ask
898 # the shell if the hook is executable. Yes that's annoying.
900 set pchook
[file join $gitdir hooks pre-commit
]
901 if {$tcl_platform(platform
) eq
{windows
}
902 && [file isfile
$pchook]} {
903 set pchook
[list sh
-c [concat \
904 "if test -x \"$pchook\";" \
905 "then exec \"$pchook\" 2>&1;" \
907 } elseif
{[file executable
$pchook]} {
908 set pchook
[list
$pchook |
& cat]
910 commit_writetree
$curHEAD $msg
914 set ui_status_value
{Calling pre-commit hook...
}
916 set fd_ph
[open
"| $pchook" r
]
917 fconfigure
$fd_ph -blocking 0 -translation binary
918 fileevent
$fd_ph readable \
919 [list commit_prehook_wait
$fd_ph $curHEAD $msg]
922 proc commit_prehook_wait
{fd_ph curHEAD msg
} {
923 global pch_error ui_status_value
925 append pch_error
[read $fd_ph]
926 fconfigure
$fd_ph -blocking 1
928 if {[catch
{close
$fd_ph}]} {
929 set ui_status_value
{Commit declined by pre-commit hook.
}
930 hook_failed_popup pre-commit
$pch_error
933 commit_writetree
$curHEAD $msg
938 fconfigure
$fd_ph -blocking 0
941 proc commit_writetree
{curHEAD msg
} {
942 global ui_status_value
944 set ui_status_value
{Committing changes...
}
945 set fd_wt
[open
"| git write-tree" r
]
946 fileevent
$fd_wt readable \
947 [list commit_committree
$fd_wt $curHEAD $msg]
950 proc commit_committree
{fd_wt curHEAD msg
} {
951 global HEAD PARENT MERGE_HEAD commit_type
952 global single_commit gitdir tcl_platform
953 global ui_status_value ui_comm selected_commit_type
954 global file_states selected_paths rescan_active
957 if {$tree_id eq
{} ||
[catch
{close
$fd_wt} err
]} {
958 error_popup
"write-tree failed:\n\n$err"
959 set ui_status_value
{Commit failed.
}
964 # -- Create the commit.
966 set cmd
[list git commit-tree
$tree_id]
967 set parents
[concat
$PARENT $MERGE_HEAD]
968 if {[llength
$parents] > 0} {
973 # git commit-tree writes to stderr during initial commit.
974 lappend cmd
2>/dev
/null
977 if {[catch
{set cmt_id
[eval exec $cmd]} err
]} {
978 error_popup
"commit-tree failed:\n\n$err"
979 set ui_status_value
{Commit failed.
}
984 # -- Update the HEAD ref.
987 if {$commit_type ne
{normal
}} {
988 append reflogm
" ($commit_type)"
990 set i
[string first
"\n" $msg]
992 append reflogm
{: } [string range
$msg 0 [expr {$i - 1}]]
994 append reflogm
{: } $msg
996 set cmd
[list git update-ref
-m $reflogm HEAD
$cmt_id $curHEAD]
997 if {[catch
{eval exec $cmd} err
]} {
998 error_popup
"update-ref failed:\n\n$err"
999 set ui_status_value
{Commit failed.
}
1004 # -- Cleanup after ourselves.
1006 catch
{file delete
[file join $gitdir MERGE_HEAD
]}
1007 catch
{file delete
[file join $gitdir MERGE_MSG
]}
1008 catch
{file delete
[file join $gitdir SQUASH_MSG
]}
1009 catch
{file delete
[file join $gitdir GITGUI_MSG
]}
1011 # -- Let rerere do its thing.
1013 if {[file isdirectory
[file join $gitdir rr-cache
]]} {
1014 catch
{exec git rerere
}
1017 # -- Run the post-commit hook.
1019 set pchook
[file join $gitdir hooks post-commit
]
1020 if {$tcl_platform(platform
) eq
{windows
} && [file isfile
$pchook]} {
1021 set pchook
[list sh
-c [concat \
1022 "if test -x \"$pchook\";" \
1023 "then exec \"$pchook\";" \
1025 } elseif
{![file executable
$pchook]} {
1028 if {$pchook ne
{}} {
1029 catch
{exec $pchook &}
1032 $ui_comm delete
0.0 end
1034 $ui_comm edit modified false
1036 if {$single_commit} do_quit
1038 # -- Update in memory status
1040 set selected_commit_type new
1041 set commit_type normal
1044 set MERGE_HEAD
[list
]
1046 foreach path
[array names file_states
] {
1047 set s
$file_states($path)
1049 switch
-glob -- $m {
1057 unset file_states
($path)
1058 catch
{unset selected_paths
($path)}
1061 set file_states
($path) [list _O
[lindex
$s 1] {} {}]
1067 set file_states
($path) [list \
1068 _
[string index
$m 1] \
1079 set ui_status_value \
1080 "Changes committed as [string range $cmt_id 0 7]."
1083 ######################################################################
1087 proc fetch_from
{remote
} {
1088 set w
[new_console
"fetch $remote" \
1089 "Fetching new changes from $remote"]
1090 set cmd
[list git fetch
]
1092 console_exec
$w $cmd
1095 proc pull_remote
{remote branch
} {
1096 global HEAD commit_type file_states repo_config
1098 if {![lock_index update
]} return
1100 # -- Our in memory state should match the repository.
1102 repository_state curType curHEAD curMERGE_HEAD
1103 if {$commit_type ne
$curType ||
$HEAD ne
$curHEAD} {
1104 error_popup
{Last scanned state does not match repository state.
1106 Its highly likely that another Git program modified the
1107 repository since our last scan. A rescan is required
1108 before a pull can be started.
1111 rescan
{set ui_status_value
{Ready.
}}
1115 # -- No differences should exist before a pull.
1117 if {[array size file_states
] != 0} {
1118 error_popup
{Uncommitted but modified files are present.
1120 You should not perform a pull with unmodified files
in your working
1121 directory as Git would be unable to recover from an incorrect merge.
1123 Commit or throw away all changes before starting a pull operation.
1129 set w
[new_console
"pull $remote $branch" \
1130 "Pulling new changes from branch $branch in $remote"]
1131 set cmd
[list git pull
]
1132 if {$repo_config(gui.pullsummary
) eq
{false
}} {
1133 lappend cmd
--no-summary
1137 console_exec
$w $cmd [list post_pull_remote
$remote $branch]
1140 proc post_pull_remote
{remote branch success
} {
1141 global HEAD PARENT MERGE_HEAD commit_type selected_commit_type
1142 global ui_status_value
1146 repository_state commit_type HEAD MERGE_HEAD
1148 set selected_commit_type new
1149 set ui_status_value
"Pulling $branch from $remote complete."
1151 rescan
[list
set ui_status_value \
1152 "Conflicts detected while pulling $branch from $remote."]
1156 proc push_to
{remote
} {
1157 set w
[new_console
"push $remote" \
1158 "Pushing changes to $remote"]
1159 set cmd
[list git push
]
1161 console_exec
$w $cmd
1164 ######################################################################
1168 proc mapcol
{state path
} {
1169 global all_cols ui_other
1171 if {[catch
{set r
$all_cols($state)}]} {
1172 puts
"error: no column for state={$state} $path"
1178 proc mapicon
{state path
} {
1181 if {[catch
{set r
$all_icons($state)}]} {
1182 puts
"error: no icon for state={$state} $path"
1188 proc mapdesc
{state path
} {
1191 if {[catch
{set r
$all_descs($state)}]} {
1192 puts
"error: no desc for state={$state} $path"
1198 proc escape_path
{path
} {
1199 regsub
-all "\n" $path "\\n" path
1203 proc short_path
{path
} {
1204 return [escape_path
[lindex
[file split $path] end
]]
1208 set null_sha1
[string repeat
0 40]
1210 proc merge_state
{path new_state
{head_info
{}} {index_info
{}}} {
1211 global file_states next_icon_id null_sha1
1213 set s0
[string index
$new_state 0]
1214 set s1
[string index
$new_state 1]
1216 if {[catch
{set info
$file_states($path)}]} {
1218 set icon n
[incr next_icon_id
]
1220 set state
[lindex
$info 0]
1221 set icon
[lindex
$info 1]
1222 if {$head_info eq
{}} {set head_info
[lindex
$info 2]}
1223 if {$index_info eq
{}} {set index_info
[lindex
$info 3]}
1226 if {$s0 eq
{?
}} {set s0
[string index
$state 0]} \
1227 elseif
{$s0 eq
{_
}} {set s0 _
}
1229 if {$s1 eq
{?
}} {set s1
[string index
$state 1]} \
1230 elseif
{$s1 eq
{_
}} {set s1 _
}
1232 if {$s0 eq
{A
} && $s1 eq
{_
} && $head_info eq
{}} {
1233 set head_info
[list
0 $null_sha1]
1234 } elseif
{$s0 ne
{_
} && [string index
$state 0] eq
{_
}
1235 && $head_info eq
{}} {
1236 set head_info
$index_info
1239 set file_states
($path) [list
$s0$s1 $icon \
1240 $head_info $index_info \
1245 proc display_file
{path state
} {
1246 global file_states file_lists selected_paths
1248 set old_m
[merge_state
$path $state]
1249 set s
$file_states($path)
1250 set new_m
[lindex
$s 0]
1251 set new_w
[mapcol
$new_m $path]
1252 set old_w
[mapcol
$old_m $path]
1253 set new_icon
[mapicon
$new_m $path]
1255 if {$new_w ne
$old_w} {
1256 set lno
[lsearch
-sorted $file_lists($old_w) $path]
1259 $old_w conf
-state normal
1260 $old_w delete
$lno.0 [expr {$lno + 1}].0
1261 $old_w conf
-state disabled
1264 lappend file_lists
($new_w) $path
1265 set file_lists
($new_w) [lsort
$file_lists($new_w)]
1266 set lno
[lsearch
-sorted $file_lists($new_w) $path]
1268 $new_w conf
-state normal
1269 $new_w image create
$lno.0 \
1270 -align center
-padx 5 -pady 1 \
1271 -name [lindex
$s 1] \
1273 $new_w insert
$lno.1 "[escape_path $path]\n"
1274 if {[catch
{set in_sel
$selected_paths($path)}]} {
1278 $new_w tag add in_sel
$lno.0 [expr {$lno + 1}].0
1280 $new_w conf
-state disabled
1281 } elseif
{$new_icon ne
[mapicon
$old_m $path]} {
1282 $new_w conf
-state normal
1283 $new_w image conf
[lindex
$s 1] -image $new_icon
1284 $new_w conf
-state disabled
1288 proc display_all_files
{} {
1289 global ui_index ui_other
1290 global file_states file_lists
1291 global last_clicked selected_paths
1293 $ui_index conf
-state normal
1294 $ui_other conf
-state normal
1296 $ui_index delete
0.0 end
1297 $ui_other delete
0.0 end
1300 set file_lists
($ui_index) [list
]
1301 set file_lists
($ui_other) [list
]
1303 foreach path
[lsort
[array names file_states
]] {
1304 set s
$file_states($path)
1306 set w
[mapcol
$m $path]
1307 lappend file_lists
($w) $path
1308 set lno
[expr {[lindex
[split [$w index end
] .
] 0] - 1}]
1309 $w image create end \
1310 -align center
-padx 5 -pady 1 \
1311 -name [lindex
$s 1] \
1312 -image [mapicon
$m $path]
1313 $w insert end
"[escape_path $path]\n"
1314 if {[catch
{set in_sel
$selected_paths($path)}]} {
1318 $w tag add in_sel
$lno.0 [expr {$lno + 1}].0
1322 $ui_index conf
-state disabled
1323 $ui_other conf
-state disabled
1326 proc update_indexinfo
{msg pathList after
} {
1327 global update_index_cp ui_status_value
1329 if {![lock_index update
]} return
1331 set update_index_cp
0
1332 set pathList
[lsort
$pathList]
1333 set totalCnt
[llength
$pathList]
1334 set batch [expr {int
($totalCnt * .01) + 1}]
1335 if {$batch > 25} {set batch 25}
1337 set ui_status_value
[format \
1338 "$msg... %i/%i files (%.2f%%)" \
1342 set fd
[open
"| git update-index -z --index-info" w
]
1348 fileevent
$fd writable
[list \
1349 write_update_indexinfo \
1359 proc write_update_indexinfo
{fd pathList totalCnt
batch msg after
} {
1360 global update_index_cp ui_status_value
1361 global file_states current_diff
1363 if {$update_index_cp >= $totalCnt} {
1370 for {set i
$batch} \
1371 {$update_index_cp < $totalCnt && $i > 0} \
1373 set path
[lindex
$pathList $update_index_cp]
1374 incr update_index_cp
1376 set s
$file_states($path)
1377 switch
-glob -- [lindex
$s 0] {
1383 set info
[lindex
$s 2]
1384 if {$info eq
{}} continue
1386 puts
-nonewline $fd $info
1387 puts
-nonewline $fd "\t"
1388 puts
-nonewline $fd $path
1389 puts
-nonewline $fd "\0"
1390 display_file
$path $new
1393 set ui_status_value
[format \
1394 "$msg... %i/%i files (%.2f%%)" \
1397 [expr {100.0 * $update_index_cp / $totalCnt}]]
1400 proc update_index
{msg pathList after
} {
1401 global update_index_cp ui_status_value
1403 if {![lock_index update
]} return
1405 set update_index_cp
0
1406 set pathList
[lsort
$pathList]
1407 set totalCnt
[llength
$pathList]
1408 set batch [expr {int
($totalCnt * .01) + 1}]
1409 if {$batch > 25} {set batch 25}
1411 set ui_status_value
[format \
1412 "$msg... %i/%i files (%.2f%%)" \
1416 set fd
[open
"| git update-index --add --remove -z --stdin" w
]
1422 fileevent
$fd writable
[list \
1423 write_update_index \
1433 proc write_update_index
{fd pathList totalCnt
batch msg after
} {
1434 global update_index_cp ui_status_value
1435 global file_states current_diff
1437 if {$update_index_cp >= $totalCnt} {
1444 for {set i
$batch} \
1445 {$update_index_cp < $totalCnt && $i > 0} \
1447 set path
[lindex
$pathList $update_index_cp]
1448 incr update_index_cp
1450 switch
-glob -- [lindex
$file_states($path) 0] {
1466 puts
-nonewline $fd $path
1467 puts
-nonewline $fd "\0"
1468 display_file
$path $new
1471 set ui_status_value
[format \
1472 "$msg... %i/%i files (%.2f%%)" \
1475 [expr {100.0 * $update_index_cp / $totalCnt}]]
1478 ######################################################################
1480 ## remote management
1482 proc load_all_remotes
{} {
1483 global gitdir all_remotes repo_config
1485 set all_remotes
[list
]
1486 set rm_dir
[file join $gitdir remotes
]
1487 if {[file isdirectory
$rm_dir]} {
1488 set all_remotes
[concat
$all_remotes [glob \
1492 -directory $rm_dir *]]
1495 foreach line
[array names repo_config remote.
*.url
] {
1496 if {[regexp ^remote\.
(.
*)\.url\$
$line line name
]} {
1497 lappend all_remotes
$name
1501 set all_remotes
[lsort
-unique $all_remotes]
1504 proc populate_fetch_menu
{m
} {
1505 global gitdir all_remotes repo_config
1507 foreach r
$all_remotes {
1509 if {![catch
{set a
$repo_config(remote.
$r.url
)}]} {
1510 if {![catch
{set a
$repo_config(remote.
$r.fetch
)}]} {
1515 set fd
[open
[file join $gitdir remotes
$r] r
]
1516 while {[gets
$fd n
] >= 0} {
1517 if {[regexp
{^Pull
:[ \t]*([^
:]+):} $n]} {
1528 -label "Fetch from $r..." \
1529 -command [list fetch_from
$r] \
1535 proc populate_push_menu
{m
} {
1536 global gitdir all_remotes repo_config
1538 foreach r
$all_remotes {
1540 if {![catch
{set a
$repo_config(remote.
$r.url
)}]} {
1541 if {![catch
{set a
$repo_config(remote.
$r.push
)}]} {
1546 set fd
[open
[file join $gitdir remotes
$r] r
]
1547 while {[gets
$fd n
] >= 0} {
1548 if {[regexp
{^Push
:[ \t]*([^
:]+):} $n]} {
1559 -label "Push to $r..." \
1560 -command [list push_to
$r] \
1566 proc populate_pull_menu
{m
} {
1567 global gitdir repo_config all_remotes disable_on_lock
1569 foreach remote
$all_remotes {
1571 if {[array get repo_config remote.
$remote.url
] ne
{}} {
1572 if {[array get repo_config remote.
$remote.fetch
] ne
{}} {
1573 regexp
{^
([^
:]+):} \
1574 [lindex
$repo_config(remote.
$remote.fetch
) 0] \
1579 set fd
[open
[file join $gitdir remotes
$remote] r
]
1580 while {[gets
$fd line
] >= 0} {
1581 if {[regexp
{^Pull
:[ \t]*([^
:]+):} $line line rb
]} {
1590 regsub ^refs
/heads
/ $rb {} rb_short
1591 if {$rb_short ne
{}} {
1593 -label "Branch $rb_short from $remote..." \
1594 -command [list pull_remote
$remote $rb] \
1596 lappend disable_on_lock \
1597 [list
$m entryconf
[$m index last
] -state]
1602 ######################################################################
1607 #define mask_width 14
1608 #define mask_height 15
1609 static unsigned char mask_bits
[] = {
1610 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1611 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1612 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
1615 image create bitmap file_plain
-background white
-foreground black
-data {
1616 #define plain_width 14
1617 #define plain_height 15
1618 static unsigned char plain_bits
[] = {
1619 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1620 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
1621 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1622 } -maskdata $filemask
1624 image create bitmap file_mod
-background white
-foreground blue
-data {
1625 #define mod_width 14
1626 #define mod_height 15
1627 static unsigned char mod_bits
[] = {
1628 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1629 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1630 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1631 } -maskdata $filemask
1633 image create bitmap file_fulltick
-background white
-foreground "#007000" -data {
1634 #define file_fulltick_width 14
1635 #define file_fulltick_height 15
1636 static unsigned char file_fulltick_bits
[] = {
1637 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
1638 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
1639 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1640 } -maskdata $filemask
1642 image create bitmap file_parttick
-background white
-foreground "#005050" -data {
1643 #define parttick_width 14
1644 #define parttick_height 15
1645 static unsigned char parttick_bits
[] = {
1646 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1647 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
1648 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1649 } -maskdata $filemask
1651 image create bitmap file_question
-background white
-foreground black
-data {
1652 #define file_question_width 14
1653 #define file_question_height 15
1654 static unsigned char file_question_bits
[] = {
1655 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
1656 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
1657 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1658 } -maskdata $filemask
1660 image create bitmap file_removed
-background white
-foreground red
-data {
1661 #define file_removed_width 14
1662 #define file_removed_height 15
1663 static unsigned char file_removed_bits
[] = {
1664 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1665 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
1666 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
1667 } -maskdata $filemask
1669 image create bitmap file_merge
-background white
-foreground blue
-data {
1670 #define file_merge_width 14
1671 #define file_merge_height 15
1672 static unsigned char file_merge_bits
[] = {
1673 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
1674 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1675 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1676 } -maskdata $filemask
1678 set ui_index .vpane.files.index.list
1679 set ui_other .vpane.files.other.list
1680 set max_status_desc
0
1682 {__ i plain
"Unmodified"}
1683 {_M i mod
"Modified"}
1684 {M_ i fulltick
"Included in commit"}
1685 {MM i parttick
"Partially included"}
1687 {_O o plain
"Untracked"}
1688 {A_ o fulltick
"Added by commit"}
1689 {AM o parttick
"Partially added"}
1690 {AD o question
"Added (but now gone)"}
1692 {_D i question
"Missing"}
1693 {DD i removed
"Removed by commit"}
1694 {DO i removed
"Removed (still exists)"}
1695 {DM i removed
"Removed (but modified)"}
1697 {UD i merge
"Merge conflicts"}
1698 {UM i merge
"Merge conflicts"}
1699 {U_ i merge
"Merge conflicts"}
1701 if {$max_status_desc < [string length
[lindex
$i 3]]} {
1702 set max_status_desc
[string length
[lindex
$i 3]]
1704 if {[lindex
$i 1] eq
{i
}} {
1705 set all_cols
([lindex
$i 0]) $ui_index
1707 set all_cols
([lindex
$i 0]) $ui_other
1709 set all_icons
([lindex
$i 0]) file_
[lindex
$i 2]
1710 set all_descs
([lindex
$i 0]) [lindex
$i 3]
1714 ######################################################################
1719 global tcl_platform tk_library
1720 if {$tcl_platform(platform
) eq
{unix
}
1721 && $tcl_platform(os
) eq
{Darwin
}
1722 && [string match
/Library
/Frameworks
/* $tk_library]} {
1728 proc bind_button3
{w cmd
} {
1729 bind $w <Any-Button-3
> $cmd
1731 bind $w <Control-Button-1
> $cmd
1735 proc incr_font_size
{font
{amt
1}} {
1736 set sz
[font configure
$font -size]
1738 font configure
$font -size $sz
1739 font configure
${font}bold
-size $sz
1742 proc hook_failed_popup
{hook msg
} {
1743 global gitdir appname
1749 label
$w.m.l1
-text "$hook hook failed:" \
1754 -background white
-borderwidth 1 \
1756 -width 80 -height 10 \
1758 -yscrollcommand [list
$w.m.sby
set]
1760 -text {You must correct the above errors before committing.
} \
1764 scrollbar
$w.m.sby
-command [list
$w.m.t yview
]
1765 pack
$w.m.l1
-side top
-fill x
1766 pack
$w.m.l2
-side bottom
-fill x
1767 pack
$w.m.sby
-side right
-fill y
1768 pack
$w.m.t
-side left
-fill both
-expand 1
1769 pack
$w.m
-side top
-fill both
-expand 1 -padx 5 -pady 10
1771 $w.m.t insert
1.0 $msg
1772 $w.m.t conf
-state disabled
1774 button
$w.ok
-text OK \
1777 -command "destroy $w"
1778 pack
$w.ok
-side bottom
-anchor e
-pady 10 -padx 10
1780 bind $w <Visibility
> "grab $w; focus $w"
1781 bind $w <Key-Return
> "destroy $w"
1782 wm title
$w "$appname ([lindex [file split \
1783 [file normalize [file dirname $gitdir]]] \
1788 set next_console_id
0
1790 proc new_console
{short_title long_title
} {
1791 global next_console_id console_data
1792 set w .console
[incr next_console_id
]
1793 set console_data
($w) [list
$short_title $long_title]
1794 return [console_init
$w]
1797 proc console_init
{w
} {
1798 global console_cr console_data
1799 global gitdir appname M1B
1801 set console_cr
($w) 1.0
1804 label
$w.m.l1
-text "[lindex $console_data($w) 1]:" \
1809 -background white
-borderwidth 1 \
1811 -width 80 -height 10 \
1814 -yscrollcommand [list
$w.m.sby
set]
1815 label
$w.m.s
-text {Working... please
wait...
} \
1819 scrollbar
$w.m.sby
-command [list
$w.m.t yview
]
1820 pack
$w.m.l1
-side top
-fill x
1821 pack
$w.m.s
-side bottom
-fill x
1822 pack
$w.m.sby
-side right
-fill y
1823 pack
$w.m.t
-side left
-fill both
-expand 1
1824 pack
$w.m
-side top
-fill both
-expand 1 -padx 5 -pady 10
1826 menu
$w.ctxm
-tearoff 0
1827 $w.ctxm add
command -label "Copy" \
1829 -command "tk_textCopy $w.m.t"
1830 $w.ctxm add
command -label "Select All" \
1832 -command "$w.m.t tag add sel 0.0 end"
1833 $w.ctxm add
command -label "Copy All" \
1836 $w.m.t tag add sel 0.0 end
1838 $w.m.t tag remove sel 0.0 end
1841 button
$w.ok
-text {Close
} \
1844 -command "destroy $w"
1845 pack
$w.ok
-side bottom
-anchor e
-pady 10 -padx 10
1847 bind_button3
$w.m.t
"tk_popup $w.ctxm %X %Y"
1848 bind $w.m.t
<$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break"
1849 bind $w.m.t
<$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break"
1850 bind $w <Visibility
> "focus $w"
1851 wm title
$w "$appname ([lindex [file split \
1852 [file normalize [file dirname $gitdir]]] \
1853 end]): [lindex $console_data($w) 0]"
1857 proc console_exec
{w cmd
{after
{}}} {
1860 # -- Windows tosses the enviroment when we exec our child.
1861 # But most users need that so we have to relogin. :-(
1863 if {$tcl_platform(platform
) eq
{windows
}} {
1864 set cmd
[list sh
--login -c "cd \"[pwd]\" && [join $cmd { }]"]
1867 # -- Tcl won't let us redirect both stdout and stderr to
1868 # the same pipe. So pass it through cat...
1870 set cmd
[concat |
$cmd |
& cat]
1872 set fd_f
[open
$cmd r
]
1873 fconfigure
$fd_f -blocking 0 -translation binary
1874 fileevent
$fd_f readable
[list console_read
$w $fd_f $after]
1877 proc console_read
{w fd after
} {
1878 global console_cr console_data
1882 if {![winfo exists
$w]} {console_init
$w}
1883 $w.m.t conf
-state normal
1885 set n
[string length
$buf]
1887 set cr
[string first
"\r" $buf $c]
1888 set lf
[string first
"\n" $buf $c]
1889 if {$cr < 0} {set cr
[expr {$n + 1}]}
1890 if {$lf < 0} {set lf
[expr {$n + 1}]}
1893 $w.m.t insert end
[string range
$buf $c $lf]
1894 set console_cr
($w) [$w.m.t index
{end
-1c}]
1898 $w.m.t delete
$console_cr($w) end
1899 $w.m.t insert end
"\n"
1900 $w.m.t insert end
[string range
$buf $c $cr]
1905 $w.m.t conf
-state disabled
1909 fconfigure
$fd -blocking 1
1911 if {[catch
{close
$fd}]} {
1912 if {![winfo exists
$w]} {console_init
$w}
1913 $w.m.s conf
-background red
-text {Error
: Command Failed
}
1914 $w.ok conf
-state normal
1916 } elseif
{[winfo exists
$w]} {
1917 $w.m.s conf
-background green
-text {Success
}
1918 $w.ok conf
-state normal
1921 array
unset console_cr
$w
1922 array
unset console_data
$w
1924 uplevel
#0 $after $ok
1928 fconfigure
$fd -blocking 0
1931 ######################################################################
1935 set starting_gitk_msg
{Please
wait... Starting gitk...
}
1938 global tcl_platform ui_status_value starting_gitk_msg
1940 set ui_status_value
$starting_gitk_msg
1942 if {$ui_status_value eq
$starting_gitk_msg} {
1943 set ui_status_value
{Ready.
}
1947 if {$tcl_platform(platform
) eq
{windows
}} {
1955 set w
[new_console
{repack
} \
1956 {Repacking the object database
}]
1957 set cmd
[list git repack
]
1960 console_exec
$w $cmd
1963 proc do_fsck_objects
{} {
1964 set w
[new_console
{fsck-objects
} \
1965 {Verifying the object database with fsck-objects
}]
1966 set cmd
[list git fsck-objects
]
1969 lappend cmd
--strict
1970 console_exec
$w $cmd
1976 global gitdir ui_comm is_quitting repo_config commit_type
1978 if {$is_quitting} return
1981 # -- Stash our current commit buffer.
1983 set save
[file join $gitdir GITGUI_MSG
]
1984 set msg
[string trim
[$ui_comm get
0.0 end
]]
1985 if {![string match amend
* $commit_type]
1986 && [$ui_comm edit modified
]
1989 set fd
[open
$save w
]
1990 puts
$fd [string trim
[$ui_comm get
0.0 end
]]
1994 catch
{file delete
$save}
1997 # -- Stash our current window geometry into this repository.
1999 set cfg_geometry
[list
]
2000 lappend cfg_geometry
[wm geometry .
]
2001 lappend cfg_geometry
[lindex
[.vpane sash coord
0] 1]
2002 lappend cfg_geometry
[lindex
[.vpane.files sash coord
0] 0]
2003 if {[catch
{set rc_geometry
$repo_config(gui.geometry
)}]} {
2006 if {$cfg_geometry ne
$rc_geometry} {
2007 catch
{exec git repo-config gui.geometry
$cfg_geometry}
2014 rescan
{set ui_status_value
{Ready.
}}
2017 proc remove_helper
{txt paths
} {
2018 global file_states current_diff
2020 if {![lock_index begin-update
]} return
2024 foreach path
$paths {
2025 switch
-glob -- [lindex
$file_states($path) 0] {
2029 lappend pathList
$path
2030 if {$path eq
$current_diff} {
2031 set after
{reshow_diff
;}
2036 if {$pathList eq
{}} {
2042 [concat
$after {set ui_status_value
{Ready.
}}]
2046 proc do_remove_selection
{} {
2047 global current_diff selected_paths
2049 if {[array size selected_paths
] > 0} {
2051 {Removing selected files from commit
} \
2052 [array names selected_paths
]
2053 } elseif
{$current_diff ne
{}} {
2055 "Removing [short_path $current_diff] from commit" \
2056 [list
$current_diff]
2060 proc include_helper
{txt paths
} {
2061 global file_states current_diff
2063 if {![lock_index begin-update
]} return
2067 foreach path
$paths {
2068 switch
-glob -- [lindex
$file_states($path) 0] {
2076 lappend pathList
$path
2077 if {$path eq
$current_diff} {
2078 set after
{reshow_diff
;}
2083 if {$pathList eq
{}} {
2089 [concat
$after {set ui_status_value
{Ready to commit.
}}]
2093 proc do_include_selection
{} {
2094 global current_diff selected_paths
2096 if {[array size selected_paths
] > 0} {
2098 {Including selected files
} \
2099 [array names selected_paths
]
2100 } elseif
{$current_diff ne
{}} {
2102 "Including [short_path $current_diff]" \
2103 [list
$current_diff]
2107 proc do_include_all
{} {
2111 foreach path
[array names file_states
] {
2112 switch
-- [lindex
$file_states($path) 0] {
2117 _D
{lappend paths
$path}
2121 {Including all modified files
} \
2125 proc do_signoff
{} {
2128 set me
[committer_ident
]
2129 if {$me eq
{}} return
2131 set sob
"Signed-off-by: $me"
2132 set last
[$ui_comm get
{end
-1c linestart
} {end
-1c}]
2133 if {$last ne
$sob} {
2134 $ui_comm edit separator
2136 && ![regexp
{^
[A-Z
][A-Za-z
]*-[A-Za-z-
]+: *} $last]} {
2137 $ui_comm insert end
"\n"
2139 $ui_comm insert end
"\n$sob"
2140 $ui_comm edit separator
2145 proc do_select_commit_type
{} {
2146 global commit_type selected_commit_type
2148 if {$selected_commit_type eq
{new
}
2149 && [string match amend
* $commit_type]} {
2151 } elseif
{$selected_commit_type eq
{amend
}
2152 && ![string match amend
* $commit_type]} {
2155 # The amend request was rejected...
2157 if {![string match amend
* $commit_type]} {
2158 set selected_commit_type new
2168 global appname copyright
2172 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
2174 label
$w.header
-text "About $appname" \
2176 pack
$w.header
-side top
-fill x
2179 button
$w.buttons.close
-text {Close
} \
2181 -command [list destroy
$w]
2182 pack
$w.buttons.close
-side right
2183 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
2186 -text "$appname - a commit creation tool for Git.
2194 pack
$w.desc
-side top
-fill x
-padx 5 -pady 5
2197 -text [exec git
--version] \
2204 pack
$w.vers
-side top
-fill x
-padx 5 -pady 5
2206 bind $w <Visibility
> "grab $w; focus $w"
2207 bind $w <Key-Escape
> "destroy $w"
2208 wm title
$w "About $appname"
2212 proc do_options
{} {
2213 global appname gitdir font_descs
2214 global repo_config global_config
2215 global repo_config_new global_config_new
2217 array
unset repo_config_new
2218 array
unset global_config_new
2219 foreach name
[array names repo_config
] {
2220 set repo_config_new
($name) $repo_config($name)
2223 foreach name
[array names repo_config
] {
2225 gui.diffcontext
{continue}
2227 set repo_config_new
($name) $repo_config($name)
2229 foreach name
[array names global_config
] {
2230 set global_config_new
($name) $global_config($name)
2232 set reponame
[lindex
[file split \
2233 [file normalize
[file dirname $gitdir]]] \
2236 set w .options_editor
2238 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
2240 label
$w.header
-text "$appname Options" \
2242 pack
$w.header
-side top
-fill x
2245 button
$w.buttons.restore
-text {Restore Defaults
} \
2247 -command do_restore_defaults
2248 pack
$w.buttons.restore
-side left
2249 button
$w.buttons.save
-text Save \
2251 -command [list do_save_config
$w]
2252 pack
$w.buttons.save
-side right
2253 button
$w.buttons.cancel
-text {Cancel
} \
2255 -command [list destroy
$w]
2256 pack
$w.buttons.cancel
-side right
2257 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
2259 labelframe
$w.repo
-text "$reponame Repository" \
2261 -relief raised
-borderwidth 2
2262 labelframe
$w.global
-text {Global
(All Repositories
)} \
2264 -relief raised
-borderwidth 2
2265 pack
$w.repo
-side left
-fill both
-expand 1 -pady 5 -padx 5
2266 pack
$w.global
-side right
-fill both
-expand 1 -pady 5 -padx 5
2269 {b partialinclude
{Allow Partially Included Files
}}
2270 {b pullsummary
{Show Pull Summary
}}
2271 {b trustmtime
{Trust File Modification Timestamps
}}
2272 {i diffcontext
{Number of Diff Context Lines
}}
2274 set type [lindex
$option 0]
2275 set name
[lindex
$option 1]
2276 set text
[lindex
$option 2]
2277 foreach f
{repo global
} {
2280 checkbutton
$w.
$f.
$name -text $text \
2281 -variable ${f}_config_new
(gui.
$name) \
2285 pack
$w.
$f.
$name -side top
-anchor w
2289 label
$w.
$f.
$name.l
-text "$text:" -font font_ui
2290 pack
$w.
$f.
$name.l
-side left
-anchor w
-fill x
2291 spinbox
$w.
$f.
$name.v \
2292 -textvariable ${f}_config_new
(gui.
$name) \
2293 -from 1 -to 99 -increment 1 \
2296 pack
$w.
$f.
$name.v
-side right
-anchor e
2297 pack
$w.
$f.
$name -side top
-anchor w
-fill x
2303 set all_fonts
[lsort
[font families
]]
2304 foreach option
$font_descs {
2305 set name
[lindex
$option 0]
2306 set font
[lindex
$option 1]
2307 set text
[lindex
$option 2]
2309 set global_config_new
(gui.
$font^^family
) \
2310 [font configure
$font -family]
2311 set global_config_new
(gui.
$font^^size
) \
2312 [font configure
$font -size]
2314 frame
$w.global.
$name
2315 label
$w.global.
$name.l
-text "$text:" -font font_ui
2316 pack
$w.global.
$name.l
-side left
-anchor w
-fill x
2317 eval tk_optionMenu
$w.global.
$name.family \
2318 global_config_new
(gui.
$font^^family
) \
2320 spinbox
$w.global.
$name.size \
2321 -textvariable global_config_new
(gui.
$font^^size
) \
2322 -from 2 -to 80 -increment 1 \
2325 pack
$w.global.
$name.size
-side right
-anchor e
2326 pack
$w.global.
$name.family
-side right
-anchor e
2327 pack
$w.global.
$name -side top
-anchor w
-fill x
2330 bind $w <Visibility
> "grab $w; focus $w"
2331 bind $w <Key-Escape
> "destroy $w"
2332 wm title
$w "$appname ($reponame): Options"
2336 proc do_restore_defaults
{} {
2337 global font_descs default_config repo_config
2338 global repo_config_new global_config_new
2340 foreach name
[array names default_config
] {
2341 set repo_config_new
($name) $default_config($name)
2342 set global_config_new
($name) $default_config($name)
2345 foreach option
$font_descs {
2346 set name
[lindex
$option 0]
2347 set repo_config
(gui.
$name) $default_config(gui.
$name)
2351 foreach option
$font_descs {
2352 set name
[lindex
$option 0]
2353 set font
[lindex
$option 1]
2354 set global_config_new
(gui.
$font^^family
) \
2355 [font configure
$font -family]
2356 set global_config_new
(gui.
$font^^size
) \
2357 [font configure
$font -size]
2361 proc do_save_config
{w
} {
2362 if {[catch
{save_config
} err
]} {
2363 error_popup
"Failed to completely save options:\n\n$err"
2369 proc do_windows_shortcut
{} {
2370 global gitdir appname argv0
2372 set reponame
[lindex
[file split \
2373 [file normalize
[file dirname $gitdir]]] \
2377 set desktop
[exec cygpath \
2385 set fn
[tk_getSaveFile \
2387 -title "$appname ($reponame): Create Desktop Icon" \
2388 -initialdir $desktop \
2389 -initialfile "Git $reponame.bat"]
2393 set sh
[exec cygpath \
2398 set me
[exec cygpath \
2402 set gd
[exec cygpath \
2406 regsub
-all ' $me "'\\''" me
2407 regsub -all ' $gd "'\\''" gd
2408 puts -nonewline $fd "\"$sh\" --login -c \""
2409 puts -nonewline $fd "GIT_DIR='$gd'"
2410 puts -nonewline $fd " '$me'"
2414 error_popup "Cannot write script:\n\n$err"
2419 proc do_macosx_app {} {
2420 global gitdir appname argv0 env
2422 set reponame [lindex [file split \
2423 [file normalize [file dirname $gitdir]]] \
2426 set fn [tk_getSaveFile \
2428 -title "$appname ($reponame): Create Desktop Icon" \
2429 -initialdir [file join $env(HOME) Desktop] \
2430 -initialfile "Git $reponame.app"]
2433 set Contents [file join $fn Contents]
2434 set MacOS [file join $Contents MacOS]
2435 set exe [file join $MacOS git-gui]
2439 set fd [open [file join $Contents Info.plist] w]
2440 puts $fd {<?xml version="1.0" encoding="UTF-8"?>
2441 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2442 <plist version="1.0">
2444 <key>CFBundleDevelopmentRegion</key>
2445 <string>English</string>
2446 <key>CFBundleExecutable</key>
2447 <string>git-gui</string>
2448 <key>CFBundleIdentifier</key>
2449 <string>org.spearce.git-gui</string>
2450 <key>CFBundleInfoDictionaryVersion</key>
2451 <string>6.0</string>
2452 <key>CFBundlePackageType</key>
2453 <string>APPL</string>
2454 <key>CFBundleSignature</key>
2455 <string>????</string>
2456 <key>CFBundleVersion</key>
2457 <string>1.0</string>
2458 <key>NSPrincipalClass</key>
2459 <string>NSApplication</string>
2464 set fd [open $exe w]
2465 set gd [file normalize $gitdir]
2466 set ep [file normalize [exec git --exec-path]]
2467 regsub -all ' $gd "'\\''" gd
2468 regsub
-all ' $ep "'\\''" ep
2469 puts $fd "#!/bin/sh"
2470 foreach name
[array names env
] {
2471 if {[string match GIT_
* $name]} {
2472 regsub
-all ' $env($name) "'\\''" v
2473 puts $fd "export $name='$v'"
2476 puts $fd "export PATH
='$ep':\
$PATH"
2477 puts $fd "export GIT_DIR
='$gd'"
2478 puts $fd "exec [file normalize
$argv0]"
2481 file attributes $exe -permissions u+x,g+x,o+x
2483 error_popup "Cannot
write icon
:\n\n$err"
2488 proc toggle_or_diff {w x y} {
2489 global file_states file_lists current_diff ui_index ui_other
2490 global last_clicked selected_paths
2492 set pos [split [$w index @$x,$y] .]
2493 set lno [lindex $pos 0]
2494 set col [lindex $pos 1]
2495 set path [lindex $file_lists($w) [expr {$lno - 1}]]
2501 set last_clicked [list $w $lno]
2502 array unset selected_paths
2503 $ui_index tag remove in_sel 0.0 end
2504 $ui_other tag remove in_sel 0.0 end
2507 if {$current_diff eq $path} {
2508 set after {reshow_diff;}
2512 switch -glob -- [lindex $file_states($path) 0] {
2519 "Removing
[short_path
$path] from commit
" \
2521 [concat $after {set ui_status_value {Ready.}}]
2525 "Including
[short_path
$path]" \
2527 [concat $after {set ui_status_value {Ready.}}]
2531 show_diff $path $w $lno
2535 proc add_one_to_selection {w x y} {
2537 global last_clicked selected_paths
2539 set pos [split [$w index @$x,$y] .]
2540 set lno [lindex $pos 0]
2541 set col [lindex $pos 1]
2542 set path [lindex $file_lists($w) [expr {$lno - 1}]]
2548 set last_clicked [list $w $lno]
2549 if {[catch {set in_sel $selected_paths($path)}]} {
2553 unset selected_paths($path)
2554 $w tag remove in_sel $lno.0 [expr {$lno + 1}].0
2556 set selected_paths($path) 1
2557 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
2561 proc add_range_to_selection {w x y} {
2563 global last_clicked selected_paths
2565 if {[lindex $last_clicked 0] ne $w} {
2566 toggle_or_diff $w $x $y
2570 set pos [split [$w index @$x,$y] .]
2571 set lno [lindex $pos 0]
2572 set lc [lindex $last_clicked 1]
2581 foreach path [lrange $file_lists($w) \
2582 [expr {$begin - 1}] \
2583 [expr {$end - 1}]] {
2584 set selected_paths($path) 1
2586 $w tag add in_sel $begin.0 [expr {$end + 1}].0
2589 ######################################################################
2593 set cursor_ptr arrow
2594 font create font_diff -family Courier -size 10
2598 eval font configure font_ui [font actual [.dummy cget -font]]
2602 font create font_uibold
2603 font create font_diffbold
2607 if {$tcl_platform(platform) eq {windows}} {
2610 } elseif {[is_MacOSX]} {
2615 proc apply_config {} {
2616 global repo_config font_descs
2618 foreach option $font_descs {
2619 set name [lindex $option 0]
2620 set font [lindex $option 1]
2622 foreach {cn cv} $repo_config(gui.$name) {
2623 font configure $font $cn $cv
2626 error_popup "Invalid font specified
in gui.
$name:\n\n$err"
2628 foreach {cn cv} [font configure $font] {
2629 font configure ${font}bold $cn $cv
2631 font configure ${font}bold -weight bold
2635 set default_config(gui.trustmtime) false
2636 set default_config(gui.pullsummary) true
2637 set default_config(gui.partialinclude) false
2638 set default_config(gui.diffcontext) 5
2639 set default_config(gui.fontui) [font configure font_ui]
2640 set default_config(gui.fontdiff) [font configure font_diff]
2642 {fontui font_ui {Main Font}}
2643 {fontdiff font_diff {Diff/Console Font}}
2648 ######################################################################
2654 menu .mbar -tearoff 0
2655 .mbar add cascade -label Repository -menu .mbar.repository
2656 .mbar add cascade -label Edit -menu .mbar.edit
2657 .mbar add cascade -label Commit -menu .mbar.commit
2658 if {!$single_commit} {
2659 .mbar add cascade -label Fetch -menu .mbar.fetch
2660 .mbar add cascade -label Pull -menu .mbar.pull
2661 .mbar add cascade -label Push -menu .mbar.push
2663 . configure -menu .mbar
2665 # -- Repository Menu
2667 menu .mbar.repository
2668 .mbar.repository add command -label Visualize \
2671 if {!$single_commit} {
2672 .mbar.repository add separator
2674 .mbar.repository add command -label {Repack Database} \
2675 -command do_repack \
2678 .mbar.repository add command -label {Verify Database} \
2679 -command do_fsck_objects \
2682 .mbar.repository add separator
2684 if {$tcl_platform(platform) eq {windows}} {
2685 .mbar.repository add command \
2686 -label {Create Desktop Icon} \
2687 -command do_windows_shortcut \
2689 } elseif {[is_MacOSX]} {
2690 .mbar.repository add command \
2691 -label {Create Desktop Icon} \
2692 -command do_macosx_app \
2696 .mbar.repository add command -label Quit \
2698 -accelerator $M1T-Q \
2704 .mbar.edit add command -label Undo \
2705 -command {catch {[focus] edit undo}} \
2706 -accelerator $M1T-Z \
2708 .mbar.edit add command -label Redo \
2709 -command {catch {[focus] edit redo}} \
2710 -accelerator $M1T-Y \
2712 .mbar.edit add separator
2713 .mbar.edit add command -label Cut \
2714 -command {catch {tk_textCut [focus]}} \
2715 -accelerator $M1T-X \
2717 .mbar.edit add command -label Copy \
2718 -command {catch {tk_textCopy [focus]}} \
2719 -accelerator $M1T-C \
2721 .mbar.edit add command -label Paste \
2722 -command {catch {tk_textPaste [focus]; [focus] see insert}} \
2723 -accelerator $M1T-V \
2725 .mbar.edit add command -label Delete \
2726 -command {catch {[focus] delete sel.first sel.last}} \
2729 .mbar.edit add separator
2730 .mbar.edit add command -label {Select All} \
2731 -command {catch {[focus] tag add sel 0.0 end}} \
2732 -accelerator $M1T-A \
2739 .mbar.commit add radiobutton \
2740 -label {New Commit} \
2741 -command do_select_commit_type \
2742 -variable selected_commit_type \
2745 lappend disable_on_lock \
2746 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2748 .mbar.commit add radiobutton \
2749 -label {Amend Last Commit} \
2750 -command do_select_commit_type \
2751 -variable selected_commit_type \
2754 lappend disable_on_lock \
2755 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2757 .mbar.commit add separator
2759 .mbar.commit add command -label Rescan \
2760 -command do_rescan \
2763 lappend disable_on_lock \
2764 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2766 .mbar.commit add command -label {Remove From Commit} \
2767 -command do_remove_selection \
2769 lappend disable_on_lock \
2770 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2772 .mbar.commit add command -label {Include In Commit} \
2773 -command do_include_selection \
2775 lappend disable_on_lock \
2776 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2778 .mbar.commit add command -label {Include All In Commit} \
2779 -command do_include_all \
2780 -accelerator $M1T-I \
2782 lappend disable_on_lock \
2783 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2785 .mbar.commit add separator
2787 .mbar.commit add command -label {Sign Off} \
2788 -command do_signoff \
2789 -accelerator $M1T-S \
2792 .mbar.commit add command -label Commit \
2793 -command do_commit \
2794 -accelerator $M1T-Return \
2796 lappend disable_on_lock \
2797 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2799 # -- Transport menus
2801 if {!$single_commit} {
2808 # -- Apple Menu (Mac OS X only)
2810 .mbar add cascade -label Apple -menu .mbar.apple
2813 .mbar.apple add command -label "About
$appname" \
2816 .mbar.apple add command -label "$appname Options...
" \
2817 -command do_options \
2822 .mbar.edit add separator
2823 .mbar.edit add command -label {Options...} \
2824 -command do_options \
2829 .mbar add cascade -label Help -menu .mbar.help
2832 .mbar.help add command -label "About
$appname" \
2838 # -- Main Window Layout
2840 panedwindow .vpane -orient vertical
2841 panedwindow .vpane.files -orient horizontal
2842 .vpane add .vpane.files -sticky nsew -height 100 -width 400
2843 pack .vpane -anchor n -side top -fill both -expand 1
2845 # -- Index File List
2847 frame .vpane.files.index -height 100 -width 400
2848 label .vpane.files.index.title -text {Modified Files} \
2851 text $ui_index -background white -borderwidth 0 \
2852 -width 40 -height 10 \
2854 -cursor $cursor_ptr \
2855 -yscrollcommand {.vpane.files.index.sb set} \
2857 scrollbar .vpane.files.index.sb -command [list $ui_index yview]
2858 pack .vpane.files.index.title -side top -fill x
2859 pack .vpane.files.index.sb -side right -fill y
2860 pack $ui_index -side left -fill both -expand 1
2861 .vpane.files add .vpane.files.index -sticky nsew
2863 # -- Other (Add) File List
2865 frame .vpane.files.other -height 100 -width 100
2866 label .vpane.files.other.title -text {Untracked Files} \
2869 text $ui_other -background white -borderwidth 0 \
2870 -width 40 -height 10 \
2872 -cursor $cursor_ptr \
2873 -yscrollcommand {.vpane.files.other.sb set} \
2875 scrollbar .vpane.files.other.sb -command [list $ui_other yview]
2876 pack .vpane.files.other.title -side top -fill x
2877 pack .vpane.files.other.sb -side right -fill y
2878 pack $ui_other -side left -fill both -expand 1
2879 .vpane.files add .vpane.files.other -sticky nsew
2881 foreach i [list $ui_index $ui_other] {
2882 $i tag conf in_diff -font font_uibold
2883 $i tag conf in_sel \
2884 -background [$i cget -foreground] \
2885 -foreground [$i cget -background]
2889 # -- Diff and Commit Area
2891 frame .vpane.lower -height 300 -width 400
2892 frame .vpane.lower.commarea
2893 frame .vpane.lower.diff -relief sunken -borderwidth 1
2894 pack .vpane.lower.commarea -side top -fill x
2895 pack .vpane.lower.diff -side bottom -fill both -expand 1
2896 .vpane add .vpane.lower -stick nsew
2898 # -- Commit Area Buttons
2900 frame .vpane.lower.commarea.buttons
2901 label .vpane.lower.commarea.buttons.l -text {} \
2905 pack .vpane.lower.commarea.buttons.l -side top -fill x
2906 pack .vpane.lower.commarea.buttons -side left -fill y
2908 button .vpane.lower.commarea.buttons.rescan -text {Rescan} \
2909 -command do_rescan \
2911 pack .vpane.lower.commarea.buttons.rescan -side top -fill x
2912 lappend disable_on_lock \
2913 {.vpane.lower.commarea.buttons.rescan conf -state}
2915 button .vpane.lower.commarea.buttons.incall -text {Include All} \
2916 -command do_include_all \
2918 pack .vpane.lower.commarea.buttons.incall -side top -fill x
2919 lappend disable_on_lock \
2920 {.vpane.lower.commarea.buttons.incall conf -state}
2922 button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \
2923 -command do_signoff \
2925 pack .vpane.lower.commarea.buttons.signoff -side top -fill x
2927 button .vpane.lower.commarea.buttons.commit -text {Commit} \
2928 -command do_commit \
2930 pack .vpane.lower.commarea.buttons.commit -side top -fill x
2931 lappend disable_on_lock \
2932 {.vpane.lower.commarea.buttons.commit conf -state}
2934 # -- Commit Message Buffer
2936 frame .vpane.lower.commarea.buffer
2937 frame .vpane.lower.commarea.buffer.header
2938 set ui_comm .vpane.lower.commarea.buffer.t
2939 set ui_coml .vpane.lower.commarea.buffer.header.l
2940 radiobutton .vpane.lower.commarea.buffer.header.new \
2941 -text {New Commit} \
2942 -command do_select_commit_type \
2943 -variable selected_commit_type \
2946 lappend disable_on_lock \
2947 [list .vpane.lower.commarea.buffer.header.new conf -state]
2948 radiobutton .vpane.lower.commarea.buffer.header.amend \
2949 -text {Amend Last Commit} \
2950 -command do_select_commit_type \
2951 -variable selected_commit_type \
2954 lappend disable_on_lock \
2955 [list .vpane.lower.commarea.buffer.header.amend conf -state]
2960 proc trace_commit_type {varname args} {
2961 global ui_coml commit_type
2962 switch -glob -- $commit_type {
2963 initial {set txt {Initial Commit Message:}}
2964 amend {set txt {Amended Commit Message:}}
2965 amend-initial {set txt {Amended Initial Commit Message:}}
2966 amend-merge {set txt {Amended Merge Commit Message:}}
2967 merge {set txt {Merge Commit Message:}}
2968 * {set txt {Commit Message:}}
2970 $ui_coml conf -text $txt
2972 trace add variable commit_type write trace_commit_type
2973 pack $ui_coml -side left -fill x
2974 pack .vpane.lower.commarea.buffer.header.amend -side right
2975 pack .vpane.lower.commarea.buffer.header.new -side right
2977 text $ui_comm -background white -borderwidth 1 \
2980 -autoseparators true \
2982 -width 75 -height 9 -wrap none \
2984 -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
2985 scrollbar .vpane.lower.commarea.buffer.sby \
2986 -command [list $ui_comm yview]
2987 pack .vpane.lower.commarea.buffer.header -side top -fill x
2988 pack .vpane.lower.commarea.buffer.sby -side right -fill y
2989 pack $ui_comm -side left -fill y
2990 pack .vpane.lower.commarea.buffer -side left -fill y
2992 # -- Commit Message Buffer Context Menu
2994 set ctxm .vpane.lower.commarea.buffer.ctxm
2995 menu $ctxm -tearoff 0
2999 -command {tk_textCut $ui_comm}
3003 -command {tk_textCopy $ui_comm}
3007 -command {tk_textPaste $ui_comm}
3011 -command {$ui_comm delete sel.first sel.last}
3014 -label {Select All} \
3016 -command {$ui_comm tag add sel 0.0 end}
3021 $ui_comm tag add sel 0.0 end
3022 tk_textCopy $ui_comm
3023 $ui_comm tag remove sel 0.0 end
3030 bind_button3 $ui_comm "tk_popup
$ctxm %X
%Y
"
3035 set diff_actions [list]
3036 proc trace_current_diff {varname args} {
3037 global current_diff diff_actions file_states
3038 if {$current_diff eq {}} {
3045 set s [mapdesc [lindex $file_states($p) 0] $p]
3047 set p [escape_path $p]
3051 .vpane.lower.diff.header.status configure -text $s
3052 .vpane.lower.diff.header.file configure -text $f
3053 .vpane.lower.diff.header.path configure -text $p
3054 foreach w $diff_actions {
3058 trace add variable current_diff write trace_current_diff
3060 frame .vpane.lower.diff.header -background orange
3061 label .vpane.lower.diff.header.status \
3062 -background orange \
3063 -width $max_status_desc \
3067 label .vpane.lower.diff.header.file \
3068 -background orange \
3072 label .vpane.lower.diff.header.path \
3073 -background orange \
3077 pack .vpane.lower.diff.header.status -side left
3078 pack .vpane.lower.diff.header.file -side left
3079 pack .vpane.lower.diff.header.path -fill x
3080 set ctxm .vpane.lower.diff.header.ctxm
3081 menu $ctxm -tearoff 0
3092 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3093 bind_button3 .vpane.lower.diff.header.path "tk_popup
$ctxm %X
%Y
"
3097 frame .vpane.lower.diff.body
3098 set ui_diff .vpane.lower.diff.body.t
3099 text $ui_diff -background white -borderwidth 0 \
3100 -width 80 -height 15 -wrap none \
3102 -xscrollcommand {.vpane.lower.diff.body.sbx set} \
3103 -yscrollcommand {.vpane.lower.diff.body.sby set} \
3105 scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
3106 -command [list $ui_diff xview]
3107 scrollbar .vpane.lower.diff.body.sby -orient vertical \
3108 -command [list $ui_diff yview]
3109 pack .vpane.lower.diff.body.sbx -side bottom -fill x
3110 pack .vpane.lower.diff.body.sby -side right -fill y
3111 pack $ui_diff -side left -fill both -expand 1
3112 pack .vpane.lower.diff.header -side top -fill x
3113 pack .vpane.lower.diff.body -side bottom -fill both -expand 1
3115 $ui_diff tag conf d_@ -font font_diffbold
3116 $ui_diff tag conf d_+ -foreground blue
3117 $ui_diff tag conf d_- -foreground red
3118 $ui_diff tag conf d_++ -foreground {#00a000}
3119 $ui_diff tag conf d_-- -foreground {#a000a0}
3120 $ui_diff tag conf d_+- \
3122 -background {light goldenrod yellow}
3123 $ui_diff tag conf d_-+ \
3127 # -- Diff Body Context Menu
3129 set ctxm .vpane.lower.diff.body.ctxm
3130 menu $ctxm -tearoff 0
3134 -command {tk_textCopy $ui_diff}
3135 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3137 -label {Select All} \
3139 -command {$ui_diff tag add sel 0.0 end}
3140 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3145 $ui_diff tag add sel 0.0 end
3146 tk_textCopy $ui_diff
3147 $ui_diff tag remove sel 0.0 end
3149 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3152 -label {Decrease Font Size} \
3154 -command {incr_font_size font_diff -1}
3155 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3157 -label {Increase Font Size} \
3159 -command {incr_font_size font_diff 1}
3160 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3163 -label {Show Less Context} \
3165 -command {if {$repo_config(gui.diffcontext) >= 2} {
3166 incr repo_config(gui.diffcontext) -1
3169 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3171 -label {Show More Context} \
3174 incr repo_config(gui.diffcontext)
3177 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3179 $ctxm add command -label {Options...} \
3182 bind_button3 $ui_diff "tk_popup
$ctxm %X
%Y
"
3186 set ui_status_value {Initializing...}
3187 label .status -textvariable ui_status_value \
3193 pack .status -anchor w -side bottom -fill x
3198 set gm $repo_config(gui.geometry)
3199 wm geometry . [lindex $gm 0]
3200 .vpane sash place 0 \
3201 [lindex [.vpane sash coord 0] 0] \
3203 .vpane.files sash place 0 \
3205 [lindex [.vpane.files sash coord 0] 1]
3211 bind $ui_comm <$M1B-Key-Return> {do_commit;break}
3212 bind $ui_comm <$M1B-Key-i> {do_include_all;break}
3213 bind $ui_comm <$M1B-Key-I> {do_include_all;break}
3214 bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
3215 bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
3216 bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
3217 bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
3218 bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
3219 bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
3220 bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
3221 bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
3223 bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
3224 bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
3225 bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
3226 bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
3227 bind $ui_diff <$M1B-Key-v> {break}
3228 bind $ui_diff <$M1B-Key-V> {break}
3229 bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
3230 bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
3231 bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
3232 bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
3233 bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
3234 bind $ui_diff <Key-Right> {catch {%W xview scroll 1 units};break}
3236 bind . <Destroy> do_quit
3237 bind all <Key-F5> do_rescan
3238 bind all <$M1B-Key-r> do_rescan
3239 bind all <$M1B-Key-R> do_rescan
3240 bind . <$M1B-Key-s> do_signoff
3241 bind . <$M1B-Key-S> do_signoff
3242 bind . <$M1B-Key-i> do_include_all
3243 bind . <$M1B-Key-I> do_include_all
3244 bind . <$M1B-Key-Return> do_commit
3245 bind all <$M1B-Key-q> do_quit
3246 bind all <$M1B-Key-Q> do_quit
3247 bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
3248 bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
3249 foreach i [list $ui_index $ui_other] {
3250 bind $i <Button-1> "toggle_or_diff
$i %x
%y
; break"
3251 bind $i <$M1B-Button-1> "add_one_to_selection
$i %x
%y
; break"
3252 bind $i <Shift-Button-1> "add_range_to_selection
$i %x
%y
; break"
3256 set file_lists($ui_index) [list]
3257 set file_lists($ui_other) [list]
3261 set MERGE_HEAD [list]
3265 set selected_commit_type new
3267 wm title . "$appname ([file normalize
[file dirname $gitdir]])"
3268 focus -force $ui_comm
3269 if {!$single_commit} {
3271 populate_fetch_menu .mbar.fetch
3272 populate_pull_menu .mbar.pull
3273 populate_push_menu .mbar.push
3275 lock_index begin-read