22# Tcl ignores the next line -*- tcl -*- \
33exec wish "$0" -- "$@"
44
5- # Copyright (C) 2005-2006 Paul Mackerras. All rights reserved.
5+ # Copyright © 2005-2008 Paul Mackerras. All rights reserved.
66# This program is free software; it may be used, copied, modified
77# and distributed under the terms of the GNU General Public Licence,
88# either version 2, or (at your option) any later version.
@@ -117,38 +117,190 @@ proc unmerged_files {files} {
117117}
118118
119119proc parseviewargs {n arglist} {
120- global viewargs vdatemode vmergeonly
120+ global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs
121121
122122 set vdatemode($n ) 0
123123 set vmergeonly($n ) 0
124- set glargs {}
125- foreach arg $viewargs($n) {
124+ set glflags {}
125+ set diffargs {}
126+ set nextisval 0
127+ set revargs {}
128+ set origargs $arglist
129+ set allknown 1
130+ set filtered 0
131+ set i -1
132+ foreach arg $arglist {
133+ incr i
134+ if {$nextisval } {
135+ lappend glflags $arg
136+ set nextisval 0
137+ continue
138+ }
126139 switch -glob -- $arg {
127140 " -d" -
128141 " --date-order" {
129142 set vdatemode($n ) 1
143+ # remove from origargs in case we hit an unknown option
144+ set origargs [lreplace $origargs $i $i ]
145+ incr i -1
146+ }
147+ # These request or affect diff output, which we don't want.
148+ # Some could be used to set our defaults for diff display.
149+ " -[ puabwcrRBMC] " -
150+ " --no-renames" - " --full-index" - " --binary" - " --abbrev=*" -
151+ " --find-copies-harder" - " -l*" - " --ext-diff" - " --no-ext-diff" -
152+ " --src-prefix=*" - " --dst-prefix=*" - " --no-prefix" -
153+ " -O*" - " --text" - " --full-diff" - " --ignore-space-at-eol" -
154+ " --ignore-space-change" - " -U*" - " --unified=*" {
155+ lappend diffargs $arg
156+ }
157+ # These cause our parsing of git log's output to fail, or else
158+ # they're options we want to set ourselves, so ignore them.
159+ " --raw" - " --patch-with-raw" - " --patch-with-stat" -
160+ " --name-only" - " --name-status" - " --color" - " --color-words" -
161+ " --log-size" - " --pretty=*" - " --decorate" - " --abbrev-commit" -
162+ " --cc" - " -z" - " --header" - " --parents" - " --boundary" -
163+ " --no-color" - " -g" - " --walk-reflogs" - " --no-walk" -
164+ " --timestamp" - " relative-date" - " --date=*" - " --stdin" -
165+ " --objects" - " --objects-edge" - " --reverse" {
166+ }
167+ # These are harmless, and some are even useful
168+ " --stat=*" - " --numstat" - " --shortstat" - " --summary" -
169+ " --check" - " --exit-code" - " --quiet" - " --topo-order" -
170+ " --full-history" - " --dense" - " --sparse" -
171+ " --follow" - " --left-right" - " --encoding=*" {
172+ lappend glflags $arg
173+ }
174+ # These mean that we get a subset of the commits
175+ " --diff-filter=*" - " --no-merges" - " --unpacked" -
176+ " --max-count=*" - " --skip=*" - " --since=*" - " --after=*" -
177+ " --until=*" - " --before=*" - " --max-age=*" - " --min-age=*" -
178+ " --author=*" - " --committer=*" - " --grep=*" - " -[ iE] " -
179+ " --remove-empty" - " --first-parent" - " --cherry-pick" -
180+ " -S*" - " --pickaxe-all" - " --pickaxe-regex" - {
181+ set filtered 1
182+ lappend glflags $arg
183+ }
184+ # This appears to be the only one that has a value as a
185+ # separate word following it
186+ " -n" {
187+ set filtered 1
188+ set nextisval 1
189+ lappend glflags $arg
190+ }
191+ " --not" {
192+ set notflag [expr {!$notflag }]
193+ lappend revargs $arg
194+ }
195+ " --all" {
196+ lappend revargs $arg
130197 }
131198 " --merge" {
132199 set vmergeonly($n ) 1
133- lappend glargs $arg
200+ # git rev-parse doesn't understand --merge
201+ lappend revargs --gitk-symmetric-diff-marker MERGE_HEAD...HEAD
134202 }
203+ # Other flag arguments including -<n>
204+ " -*" {
205+ if {[string is digit -strict [string range $arg 1 end]]} {
206+ set filtered 1
207+ } else {
208+ # a flag argument that we don't recognize;
209+ # that means we can't optimize
210+ set allknown 0
211+ }
212+ lappend glflags $arg
213+ }
214+ # Non-flag arguments specify commits or ranges of commits
135215 default {
136- lappend glargs $arg
216+ if {[string match " *...*" $arg ]} {
217+ lappend revargs --gitk-symmetric-diff-marker
218+ }
219+ lappend revargs $arg
220+ }
221+ }
222+ }
223+ set vdflags($n ) $diffargs
224+ set vflags($n ) $glflags
225+ set vrevs($n ) $revargs
226+ set vfiltered($n ) $filtered
227+ set vorigargs($n ) $origargs
228+ return $allknown
229+ }
230+
231+ proc parseviewrevs {view revs} {
232+ global vposids vnegids
233+
234+ if {$revs eq {}} {
235+ set revs HEAD
236+ }
237+ if {[catch {set ids [eval exec git rev-parse $revs ]} err]} {
238+ # we get stdout followed by stderr in $err
239+ # for an unknown rev, git rev-parse echoes it and then errors out
240+ set errlines [split $err " \n " ]
241+ set badrev {}
242+ for {set l 0} {$l < [llength $errlines ]} {incr l} {
243+ set line [lindex $errlines $l ]
244+ if {!([string length $line ] == 40 && [string is xdigit $line ])} {
245+ if {[string match " fatal:*" $line ]} {
246+ if {[string match " fatal: ambiguous argument*" $line ]
247+ && $badrev ne {}} {
248+ if {[llength $badrev ] == 1} {
249+ set err " unknown revision $badrev "
250+ } else {
251+ set err " unknown revisions: [ join $badrev " , " ] "
252+ }
253+ } else {
254+ set err [join [lrange $errlines $l end] " \n " ]
255+ }
256+ break
257+ }
258+ lappend badrev $line
259+ }
260+ }
261+ error_popup " Error parsing revisions: $err "
262+ return {}
263+ }
264+ set ret {}
265+ set pos {}
266+ set neg {}
267+ set sdm 0
268+ foreach id [split $ids " \n " ] {
269+ if {$id eq " --gitk-symmetric-diff-marker" } {
270+ set sdm 4
271+ } elseif {[string match " ^*" $id ]} {
272+ if {$sdm != 1} {
273+ lappend ret $id
274+ if {$sdm == 3} {
275+ set sdm 0
276+ }
137277 }
278+ lappend neg [string range $id 1 end]
279+ } else {
280+ if {$sdm != 2} {
281+ lappend ret $id
282+ } else {
283+ lset ret end [lindex $ret end]...$id
284+ }
285+ lappend pos $id
138286 }
287+ incr sdm -1
139288 }
140- return $glargs
289+ set vposids($view ) $pos
290+ set vnegids($view ) $neg
291+ return $ret
141292}
142293
143294# Start off a git log process and arrange to read its output
144295proc start_rev_list {view} {
145296 global startmsecs commitidx viewcomplete
146297 global commfd leftover tclencoding
147- global viewargs viewargscmd vactualargs viewfiles vfilelimit
298+ global viewargs viewargscmd viewfiles vfilelimit
148299 global showlocalchanges commitinterest mainheadid
149300 global progressdirn progresscoords proglastnc curview
150301 global viewactive loginstance viewinstances vmergeonly
151302 global pending_select mainheadid
303+ global vcanopt vflags vrevs vorigargs
152304
153305 set startmsecs [clock clicks -milliseconds]
154306 set commitidx($view ) 0
@@ -167,8 +319,7 @@ proc start_rev_list {view} {
167319 }
168320 set args [concat $args [split $str " \n " ]]
169321 }
170- set args [parseviewargs $view $args ]
171- set vactualargs($view ) $args
322+ set vcanopt($view ) [parseviewargs $view $args ]
172323
173324 set files $viewfiles($view)
174325 if {$vmergeonly($view) } {
@@ -187,6 +338,16 @@ proc start_rev_list {view} {
187338 }
188339 set vfilelimit($view ) $files
189340
341+ if {$vcanopt($view) } {
342+ set revs [parseviewrevs $view $vrevs($view) ]
343+ if {$revs eq {}} {
344+ return 0
345+ }
346+ set args [concat $vflags($view) $revs ]
347+ } else {
348+ set args $vorigargs($view)
349+ }
350+
190351 if {[catch {
191352 set fd [open [concat | git log --no-color -z --pretty=raw --parents \
192353 --boundary $args " --" $files ] r]
@@ -248,11 +409,12 @@ proc getcommits {} {
248409}
249410
250411proc updatecommits {} {
251- global curview vactualargs vfilelimit viewinstances
412+ global curview vcanopt vorigargs vfilelimit viewinstances
252413 global viewactive viewcomplete loginstance tclencoding mainheadid
253414 global startmsecs commfd showneartags showlocalchanges leftover
254415 global mainheadid pending_select
255416 global isworktree
417+ global varcid vposids vnegids vflags vrevs
256418
257419 set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == " true" }]
258420 set oldmainid $mainheadid
@@ -266,13 +428,46 @@ proc updatecommits {} {
266428 }
267429 }
268430 set view $curview
431+ if {$vcanopt($view) } {
432+ set oldpos $vposids($view)
433+ set oldneg $vnegids($view)
434+ set revs [parseviewrevs $view $vrevs($view) ]
435+ if {$revs eq {}} {
436+ return
437+ }
438+ # note: getting the delta when negative refs change is hard,
439+ # and could require multiple git log invocations, so in that
440+ # case we ask git log for all the commits (not just the delta)
441+ if {$oldneg eq $vnegids($view) } {
442+ set newrevs {}
443+ set npos 0
444+ # take out positive refs that we asked for before or
445+ # that we have already seen
446+ foreach rev $revs {
447+ if {[string length $rev ] == 40} {
448+ if {[lsearch -exact $oldpos $rev ] < 0
449+ && ![info exists varcid($view ,$rev )]} {
450+ lappend newrevs $rev
451+ incr npos
452+ }
453+ } else {
454+ lappend $newrevs $rev
455+ }
456+ }
457+ if {$npos == 0} return
458+ set revs $newrevs
459+ set vposids($view ) [lsort -unique [concat $oldpos $vposids($view) ]]
460+ }
461+ set args [concat $vflags($view) $revs --not $oldpos ]
462+ } else {
463+ set args $vorigargs($view)
464+ }
269465 if {[catch {
270466 set fd [open [concat | git log --no-color -z --pretty=raw --parents \
271- --boundary $vactualargs($view) --not [seeds $view ] \
272- " --" $vfilelimit($view) ] r]
467+ --boundary $args " --" $vfilelimit($view) ] r]
273468 } err]} {
274469 error_popup " Error executing git log: $err "
275- exit 1
470+ return
276471 }
277472 if {$viewactive($view) == 0} {
278473 set startmsecs [clock clicks -milliseconds]
@@ -2217,7 +2412,7 @@ proc about {} {
22172412 message $w .m -text [mc "
22182413Gitk - a commit viewer for git
22192414
2220- Copyright © 2005-2006 Paul Mackerras
2415+ Copyright © 2005-2008 Paul Mackerras
22212416
22222417Use and redistribute under the terms of the GNU General Public License" ] \
22232418 -justify center -aspect 400 -border 2 -bg white -relief groove
0 commit comments