@matheusbsbs / matheus-bagliano / commits / ee66e089c1
Fork 0

gitk: Make updates go faster

This goes back to the method of doing updates where we translate the revisions we're given to SHA1 ids and then remove the ones we've asked for before or that we've already come across. This speeds up updates enormously in most cases since it means git log doesn't have to traverse large parts of the tree. We used to do this, but it had bugs, and commit 468bcaedbb1589f16955e63b6bfba01c2f53e433 (gitk: Don't filter view arguments through git rev-parse) went to the slower method to avoid the bugs. In order to do this properly, we have to parse the command line and understand all the flag arguments. So this adds a parser that checks all the flag arguments. If there are any we don't know about, we disable the optimization and just pass the whole lot to git log (except for -d/--date-order, which we remove from the list). With this we can then use git rev-parse on the non-flag arguments to work out exactly what SHA1 ids are included and excluded in the list, which then enables us to ask for just the new ones when updating. One wrinkle is that we have to turn symmetric diff arguments (of the form a...b) back into symmetric diff form so that --left-right still works, as git rev parse turns a...b into a b ^merge_base(a,b). This also updates a couple of copyright notices. Signed-off-by: Paul Mackerras <paulus@samba.org>

Paul Mackerras committed May 9, 2008 at 10:14 UTC ee66e089c1d4162765a3cec79fd5406e515b0db1
1 file changed +210 -15
gitk
+210 -15
@@ -2,7 +2,7 @@
2 # Tcl ignores the next line -*- tcl -*- \
3 exec wish "$0" -- "$@"
4
5 -# Copyright (C) 2005-2006 Paul Mackerras. All rights reserved.
5 +# Copyright © 2005-2008 Paul Mackerras. All rights reserved.
6 # This program is free software; it may be used, copied, modified
7 # and distributed under the terms of the GNU General Public Licence,
8 # either version 2, or (at your option) any later version.
@@ -117,38 +117,190 @@ proc unmerged_files {files} {
117 }
118
119 proc parseviewargs {n arglist} {
120 - global viewargs vdatemode vmergeonly
120 + global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs
121
122 set vdatemode($n) 0
123 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 + }
139 switch -glob -- $arg {
140 "-d" -
141 "--date-order" {
142 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
197 }
198 "--merge" {
199 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
202 }
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
215 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 + }
277 }
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
286 }
287 + incr sdm -1
288 }
140 - return $glargs
289 + set vposids($view) $pos
290 + set vnegids($view) $neg
291 + return $ret
292 }
293
294 # Start off a git log process and arrange to read its output
295 proc start_rev_list {view} {
296 global startmsecs commitidx viewcomplete
297 global commfd leftover tclencoding
147 - global viewargs viewargscmd vactualargs viewfiles vfilelimit
298 + global viewargs viewargscmd viewfiles vfilelimit
299 global showlocalchanges commitinterest mainheadid
300 global progressdirn progresscoords proglastnc curview
301 global viewactive loginstance viewinstances vmergeonly
302 global pending_select mainheadid
303 + global vcanopt vflags vrevs vorigargs
304
305 set startmsecs [clock clicks -milliseconds]
306 set commitidx($view) 0
@@ -167,8 +319,7 @@ proc start_rev_list {view} {
319 }
320 set args [concat $args [split $str "\n"]]
321 }
170 - set args [parseviewargs $view $args]
171 - set vactualargs($view) $args
322 + set vcanopt($view) [parseviewargs $view $args]
323
324 set files $viewfiles($view)
325 if {$vmergeonly($view)} {
@@ -187,6 +338,16 @@ proc start_rev_list {view} {
338 }
339 set vfilelimit($view) $files
340
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 +
351 if {[catch {
352 set fd [open [concat | git log --no-color -z --pretty=raw --parents \
353 --boundary $args "--" $files] r]
@@ -248,11 +409,12 @@ proc getcommits {} {
409 }
410
411 proc updatecommits {} {
251 - global curview vactualargs vfilelimit viewinstances
412 + global curview vcanopt vorigargs vfilelimit viewinstances
413 global viewactive viewcomplete loginstance tclencoding mainheadid
414 global startmsecs commfd showneartags showlocalchanges leftover
415 global mainheadid pending_select
416 global isworktree
417 + global varcid vposids vnegids vflags vrevs
418
419 set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
420 set oldmainid $mainheadid
@@ -266,13 +428,46 @@ proc updatecommits {} {
428 }
429 }
430 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 + }
465 if {[catch {
466 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]
468 } err]} {
469 error_popup "Error executing git log: $err"
275 - exit 1
470 + return
471 }
472 if {$viewactive($view) == 0} {
473 set startmsecs [clock clicks -milliseconds]
@@ -2217,7 +2412,7 @@ proc about {} {
2412 message $w.m -text [mc "
2413 Gitk - a commit viewer for git
2414
2220 -Copyright © 2005-2006 Paul Mackerras
2415 +Copyright © 2005-2008 Paul Mackerras
2416
2417 Use and redistribute under the terms of the GNU General Public License"] \
2418 -justify center -aspect 400 -border 2 -bg white -relief groove