# Tcl ignores the next line -*- tcl -*- \
exec wish "$0" -- "$@"
-# Copyright © 2005-2008 Paul Mackerras. All rights reserved.
+# Copyright © 2005-2009 Paul Mackerras. All rights reserved.
# This program is free software; it may be used, copied, modified
# and distributed under the terms of the GNU General Public Licence,
# either version 2, or (at your option) any later version.
"--until=*" - "--before=*" - "--max-age=*" - "--min-age=*" -
"--author=*" - "--committer=*" - "--grep=*" - "-[iE]" -
"--remove-empty" - "--first-parent" - "--cherry-pick" -
- "-S*" - "--pickaxe-all" - "--pickaxe-regex" {
+ "-S*" - "--pickaxe-all" - "--pickaxe-regex" -
+ "--simplify-by-decoration" {
# These mean that we get a subset of the commits
set filtered 1
lappend glflags $arg
if {$sdm != 2} {
lappend ret $id
} else {
- lset ret end [lindex $ret end]...$id
+ lset ret end $id...[lindex $ret end]
}
lappend pos $id
}
drawvisible
}
+proc real_children {vp} {
+ global children nullid nullid2
+
+ set kids {}
+ foreach id $children($vp) {
+ if {$id ne $nullid && $id ne $nullid2} {
+ lappend kids $id
+ }
+ }
+ return $kids
+}
+
proc first_real_child {vp} {
global children nullid nullid2
global tagids idtags headids idheads tagobjid
global otherrefids idotherrefs mainhead mainheadid
global selecthead selectheadid
+ global hideremotes
foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
catch {unset $v}
if {![string match "refs/*" $ref]} continue
set name [string range $ref 5 end]
if {[string match "remotes/*" $name]} {
- if {![string match "*/HEAD" $name]} {
+ if {![string match "*/HEAD" $name] && !$hideremotes} {
set headids($name) $id
lappend idheads($id) $name
}
proc show_error {w top msg} {
global NS
+ if {![info exists NS]} {set NS ""}
if {[wm state $top] eq "withdrawn"} { wm deiconify $top }
message $w.m -text $msg -justify center -aspect 400
pack $w.m -side top -fill x -padx 20 -pady 20
proc makedroplist {w varname args} {
global use_ttk
if {$use_ttk} {
- set gm [ttk::combobox $w -width 10 -state readonly\
+ set width 0
+ foreach label $args {
+ set cx [string length $label]
+ if {$cx > $width} {set width $cx}
+ }
+ set gm [ttk::combobox $w -width $width -state readonly\
-textvariable $varname -values $args]
} else {
set gm [eval [linsert $args 0 tk_optionMenu $w $varname]]
pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \
-side left -fill y
set gdttype [mc "containing:"]
- set gm [makedroplist .tf.lbar.gdttype gdtype \
+ set gm [makedroplist .tf.lbar.gdttype gdttype \
[mc "containing:"] \
[mc "touching paths:"] \
[mc "adding/removing string:"]]
${NS}::label .bleft.mid.labeldiffcontext -text " [mc "Lines of context"]: "
pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left
spinbox .bleft.mid.diffcontext -width 5 -font textfont \
- -from 1 -increment 1 -to 10000000 \
+ -from 0 -increment 1 -to 10000000 \
-validate all -validatecommand "diffcontextvalidate %P" \
-textvariable diffcontextstring
.bleft.mid.diffcontext set $diffcontext
global cmitmode wrapcomment datetimeformat limitdiffs
global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
global autoselect extdifftool perfile_attrs markbgcolor use_ttk
+ global hideremotes want_ttk
if {$stuffsaved} return
if {![winfo viewable .]} return
puts $f [list set wrapcomment $wrapcomment]
puts $f [list set autoselect $autoselect]
puts $f [list set showneartags $showneartags]
+ puts $f [list set hideremotes $hideremotes]
puts $f [list set showlocalchanges $showlocalchanges]
puts $f [list set datetimeformat $datetimeformat]
puts $f [list set limitdiffs $limitdiffs]
+ puts $f [list set want_ttk $want_ttk]
puts $f [list set bgcolor $bgcolor]
puts $f [list set fgcolor $fgcolor]
puts $f [list set colors $colors]
set gdttype [mc "touching paths:"]
}
+proc gitknewtmpdir {} {
+ global diffnum gitktmpdir gitdir
+
+ if {![info exists gitktmpdir]} {
+ set gitktmpdir [file join [file dirname $gitdir] \
+ [format ".gitk-tmp.%s" [pid]]]
+ if {[catch {file mkdir $gitktmpdir} err]} {
+ error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
+ unset gitktmpdir
+ return {}
+ }
+ set diffnum 0
+ }
+ incr diffnum
+ set diffdir [file join $gitktmpdir $diffnum]
+ if {[catch {file mkdir $diffdir} err]} {
+ error_popup "[mc "Error creating temporary directory %s:" $diffdir] $err"
+ return {}
+ }
+ return $diffdir
+}
+
proc save_file_from_commit {filename output what} {
global nullfile
}
proc external_diff {} {
- global gitktmpdir nullid nullid2
+ global nullid nullid2
global flist_menu_file
global diffids
- global diffnum
- global gitdir extdifftool
+ global extdifftool
if {[llength $diffids] == 1} {
# no reference commit given
}
# make sure that several diffs wont collide
- if {![info exists gitktmpdir]} {
- set gitktmpdir [file join [file dirname $gitdir] \
- [format ".gitk-tmp.%s" [pid]]]
- if {[catch {file mkdir $gitktmpdir} err]} {
- error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
- unset gitktmpdir
- return
- }
- set diffnum 0
- }
- incr diffnum
- set diffdir [file join $gitktmpdir $diffnum]
- if {[catch {file mkdir $diffdir} err]} {
- error_popup "[mc "Error creating temporary directory %s:" $diffdir] $err"
- return
- }
+ set diffdir [gitknewtmpdir]
+ if {$diffdir eq {}} return
# gather files to diff
set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir]
}
set known_view_options {
- {perm b . {} {mc "Remember this view"}}
- {args t50= + {} {mc "Commits to include (arguments to git log):"}}
- {all b * "--all" {mc "Use all refs"}}
- {dorder b . {"--date-order" "-d"} {mc "Strictly sort by date"}}
- {lright b . "--left-right" {mc "Mark branch sides"}}
- {since t15 + {"--since=*" "--after=*"} {mc "Since date:"}}
- {until t15 . {"--until=*" "--before=*"} {mc "Until date:"}}
- {limit t10 + "--max-count=*" {mc "Max count:"}}
- {skip t10 . "--skip=*" {mc "Skip:"}}
- {first b . "--first-parent" {mc "Limit to first parent"}}
- {cmd t50= + {} {mc "Command to generate more commits to include:"}}
+ {perm b . {} {mc "Remember this view"}}
+ {reflabel l + {} {mc "References (space separated list):"}}
+ {refs t15 .. {} {mc "Branches & tags:"}}
+ {allrefs b *. "--all" {mc "All refs"}}
+ {branches b . "--branches" {mc "All (local) branches"}}
+ {tags b . "--tags" {mc "All tags"}}
+ {remotes b . "--remotes" {mc "All remote-tracking branches"}}
+ {commitlbl l + {} {mc "Commit Info (regular expressions):"}}
+ {author t15 .. "--author=*" {mc "Author:"}}
+ {committer t15 . "--committer=*" {mc "Committer:"}}
+ {loginfo t15 .. "--grep=*" {mc "Commit Message:"}}
+ {allmatch b .. "--all-match" {mc "Matches all Commit Info criteria"}}
+ {changes_l l + {} {mc "Changes to Files:"}}
+ {pickaxe_s r0 . {} {mc "Fixed String"}}
+ {pickaxe_t r1 . "--pickaxe-regex" {mc "Regular Expression"}}
+ {pickaxe t15 .. "-S*" {mc "Search string:"}}
+ {datelabel l + {} {mc "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 15:27:38\"):"}}
+ {since t15 .. {"--since=*" "--after=*"} {mc "Since:"}}
+ {until t15 . {"--until=*" "--before=*"} {mc "Until:"}}
+ {limit_lbl l + {} {mc "Limit and/or skip a number of revisions (positive integer):"}}
+ {limit t10 *. "--max-count=*" {mc "Number to show:"}}
+ {skip t10 . "--skip=*" {mc "Number to skip:"}}
+ {misc_lbl l + {} {mc "Miscellaneous options:"}}
+ {dorder b *. {"--date-order" "-d"} {mc "Strictly sort by date"}}
+ {lright b . "--left-right" {mc "Mark branch sides"}}
+ {first b . "--first-parent" {mc "Limit to first parent"}}
+ {smplhst b . "--simplify-by-decoration" {mc "Simple history"}}
+ {args t50 *. {} {mc "Additional arguments to git log:"}}
+ {allpaths path + {} {mc "Enter files and directories to include, one per line:"}}
+ {cmd t50= + {} {mc "Command to generate more commits to include:"}}
}
proc encode_view_opts {n} {
if {$patterns eq {}} continue
set pattern [lindex $patterns 0]
- set val $newviewopts($n,[lindex $opt 0])
-
if {[lindex $opt 1] eq "b"} {
+ set val $newviewopts($n,[lindex $opt 0])
if {$val} {
lappend rargs $pattern
}
+ } elseif {[regexp {^r(\d+)$} [lindex $opt 1] type value]} {
+ regexp {^(.*_)} [lindex $opt 0] uselessvar button_id
+ set val $newviewopts($n,$button_id)
+ if {$val eq $value} {
+ lappend rargs $pattern
+ }
} else {
+ set val $newviewopts($n,[lindex $opt 0])
set val [string trim $val]
if {$val ne {}} {
set pfix [string range $pattern 0 end-1]
}
}
}
+ set rargs [concat $rargs [shellsplit $newviewopts($n,refs)]]
return [concat $rargs [shellsplit $newviewopts($n,args)]]
}
global known_view_options newviewopts
foreach opt $known_view_options {
+ set id [lindex $opt 0]
if {[lindex $opt 1] eq "b"} {
+ # Checkboxes
+ set val 0
+ } elseif {[regexp {^r(\d+)$} [lindex $opt 1]]} {
+ # Radiobuttons
+ regexp {^(.*_)} $id uselessvar id
set val 0
} else {
+ # Text fields
set val {}
}
- set newviewopts($n,[lindex $opt 0]) $val
+ set newviewopts($n,$id) $val
}
set oargs [list]
+ set refargs [list]
foreach arg $view_args {
if {[regexp -- {^-([0-9]+)$} $arg arg cnt]
&& ![info exists found(limit)]} {
if {[info exists found($id)]} continue
foreach pattern [lindex $opt 3] {
if {![string match $pattern $arg]} continue
- if {[lindex $opt 1] ne "b"} {
+ if {[lindex $opt 1] eq "b"} {
+ # Check buttons
+ set val 1
+ } elseif {[regexp {^r(\d+)$} [lindex $opt 1] match num]} {
+ # Radio buttons
+ regexp {^(.*_)} $id uselessvar id
+ set val $num
+ } else {
+ # Text input fields
set size [string length $pattern]
set val [string range $arg [expr {$size-1}] end]
- } else {
- set val 1
}
set newviewopts($n,$id) $val
set found($id) 1
if {[info exists val]} break
}
if {[info exists val]} continue
- lappend oargs $arg
+ if {[regexp {^-} $arg]} {
+ lappend oargs $arg
+ } else {
+ lappend refargs $arg
+ }
}
+ set newviewopts($n,refs) [shellarglist $refargs]
set newviewopts($n,args) [shellarglist $oargs]
}
global known_view_options NS
ttk_toplevel $top
- wm title $top $title
+ wm title $top [concat $title [mc "-- criteria for selecting revisions"]]
make_transient $top .
# View name
${NS}::frame $top.nfr
- ${NS}::label $top.nl -text [mc "Name"]
+ ${NS}::label $top.nl -text [mc "View Name"]
${NS}::entry $top.name -width 20 -textvariable newviewname($n)
pack $top.nfr -in $top -fill x -pady 5 -padx 3
- pack $top.nl -in $top.nfr -side left -padx {0 30}
- pack $top.name -in $top.nfr -side left
+ pack $top.nl -in $top.nfr -side left -padx {0 5}
+ pack $top.name -in $top.nfr -side left -padx {0 25}
# View options
set cframe $top.nfr
${NS}::frame $cframe
pack $cframe -in $top -fill x -pady 3 -padx 3
set cexpand [expr {$flags eq "*"}]
+ } elseif {$flags eq ".." || $flags eq "*."} {
+ set cframe $top.fr$cnt
+ incr cnt
+ ${NS}::frame $cframe
+ pack $cframe -in $top -fill x -pady 3 -padx [list 15 3]
+ set cexpand [expr {$flags eq "*."}]
} else {
set lxpad 5
}
- if {$type eq "b"} {
+ if {$type eq "l"} {
+ ${NS}::label $cframe.l_$id -text $title
+ pack $cframe.l_$id -in $cframe -side left -pady [list 3 0] -anchor w
+ } elseif {$type eq "b"} {
${NS}::checkbutton $cframe.c_$id -text $title -variable newviewopts($n,$id)
pack $cframe.c_$id -in $cframe -side left \
-padx [list $lxpad 0] -expand $cexpand -anchor w
+ } elseif {[regexp {^r(\d+)$} $type type sz]} {
+ regexp {^(.*_)} $id uselessvar button_id
+ ${NS}::radiobutton $cframe.c_$id -text $title -variable newviewopts($n,$button_id) -value $sz
+ pack $cframe.c_$id -in $cframe -side left \
+ -padx [list $lxpad 0] -expand $cexpand -anchor w
} elseif {[regexp {^t(\d+)$} $type type sz]} {
${NS}::label $cframe.l_$id -text $title
${NS}::entry $cframe.e_$id -width $sz -background $bgcolor \
-textvariable newviewopts($n,$id)
pack $cframe.l_$id -in $cframe -side top -pady [list 3 0] -anchor w
pack $cframe.e_$id -in $cframe -side top -fill x
+ } elseif {$type eq "path"} {
+ ${NS}::label $top.l -text $title
+ pack $top.l -in $top -side top -pady [list 3 0] -anchor w -padx 3
+ text $top.t -width 40 -height 5 -background $bgcolor -font uifont
+ if {[info exists viewfiles($n)]} {
+ foreach f $viewfiles($n) {
+ $top.t insert end $f
+ $top.t insert end "\n"
+ }
+ $top.t delete {end - 1c} end
+ $top.t mark set insert 0.0
+ }
+ pack $top.t -in $top -side top -pady [list 0 5] -fill both -expand 1 -padx 3
}
}
- # Path list
- ${NS}::label $top.l \
- -text [mc "Enter files and directories to include, one per line:"]
- pack $top.l -in $top -side top -pady [list 7 0] -anchor w -padx 3
- text $top.t -width 40 -height 5 -background $bgcolor -font uifont
- if {[info exists viewfiles($n)]} {
- foreach f $viewfiles($n) {
- $top.t insert end $f
- $top.t insert end "\n"
- }
- $top.t delete {end - 1c} end
- $top.t mark set insert 0.0
- }
- pack $top.t -in $top -side top -pady [list 0 5] -fill both -expand 1 -padx 3
${NS}::frame $top.buts
${NS}::button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
${NS}::button $top.buts.apply -text [mc "Apply (F5)"] -command [list newviewok $top $n 1]
global diffcontextstring diffcontext
if {[string is integer -strict $diffcontextstring]} {
- if {$diffcontextstring > 0} {
+ if {$diffcontextstring >= 0} {
set diffcontext $diffcontextstring
reselectline
}
global ignorespace
global limitdiffs vfilelimit curview
global diffencoding targetline diffnparents
+ global git_version
- set cmd [diffcmd $ids "-p -C --cc --no-commit-id -U$diffcontext"]
+ set textconv {}
+ if {[package vcompare $git_version "1.6.1"] >= 0} {
+ set textconv "--textconv"
+ }
+ set cmd [diffcmd $ids "-p $textconv -C --cc --no-commit-id -U$diffcontext"]
if {$ignorespace} {
append cmd " -w"
}
$ctext conf -state normal
while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
- close $bdf
+ catch {close $bdf}
return 0
}
if {![string compare -length 5 "diff " $line]} {
maybe_scroll_ctext [eof $bdf]
$ctext conf -state disabled
if {[eof $bdf]} {
- close $bdf
+ catch {close $bdf}
return 0
}
return [expr {$nr >= 1000? 2: 1}]
}
set id [lindex $matches 0]
}
+ } else {
+ if {[catch {set id [exec git rev-parse --verify $sha1string]}]} {
+ error_popup [mc "Revision %s is not known" $sha1string]
+ return
+ }
}
}
if {[commitinview $id $curview]} {
if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
set msg [mc "SHA1 id %s is not known" $sha1string]
} else {
- set msg [mc "Tag/Head %s is not known" $sha1string]
+ set msg [mc "Revision %s is not in the current view" $sha1string]
}
error_popup $msg
}
appendshortlink $a [mc "Commit "] " $heada\n"
appendshortlink $b [mc " differs from\n "] \
" $headb\n"
- $ctext insert end [mc "- stopping\n"]
- break
+ $ctext insert end [mc "Diff of commits:\n\n"]
+ $ctext conf -state disabled
+ update
+ diffcommits $a $b
+ return
}
}
if {$skipa} {
- if {[llength $children($curview,$a)] != 1} {
+ set kids [real_children $curview,$a]
+ if {[llength $kids] != 1} {
$ctext insert end "\n"
appendshortlink $a [mc "Commit "] \
- [mc " has %s children - stopping\n" \
- [llength $children($curview,$a)]]
+ [mc " has %s children - stopping\n" [llength $kids]]
break
}
- set a [lindex $children($curview,$a) 0]
+ set a [lindex $kids 0]
}
if {$skipb} {
- if {[llength $children($curview,$b)] != 1} {
+ set kids [real_children $curview,$b]
+ if {[llength $kids] != 1} {
appendshortlink $b [mc "Commit "] \
- [mc " has %s children - stopping\n" \
- [llength $children($curview,$b)]]
+ [mc " has %s children - stopping\n" [llength $kids]]
break
}
- set b [lindex $children($curview,$b) 0]
+ set b [lindex $kids 0]
}
}
$ctext conf -state disabled
}
+proc diffcommits {a b} {
+ global diffcontext diffids blobdifffd diffinhdr
+
+ set tmpdir [gitknewtmpdir]
+ set fna [file join $tmpdir "commit-[string range $a 0 7]"]
+ set fnb [file join $tmpdir "commit-[string range $b 0 7]"]
+ if {[catch {
+ exec git diff-tree -p --pretty $a >$fna
+ exec git diff-tree -p --pretty $b >$fnb
+ } err]} {
+ error_popup [mc "Error writing commit to file: %s" $err]
+ return
+ }
+ if {[catch {
+ set fd [open "| diff -U$diffcontext $fna $fnb" r]
+ } err]} {
+ error_popup [mc "Error diffing commits: %s" $err]
+ return
+ }
+ set diffids [list commits $a $b]
+ set blobdifffd($diffids) $fd
+ set diffinhdr 0
+ filerun $fd [list getblobdiffline $fd $diffids]
+}
+
proc diffvssel {dirn} {
global rowmenuid selectedline
global oldprefs prefstop showneartags showlocalchanges
global bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
global tabstop limitdiffs autoselect extdifftool perfile_attrs
+ global hideremotes want_ttk have_ttk
set top .gitkprefs
set prefstop $top
return
}
foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
- limitdiffs tabstop perfile_attrs} {
+ limitdiffs tabstop perfile_attrs hideremotes want_ttk} {
set oldprefs($v) [set $v]
}
ttk_toplevel $top
${NS}::checkbutton $top.autoselect -text [mc "Auto-select SHA1"] \
-variable autoselect
grid x $top.autoselect -sticky w
+ ${NS}::checkbutton $top.hideremotes -text [mc "Hide remote refs"] \
+ -variable hideremotes
+ grid x $top.hideremotes -sticky w
${NS}::label $top.ddisp -text [mc "Diff display options"]
grid $top.ddisp - -sticky w -pady 10
pack configure $top.extdifff.l -padx 10
grid x $top.extdifff $top.extdifft -sticky ew
+ ${NS}::label $top.lgen -text [mc "General options"]
+ grid $top.lgen - -sticky w -pady 10
+ ${NS}::checkbutton $top.want_ttk -variable want_ttk \
+ -text [mc "Use themed widgets"]
+ if {$have_ttk} {
+ ${NS}::label $top.ttk_note -text [mc "(change requires restart)"]
+ } else {
+ ${NS}::label $top.ttk_note -text [mc "(currently unavailable)"]
+ }
+ grid x $top.want_ttk $top.ttk_note -sticky w
+
${NS}::label $top.cdisp -text [mc "Colors: press to choose"]
grid $top.cdisp - -sticky w -pady 10
label $top.bg -padx 40 -relief sunk -background $bgcolor
if {!$use_ttk} {
foreach w {maxpctl maxwidthl showlocal autoselect tabstopl ntag
ldiff lattr extdifff.l extdifff.b bgbut fgbut
- diffoldbut diffnewbut hunksepbut markbgbut selbgbut} {
+ diffoldbut diffnewbut hunksepbut markbgbut selbgbut
+ want_ttk ttk_note} {
$top.$w configure -font optionfont
}
}
global oldprefs prefstop
foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
- limitdiffs tabstop perfile_attrs} {
+ limitdiffs tabstop perfile_attrs hideremotes want_ttk} {
global $v
set $v $oldprefs($v)
}
global oldprefs prefstop showneartags showlocalchanges
global fontpref mainfont textfont uifont
global limitdiffs treediffs perfile_attrs
+ global hideremotes
catch {destroy $prefstop}
unset prefstop
$limitdiffs != $oldprefs(limitdiffs)} {
reselectline
}
+ if {$hideremotes != $oldprefs(hideremotes)} {
+ rereadrefs
+ }
}
proc formatdate {d} {
} else {
set r "unspecified"
if {![catch {set line [exec git check-attr $attr -- $path]}]} {
- regexp "(.*): encoding: (.*)" $line m f r
+ regexp "(.*): $attr: (.*)" $line m f r
}
set path_attr_cache($attr,$path) $r
}
set newlist [lrange $newlist $lim end]
if {![catch {set rlist [eval exec git check-attr $attr -- $head]}]} {
foreach row [split $rlist "\n"] {
- if {[regexp "(.*): encoding: (.*)" $row m path value]} {
+ if {[regexp "(.*): $attr: (.*)" $row m path value]} {
if {[string index $path 0] eq "\""} {
set path [encoding convertfrom [lindex $path 0]]
}
set cmitmode "patch"
set wrapcomment "none"
set showneartags 1
+set hideremotes 0
set maxrefs 20
set maxlinelen 200
set showlocalchanges 1
set datetimeformat "%Y-%m-%d %H:%M:%S"
set autoselect 1
set perfile_attrs 0
+set want_ttk 1
if {[tk windowingsystem] eq "aqua"} {
set extdifftool "opendiff"
set nullfile "/dev/null"
set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
-if {![info exists use_ttk]} {
- set use_ttk [llength [info commands ::ttk::style]]
+if {![info exists have_ttk]} {
+ set have_ttk [llength [info commands ::ttk::style]]
}
+set use_ttk [expr {$have_ttk && $want_ttk}]
set NS [expr {$use_ttk ? "ttk" : ""}]
+set git_version [join [lrange [split [lindex [exec git version] end] .] 0 2] .]
+
set runq {}
set history {}
set historyindex 0