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
]
41 return [eval [concat
[list
file join $_gitdir] $args]]
49 ######################################################################
53 proc is_many_config
{name
} {
54 switch
-glob -- $name {
63 proc load_config
{include_global
} {
64 global repo_config global_config default_config
66 array
unset global_config
67 if {$include_global} {
69 set fd_rc
[open
"| git repo-config --global --list" r
]
70 while {[gets
$fd_rc line
] >= 0} {
71 if {[regexp
{^
([^
=]+)=(.
*)$
} $line line name value
]} {
72 if {[is_many_config
$name]} {
73 lappend global_config
($name) $value
75 set global_config
($name) $value
83 array
unset repo_config
85 set fd_rc
[open
"| git repo-config --list" r
]
86 while {[gets
$fd_rc line
] >= 0} {
87 if {[regexp
{^
([^
=]+)=(.
*)$
} $line line name value
]} {
88 if {[is_many_config
$name]} {
89 lappend repo_config
($name) $value
91 set repo_config
($name) $value
98 foreach name
[array names default_config
] {
99 if {[catch
{set v
$global_config($name)}]} {
100 set global_config
($name) $default_config($name)
102 if {[catch
{set v
$repo_config($name)}]} {
103 set repo_config
($name) $default_config($name)
108 proc save_config
{} {
109 global default_config font_descs
110 global repo_config global_config
111 global repo_config_new global_config_new
113 foreach option
$font_descs {
114 set name
[lindex
$option 0]
115 set font
[lindex
$option 1]
116 font configure
$font \
117 -family $global_config_new(gui.
$font^^family
) \
118 -size $global_config_new(gui.
$font^^size
)
119 font configure
${font}bold \
120 -family $global_config_new(gui.
$font^^family
) \
121 -size $global_config_new(gui.
$font^^size
)
122 set global_config_new
(gui.
$name) [font configure
$font]
123 unset global_config_new
(gui.
$font^^family
)
124 unset global_config_new
(gui.
$font^^size
)
127 foreach name
[array names default_config
] {
128 set value
$global_config_new($name)
129 if {$value ne
$global_config($name)} {
130 if {$value eq
$default_config($name)} {
131 catch
{exec git repo-config
--global --unset $name}
133 regsub
-all "\[{}\]" $value {"} value
134 exec git repo-config --global $name $value
136 set global_config($name) $value
137 if {$value eq $repo_config($name)} {
138 catch {exec git repo-config --unset $name}
139 set repo_config($name) $value
144 foreach name [array names default_config] {
145 set value $repo_config_new($name)
146 if {$value ne $repo_config($name)} {
147 if {$value eq $global_config($name)} {
148 catch {exec git repo-config --unset $name}
150 regsub -all "\
[{}\
]" $value {"} value
151 exec git repo-config
$name $value
153 set repo_config
($name) $value
158 proc error_popup
{msg
} {
160 if {[reponame
] ne
{}} {
161 append title
" ([reponame])"
163 set cmd
[list tk_messageBox \
166 -title "$title: error" \
168 if {[winfo ismapped .
]} {
169 lappend cmd
-parent .
174 proc warn_popup
{msg
} {
176 if {[reponame
] ne
{}} {
177 append title
" ([reponame])"
179 set cmd
[list tk_messageBox \
182 -title "$title: warning" \
184 if {[winfo ismapped .
]} {
185 lappend cmd
-parent .
190 proc info_popup
{msg
} {
192 if {[reponame
] ne
{}} {
193 append title
" ([reponame])"
203 proc ask_popup
{msg
} {
205 if {[reponame
] ne
{}} {
206 append title
" ([reponame])"
208 return [tk_messageBox \
216 ######################################################################
220 if { [catch
{set _gitdir
$env(GIT_DIR
)}]
221 && [catch
{set _gitdir
[exec git rev-parse
--git-dir]} err
]} {
222 catch
{wm withdraw .
}
223 error_popup
"Cannot find the git directory:\n\n$err"
226 if {![file isdirectory
$_gitdir]} {
227 catch
{wm withdraw .
}
228 error_popup
"Git directory not found:\n\n$_gitdir"
231 if {[lindex
[file split $_gitdir] end
] ne
{.git
}} {
232 catch
{wm withdraw .
}
233 error_popup
"Cannot use funny .git directory:\n\n$gitdir"
236 if {[catch
{cd [file dirname $_gitdir]} err
]} {
237 catch
{wm withdraw .
}
238 error_popup
"No working directory [file dirname $_gitdir]:\n\n$err"
241 set _reponame
[lindex
[file split \
242 [file normalize
[file dirname $_gitdir]]] \
246 if {[appname
] eq
{git-citool
}} {
250 ######################################################################
258 set disable_on_lock
[list
]
259 set index_lock_type none
261 proc lock_index
{type} {
262 global index_lock_type disable_on_lock
264 if {$index_lock_type eq
{none
}} {
265 set index_lock_type
$type
266 foreach w
$disable_on_lock {
267 uplevel
#0 $w disabled
270 } elseif
{$index_lock_type eq
"begin-$type"} {
271 set index_lock_type
$type
277 proc unlock_index
{} {
278 global index_lock_type disable_on_lock
280 set index_lock_type none
281 foreach w
$disable_on_lock {
286 ######################################################################
290 proc repository_state
{ctvar hdvar mhvar
} {
291 global current_branch
292 upvar
$ctvar ct
$hdvar hd
$mhvar mh
296 if {[catch
{set current_branch
[exec git symbolic-ref HEAD
]}]} {
297 set current_branch
{}
299 regsub ^refs
/((heads|tags|remotes
)/)? \
305 if {[catch
{set hd
[exec git rev-parse
--verify HEAD
]}]} {
311 set merge_head
[gitdir MERGE_HEAD
]
312 if {[file exists
$merge_head]} {
314 set fd_mh
[open
$merge_head r
]
315 while {[gets
$fd_mh line
] >= 0} {
326 global PARENT empty_tree
328 set p
[lindex
$PARENT 0]
332 if {$empty_tree eq
{}} {
333 set empty_tree
[exec git mktree
<< {}]
338 proc rescan
{after
{honor_trustmtime
1}} {
339 global HEAD PARENT MERGE_HEAD commit_type
340 global ui_index ui_workdir ui_status_value ui_comm
341 global rescan_active file_states
344 if {$rescan_active > 0 ||
![lock_index
read]} return
346 repository_state newType newHEAD newMERGE_HEAD
347 if {[string match amend
* $commit_type]
348 && $newType eq
{normal
}
349 && $newHEAD eq
$HEAD} {
353 set MERGE_HEAD
$newMERGE_HEAD
354 set commit_type
$newType
357 array
unset file_states
359 if {![$ui_comm edit modified
]
360 ||
[string trim
[$ui_comm get
0.0 end
]] eq
{}} {
361 if {[load_message GITGUI_MSG
]} {
362 } elseif
{[load_message MERGE_MSG
]} {
363 } elseif
{[load_message SQUASH_MSG
]} {
366 $ui_comm edit modified false
369 if {$honor_trustmtime && $repo_config(gui.trustmtime
) eq
{true
}} {
370 rescan_stage2
{} $after
373 set ui_status_value
{Refreshing
file status...
}
374 set cmd
[list git update-index
]
376 lappend cmd
--unmerged
377 lappend cmd
--ignore-missing
378 lappend cmd
--refresh
379 set fd_rf
[open
"| $cmd" r
]
380 fconfigure
$fd_rf -blocking 0 -translation binary
381 fileevent
$fd_rf readable \
382 [list rescan_stage2
$fd_rf $after]
386 proc rescan_stage2
{fd after
} {
387 global ui_status_value
388 global rescan_active buf_rdi buf_rdf buf_rlo
392 if {![eof
$fd]} return
396 set ls_others
[list | git ls-files
--others -z \
397 --exclude-per-directory=.gitignore
]
398 set info_exclude
[gitdir info exclude
]
399 if {[file readable
$info_exclude]} {
400 lappend ls_others
"--exclude-from=$info_exclude"
408 set ui_status_value
{Scanning
for modified files ...
}
409 set fd_di
[open
"| git diff-index --cached -z [PARENT]" r
]
410 set fd_df
[open
"| git diff-files -z" r
]
411 set fd_lo
[open
$ls_others r
]
413 fconfigure
$fd_di -blocking 0 -translation binary
414 fconfigure
$fd_df -blocking 0 -translation binary
415 fconfigure
$fd_lo -blocking 0 -translation binary
416 fileevent
$fd_di readable
[list read_diff_index
$fd_di $after]
417 fileevent
$fd_df readable
[list read_diff_files
$fd_df $after]
418 fileevent
$fd_lo readable
[list read_ls_others
$fd_lo $after]
421 proc load_message
{file} {
425 if {[file isfile
$f]} {
426 if {[catch
{set fd
[open
$f r
]}]} {
429 set content
[string trim
[read $fd]]
431 $ui_comm delete
0.0 end
432 $ui_comm insert end
$content
438 proc read_diff_index
{fd after
} {
441 append buf_rdi
[read $fd]
443 set n
[string length
$buf_rdi]
445 set z1
[string first
"\0" $buf_rdi $c]
448 set z2
[string first
"\0" $buf_rdi $z1]
452 set i
[split [string range
$buf_rdi $c [expr {$z1 - 2}]] { }]
454 [string range
$buf_rdi $z1 [expr {$z2 - 1}]] \
456 [list
[lindex
$i 0] [lindex
$i 2]] \
462 set buf_rdi
[string range
$buf_rdi $c end
]
467 rescan_done
$fd buf_rdi
$after
470 proc read_diff_files
{fd after
} {
473 append buf_rdf
[read $fd]
475 set n
[string length
$buf_rdf]
477 set z1
[string first
"\0" $buf_rdf $c]
480 set z2
[string first
"\0" $buf_rdf $z1]
484 set i
[split [string range
$buf_rdf $c [expr {$z1 - 2}]] { }]
486 [string range
$buf_rdf $z1 [expr {$z2 - 1}]] \
489 [list
[lindex
$i 0] [lindex
$i 2]]
494 set buf_rdf
[string range
$buf_rdf $c end
]
499 rescan_done
$fd buf_rdf
$after
502 proc read_ls_others
{fd after
} {
505 append buf_rlo
[read $fd]
506 set pck
[split $buf_rlo "\0"]
507 set buf_rlo
[lindex
$pck end
]
508 foreach p
[lrange
$pck 0 end-1
] {
511 rescan_done
$fd buf_rlo
$after
514 proc rescan_done
{fd buf after
} {
516 global file_states repo_config
519 if {![eof
$fd]} return
522 if {[incr rescan_active
-1] > 0} return
531 proc prune_selection
{} {
532 global file_states selected_paths
534 foreach path
[array names selected_paths
] {
535 if {[catch
{set still_here
$file_states($path)}]} {
536 unset selected_paths
($path)
541 ######################################################################
546 global ui_diff current_diff_path ui_index ui_workdir
548 $ui_diff conf
-state normal
549 $ui_diff delete
0.0 end
550 $ui_diff conf
-state disabled
552 set current_diff_path
{}
554 $ui_index tag remove in_diff
0.0 end
555 $ui_workdir tag remove in_diff
0.0 end
558 proc reshow_diff
{} {
559 global ui_status_value file_states file_lists
560 global current_diff_path current_diff_side
562 set p
$current_diff_path
564 ||
$current_diff_side eq
{}
565 ||
[catch
{set s
$file_states($p)}]
566 ||
[lsearch
-sorted $file_lists($current_diff_side) $p] == -1} {
569 show_diff
$p $current_diff_side
573 proc handle_empty_diff
{} {
574 global current_diff_path file_states file_lists
576 set path
$current_diff_path
577 set s
$file_states($path)
578 if {[lindex
$s 0] ne
{_M
}} return
580 info_popup
"No differences detected.
582 [short_path $path] has no changes.
584 The modification date of this file was updated
585 by another application and you currently have
586 the Trust File Modification Timestamps option
587 enabled, so Git did not automatically detect
588 that there are no content differences in this
592 display_file
$path __
593 rescan
{set ui_status_value
{Ready.
}} 0
596 proc show_diff
{path w
{lno
{}}} {
597 global file_states file_lists
598 global is_3way_diff diff_active repo_config
599 global ui_diff ui_status_value ui_index ui_workdir
600 global current_diff_path current_diff_side
602 if {$diff_active ||
![lock_index
read]} return
605 if {$w eq
{} ||
$lno == {}} {
606 foreach w
[array names file_lists
] {
607 set lno
[lsearch
-sorted $file_lists($w) $path]
614 if {$w ne
{} && $lno >= 1} {
615 $w tag add in_diff
$lno.0 [expr {$lno + 1}].0
618 set s
$file_states($path)
622 set current_diff_path
$path
623 set current_diff_side
$w
624 set ui_status_value
"Loading diff of [escape_path $path]..."
626 # - Git won't give us the diff, there's nothing to compare to!
630 set fd
[open
$path r
]
631 set content
[read $fd]
636 set ui_status_value
"Unable to display [escape_path $path]"
637 error_popup
"Error loading file:\n\n$err"
640 $ui_diff conf
-state normal
641 $ui_diff insert end
$content
642 $ui_diff conf
-state disabled
645 set ui_status_value
{Ready.
}
650 if {$w eq
$ui_index} {
651 lappend cmd diff-index
653 } elseif
{$w eq
$ui_workdir} {
654 if {[string index
$m 0] eq
{U
}} {
657 lappend cmd diff-files
662 lappend cmd
--no-color
663 if {$repo_config(gui.diffcontext
) > 0} {
664 lappend cmd
"-U$repo_config(gui.diffcontext)"
666 if {$w eq
$ui_index} {
672 if {[catch
{set fd
[open
$cmd r
]} err
]} {
675 set ui_status_value
"Unable to display [escape_path $path]"
676 error_popup
"Error loading diff:\n\n$err"
680 fconfigure
$fd -blocking 0 -translation auto
681 fileevent
$fd readable
[list read_diff
$fd]
684 proc read_diff
{fd
} {
685 global ui_diff ui_status_value is_3way_diff diff_active
688 $ui_diff conf
-state normal
689 while {[gets
$fd line
] >= 0} {
690 # -- Cleanup uninteresting diff header lines.
692 if {[string match
{diff --git *} $line]} continue
693 if {[string match
{diff --cc *} $line]} continue
694 if {[string match
{diff --combined *} $line]} continue
695 if {[string match
{--- *} $line]} continue
696 if {[string match
{+++ *} $line]} continue
697 if {$line eq
{deleted
file mode
120000}} {
698 set line
"deleted symlink"
701 # -- Automatically detect if this is a 3 way diff.
703 if {[string match
{@@@
*} $line]} {set is_3way_diff
1}
705 if {[string match
{index
*} $line]
706 ||
[string match
{mode
*} $line]
707 ||
[string match
{new
file *} $line]
708 ||
[string match
{deleted
file *} $line]
709 ||
[regexp
{^\
* Unmerged path
} $line]} {
711 } elseif
{$is_3way_diff} {
712 set op
[string range
$line 0 1]
722 if {[regexp
{^\
+\
+([<>]{7} |
={7})} $line _g op
]} {
723 set line
[string replace
$line 0 1 { }]
730 puts
"error: Unhandled 3 way diff marker: {$op}"
735 set op
[string index
$line 0]
741 if {[regexp
{^\
+([<>]{7} |
={7})} $line _g op
]} {
742 set line
[string replace
$line 0 0 { }]
749 puts
"error: Unhandled 2 way diff marker: {$op}"
754 $ui_diff insert end
$line $tags
755 $ui_diff insert end
"\n" $tags
757 $ui_diff conf
-state disabled
763 set ui_status_value
{Ready.
}
765 if {$repo_config(gui.trustmtime
) eq
{true
}
766 && [$ui_diff index end
] eq
{2.0}} {
772 ######################################################################
776 proc load_last_commit
{} {
777 global HEAD PARENT MERGE_HEAD commit_type ui_comm
779 if {[llength
$PARENT] == 0} {
780 error_popup
{There is nothing to amend.
782 You are about to create the initial commit.
783 There is no commit before this to amend.
788 repository_state curType curHEAD curMERGE_HEAD
789 if {$curType eq
{merge
}} {
790 error_popup
{Cannot amend
while merging.
792 You are currently
in the middle of a merge that
793 has not been fully completed. You cannot amend
794 the prior commit unless you first abort the
795 current merge activity.
803 set fd
[open
"| git cat-file commit $curHEAD" r
]
804 while {[gets
$fd line
] > 0} {
805 if {[string match
{parent
*} $line]} {
806 lappend parents
[string range
$line 7 end
]
809 set msg
[string trim
[read $fd]]
812 error_popup
"Error loading commit data for amend:\n\n$err"
818 set MERGE_HEAD
[list
]
819 switch
-- [llength
$parents] {
820 0 {set commit_type amend-initial
}
821 1 {set commit_type amend
}
822 default
{set commit_type amend-merge
}
825 $ui_comm delete
0.0 end
826 $ui_comm insert end
$msg
828 $ui_comm edit modified false
829 rescan
{set ui_status_value
{Ready.
}}
832 proc create_new_commit
{} {
833 global commit_type ui_comm
835 set commit_type normal
836 $ui_comm delete
0.0 end
838 $ui_comm edit modified false
839 rescan
{set ui_status_value
{Ready.
}}
842 set GIT_COMMITTER_IDENT
{}
844 proc committer_ident
{} {
845 global GIT_COMMITTER_IDENT
847 if {$GIT_COMMITTER_IDENT eq
{}} {
848 if {[catch
{set me
[exec git var GIT_COMMITTER_IDENT
]} err
]} {
849 error_popup
"Unable to obtain your identity:\n\n$err"
852 if {![regexp
{^
(.
*) [0-9]+ [-+0-9]+$
} \
853 $me me GIT_COMMITTER_IDENT
]} {
854 error_popup
"Invalid GIT_COMMITTER_IDENT:\n\n$me"
859 return $GIT_COMMITTER_IDENT
862 proc commit_tree
{} {
863 global HEAD commit_type file_states ui_comm repo_config
864 global ui_status_value pch_error
866 if {![lock_index update
]} return
867 if {[committer_ident
] eq
{}} return
869 # -- Our in memory state should match the repository.
871 repository_state curType curHEAD curMERGE_HEAD
872 if {[string match amend
* $commit_type]
873 && $curType eq
{normal
}
874 && $curHEAD eq
$HEAD} {
875 } elseif
{$commit_type ne
$curType ||
$HEAD ne
$curHEAD} {
876 info_popup
{Last scanned state does not match repository state.
878 Another Git program has modified this repository
879 since the last scan. A rescan must be performed
880 before another commit can be created.
882 The rescan will be automatically started now.
885 rescan
{set ui_status_value
{Ready.
}}
889 # -- At least one file should differ in the index.
892 foreach path
[array names file_states
] {
893 switch
-glob -- [lindex
$file_states($path) 0] {
897 M?
{set files_ready
1}
899 error_popup
"Unmerged files cannot be committed.
901 File [short_path $path] has merge conflicts.
902 You must resolve them and add the file before committing.
908 error_popup
"Unknown file state [lindex $s 0] detected.
910 File [short_path $path] cannot be committed by this program.
916 info_popup
{No changes to commit.
918 You must add
at least
1 file before you can commit.
924 # -- A message is required.
926 set msg
[string trim
[$ui_comm get
1.0 end
]]
928 error_popup
{Please supply a commit message.
930 A good commit message has the following format
:
932 - First line
: Describe
in one sentance what you did.
934 - Remaining lines
: Describe why this change is good.
940 # -- Run the pre-commit hook.
942 set pchook
[gitdir hooks pre-commit
]
944 # On Cygwin [file executable] might lie so we need to ask
945 # the shell if the hook is executable. Yes that's annoying.
947 if {[is_Windows
] && [file isfile
$pchook]} {
948 set pchook
[list sh
-c [concat \
949 "if test -x \"$pchook\";" \
950 "then exec \"$pchook\" 2>&1;" \
952 } elseif
{[file executable
$pchook]} {
953 set pchook
[list
$pchook |
& cat]
955 commit_writetree
$curHEAD $msg
959 set ui_status_value
{Calling pre-commit hook...
}
961 set fd_ph
[open
"| $pchook" r
]
962 fconfigure
$fd_ph -blocking 0 -translation binary
963 fileevent
$fd_ph readable \
964 [list commit_prehook_wait
$fd_ph $curHEAD $msg]
967 proc commit_prehook_wait
{fd_ph curHEAD msg
} {
968 global pch_error ui_status_value
970 append pch_error
[read $fd_ph]
971 fconfigure
$fd_ph -blocking 1
973 if {[catch
{close
$fd_ph}]} {
974 set ui_status_value
{Commit declined by pre-commit hook.
}
975 hook_failed_popup pre-commit
$pch_error
978 commit_writetree
$curHEAD $msg
983 fconfigure
$fd_ph -blocking 0
986 proc commit_writetree
{curHEAD msg
} {
987 global ui_status_value
989 set ui_status_value
{Committing changes...
}
990 set fd_wt
[open
"| git write-tree" r
]
991 fileevent
$fd_wt readable \
992 [list commit_committree
$fd_wt $curHEAD $msg]
995 proc commit_committree
{fd_wt curHEAD msg
} {
996 global HEAD PARENT MERGE_HEAD commit_type
997 global single_commit all_heads current_branch
998 global ui_status_value ui_comm selected_commit_type
999 global file_states selected_paths rescan_active
1002 if {$tree_id eq
{} ||
[catch
{close
$fd_wt} err
]} {
1003 error_popup
"write-tree failed:\n\n$err"
1004 set ui_status_value
{Commit failed.
}
1009 # -- Create the commit.
1011 set cmd
[list git commit-tree
$tree_id]
1012 set parents
[concat
$PARENT $MERGE_HEAD]
1013 if {[llength
$parents] > 0} {
1014 foreach p
$parents {
1018 # git commit-tree writes to stderr during initial commit.
1019 lappend cmd
2>/dev
/null
1022 if {[catch
{set cmt_id
[eval exec $cmd]} err
]} {
1023 error_popup
"commit-tree failed:\n\n$err"
1024 set ui_status_value
{Commit failed.
}
1029 # -- Update the HEAD ref.
1032 if {$commit_type ne
{normal
}} {
1033 append reflogm
" ($commit_type)"
1035 set i
[string first
"\n" $msg]
1037 append reflogm
{: } [string range
$msg 0 [expr {$i - 1}]]
1039 append reflogm
{: } $msg
1041 set cmd
[list git update-ref
-m $reflogm HEAD
$cmt_id $curHEAD]
1042 if {[catch
{eval exec $cmd} err
]} {
1043 error_popup
"update-ref failed:\n\n$err"
1044 set ui_status_value
{Commit failed.
}
1049 # -- Make sure our current branch exists.
1051 if {$commit_type eq
{initial
}} {
1052 lappend all_heads
$current_branch
1053 set all_heads
[lsort
-unique $all_heads]
1054 populate_branch_menu
1057 # -- Cleanup after ourselves.
1059 catch
{file delete
[gitdir MERGE_HEAD
]}
1060 catch
{file delete
[gitdir MERGE_MSG
]}
1061 catch
{file delete
[gitdir SQUASH_MSG
]}
1062 catch
{file delete
[gitdir GITGUI_MSG
]}
1064 # -- Let rerere do its thing.
1066 if {[file isdirectory
[gitdir rr-cache
]]} {
1067 catch
{exec git rerere
}
1070 # -- Run the post-commit hook.
1072 set pchook
[gitdir hooks post-commit
]
1073 if {[is_Windows
] && [file isfile
$pchook]} {
1074 set pchook
[list sh
-c [concat \
1075 "if test -x \"$pchook\";" \
1076 "then exec \"$pchook\";" \
1078 } elseif
{![file executable
$pchook]} {
1081 if {$pchook ne
{}} {
1082 catch
{exec $pchook &}
1085 $ui_comm delete
0.0 end
1087 $ui_comm edit modified false
1089 if {$single_commit} do_quit
1091 # -- Update in memory status
1093 set selected_commit_type new
1094 set commit_type normal
1097 set MERGE_HEAD
[list
]
1099 foreach path
[array names file_states
] {
1100 set s
$file_states($path)
1102 switch
-glob -- $m {
1110 unset file_states
($path)
1111 catch
{unset selected_paths
($path)}
1114 set file_states
($path) [list _O
[lindex
$s 1] {} {}]
1120 set file_states
($path) [list \
1121 _
[string index
$m 1] \
1132 set ui_status_value \
1133 "Changes committed as [string range $cmt_id 0 7]."
1136 ######################################################################
1140 proc fetch_from
{remote
} {
1141 set w
[new_console
"fetch $remote" \
1142 "Fetching new changes from $remote"]
1143 set cmd
[list git fetch
]
1145 console_exec
$w $cmd
1148 proc pull_remote
{remote branch
} {
1149 global HEAD commit_type file_states repo_config
1151 if {![lock_index update
]} return
1153 # -- Our in memory state should match the repository.
1155 repository_state curType curHEAD curMERGE_HEAD
1156 if {$commit_type ne
$curType ||
$HEAD ne
$curHEAD} {
1157 info_popup
{Last scanned state does not match repository state.
1159 Another Git program has modified this repository
1160 since the last scan. A rescan must be performed
1161 before a pull operation can be started.
1163 The rescan will be automatically started now.
1166 rescan
{set ui_status_value
{Ready.
}}
1170 # -- No differences should exist before a pull.
1172 if {[array size file_states
] != 0} {
1173 error_popup
{Uncommitted but modified files are present.
1175 You should not perform a pull with unmodified
1176 files
in your working directory as Git will be
1177 unable to recover from an incorrect merge.
1179 You should commit or revert all changes before
1180 starting a pull operation.
1186 set w
[new_console
"pull $remote $branch" \
1187 "Pulling new changes from branch $branch in $remote"]
1188 set cmd
[list git pull
]
1189 if {$repo_config(gui.pullsummary
) eq
{false
}} {
1190 lappend cmd
--no-summary
1194 console_exec
$w $cmd [list post_pull_remote
$remote $branch]
1197 proc post_pull_remote
{remote branch success
} {
1198 global HEAD PARENT MERGE_HEAD commit_type selected_commit_type
1199 global ui_status_value
1203 repository_state commit_type HEAD MERGE_HEAD
1205 set selected_commit_type new
1206 set ui_status_value
"Pulling $branch from $remote complete."
1208 rescan
[list
set ui_status_value \
1209 "Conflicts detected while pulling $branch from $remote."]
1213 proc push_to
{remote
} {
1214 set w
[new_console
"push $remote" \
1215 "Pushing changes to $remote"]
1216 set cmd
[list git push
]
1218 console_exec
$w $cmd
1221 ######################################################################
1225 proc mapicon
{w state path
} {
1228 if {[catch
{set r
$all_icons($state$w)}]} {
1229 puts
"error: no icon for $w state={$state} $path"
1235 proc mapdesc
{state path
} {
1238 if {[catch
{set r
$all_descs($state)}]} {
1239 puts
"error: no desc for state={$state} $path"
1245 proc escape_path
{path
} {
1246 regsub
-all "\n" $path "\\n" path
1250 proc short_path
{path
} {
1251 return [escape_path
[lindex
[file split $path] end
]]
1255 set null_sha1
[string repeat
0 40]
1257 proc merge_state
{path new_state
{head_info
{}} {index_info
{}}} {
1258 global file_states next_icon_id null_sha1
1260 set s0
[string index
$new_state 0]
1261 set s1
[string index
$new_state 1]
1263 if {[catch
{set info
$file_states($path)}]} {
1265 set icon n
[incr next_icon_id
]
1267 set state
[lindex
$info 0]
1268 set icon
[lindex
$info 1]
1269 if {$head_info eq
{}} {set head_info
[lindex
$info 2]}
1270 if {$index_info eq
{}} {set index_info
[lindex
$info 3]}
1273 if {$s0 eq
{?
}} {set s0
[string index
$state 0]} \
1274 elseif
{$s0 eq
{_
}} {set s0 _
}
1276 if {$s1 eq
{?
}} {set s1
[string index
$state 1]} \
1277 elseif
{$s1 eq
{_
}} {set s1 _
}
1279 if {$s0 eq
{A
} && $s1 eq
{_
} && $head_info eq
{}} {
1280 set head_info
[list
0 $null_sha1]
1281 } elseif
{$s0 ne
{_
} && [string index
$state 0] eq
{_
}
1282 && $head_info eq
{}} {
1283 set head_info
$index_info
1286 set file_states
($path) [list
$s0$s1 $icon \
1287 $head_info $index_info \
1292 proc display_file_helper
{w path icon_name old_m new_m
} {
1295 if {$new_m eq
{_
}} {
1296 set lno
[lsearch
-sorted $file_lists($w) $path]
1298 set file_lists
($w) [lreplace
$file_lists($w) $lno $lno]
1300 $w conf
-state normal
1301 $w delete
$lno.0 [expr {$lno + 1}].0
1302 $w conf
-state disabled
1304 } elseif
{$old_m eq
{_
} && $new_m ne
{_
}} {
1305 lappend file_lists
($w) $path
1306 set file_lists
($w) [lsort
-unique $file_lists($w)]
1307 set lno
[lsearch
-sorted $file_lists($w) $path]
1309 $w conf
-state normal
1310 $w image create
$lno.0 \
1311 -align center
-padx 5 -pady 1 \
1313 -image [mapicon
$w $new_m $path]
1314 $w insert
$lno.1 "[escape_path $path]\n"
1315 $w conf
-state disabled
1316 } elseif
{$old_m ne
$new_m} {
1317 $w conf
-state normal
1318 $w image conf
$icon_name -image [mapicon
$w $new_m $path]
1319 $w conf
-state disabled
1323 proc display_file
{path state
} {
1324 global file_states selected_paths
1325 global ui_index ui_workdir
1327 set old_m
[merge_state
$path $state]
1328 set s
$file_states($path)
1329 set new_m
[lindex
$s 0]
1330 set icon_name
[lindex
$s 1]
1332 set o
[string index
$old_m 0]
1333 set n
[string index
$new_m 0]
1340 display_file_helper
$ui_index $path $icon_name $o $n
1342 if {[string index
$old_m 0] eq
{U
}} {
1345 set o
[string index
$old_m 1]
1347 if {[string index
$new_m 0] eq
{U
}} {
1350 set n
[string index
$new_m 1]
1352 display_file_helper
$ui_workdir $path $icon_name $o $n
1354 if {$new_m eq
{__
}} {
1355 unset file_states
($path)
1356 catch
{unset selected_paths
($path)}
1360 proc display_all_files_helper
{w path icon_name m
} {
1363 lappend file_lists
($w) $path
1364 set lno
[expr {[lindex
[split [$w index end
] .
] 0] - 1}]
1365 $w image create end \
1366 -align center
-padx 5 -pady 1 \
1368 -image [mapicon
$w $m $path]
1369 $w insert end
"[escape_path $path]\n"
1372 proc display_all_files
{} {
1373 global ui_index ui_workdir
1374 global file_states file_lists
1377 $ui_index conf
-state normal
1378 $ui_workdir conf
-state normal
1380 $ui_index delete
0.0 end
1381 $ui_workdir delete
0.0 end
1384 set file_lists
($ui_index) [list
]
1385 set file_lists
($ui_workdir) [list
]
1387 foreach path
[lsort
[array names file_states
]] {
1388 set s
$file_states($path)
1390 set icon_name
[lindex
$s 1]
1392 set s
[string index
$m 0]
1393 if {$s ne
{U
} && $s ne
{_
}} {
1394 display_all_files_helper
$ui_index $path \
1398 if {[string index
$m 0] eq
{U
}} {
1401 set s
[string index
$m 1]
1404 display_all_files_helper
$ui_workdir $path \
1409 $ui_index conf
-state disabled
1410 $ui_workdir conf
-state disabled
1413 proc update_indexinfo
{msg pathList after
} {
1414 global update_index_cp ui_status_value
1416 if {![lock_index update
]} return
1418 set update_index_cp
0
1419 set pathList
[lsort
$pathList]
1420 set totalCnt
[llength
$pathList]
1421 set batch [expr {int
($totalCnt * .01) + 1}]
1422 if {$batch > 25} {set batch 25}
1424 set ui_status_value
[format \
1425 "$msg... %i/%i files (%.2f%%)" \
1429 set fd
[open
"| git update-index -z --index-info" w
]
1435 fileevent
$fd writable
[list \
1436 write_update_indexinfo \
1446 proc write_update_indexinfo
{fd pathList totalCnt
batch msg after
} {
1447 global update_index_cp ui_status_value
1448 global file_states current_diff_path
1450 if {$update_index_cp >= $totalCnt} {
1457 for {set i
$batch} \
1458 {$update_index_cp < $totalCnt && $i > 0} \
1460 set path
[lindex
$pathList $update_index_cp]
1461 incr update_index_cp
1463 set s
$file_states($path)
1464 switch
-glob -- [lindex
$s 0] {
1471 set info
[lindex
$s 2]
1472 if {$info eq
{}} continue
1474 puts
-nonewline $fd "$info\t$path\0"
1475 display_file
$path $new
1478 set ui_status_value
[format \
1479 "$msg... %i/%i files (%.2f%%)" \
1482 [expr {100.0 * $update_index_cp / $totalCnt}]]
1485 proc update_index
{msg pathList after
} {
1486 global update_index_cp ui_status_value
1488 if {![lock_index update
]} return
1490 set update_index_cp
0
1491 set pathList
[lsort
$pathList]
1492 set totalCnt
[llength
$pathList]
1493 set batch [expr {int
($totalCnt * .01) + 1}]
1494 if {$batch > 25} {set batch 25}
1496 set ui_status_value
[format \
1497 "$msg... %i/%i files (%.2f%%)" \
1501 set fd
[open
"| git update-index --add --remove -z --stdin" w
]
1507 fileevent
$fd writable
[list \
1508 write_update_index \
1518 proc write_update_index
{fd pathList totalCnt
batch msg after
} {
1519 global update_index_cp ui_status_value
1520 global file_states current_diff_path
1522 if {$update_index_cp >= $totalCnt} {
1529 for {set i
$batch} \
1530 {$update_index_cp < $totalCnt && $i > 0} \
1532 set path
[lindex
$pathList $update_index_cp]
1533 incr update_index_cp
1535 switch
-glob -- [lindex
$file_states($path) 0] {
1541 if {[file exists
$path]} {
1550 puts
-nonewline $fd "$path\0"
1551 display_file
$path $new
1554 set ui_status_value
[format \
1555 "$msg... %i/%i files (%.2f%%)" \
1558 [expr {100.0 * $update_index_cp / $totalCnt}]]
1561 proc checkout_index
{msg pathList after
} {
1562 global update_index_cp ui_status_value
1564 if {![lock_index update
]} return
1566 set update_index_cp
0
1567 set pathList
[lsort
$pathList]
1568 set totalCnt
[llength
$pathList]
1569 set batch [expr {int
($totalCnt * .01) + 1}]
1570 if {$batch > 25} {set batch 25}
1572 set ui_status_value
[format \
1573 "$msg... %i/%i files (%.2f%%)" \
1577 set cmd
[list git checkout-index
]
1583 set fd
[open
"| $cmd " w
]
1589 fileevent
$fd writable
[list \
1590 write_checkout_index \
1600 proc write_checkout_index
{fd pathList totalCnt
batch msg after
} {
1601 global update_index_cp ui_status_value
1602 global file_states current_diff_path
1604 if {$update_index_cp >= $totalCnt} {
1611 for {set i
$batch} \
1612 {$update_index_cp < $totalCnt && $i > 0} \
1614 set path
[lindex
$pathList $update_index_cp]
1615 incr update_index_cp
1616 switch
-glob -- [lindex
$file_states($path) 0] {
1620 puts
-nonewline $fd "$path\0"
1621 display_file
$path ?_
1626 set ui_status_value
[format \
1627 "$msg... %i/%i files (%.2f%%)" \
1630 [expr {100.0 * $update_index_cp / $totalCnt}]]
1633 ######################################################################
1635 ## branch management
1637 proc is_tracking_branch
{name
} {
1638 global tracking_branches
1640 if {![catch
{set info
$tracking_branches($name)}]} {
1643 foreach t
[array names tracking_branches
] {
1644 if {[string match
{*/\
*} $t] && [string match
$t $name]} {
1651 proc load_all_heads
{} {
1654 set all_heads
[list
]
1655 set fd
[open
"| git for-each-ref --format=%(refname) refs/heads" r
]
1656 while {[gets
$fd line
] > 0} {
1657 if {[is_tracking_branch
$line]} continue
1658 if {![regsub ^refs
/heads
/ $line {} name
]} continue
1659 lappend all_heads
$name
1663 set all_heads
[lsort
$all_heads]
1666 proc populate_branch_menu
{} {
1667 global all_heads disable_on_lock
1670 set last
[$m index last
]
1671 for {set i
0} {$i <= $last} {incr i
} {
1672 if {[$m type $i] eq
{separator
}} {
1675 foreach a
$disable_on_lock {
1676 if {[lindex
$a 0] ne
$m ||
[lindex
$a 2] < $i} {
1680 set disable_on_lock
$new_dol
1686 foreach b
$all_heads {
1687 $m add radiobutton \
1689 -command [list switch_branch
$b] \
1690 -variable current_branch \
1693 lappend disable_on_lock \
1694 [list
$m entryconf
[$m index last
] -state]
1698 proc all_tracking_branches
{} {
1699 global tracking_branches
1701 set all_trackings
{}
1703 foreach name
[array names tracking_branches
] {
1704 if {[regsub
{/\
*$
} $name {} name
]} {
1707 regsub ^refs
/(heads|remotes
)/ $name {} name
1708 lappend all_trackings
$name
1713 set fd
[open
"| git for-each-ref --format=%(refname) $cmd" r
]
1714 while {[gets
$fd name
] > 0} {
1715 regsub ^refs
/(heads|remotes
)/ $name {} name
1716 lappend all_trackings
$name
1721 return [lsort
-unique $all_trackings]
1724 proc do_create_branch_action
{w
} {
1725 global all_heads null_sha1 repo_config
1726 global create_branch_checkout create_branch_revtype
1727 global create_branch_head create_branch_trackinghead
1729 set newbranch
[string trim
[$w.desc.name_t get
0.0 end
]]
1730 if {$newbranch eq
{}
1731 ||
$newbranch eq
$repo_config(gui.newbranchtemplate
)} {
1735 -title [wm title
$w] \
1737 -message "Please supply a branch name."
1738 focus
$w.desc.name_t
1741 if {![catch
{exec git show-ref
--verify -- "refs/heads/$newbranch"}]} {
1745 -title [wm title
$w] \
1747 -message "Branch '$newbranch' already exists."
1748 focus
$w.desc.name_t
1751 if {[catch
{exec git check-ref-format
"heads/$newbranch"}]} {
1755 -title [wm title
$w] \
1757 -message "We do not like '$newbranch' as a branch name."
1758 focus
$w.desc.name_t
1763 switch
-- $create_branch_revtype {
1764 head {set rev $create_branch_head}
1765 tracking
{set rev $create_branch_trackinghead}
1766 expression
{set rev [string trim
[$w.from.exp_t get
0.0 end
]]}
1768 if {[catch
{set cmt
[exec git rev-parse
--verify "${rev}^0"]}]} {
1772 -title [wm title
$w] \
1774 -message "Invalid starting revision: $rev"
1777 set cmd
[list git update-ref
]
1779 lappend cmd
"branch: Created from $rev"
1780 lappend cmd
"refs/heads/$newbranch"
1782 lappend cmd
$null_sha1
1783 if {[catch
{eval exec $cmd} err
]} {
1787 -title [wm title
$w] \
1789 -message "Failed to create '$newbranch'.\n\n$err"
1793 lappend all_heads
$newbranch
1794 set all_heads
[lsort
$all_heads]
1795 populate_branch_menu
1797 if {$create_branch_checkout} {
1798 switch_branch
$newbranch
1802 proc radio_selector
{varname value args
} {
1803 upvar
#0 $varname var
1807 trace add variable create_branch_head
write \
1808 [list radio_selector create_branch_revtype
head]
1809 trace add variable create_branch_trackinghead
write \
1810 [list radio_selector create_branch_revtype tracking
]
1812 trace add variable delete_branch_head
write \
1813 [list radio_selector delete_branch_checktype
head]
1814 trace add variable delete_branch_trackinghead
write \
1815 [list radio_selector delete_branch_checktype tracking
]
1817 proc do_create_branch
{} {
1818 global all_heads current_branch repo_config
1819 global create_branch_checkout create_branch_revtype
1820 global create_branch_head create_branch_trackinghead
1822 set w .branch_editor
1824 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
1826 label
$w.header
-text {Create New Branch
} \
1828 pack
$w.header
-side top
-fill x
1831 button
$w.buttons.create
-text Create \
1834 -command [list do_create_branch_action
$w]
1835 pack
$w.buttons.create
-side right
1836 button
$w.buttons.cancel
-text {Cancel
} \
1838 -command [list destroy
$w]
1839 pack
$w.buttons.cancel
-side right
-padx 5
1840 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
1842 labelframe
$w.desc \
1843 -text {Branch Description
} \
1845 label
$w.desc.name_l
-text {Name
:} -font font_ui
1846 text
$w.desc.name_t \
1852 $w.desc.name_t insert
0.0 $repo_config(gui.newbranchtemplate
)
1853 grid
$w.desc.name_l
$w.desc.name_t
-stick we
-padx {0 5}
1854 bind $w.desc.name_t
<Shift-Key-Tab
> {focus
[tk_focusPrev
%W
];break}
1855 bind $w.desc.name_t
<Key-Tab
> {focus
[tk_focusNext
%W
];break}
1856 bind $w.desc.name_t
<Key-Return
> "do_create_branch_action $w;break"
1857 bind $w.desc.name_t
<Key
> {
1858 if {{%K
} ne
{BackSpace
}
1861 && {%K
} ne
{Return
}} {
1863 if {[string first
%A
{~^
:?
*[}] >= 0} break
1866 grid columnconfigure
$w.desc
1 -weight 1
1867 pack
$w.desc
-anchor nw
-fill x
-pady 5 -padx 5
1869 labelframe
$w.from \
1870 -text {Starting Revision
} \
1872 radiobutton
$w.from.head_r \
1873 -text {Local Branch
:} \
1875 -variable create_branch_revtype \
1877 eval tk_optionMenu
$w.from.head_m create_branch_head
$all_heads
1878 grid
$w.from.head_r
$w.from.head_m
-sticky w
1879 set all_trackings
[all_tracking_branches
]
1880 if {$all_trackings ne
{}} {
1881 set create_branch_trackinghead
[lindex
$all_trackings 0]
1882 radiobutton
$w.from.tracking_r \
1883 -text {Tracking Branch
:} \
1885 -variable create_branch_revtype \
1887 eval tk_optionMenu
$w.from.tracking_m \
1888 create_branch_trackinghead \
1890 grid
$w.from.tracking_r
$w.from.tracking_m
-sticky w
1892 radiobutton
$w.from.exp_r \
1893 -text {Revision Expression
:} \
1895 -variable create_branch_revtype \
1897 text
$w.from.exp_t \
1903 grid
$w.from.exp_r
$w.from.exp_t
-stick we
-padx {0 5}
1904 bind $w.from.exp_t
<Shift-Key-Tab
> {focus
[tk_focusPrev
%W
];break}
1905 bind $w.from.exp_t
<Key-Tab
> {focus
[tk_focusNext
%W
];break}
1906 bind $w.from.exp_t
<Key-Return
> "do_create_branch_action $w;break"
1907 bind $w.from.exp_t
<Key-space
> break
1908 bind $w.from.exp_t
<Key
> {set create_branch_revtype expression
}
1909 grid columnconfigure
$w.from
1 -weight 1
1910 pack
$w.from
-anchor nw
-fill x
-pady 5 -padx 5
1912 labelframe
$w.postActions \
1913 -text {Post Creation Actions
} \
1915 checkbutton
$w.postActions.checkout \
1916 -text {Checkout after creation
} \
1917 -variable create_branch_checkout \
1919 pack
$w.postActions.checkout
-anchor nw
1920 pack
$w.postActions
-anchor nw
-fill x
-pady 5 -padx 5
1922 set create_branch_checkout
1
1923 set create_branch_head
$current_branch
1924 set create_branch_revtype
head
1926 bind $w <Visibility
> "grab $w; focus $w.desc.name_t"
1927 bind $w <Key-Escape
> "destroy $w"
1928 bind $w <Key-Return
> "do_create_branch_action $w;break"
1929 wm title
$w "[appname] ([reponame]): Create Branch"
1933 proc do_delete_branch_action
{w
} {
1935 global delete_branch_checktype delete_branch_head delete_branch_trackinghead
1938 switch
-- $delete_branch_checktype {
1939 head {set check_rev
$delete_branch_head}
1940 tracking
{set check_rev
$delete_branch_trackinghead}
1941 always
{set check_rev
{:none
}}
1943 if {$check_rev eq
{:none
}} {
1945 } elseif
{[catch
{set check_cmt
[exec git rev-parse
--verify "${check_rev}^0"]}]} {
1949 -title [wm title
$w] \
1951 -message "Invalid check revision: $check_rev"
1955 set to_delete
[list
]
1956 set not_merged
[list
]
1957 foreach i
[$w.list.l curselection
] {
1958 set b
[$w.list.l get
$i]
1959 if {[catch
{set o
[exec git rev-parse
--verify $b]}]} continue
1960 if {$check_cmt ne
{}} {
1961 if {$b eq
$check_rev} continue
1962 if {[catch
{set m
[exec git merge-base
$o $check_cmt]}]} continue
1964 lappend not_merged
$b
1968 lappend to_delete
[list
$b $o]
1970 if {$not_merged ne
{}} {
1971 set msg
"The following branches are not completely merged into $check_rev:
1973 - [join $not_merged "\n - "]"
1977 -title [wm title
$w] \
1981 if {$to_delete eq
{}} return
1982 if {$delete_branch_checktype eq
{always
}} {
1983 set msg
{Recovering deleted branches is difficult.
1985 Delete the selected branches?
}
1986 if {[tk_messageBox \
1989 -title [wm title
$w] \
1991 -message $msg] ne
yes} {
1997 foreach i
$to_delete {
2000 if {[catch
{exec git update-ref
-d "refs/heads/$b" $o} err
]} {
2001 append failed
" - $b: $err\n"
2003 set x
[lsearch
-sorted $all_heads $b]
2005 set all_heads
[lreplace
$all_heads $x $x]
2010 if {$failed ne
{}} {
2014 -title [wm title
$w] \
2016 -message "Failed to delete branches:\n$failed"
2019 set all_heads
[lsort
$all_heads]
2020 populate_branch_menu
2024 proc do_delete_branch
{} {
2025 global all_heads tracking_branches current_branch
2026 global delete_branch_checktype delete_branch_head delete_branch_trackinghead
2028 set w .branch_editor
2030 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
2032 label
$w.header
-text {Delete Local Branch
} \
2034 pack
$w.header
-side top
-fill x
2037 button
$w.buttons.create
-text Delete \
2039 -command [list do_delete_branch_action
$w]
2040 pack
$w.buttons.create
-side right
2041 button
$w.buttons.cancel
-text {Cancel
} \
2043 -command [list destroy
$w]
2044 pack
$w.buttons.cancel
-side right
-padx 5
2045 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
2047 labelframe
$w.list \
2048 -text {Local Branches
} \
2053 -selectmode extended \
2055 foreach h
$all_heads {
2056 if {$h ne
$current_branch} {
2057 $w.list.l insert end
$h
2060 pack
$w.list.l
-fill both
-pady 5 -padx 5
2061 pack
$w.list
-fill both
-pady 5 -padx 5
2063 labelframe
$w.validate \
2064 -text {Delete Only If
} \
2066 radiobutton
$w.validate.head_r \
2067 -text {Merged Into Local Branch
:} \
2069 -variable delete_branch_checktype \
2071 eval tk_optionMenu
$w.validate.head_m delete_branch_head
$all_heads
2072 grid
$w.validate.head_r
$w.validate.head_m
-sticky w
2073 set all_trackings
[all_tracking_branches
]
2074 if {$all_trackings ne
{}} {
2075 set delete_branch_trackinghead
[lindex
$all_trackings 0]
2076 radiobutton
$w.validate.tracking_r \
2077 -text {Merged Into Tracking Branch
:} \
2079 -variable delete_branch_checktype \
2081 eval tk_optionMenu
$w.validate.tracking_m \
2082 delete_branch_trackinghead \
2084 grid
$w.validate.tracking_r
$w.validate.tracking_m
-sticky w
2086 radiobutton
$w.validate.always_r \
2087 -text {Always
(Do not perform merge checks
)} \
2089 -variable delete_branch_checktype \
2091 grid
$w.validate.always_r
-columnspan 2 -sticky w
2092 grid columnconfigure
$w.validate
1 -weight 1
2093 pack
$w.validate
-anchor nw
-fill x
-pady 5 -padx 5
2095 set delete_branch_head
$current_branch
2096 set delete_branch_checktype
head
2098 bind $w <Visibility
> "grab $w; focus $w"
2099 bind $w <Key-Escape
> "destroy $w"
2100 wm title
$w "[appname] ([reponame]): Delete Branch"
2104 proc switch_branch
{b
} {
2105 global HEAD commit_type file_states current_branch
2106 global selected_commit_type ui_comm
2108 if {![lock_index switch
]} return
2110 # -- Backup the selected branch (repository_state resets it)
2112 set new_branch
$current_branch
2114 # -- Our in memory state should match the repository.
2116 repository_state curType curHEAD curMERGE_HEAD
2117 if {[string match amend
* $commit_type]
2118 && $curType eq
{normal
}
2119 && $curHEAD eq
$HEAD} {
2120 } elseif
{$commit_type ne
$curType ||
$HEAD ne
$curHEAD} {
2121 info_popup
{Last scanned state does not match repository state.
2123 Another Git program has modified this repository
2124 since the last scan. A rescan must be performed
2125 before the current branch can be changed.
2127 The rescan will be automatically started now.
2130 rescan
{set ui_status_value
{Ready.
}}
2134 # -- Toss the message buffer if we are in amend mode.
2136 if {[string match amend
* $curType]} {
2137 $ui_comm delete
0.0 end
2139 $ui_comm edit modified false
2142 set selected_commit_type new
2143 set current_branch
$new_branch
2146 error
"NOT FINISHED"
2149 ######################################################################
2151 ## remote management
2153 proc load_all_remotes
{} {
2155 global all_remotes tracking_branches
2157 set all_remotes
[list
]
2158 array
unset tracking_branches
2160 set rm_dir
[gitdir remotes
]
2161 if {[file isdirectory
$rm_dir]} {
2162 set all_remotes
[glob \
2166 -directory $rm_dir *]
2168 foreach name
$all_remotes {
2170 set fd
[open
[file join $rm_dir $name] r
]
2171 while {[gets
$fd line
] >= 0} {
2172 if {![regexp
{^Pull
:[ ]*([^
:]+):(.
+)$
} \
2173 $line line src dst
]} continue
2174 if {![regexp ^refs
/ $dst]} {
2175 set dst
"refs/heads/$dst"
2177 set tracking_branches
($dst) [list
$name $src]
2184 foreach line
[array names repo_config remote.
*.url
] {
2185 if {![regexp ^remote\.
(.
*)\.url\$
$line line name
]} continue
2186 lappend all_remotes
$name
2188 if {[catch
{set fl
$repo_config(remote.
$name.fetch
)}]} {
2192 if {![regexp
{^
([^
:]+):(.
+)$
} $line line src dst
]} continue
2193 if {![regexp ^refs
/ $dst]} {
2194 set dst
"refs/heads/$dst"
2196 set tracking_branches
($dst) [list
$name $src]
2200 set all_remotes
[lsort
-unique $all_remotes]
2203 proc populate_fetch_menu
{m
} {
2204 global all_remotes repo_config
2206 foreach r
$all_remotes {
2208 if {![catch
{set a
$repo_config(remote.
$r.url
)}]} {
2209 if {![catch
{set a
$repo_config(remote.
$r.fetch
)}]} {
2214 set fd
[open
[gitdir remotes
$r] r
]
2215 while {[gets
$fd n
] >= 0} {
2216 if {[regexp
{^Pull
:[ \t]*([^
:]+):} $n]} {
2227 -label "Fetch from $r..." \
2228 -command [list fetch_from
$r] \
2234 proc populate_push_menu
{m
} {
2235 global all_remotes repo_config
2237 foreach r
$all_remotes {
2239 if {![catch
{set a
$repo_config(remote.
$r.url
)}]} {
2240 if {![catch
{set a
$repo_config(remote.
$r.push
)}]} {
2245 set fd
[open
[gitdir remotes
$r] r
]
2246 while {[gets
$fd n
] >= 0} {
2247 if {[regexp
{^Push
:[ \t]*([^
:]+):} $n]} {
2258 -label "Push to $r..." \
2259 -command [list push_to
$r] \
2265 proc populate_pull_menu
{m
} {
2266 global repo_config all_remotes disable_on_lock
2268 foreach remote
$all_remotes {
2270 if {[array get repo_config remote.
$remote.url
] ne
{}} {
2271 if {[array get repo_config remote.
$remote.fetch
] ne
{}} {
2272 foreach line
$repo_config(remote.
$remote.fetch
) {
2273 if {[regexp
{^
([^
:]+):} $line line rb
]} {
2280 set fd
[open
[gitdir remotes
$remote] r
]
2281 while {[gets
$fd line
] >= 0} {
2282 if {[regexp
{^Pull
:[ \t]*([^
:]+):} $line line rb
]} {
2290 foreach rb
$rb_list {
2291 regsub ^refs
/heads
/ $rb {} rb_short
2293 -label "Branch $rb_short from $remote..." \
2294 -command [list pull_remote
$remote $rb] \
2296 lappend disable_on_lock \
2297 [list
$m entryconf
[$m index last
] -state]
2302 ######################################################################
2307 #define mask_width 14
2308 #define mask_height 15
2309 static unsigned char mask_bits
[] = {
2310 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
2311 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
2312 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
2315 image create bitmap file_plain
-background white
-foreground black
-data {
2316 #define plain_width 14
2317 #define plain_height 15
2318 static unsigned char plain_bits
[] = {
2319 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
2320 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
2321 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
2322 } -maskdata $filemask
2324 image create bitmap file_mod
-background white
-foreground blue
-data {
2325 #define mod_width 14
2326 #define mod_height 15
2327 static unsigned char mod_bits
[] = {
2328 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
2329 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
2330 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
2331 } -maskdata $filemask
2333 image create bitmap file_fulltick
-background white
-foreground "#007000" -data {
2334 #define file_fulltick_width 14
2335 #define file_fulltick_height 15
2336 static unsigned char file_fulltick_bits
[] = {
2337 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
2338 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
2339 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
2340 } -maskdata $filemask
2342 image create bitmap file_parttick
-background white
-foreground "#005050" -data {
2343 #define parttick_width 14
2344 #define parttick_height 15
2345 static unsigned char parttick_bits
[] = {
2346 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
2347 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
2348 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
2349 } -maskdata $filemask
2351 image create bitmap file_question
-background white
-foreground black
-data {
2352 #define file_question_width 14
2353 #define file_question_height 15
2354 static unsigned char file_question_bits
[] = {
2355 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
2356 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
2357 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
2358 } -maskdata $filemask
2360 image create bitmap file_removed
-background white
-foreground red
-data {
2361 #define file_removed_width 14
2362 #define file_removed_height 15
2363 static unsigned char file_removed_bits
[] = {
2364 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
2365 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
2366 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
2367 } -maskdata $filemask
2369 image create bitmap file_merge
-background white
-foreground blue
-data {
2370 #define file_merge_width 14
2371 #define file_merge_height 15
2372 static unsigned char file_merge_bits
[] = {
2373 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
2374 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
2375 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
2376 } -maskdata $filemask
2378 set ui_index .vpane.files.index.list
2379 set ui_workdir .vpane.files.workdir.list
2381 set all_icons
(_
$ui_index) file_plain
2382 set all_icons
(A
$ui_index) file_fulltick
2383 set all_icons
(M
$ui_index) file_fulltick
2384 set all_icons
(D
$ui_index) file_removed
2385 set all_icons
(U
$ui_index) file_merge
2387 set all_icons
(_
$ui_workdir) file_plain
2388 set all_icons
(M
$ui_workdir) file_mod
2389 set all_icons
(D
$ui_workdir) file_question
2390 set all_icons
(U
$ui_workdir) file_merge
2391 set all_icons
(O
$ui_workdir) file_plain
2393 set max_status_desc
0
2397 {_M
"Modified, not staged"}
2398 {M_
"Staged for commit"}
2399 {MM
"Portions staged for commit"}
2400 {MD
"Staged for commit, missing"}
2402 {_O
"Untracked, not staged"}
2403 {A_
"Staged for commit"}
2404 {AM
"Portions staged for commit"}
2405 {AD
"Staged for commit, missing"}
2408 {D_
"Staged for removal"}
2409 {DO
"Staged for removal, still present"}
2411 {U_
"Requires merge resolution"}
2412 {UU
"Requires merge resolution"}
2413 {UM
"Requires merge resolution"}
2414 {UD
"Requires merge resolution"}
2416 if {$max_status_desc < [string length
[lindex
$i 1]]} {
2417 set max_status_desc
[string length
[lindex
$i 1]]
2419 set all_descs
([lindex
$i 0]) [lindex
$i 1]
2423 ######################################################################
2428 global tcl_platform tk_library
2429 if {[tk windowingsystem
] eq
{aqua
}} {
2435 proc is_Windows
{} {
2437 if {$tcl_platform(platform
) eq
{windows
}} {
2443 proc bind_button3
{w cmd
} {
2444 bind $w <Any-Button-3
> $cmd
2446 bind $w <Control-Button-1
> $cmd
2450 proc incr_font_size
{font
{amt
1}} {
2451 set sz
[font configure
$font -size]
2453 font configure
$font -size $sz
2454 font configure
${font}bold
-size $sz
2457 proc hook_failed_popup
{hook msg
} {
2462 label
$w.m.l1
-text "$hook hook failed:" \
2467 -background white
-borderwidth 1 \
2469 -width 80 -height 10 \
2471 -yscrollcommand [list
$w.m.sby
set]
2473 -text {You must correct the above errors before committing.
} \
2477 scrollbar
$w.m.sby
-command [list
$w.m.t yview
]
2478 pack
$w.m.l1
-side top
-fill x
2479 pack
$w.m.l2
-side bottom
-fill x
2480 pack
$w.m.sby
-side right
-fill y
2481 pack
$w.m.t
-side left
-fill both
-expand 1
2482 pack
$w.m
-side top
-fill both
-expand 1 -padx 5 -pady 10
2484 $w.m.t insert
1.0 $msg
2485 $w.m.t conf
-state disabled
2487 button
$w.ok
-text OK \
2490 -command "destroy $w"
2491 pack
$w.ok
-side bottom
-anchor e
-pady 10 -padx 10
2493 bind $w <Visibility
> "grab $w; focus $w"
2494 bind $w <Key-Return
> "destroy $w"
2495 wm title
$w "[appname] ([reponame]): error"
2499 set next_console_id
0
2501 proc new_console
{short_title long_title
} {
2502 global next_console_id console_data
2503 set w .console
[incr next_console_id
]
2504 set console_data
($w) [list
$short_title $long_title]
2505 return [console_init
$w]
2508 proc console_init
{w
} {
2509 global console_cr console_data M1B
2511 set console_cr
($w) 1.0
2514 label
$w.m.l1
-text "[lindex $console_data($w) 1]:" \
2519 -background white
-borderwidth 1 \
2521 -width 80 -height 10 \
2524 -yscrollcommand [list
$w.m.sby
set]
2525 label
$w.m.s
-text {Working... please
wait...
} \
2529 scrollbar
$w.m.sby
-command [list
$w.m.t yview
]
2530 pack
$w.m.l1
-side top
-fill x
2531 pack
$w.m.s
-side bottom
-fill x
2532 pack
$w.m.sby
-side right
-fill y
2533 pack
$w.m.t
-side left
-fill both
-expand 1
2534 pack
$w.m
-side top
-fill both
-expand 1 -padx 5 -pady 10
2536 menu
$w.ctxm
-tearoff 0
2537 $w.ctxm add
command -label "Copy" \
2539 -command "tk_textCopy $w.m.t"
2540 $w.ctxm add
command -label "Select All" \
2542 -command "$w.m.t tag add sel 0.0 end"
2543 $w.ctxm add
command -label "Copy All" \
2546 $w.m.t tag add sel 0.0 end
2548 $w.m.t tag remove sel 0.0 end
2551 button
$w.ok
-text {Close
} \
2554 -command "destroy $w"
2555 pack
$w.ok
-side bottom
-anchor e
-pady 10 -padx 10
2557 bind_button3
$w.m.t
"tk_popup $w.ctxm %X %Y"
2558 bind $w.m.t
<$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break"
2559 bind $w.m.t
<$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break"
2560 bind $w <Visibility
> "focus $w"
2561 wm title
$w "[appname] ([reponame]): [lindex $console_data($w) 0]"
2565 proc console_exec
{w cmd
{after
{}}} {
2566 # -- Windows tosses the enviroment when we exec our child.
2567 # But most users need that so we have to relogin. :-(
2570 set cmd
[list sh
--login -c "cd \"[pwd]\" && [join $cmd { }]"]
2573 # -- Tcl won't let us redirect both stdout and stderr to
2574 # the same pipe. So pass it through cat...
2576 set cmd
[concat |
$cmd |
& cat]
2578 set fd_f
[open
$cmd r
]
2579 fconfigure
$fd_f -blocking 0 -translation binary
2580 fileevent
$fd_f readable
[list console_read
$w $fd_f $after]
2583 proc console_read
{w fd after
} {
2584 global console_cr console_data
2588 if {![winfo exists
$w]} {console_init
$w}
2589 $w.m.t conf
-state normal
2591 set n
[string length
$buf]
2593 set cr
[string first
"\r" $buf $c]
2594 set lf
[string first
"\n" $buf $c]
2595 if {$cr < 0} {set cr
[expr {$n + 1}]}
2596 if {$lf < 0} {set lf
[expr {$n + 1}]}
2599 $w.m.t insert end
[string range
$buf $c $lf]
2600 set console_cr
($w) [$w.m.t index
{end
-1c}]
2604 $w.m.t delete
$console_cr($w) end
2605 $w.m.t insert end
"\n"
2606 $w.m.t insert end
[string range
$buf $c $cr]
2611 $w.m.t conf
-state disabled
2615 fconfigure
$fd -blocking 1
2617 if {[catch
{close
$fd}]} {
2618 if {![winfo exists
$w]} {console_init
$w}
2619 $w.m.s conf
-background red
-text {Error
: Command Failed
}
2620 $w.ok conf
-state normal
2622 } elseif
{[winfo exists
$w]} {
2623 $w.m.s conf
-background green
-text {Success
}
2624 $w.ok conf
-state normal
2627 array
unset console_cr
$w
2628 array
unset console_data
$w
2630 uplevel
#0 $after $ok
2634 fconfigure
$fd -blocking 0
2637 ######################################################################
2641 set starting_gitk_msg
{Starting gitk... please
wait...
}
2643 proc do_gitk
{revs
} {
2644 global ui_status_value starting_gitk_msg
2652 set cmd
"sh -c \"exec $cmd\""
2656 if {[catch
{eval exec $cmd} err
]} {
2657 error_popup
"Failed to start gitk:\n\n$err"
2659 set ui_status_value
$starting_gitk_msg
2661 if {$ui_status_value eq
$starting_gitk_msg} {
2662 set ui_status_value
{Ready.
}
2669 set w
[new_console
{gc
} {Compressing the object database
}]
2670 console_exec
$w {git gc
}
2673 proc do_fsck_objects
{} {
2674 set w
[new_console
{fsck-objects
} \
2675 {Verifying the object database with fsck-objects
}]
2676 set cmd
[list git fsck-objects
]
2679 lappend cmd
--strict
2680 console_exec
$w $cmd
2686 global ui_comm is_quitting repo_config commit_type
2688 if {$is_quitting} return
2691 # -- Stash our current commit buffer.
2693 set save
[gitdir GITGUI_MSG
]
2694 set msg
[string trim
[$ui_comm get
0.0 end
]]
2695 if {![string match amend
* $commit_type]
2696 && [$ui_comm edit modified
]
2699 set fd
[open
$save w
]
2700 puts
$fd [string trim
[$ui_comm get
0.0 end
]]
2704 catch
{file delete
$save}
2707 # -- Stash our current window geometry into this repository.
2709 set cfg_geometry
[list
]
2710 lappend cfg_geometry
[wm geometry .
]
2711 lappend cfg_geometry
[lindex
[.vpane sash coord
0] 1]
2712 lappend cfg_geometry
[lindex
[.vpane.files sash coord
0] 0]
2713 if {[catch
{set rc_geometry
$repo_config(gui.geometry
)}]} {
2716 if {$cfg_geometry ne
$rc_geometry} {
2717 catch
{exec git repo-config gui.geometry
$cfg_geometry}
2724 rescan
{set ui_status_value
{Ready.
}}
2727 proc unstage_helper
{txt paths
} {
2728 global file_states current_diff_path
2730 if {![lock_index begin-update
]} return
2734 foreach path
$paths {
2735 switch
-glob -- [lindex
$file_states($path) 0] {
2739 lappend pathList
$path
2740 if {$path eq
$current_diff_path} {
2741 set after
{reshow_diff
;}
2746 if {$pathList eq
{}} {
2752 [concat
$after {set ui_status_value
{Ready.
}}]
2756 proc do_unstage_selection
{} {
2757 global current_diff_path selected_paths
2759 if {[array size selected_paths
] > 0} {
2761 {Unstaging selected files from commit
} \
2762 [array names selected_paths
]
2763 } elseif
{$current_diff_path ne
{}} {
2765 "Unstaging [short_path $current_diff_path] from commit" \
2766 [list
$current_diff_path]
2770 proc add_helper
{txt paths
} {
2771 global file_states current_diff_path
2773 if {![lock_index begin-update
]} return
2777 foreach path
$paths {
2778 switch
-glob -- [lindex
$file_states($path) 0] {
2783 lappend pathList
$path
2784 if {$path eq
$current_diff_path} {
2785 set after
{reshow_diff
;}
2790 if {$pathList eq
{}} {
2796 [concat
$after {set ui_status_value
{Ready to commit.
}}]
2800 proc do_add_selection
{} {
2801 global current_diff_path selected_paths
2803 if {[array size selected_paths
] > 0} {
2805 {Adding selected files
} \
2806 [array names selected_paths
]
2807 } elseif
{$current_diff_path ne
{}} {
2809 "Adding [short_path $current_diff_path]" \
2810 [list
$current_diff_path]
2814 proc do_add_all
{} {
2818 foreach path
[array names file_states
] {
2819 switch
-glob -- [lindex
$file_states($path) 0] {
2822 ?D
{lappend paths
$path}
2825 add_helper
{Adding all changed files
} $paths
2828 proc revert_helper
{txt paths
} {
2829 global file_states current_diff_path
2831 if {![lock_index begin-update
]} return
2835 foreach path
$paths {
2836 switch
-glob -- [lindex
$file_states($path) 0] {
2840 lappend pathList
$path
2841 if {$path eq
$current_diff_path} {
2842 set after
{reshow_diff
;}
2848 set n
[llength
$pathList]
2852 } elseif
{$n == 1} {
2853 set s
"[short_path [lindex $pathList]]"
2855 set s
"these $n files"
2858 set reply
[tk_dialog \
2860 "[appname] ([reponame])" \
2861 "Revert changes in $s?
2863 Any unadded changes will be permanently lost by the revert." \
2873 [concat
$after {set ui_status_value
{Ready.
}}]
2879 proc do_revert_selection
{} {
2880 global current_diff_path selected_paths
2882 if {[array size selected_paths
] > 0} {
2884 {Reverting selected files
} \
2885 [array names selected_paths
]
2886 } elseif
{$current_diff_path ne
{}} {
2888 "Reverting [short_path $current_diff_path]" \
2889 [list
$current_diff_path]
2893 proc do_signoff
{} {
2896 set me
[committer_ident
]
2897 if {$me eq
{}} return
2899 set sob
"Signed-off-by: $me"
2900 set last
[$ui_comm get
{end
-1c linestart
} {end
-1c}]
2901 if {$last ne
$sob} {
2902 $ui_comm edit separator
2904 && ![regexp
{^
[A-Z
][A-Za-z
]*-[A-Za-z-
]+: *} $last]} {
2905 $ui_comm insert end
"\n"
2907 $ui_comm insert end
"\n$sob"
2908 $ui_comm edit separator
2913 proc do_select_commit_type
{} {
2914 global commit_type selected_commit_type
2916 if {$selected_commit_type eq
{new
}
2917 && [string match amend
* $commit_type]} {
2919 } elseif
{$selected_commit_type eq
{amend
}
2920 && ![string match amend
* $commit_type]} {
2923 # The amend request was rejected...
2925 if {![string match amend
* $commit_type]} {
2926 set selected_commit_type new
2936 global appvers copyright
2937 global tcl_patchLevel tk_patchLevel
2941 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
2943 label
$w.header
-text "About [appname]" \
2945 pack
$w.header
-side top
-fill x
2948 button
$w.buttons.close
-text {Close
} \
2950 -command [list destroy
$w]
2951 pack
$w.buttons.close
-side right
2952 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
2955 -text "[appname] - a commit creation tool for Git.
2963 pack
$w.desc
-side top
-fill x
-padx 5 -pady 5
2966 append v
"[appname] version $appvers\n"
2967 append v
"[exec git version]\n"
2969 if {$tcl_patchLevel eq
$tk_patchLevel} {
2970 append v
"Tcl/Tk version $tcl_patchLevel"
2972 append v
"Tcl version $tcl_patchLevel"
2973 append v
", Tk version $tk_patchLevel"
2984 pack
$w.vers
-side top
-fill x
-padx 5 -pady 5
2986 menu
$w.ctxm
-tearoff 0
2987 $w.ctxm add
command \
2992 clipboard append -format STRING -type STRING -- \[$w.vers cget -text\]
2995 bind $w <Visibility
> "grab $w; focus $w"
2996 bind $w <Key-Escape
> "destroy $w"
2997 bind_button3
$w.vers
"tk_popup $w.ctxm %X %Y; grab $w; focus $w"
2998 wm title
$w "About [appname]"
3002 proc do_options
{} {
3003 global repo_config global_config font_descs
3004 global repo_config_new global_config_new
3006 array
unset repo_config_new
3007 array
unset global_config_new
3008 foreach name
[array names repo_config
] {
3009 set repo_config_new
($name) $repo_config($name)
3012 foreach name
[array names repo_config
] {
3014 gui.diffcontext
{continue}
3016 set repo_config_new
($name) $repo_config($name)
3018 foreach name
[array names global_config
] {
3019 set global_config_new
($name) $global_config($name)
3022 set w .options_editor
3024 wm geometry
$w "+[winfo rootx .]+[winfo rooty .]"
3026 label
$w.header
-text "[appname] Options" \
3028 pack
$w.header
-side top
-fill x
3031 button
$w.buttons.restore
-text {Restore Defaults
} \
3033 -command do_restore_defaults
3034 pack
$w.buttons.restore
-side left
3035 button
$w.buttons.save
-text Save \
3038 catch {eval \[bind \[focus -displayof $w\] <FocusOut>\]}
3041 pack
$w.buttons.save
-side right
3042 button
$w.buttons.cancel
-text {Cancel
} \
3044 -command [list destroy
$w]
3045 pack
$w.buttons.cancel
-side right
-padx 5
3046 pack
$w.buttons
-side bottom
-fill x
-pady 10 -padx 10
3048 labelframe
$w.repo
-text "[reponame] Repository" \
3050 -relief raised
-borderwidth 2
3051 labelframe
$w.global
-text {Global
(All Repositories
)} \
3053 -relief raised
-borderwidth 2
3054 pack
$w.repo
-side left
-fill both
-expand 1 -pady 5 -padx 5
3055 pack
$w.global
-side right
-fill both
-expand 1 -pady 5 -padx 5
3058 {b pullsummary
{Show Pull Summary
}}
3059 {b trustmtime
{Trust File Modification Timestamps
}}
3060 {i diffcontext
{Number of Diff Context Lines
}}
3061 {t newbranchtemplate
{New Branch Name Template
}}
3063 set type [lindex
$option 0]
3064 set name
[lindex
$option 1]
3065 set text
[lindex
$option 2]
3066 foreach f
{repo global
} {
3069 checkbutton
$w.
$f.
$name -text $text \
3070 -variable ${f}_config_new
(gui.
$name) \
3074 pack
$w.
$f.
$name -side top
-anchor w
3078 label
$w.
$f.
$name.l
-text "$text:" -font font_ui
3079 pack
$w.
$f.
$name.l
-side left
-anchor w
-fill x
3080 spinbox
$w.
$f.
$name.v \
3081 -textvariable ${f}_config_new
(gui.
$name) \
3082 -from 1 -to 99 -increment 1 \
3085 bind $w.
$f.
$name.v
<FocusIn
> {%W selection range
0 end
}
3086 pack
$w.
$f.
$name.v
-side right
-anchor e
-padx 5
3087 pack
$w.
$f.
$name -side top
-anchor w
-fill x
3091 label
$w.
$f.
$name.l
-text "$text:" -font font_ui
3092 text
$w.
$f.
$name.v \
3098 $w.
$f.
$name.v insert
0.0 [set ${f}_config_new
(gui.
$name)]
3099 bind $w.
$f.
$name.v
<Shift-Key-Tab
> {focus
[tk_focusPrev
%W
];break}
3100 bind $w.
$f.
$name.v
<Key-Tab
> {focus
[tk_focusNext
%W
];break}
3101 bind $w.
$f.
$name.v
<Key-Return
> break
3102 bind $w.
$f.
$name.v
<FocusIn
> "$w.$f.$name.v tag add sel 0.0 end"
3103 bind $w.
$f.
$name.v
<FocusOut
> "
3104 set ${f}_config_new(gui.$name) \
3105 \[string trim \[$w.$f.$name.v get 0.0 end\]\]
3107 pack
$w.
$f.
$name.l
-side left
-anchor w
3108 pack
$w.
$f.
$name.v
-side left
-anchor w \
3111 pack
$w.
$f.
$name -side top
-anchor w
-fill x
3117 set all_fonts
[lsort
[font families
]]
3118 foreach option
$font_descs {
3119 set name
[lindex
$option 0]
3120 set font
[lindex
$option 1]
3121 set text
[lindex
$option 2]
3123 set global_config_new
(gui.
$font^^family
) \
3124 [font configure
$font -family]
3125 set global_config_new
(gui.
$font^^size
) \
3126 [font configure
$font -size]
3128 frame
$w.global.
$name
3129 label
$w.global.
$name.l
-text "$text:" -font font_ui
3130 pack
$w.global.
$name.l
-side left
-anchor w
-fill x
3131 eval tk_optionMenu
$w.global.
$name.family \
3132 global_config_new
(gui.
$font^^family
) \
3134 spinbox
$w.global.
$name.size \
3135 -textvariable global_config_new
(gui.
$font^^size
) \
3136 -from 2 -to 80 -increment 1 \
3139 bind $w.global.
$name.size
<FocusIn
> {%W selection range
0 end
}
3140 pack
$w.global.
$name.size
-side right
-anchor e
3141 pack
$w.global.
$name.family
-side right
-anchor e
3142 pack
$w.global.
$name -side top
-anchor w
-fill x
3145 bind $w <Visibility
> "grab $w; focus $w"
3146 bind $w <Key-Escape
> "destroy $w"
3147 wm title
$w "[appname] ([reponame]): Options"
3151 proc do_restore_defaults
{} {
3152 global font_descs default_config repo_config
3153 global repo_config_new global_config_new
3155 foreach name
[array names default_config
] {
3156 set repo_config_new
($name) $default_config($name)
3157 set global_config_new
($name) $default_config($name)
3160 foreach option
$font_descs {
3161 set name
[lindex
$option 0]
3162 set repo_config
(gui.
$name) $default_config(gui.
$name)
3166 foreach option
$font_descs {
3167 set name
[lindex
$option 0]
3168 set font
[lindex
$option 1]
3169 set global_config_new
(gui.
$font^^family
) \
3170 [font configure
$font -family]
3171 set global_config_new
(gui.
$font^^size
) \
3172 [font configure
$font -size]
3176 proc do_save_config
{w
} {
3177 if {[catch
{save_config
} err
]} {
3178 error_popup
"Failed to completely save options:\n\n$err"
3184 proc do_windows_shortcut
{} {
3188 set desktop
[exec cygpath \
3196 set fn
[tk_getSaveFile \
3198 -title "[appname] ([reponame]): Create Desktop Icon" \
3199 -initialdir $desktop \
3200 -initialfile "Git [reponame].bat"]
3204 set sh
[exec cygpath \
3208 set me
[exec cygpath \
3212 set gd
[exec cygpath \
3216 set gw
[exec cygpath \
3219 [file dirname [gitdir
]]]
3220 regsub
-all ' $me "'\\''" me
3221 regsub -all ' $gd "'\\''" gd
3222 puts $fd "@ECHO Entering $gw"
3223 puts $fd "@ECHO Starting git-gui... please wait..."
3224 puts -nonewline $fd "@\"$sh\" --login -c \""
3225 puts -nonewline $fd "GIT_DIR='$gd'"
3226 puts -nonewline $fd " '$me'"
3230 error_popup "Cannot write script:\n\n$err"
3235 proc do_macosx_app {} {
3238 set fn [tk_getSaveFile \
3240 -title "[appname] ([reponame]): Create Desktop Icon" \
3241 -initialdir [file join $env(HOME) Desktop] \
3242 -initialfile "Git [reponame].app"]
3245 set Contents [file join $fn Contents]
3246 set MacOS [file join $Contents MacOS]
3247 set exe [file join $MacOS git-gui]
3251 set fd [open [file join $Contents Info.plist] w]
3252 puts $fd {<?xml version="1.0" encoding="UTF-8"?>
3253 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3254 <plist version="1.0">
3256 <key>CFBundleDevelopmentRegion</key>
3257 <string>English</string>
3258 <key>CFBundleExecutable</key>
3259 <string>git-gui</string>
3260 <key>CFBundleIdentifier</key>
3261 <string>org.spearce.git-gui</string>
3262 <key>CFBundleInfoDictionaryVersion</key>
3263 <string>6.0</string>
3264 <key>CFBundlePackageType</key>
3265 <string>APPL</string>
3266 <key>CFBundleSignature</key>
3267 <string>????</string>
3268 <key>CFBundleVersion</key>
3269 <string>1.0</string>
3270 <key>NSPrincipalClass</key>
3271 <string>NSApplication</string>
3276 set fd [open $exe w]
3277 set gd [file normalize [gitdir]]
3278 set ep [file normalize [exec git --exec-path]]
3279 regsub -all ' $gd "'\\''" gd
3280 regsub
-all ' $ep "'\\''" ep
3281 puts $fd "#!/bin/sh"
3282 foreach name
[array names env
] {
3283 if {[string match GIT_
* $name]} {
3284 regsub
-all ' $env($name) "'\\''" v
3285 puts $fd "export $name='$v'"
3288 puts $fd "export PATH
='$ep':\
$PATH"
3289 puts $fd "export GIT_DIR
='$gd'"
3290 puts $fd "exec [file normalize
$argv0]"
3293 file attributes $exe -permissions u+x,g+x,o+x
3295 error_popup "Cannot
write icon
:\n\n$err"
3300 proc toggle_or_diff {w x y} {
3301 global file_states file_lists current_diff_path ui_index ui_workdir
3302 global last_clicked selected_paths
3304 set pos [split [$w index @$x,$y] .]
3305 set lno [lindex $pos 0]
3306 set col [lindex $pos 1]
3307 set path [lindex $file_lists($w) [expr {$lno - 1}]]
3313 set last_clicked [list $w $lno]
3314 array unset selected_paths
3315 $ui_index tag remove in_sel 0.0 end
3316 $ui_workdir tag remove in_sel 0.0 end
3319 if {$current_diff_path eq $path} {
3320 set after {reshow_diff;}
3324 if {$w eq $ui_index} {
3326 "Unstaging
[short_path
$path] from commit
" \
3328 [concat $after {set ui_status_value {Ready.}}]
3329 } elseif {$w eq $ui_workdir} {
3331 "Adding
[short_path
$path]" \
3333 [concat $after {set ui_status_value {Ready.}}]
3336 show_diff $path $w $lno
3340 proc add_one_to_selection {w x y} {
3341 global file_lists last_clicked selected_paths
3343 set lno [lindex [split [$w index @$x,$y] .] 0]
3344 set path [lindex $file_lists($w) [expr {$lno - 1}]]
3350 if {$last_clicked ne {}
3351 && [lindex $last_clicked 0] ne $w} {
3352 array unset selected_paths
3353 [lindex $last_clicked 0] tag remove in_sel 0.0 end
3356 set last_clicked [list $w $lno]
3357 if {[catch {set in_sel $selected_paths($path)}]} {
3361 unset selected_paths($path)
3362 $w tag remove in_sel $lno.0 [expr {$lno + 1}].0
3364 set selected_paths($path) 1
3365 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
3369 proc add_range_to_selection {w x y} {
3370 global file_lists last_clicked selected_paths
3372 if {[lindex $last_clicked 0] ne $w} {
3373 toggle_or_diff $w $x $y
3377 set lno [lindex [split [$w index @$x,$y] .] 0]
3378 set lc [lindex $last_clicked 1]
3387 foreach path [lrange $file_lists($w) \
3388 [expr {$begin - 1}] \
3389 [expr {$end - 1}]] {
3390 set selected_paths($path) 1
3392 $w tag add in_sel $begin.0 [expr {$end + 1}].0
3395 ######################################################################
3399 set cursor_ptr arrow
3400 font create font_diff -family Courier -size 10
3404 eval font configure font_ui [font actual [.dummy cget -font]]
3408 font create font_uibold
3409 font create font_diffbold
3414 } elseif {[is_MacOSX]} {
3422 proc apply_config {} {
3423 global repo_config font_descs
3425 foreach option $font_descs {
3426 set name [lindex $option 0]
3427 set font [lindex $option 1]
3429 foreach {cn cv} $repo_config(gui.$name) {
3430 font configure $font $cn $cv
3433 error_popup "Invalid font specified
in gui.
$name:\n\n$err"
3435 foreach {cn cv} [font configure $font] {
3436 font configure ${font}bold $cn $cv
3438 font configure ${font}bold -weight bold
3442 set default_config(gui.trustmtime) false
3443 set default_config(gui.pullsummary) true
3444 set default_config(gui.diffcontext) 5
3445 set default_config(gui.newbranchtemplate) {}
3446 set default_config(gui.fontui) [font configure font_ui]
3447 set default_config(gui.fontdiff) [font configure font_diff]
3449 {fontui font_ui {Main Font}}
3450 {fontdiff font_diff {Diff/Console Font}}
3455 ######################################################################
3461 menu .mbar -tearoff 0
3462 .mbar add cascade -label Repository -menu .mbar.repository
3463 .mbar add cascade -label Edit -menu .mbar.edit
3464 if {!$single_commit} {
3465 .mbar add cascade -label Branch -menu .mbar.branch
3467 .mbar add cascade -label Commit -menu .mbar.commit
3468 if {!$single_commit} {
3469 .mbar add cascade -label Fetch -menu .mbar.fetch
3470 .mbar add cascade -label Pull -menu .mbar.pull
3471 .mbar add cascade -label Push -menu .mbar.push
3473 . configure -menu .mbar
3475 # -- Repository Menu
3477 menu .mbar.repository
3478 .mbar.repository add command \
3479 -label {Visualize Current Branch} \
3480 -command {do_gitk {}} \
3483 .mbar.repository add command \
3484 -label {Visualize All Branches} \
3485 -command {do_gitk {--all}} \
3488 .mbar.repository add separator
3490 if {!$single_commit} {
3491 .mbar.repository add command -label {Compress Database} \
3495 .mbar.repository add command -label {Verify Database} \
3496 -command do_fsck_objects \
3499 .mbar.repository add separator
3502 .mbar.repository add command \
3503 -label {Create Desktop Icon} \
3504 -command do_windows_shortcut \
3506 } elseif {[is_MacOSX]} {
3507 .mbar.repository add command \
3508 -label {Create Desktop Icon} \
3509 -command do_macosx_app \
3514 .mbar.repository add command -label Quit \
3516 -accelerator $M1T-Q \
3522 .mbar.edit add command -label Undo \
3523 -command {catch {[focus] edit undo}} \
3524 -accelerator $M1T-Z \
3526 .mbar.edit add command -label Redo \
3527 -command {catch {[focus] edit redo}} \
3528 -accelerator $M1T-Y \
3530 .mbar.edit add separator
3531 .mbar.edit add command -label Cut \
3532 -command {catch {tk_textCut [focus]}} \
3533 -accelerator $M1T-X \
3535 .mbar.edit add command -label Copy \
3536 -command {catch {tk_textCopy [focus]}} \
3537 -accelerator $M1T-C \
3539 .mbar.edit add command -label Paste \
3540 -command {catch {tk_textPaste [focus]; [focus] see insert}} \
3541 -accelerator $M1T-V \
3543 .mbar.edit add command -label Delete \
3544 -command {catch {[focus] delete sel.first sel.last}} \
3547 .mbar.edit add separator
3548 .mbar.edit add command -label {Select All} \
3549 -command {catch {[focus] tag add sel 0.0 end}} \
3550 -accelerator $M1T-A \
3555 if {!$single_commit} {
3558 .mbar.branch add command -label {Create...} \
3559 -command do_create_branch \
3560 -accelerator $M1T-N \
3562 lappend disable_on_lock [list .mbar.branch entryconf \
3563 [.mbar.branch index last] -state]
3565 .mbar.branch add command -label {Delete...} \
3566 -command do_delete_branch \
3568 lappend disable_on_lock [list .mbar.branch entryconf \
3569 [.mbar.branch index last] -state]
3576 .mbar.commit add radiobutton \
3577 -label {New Commit} \
3578 -command do_select_commit_type \
3579 -variable selected_commit_type \
3582 lappend disable_on_lock \
3583 [list .mbar.commit entryconf [.mbar.commit index last] -state]
3585 .mbar.commit add radiobutton \
3586 -label {Amend Last Commit} \
3587 -command do_select_commit_type \
3588 -variable selected_commit_type \
3591 lappend disable_on_lock \
3592 [list .mbar.commit entryconf [.mbar.commit index last] -state]
3594 .mbar.commit add separator
3596 .mbar.commit add command -label Rescan \
3597 -command do_rescan \
3600 lappend disable_on_lock \
3601 [list .mbar.commit entryconf [.mbar.commit index last] -state]
3603 .mbar.commit add command -label {Add To Commit} \
3604 -command do_add_selection \
3606 lappend disable_on_lock \
3607 [list .mbar.commit entryconf [.mbar.commit index last] -state]
3609 .mbar.commit add command -label {Add All To Commit} \
3610 -command do_add_all \
3611 -accelerator $M1T-I \
3613 lappend disable_on_lock \
3614 [list .mbar.commit entryconf [.mbar.commit index last] -state]
3616 .mbar.commit add command -label {Unstage From Commit} \
3617 -command do_unstage_selection \
3619 lappend disable_on_lock \
3620 [list .mbar.commit entryconf [.mbar.commit index last] -state]
3622 .mbar.commit add command -label {Revert Changes} \
3623 -command do_revert_selection \
3625 lappend disable_on_lock \
3626 [list .mbar.commit entryconf [.mbar.commit index last] -state]
3628 .mbar.commit add separator
3630 .mbar.commit add command -label {Sign Off} \
3631 -command do_signoff \
3632 -accelerator $M1T-S \
3635 .mbar.commit add command -label Commit \
3636 -command do_commit \
3637 -accelerator $M1T-Return \
3639 lappend disable_on_lock \
3640 [list .mbar.commit entryconf [.mbar.commit index last] -state]
3642 # -- Transport menus
3644 if {!$single_commit} {
3651 # -- Apple Menu (Mac OS X only)
3653 .mbar add cascade -label Apple -menu .mbar.apple
3656 .mbar.apple add command -label "About
[appname
]" \
3659 .mbar.apple add command -label "[appname
] Options...
" \
3660 -command do_options \
3665 .mbar.edit add separator
3666 .mbar.edit add command -label {Options...} \
3667 -command do_options \
3672 if {[file exists /usr/local/miga/lib/gui-miga]
3673 && [file exists .pvcsrc]} {
3675 global ui_status_value
3676 if {![lock_index update]} return
3677 set cmd [list sh --login -c "/usr
/local
/miga
/lib
/gui-miga
\"[pwd]\""]
3678 set miga_fd [open "|
$cmd" r]
3679 fconfigure $miga_fd -blocking 0
3680 fileevent $miga_fd readable [list miga_done $miga_fd]
3681 set ui_status_value {Running miga...}
3683 proc miga_done {fd} {
3688 rescan [list set ui_status_value {Ready.}]
3691 .mbar add cascade -label Tools -menu .mbar.tools
3693 .mbar.tools add command -label "Migrate
" \
3696 lappend disable_on_lock \
3697 [list .mbar.tools entryconf [.mbar.tools index last] -state]
3702 .mbar add cascade -label Help -menu .mbar.help
3705 .mbar.help add command -label "About
[appname
]" \
3717 -text {Current Branch:} \
3722 -textvariable current_branch \
3726 pack .branch.l1 -side left
3727 pack .branch.cb -side left -fill x
3728 pack .branch -side top -fill x
3730 # -- Main Window Layout
3732 panedwindow .vpane -orient vertical
3733 panedwindow .vpane.files -orient horizontal
3734 .vpane add .vpane.files -sticky nsew -height 100 -width 200
3735 pack .vpane -anchor n -side top -fill both -expand 1
3737 # -- Index File List
3739 frame .vpane.files.index -height 100 -width 200
3740 label .vpane.files.index.title -text {Changes To Be Committed} \
3743 text $ui_index -background white -borderwidth 0 \
3744 -width 20 -height 10 \
3747 -cursor $cursor_ptr \
3748 -xscrollcommand {.vpane.files.index.sx set} \
3749 -yscrollcommand {.vpane.files.index.sy set} \
3751 scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview]
3752 scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview]
3753 pack .vpane.files.index.title -side top -fill x
3754 pack .vpane.files.index.sx -side bottom -fill x
3755 pack .vpane.files.index.sy -side right -fill y
3756 pack $ui_index -side left -fill both -expand 1
3757 .vpane.files add .vpane.files.index -sticky nsew
3759 # -- Working Directory File List
3761 frame .vpane.files.workdir -height 100 -width 200
3762 label .vpane.files.workdir.title -text {Changed But Not Updated} \
3765 text $ui_workdir -background white -borderwidth 0 \
3766 -width 20 -height 10 \
3769 -cursor $cursor_ptr \
3770 -xscrollcommand {.vpane.files.workdir.sx set} \
3771 -yscrollcommand {.vpane.files.workdir.sy set} \
3773 scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
3774 scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
3775 pack .vpane.files.workdir.title -side top -fill x
3776 pack .vpane.files.workdir.sx -side bottom -fill x
3777 pack .vpane.files.workdir.sy -side right -fill y
3778 pack $ui_workdir -side left -fill both -expand 1
3779 .vpane.files add .vpane.files.workdir -sticky nsew
3781 foreach i [list $ui_index $ui_workdir] {
3782 $i tag conf in_diff -font font_uibold
3783 $i tag conf in_sel \
3784 -background [$i cget -foreground] \
3785 -foreground [$i cget -background]
3789 # -- Diff and Commit Area
3791 frame .vpane.lower -height 300 -width 400
3792 frame .vpane.lower.commarea
3793 frame .vpane.lower.diff -relief sunken -borderwidth 1
3794 pack .vpane.lower.commarea -side top -fill x
3795 pack .vpane.lower.diff -side bottom -fill both -expand 1
3796 .vpane add .vpane.lower -stick nsew
3798 # -- Commit Area Buttons
3800 frame .vpane.lower.commarea.buttons
3801 label .vpane.lower.commarea.buttons.l -text {} \
3805 pack .vpane.lower.commarea.buttons.l -side top -fill x
3806 pack .vpane.lower.commarea.buttons -side left -fill y
3808 button .vpane.lower.commarea.buttons.rescan -text {Rescan} \
3809 -command do_rescan \
3811 pack .vpane.lower.commarea.buttons.rescan -side top -fill x
3812 lappend disable_on_lock \
3813 {.vpane.lower.commarea.buttons.rescan conf -state}
3815 button .vpane.lower.commarea.buttons.incall -text {Add All} \
3816 -command do_add_all \
3818 pack .vpane.lower.commarea.buttons.incall -side top -fill x
3819 lappend disable_on_lock \
3820 {.vpane.lower.commarea.buttons.incall conf -state}
3822 button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \
3823 -command do_signoff \
3825 pack .vpane.lower.commarea.buttons.signoff -side top -fill x
3827 button .vpane.lower.commarea.buttons.commit -text {Commit} \
3828 -command do_commit \
3830 pack .vpane.lower.commarea.buttons.commit -side top -fill x
3831 lappend disable_on_lock \
3832 {.vpane.lower.commarea.buttons.commit conf -state}
3834 # -- Commit Message Buffer
3836 frame .vpane.lower.commarea.buffer
3837 frame .vpane.lower.commarea.buffer.header
3838 set ui_comm .vpane.lower.commarea.buffer.t
3839 set ui_coml .vpane.lower.commarea.buffer.header.l
3840 radiobutton .vpane.lower.commarea.buffer.header.new \
3841 -text {New Commit} \
3842 -command do_select_commit_type \
3843 -variable selected_commit_type \
3846 lappend disable_on_lock \
3847 [list .vpane.lower.commarea.buffer.header.new conf -state]
3848 radiobutton .vpane.lower.commarea.buffer.header.amend \
3849 -text {Amend Last Commit} \
3850 -command do_select_commit_type \
3851 -variable selected_commit_type \
3854 lappend disable_on_lock \
3855 [list .vpane.lower.commarea.buffer.header.amend conf -state]
3860 proc trace_commit_type {varname args} {
3861 global ui_coml commit_type
3862 switch -glob -- $commit_type {
3863 initial {set txt {Initial Commit Message:}}
3864 amend {set txt {Amended Commit Message:}}
3865 amend-initial {set txt {Amended Initial Commit Message:}}
3866 amend-merge {set txt {Amended Merge Commit Message:}}
3867 merge {set txt {Merge Commit Message:}}
3868 * {set txt {Commit Message:}}
3870 $ui_coml conf -text $txt
3872 trace add variable commit_type write trace_commit_type
3873 pack $ui_coml -side left -fill x
3874 pack .vpane.lower.commarea.buffer.header.amend -side right
3875 pack .vpane.lower.commarea.buffer.header.new -side right
3877 text $ui_comm -background white -borderwidth 1 \
3880 -autoseparators true \
3882 -width 75 -height 9 -wrap none \
3884 -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
3885 scrollbar .vpane.lower.commarea.buffer.sby \
3886 -command [list $ui_comm yview]
3887 pack .vpane.lower.commarea.buffer.header -side top -fill x
3888 pack .vpane.lower.commarea.buffer.sby -side right -fill y
3889 pack $ui_comm -side left -fill y
3890 pack .vpane.lower.commarea.buffer -side left -fill y
3892 # -- Commit Message Buffer Context Menu
3894 set ctxm .vpane.lower.commarea.buffer.ctxm
3895 menu $ctxm -tearoff 0
3899 -command {tk_textCut $ui_comm}
3903 -command {tk_textCopy $ui_comm}
3907 -command {tk_textPaste $ui_comm}
3911 -command {$ui_comm delete sel.first sel.last}
3914 -label {Select All} \
3916 -command {$ui_comm tag add sel 0.0 end}
3921 $ui_comm tag add sel 0.0 end
3922 tk_textCopy $ui_comm
3923 $ui_comm tag remove sel 0.0 end
3930 bind_button3 $ui_comm "tk_popup
$ctxm %X
%Y
"
3934 set current_diff_path {}
3935 set diff_actions [list]
3936 proc trace_current_diff_path {varname args} {
3937 global current_diff_path diff_actions file_states
3938 if {$current_diff_path eq {}} {
3944 set p $current_diff_path
3945 set s [mapdesc [lindex $file_states($p) 0] $p]
3947 set p [escape_path $p]
3951 .vpane.lower.diff.header.status configure -text $s
3952 .vpane.lower.diff.header.file configure -text $f
3953 .vpane.lower.diff.header.path configure -text $p
3954 foreach w $diff_actions {
3958 trace add variable current_diff_path write trace_current_diff_path
3960 frame .vpane.lower.diff.header -background orange
3961 label .vpane.lower.diff.header.status \
3962 -background orange \
3963 -width $max_status_desc \
3967 label .vpane.lower.diff.header.file \
3968 -background orange \
3972 label .vpane.lower.diff.header.path \
3973 -background orange \
3977 pack .vpane.lower.diff.header.status -side left
3978 pack .vpane.lower.diff.header.file -side left
3979 pack .vpane.lower.diff.header.path -fill x
3980 set ctxm .vpane.lower.diff.header.ctxm
3981 menu $ctxm -tearoff 0
3990 -- $current_diff_path
3992 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3993 bind_button3 .vpane.lower.diff.header.path "tk_popup
$ctxm %X
%Y
"
3997 frame .vpane.lower.diff.body
3998 set ui_diff .vpane.lower.diff.body.t
3999 text $ui_diff -background white -borderwidth 0 \
4000 -width 80 -height 15 -wrap none \
4002 -xscrollcommand {.vpane.lower.diff.body.sbx set} \
4003 -yscrollcommand {.vpane.lower.diff.body.sby set} \
4005 scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
4006 -command [list $ui_diff xview]
4007 scrollbar .vpane.lower.diff.body.sby -orient vertical \
4008 -command [list $ui_diff yview]
4009 pack .vpane.lower.diff.body.sbx -side bottom -fill x
4010 pack .vpane.lower.diff.body.sby -side right -fill y
4011 pack $ui_diff -side left -fill both -expand 1
4012 pack .vpane.lower.diff.header -side top -fill x
4013 pack .vpane.lower.diff.body -side bottom -fill both -expand 1
4015 $ui_diff tag conf d_@ -foreground blue -font font_diffbold
4016 $ui_diff tag conf d_+ -foreground {#00a000}
4017 $ui_diff tag conf d_- -foreground red
4019 $ui_diff tag conf d_++ -foreground {#00a000}
4020 $ui_diff tag conf d_-- -foreground red
4021 $ui_diff tag conf d_+s \
4022 -foreground {#00a000} \
4023 -background {#e2effa}
4024 $ui_diff tag conf d_-s \
4026 -background {#e2effa}
4027 $ui_diff tag conf d_s+ \
4028 -foreground {#00a000} \
4030 $ui_diff tag conf d_s- \
4034 $ui_diff tag conf d<<<<<<< \
4035 -foreground orange \
4037 $ui_diff tag conf d======= \
4038 -foreground orange \
4040 $ui_diff tag conf d>>>>>>> \
4041 -foreground orange \
4044 $ui_diff tag raise sel
4046 # -- Diff Body Context Menu
4048 set ctxm .vpane.lower.diff.body.ctxm
4049 menu $ctxm -tearoff 0
4053 -command reshow_diff
4057 -command {tk_textCopy $ui_diff}
4058 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
4060 -label {Select All} \
4062 -command {$ui_diff tag add sel 0.0 end}
4063 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
4068 $ui_diff tag add sel 0.0 end
4069 tk_textCopy $ui_diff
4070 $ui_diff tag remove sel 0.0 end
4072 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
4075 -label {Decrease Font Size} \
4077 -command {incr_font_size font_diff -1}
4078 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
4080 -label {Increase Font Size} \
4082 -command {incr_font_size font_diff 1}
4083 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
4086 -label {Show Less Context} \
4088 -command {if {$repo_config(gui.diffcontext) >= 2} {
4089 incr repo_config(gui.diffcontext) -1
4092 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
4094 -label {Show More Context} \
4097 incr repo_config(gui.diffcontext)
4100 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
4102 $ctxm add command -label {Options...} \
4105 bind_button3 $ui_diff "tk_popup
$ctxm %X
%Y
"
4109 set ui_status_value {Initializing...}
4110 label .status -textvariable ui_status_value \
4116 pack .status -anchor w -side bottom -fill x
4121 set gm $repo_config(gui.geometry)
4122 wm geometry . [lindex $gm 0]
4123 .vpane sash place 0 \
4124 [lindex [.vpane sash coord 0] 0] \
4126 .vpane.files sash place 0 \
4128 [lindex [.vpane.files sash coord 0] 1]
4134 bind $ui_comm <$M1B-Key-Return> {do_commit;break}
4135 bind $ui_comm <$M1B-Key-i> {do_add_all;break}
4136 bind $ui_comm <$M1B-Key-I> {do_add_all;break}
4137 bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
4138 bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
4139 bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
4140 bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
4141 bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
4142 bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
4143 bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
4144 bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
4146 bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
4147 bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
4148 bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
4149 bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
4150 bind $ui_diff <$M1B-Key-v> {break}
4151 bind $ui_diff <$M1B-Key-V> {break}
4152 bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
4153 bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
4154 bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
4155 bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
4156 bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
4157 bind $ui_diff <Key-Right> {catch {%W xview scroll 1 units};break}
4159 if {!$single_commit} {
4160 bind . <$M1B-Key-n> do_create_branch
4161 bind . <$M1B-Key-N> do_create_branch
4164 bind . <Destroy> do_quit
4165 bind all <Key-F5> do_rescan
4166 bind all <$M1B-Key-r> do_rescan
4167 bind all <$M1B-Key-R> do_rescan
4168 bind . <$M1B-Key-s> do_signoff
4169 bind . <$M1B-Key-S> do_signoff
4170 bind . <$M1B-Key-i> do_add_all
4171 bind . <$M1B-Key-I> do_add_all
4172 bind . <$M1B-Key-Return> do_commit
4173 bind all <$M1B-Key-q> do_quit
4174 bind all <$M1B-Key-Q> do_quit
4175 bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
4176 bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
4177 foreach i [list $ui_index $ui_workdir] {
4178 bind $i <Button-1> "toggle_or_diff
$i %x
%y
; break"
4179 bind $i <$M1B-Button-1> "add_one_to_selection
$i %x
%y
; break"
4180 bind $i <Shift-Button-1> "add_range_to_selection
$i %x
%y
; break"
4184 set file_lists($ui_index) [list]
4185 set file_lists($ui_workdir) [list]
4189 set MERGE_HEAD [list]
4192 set current_branch {}
4193 set current_diff_path {}
4194 set selected_commit_type new
4196 wm title . "[appname
] ([file normalize
[file dirname [gitdir
]]])"
4197 focus -force $ui_comm
4199 # -- Warn the user about environmental problems. Cygwin's Tcl
4200 # does *not* pass its env array onto any processes it spawns.
4201 # This means that git processes get none of our environment.
4206 set msg "Possible environment issues exist.
4208 The following environment variables are probably
4209 going to be ignored by any Git subprocess run
4213 foreach name [array names env] {
4214 switch -regexp -- $name {
4215 {^GIT_INDEX_FILE$} -
4216 {^GIT_OBJECT_DIRECTORY$} -
4217 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} -
4219 {^GIT_EXTERNAL_DIFF$} -
4223 {^GIT_CONFIG_LOCAL$} -
4224 {^GIT_(AUTHOR|COMMITTER)_DATE$} {
4225 append msg " - $name\n"
4228 {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} {
4229 append msg " - $name\n"
4231 set suggest_user $name
4235 if {$ignored_env > 0} {
4237 This is due to a known issue with the
4238 Tcl binary distributed by Cygwin.
"
4240 if {$suggest_user ne {}} {
4243 A good replacement
for $suggest_user
4244 is placing values
for the user.name and
4245 user.email settings into your personal
4251 unset ignored_env msg suggest_user name
4254 # -- Only initialize complex UI if we are going to stay running.
4256 if {!$single_commit} {
4260 populate_branch_menu
4261 populate_fetch_menu .mbar.fetch
4262 populate_pull_menu .mbar.pull
4263 populate_push_menu .mbar.push
4266 # -- Only suggest a gc run if we are going to stay running.
4268 if {!$single_commit} {
4269 set object_limit 2000
4270 if {[is_Windows]} {set object_limit 200}
4271 regexp {^([0-9]+) objects,} [exec git count-objects] _junk objects_current
4272 if {$objects_current >= $object_limit} {
4274 "This repository currently has
$objects_current loose objects.
4276 To maintain optimal performance it is strongly
4277 recommended that you
compress the database
4278 when
more than
$object_limit loose objects exist.
4280 Compress the database now?
"] eq yes} {
4284 unset object_limit _junk objects_current
4287 lock_index begin-read