git-gui: convert git_read*, git_write to be non-variadic

We are going to treat command arguments and redirections differently to avoid passing arguments that look like redirections to the command accidentally. To do so, it will be necessary to know which arguments are intentional redirections. As a preparation, convert git_read, git_read_nice, and git_write to take just a single argument that is the command in a list. Adjust all call sites accordingly. In the future, this argument will be the regular command arguments and a second argument will be the redirection operations. Signed-off-by: Johannes Sixt <j6t@kdbg.org> Signed-off-by: Taylor Blau <me@ttaylorr.com>

Johannes Sixt committed May 3, 2025 at 13:24 UTC dc9ecb1aab1a3438fceeb44db67ddf8e8d938324
16 files changed +62 -62
git-gui.sh
+24 -24
@@ -621,7 +621,7 @@ proc _lappend_nice {cmd_var} {
621 }
622
623 proc git {args} {
624 - set fd [eval [list git_read] $args]
624 + set fd [git_read $args]
625 fconfigure $fd -translation binary -encoding utf-8
626 set result [string trimright [read $fd] "\n"]
627 close $fd
@@ -642,34 +642,34 @@ proc _open_stdout_stderr {cmd} {
642 return $fd
643 }
644
645 -proc git_read {args} {
646 - set cmdp [_git_cmd [lindex $args 0]]
647 - set args [lrange $args 1 end]
645 +proc git_read {cmd} {
646 + set cmdp [_git_cmd [lindex $cmd 0]]
647 + set cmd [lrange $cmd 1 end]
648
649 - return [_open_stdout_stderr [concat $cmdp $args]]
649 + return [_open_stdout_stderr [concat $cmdp $cmd]]
650 }
651
652 -proc git_read_nice {args} {
652 +proc git_read_nice {cmd} {
653 set opt [list]
654
655 _lappend_nice opt
656
657 - set cmdp [_git_cmd [lindex $args 0]]
658 - set args [lrange $args 1 end]
657 + set cmdp [_git_cmd [lindex $cmd 0]]
658 + set cmd [lrange $cmd 1 end]
659
660 - return [_open_stdout_stderr [concat $opt $cmdp $args]]
660 + return [_open_stdout_stderr [concat $opt $cmdp $cmd]]
661 }
662
663 -proc git_write {args} {
664 - set cmdp [_git_cmd [lindex $args 0]]
665 - set args [lrange $args 1 end]
663 +proc git_write {cmd} {
664 + set cmdp [_git_cmd [lindex $cmd 0]]
665 + set cmd [lrange $cmd 1 end]
666
667 - _trace_exec [concat $cmdp $args]
668 - return [open [concat [list | ] $cmdp $args] w]
667 + _trace_exec [concat $cmdp $cmd]
668 + return [open [concat [list | ] $cmdp $cmd] w]
669 }
670
671 proc githook_read {hook_name args} {
672 - git_read hook run --ignore-missing $hook_name -- $args 2>@1
672 + git_read [concat [list hook run --ignore-missing $hook_name --] $args 2>@1]
673 }
674
675 proc kill_file_process {fd} {
@@ -1110,10 +1110,10 @@ proc _parse_config {arr_name args} {
1110 array unset arr
1111 set buf {}
1112 catch {
1113 - set fd_rc [eval \
1114 - [list git_read config] \
1113 + set fd_rc [git_read \
1114 + [concat config \
1115 $args \
1116 - [list --null --list]]
1116 + --null --list]]
1117 fconfigure $fd_rc -translation binary -encoding utf-8
1118 set buf [read $fd_rc]
1119 close $fd_rc
@@ -1482,12 +1482,12 @@ proc rescan {after {honor_trustmtime 1}} {
1482 } else {
1483 set rescan_active 1
1484 ui_status [mc "Refreshing file status..."]
1485 - set fd_rf [git_read update-index \
1485 + set fd_rf [git_read [list update-index \
1486 -q \
1487 --unmerged \
1488 --ignore-missing \
1489 --refresh \
1490 - ]
1490 + ]]
1491 fconfigure $fd_rf -blocking 0 -translation binary
1492 fileevent $fd_rf readable \
1493 [list rescan_stage2 $fd_rf $after]
@@ -1527,11 +1527,11 @@ proc rescan_stage2 {fd after} {
1527 set rescan_active 2
1528 ui_status [mc "Scanning for modified files ..."]
1529 if {[git-version >= "1.7.2"]} {
1530 - set fd_di [git_read diff-index --cached --ignore-submodules=dirty -z [PARENT]]
1530 + set fd_di [git_read [list diff-index --cached --ignore-submodules=dirty -z [PARENT]]]
1531 } else {
1532 - set fd_di [git_read diff-index --cached -z [PARENT]]
1532 + set fd_di [git_read [list diff-index --cached -z [PARENT]]]
1533 }
1534 - set fd_df [git_read diff-files -z]
1534 + set fd_df [git_read [list diff-files -z]]
1535
1536 fconfigure $fd_di -blocking 0 -translation binary -encoding binary
1537 fconfigure $fd_df -blocking 0 -translation binary -encoding binary
@@ -1540,7 +1540,7 @@ proc rescan_stage2 {fd after} {
1540 fileevent $fd_df readable [list read_diff_files $fd_df $after]
1541
1542 if {[is_config_true gui.displayuntracked]} {
1543 - set fd_lo [eval git_read ls-files --others -z $ls_others]
1543 + set fd_lo [git_read [concat ls-files --others -z $ls_others]]
1544 fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
1545 fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
1546 incr rescan_active
lib/blame.tcl
+5 -5
@@ -486,9 +486,9 @@ method _load {jump} {
486 fconfigure $fd -eofchar {}
487 } else {
488 if {$do_textconv ne 0} {
489 - set fd [git_read cat-file --textconv "$commit:$path"]
489 + set fd [git_read [list cat-file --textconv "$commit:$path"]]
490 } else {
491 - set fd [git_read cat-file blob "$commit:$path"]
491 + set fd [git_read [list cat-file blob "$commit:$path"]]
492 }
493 }
494 fconfigure $fd \
@@ -617,7 +617,7 @@ method _exec_blame {cur_w cur_d options cur_s} {
617 }
618
619 lappend options -- $path
620 - set fd [eval git_read_nice blame $options]
620 + set fd [git_read_nice [concat blame $options]]
621 fconfigure $fd -blocking 0 -translation lf -encoding utf-8
622 fileevent $fd readable [cb _read_blame $fd $cur_w $cur_d]
623 set current_fd $fd
@@ -986,7 +986,7 @@ method _showcommit {cur_w lno} {
986 if {[catch {set msg $header($cmit,message)}]} {
987 set msg {}
988 catch {
989 - set fd [git_read cat-file commit $cmit]
989 + set fd [git_read [list cat-file commit $cmit]]
990 fconfigure $fd -encoding binary -translation lf
991 # By default commits are assumed to be in utf-8
992 set enc utf-8
@@ -1134,7 +1134,7 @@ method _blameparent {} {
1134 } else {
1135 set diffcmd [list diff-tree --unified=0 $cparent $cmit -- $new_path]
1136 }
1137 - if {[catch {set fd [eval git_read $diffcmd]} err]} {
1137 + if {[catch {set fd [git_read $diffcmd]} err]} {
1138 $status_operation stop [mc "Unable to display parent"]
1139 error_popup [strcat [mc "Error loading diff:"] "\n\n$err"]
1140 return
lib/branch.tcl
+3 -3
@@ -7,7 +7,7 @@ proc load_all_heads {} {
7 set rh refs/heads
8 set rh_len [expr {[string length $rh] + 1}]
9 set all_heads [list]
10 - set fd [git_read for-each-ref --format=%(refname) $rh]
10 + set fd [git_read [list for-each-ref --format=%(refname) $rh]]
11 fconfigure $fd -translation binary -encoding utf-8
12 while {[gets $fd line] > 0} {
13 if {!$some_heads_tracking || ![is_tracking_branch $line]} {
@@ -21,10 +21,10 @@ proc load_all_heads {} {
21
22 proc load_all_tags {} {
23 set all_tags [list]
24 - set fd [git_read for-each-ref \
24 + set fd [git_read [list for-each-ref \
25 --sort=-taggerdate \
26 --format=%(refname) \
27 - refs/tags]
27 + refs/tags]]
28 fconfigure $fd -translation binary -encoding utf-8
29 while {[gets $fd line] > 0} {
30 if {![regsub ^refs/tags/ $line {} name]} continue
lib/browser.tcl
+1 -1
@@ -196,7 +196,7 @@ method _ls {tree_id {name {}}} {
196 lappend browser_stack [list $tree_id $name]
197 $w conf -state disabled
198
199 - set fd [git_read ls-tree -z $tree_id]
199 + set fd [git_read [list ls-tree -z $tree_id]]
200 fconfigure $fd -blocking 0 -translation binary -encoding utf-8
201 fileevent $fd readable [cb _read $fd]
202 }
lib/checkout_op.tcl
+5 -5
@@ -304,12 +304,12 @@ The rescan will be automatically started now.
304 _readtree $this
305 } else {
306 ui_status [mc "Refreshing file status..."]
307 - set fd [git_read update-index \
307 + set fd [git_read [list update-index \
308 -q \
309 --unmerged \
310 --ignore-missing \
311 --refresh \
312 - ]
312 + ]]
313 fconfigure $fd -blocking 0 -translation binary
314 fileevent $fd readable [cb _refresh_wait $fd]
315 }
@@ -345,7 +345,7 @@ method _readtree {} {
345 [mc "Updating working directory to '%s'..." [_name $this]] \
346 [mc "files checked out"]]
347
348 - set fd [git_read read-tree \
348 + set fd [git_read [list read-tree \
349 -m \
350 -u \
351 -v \
@@ -353,7 +353,7 @@ method _readtree {} {
353 $HEAD \
354 $new_hash \
355 2>@1 \
356 - ]
356 + ]]
357 fconfigure $fd -blocking 0 -translation binary
358 fileevent $fd readable [cb _readtree_wait $fd $status_bar_operation]
359 }
@@ -573,7 +573,7 @@ method _confirm_reset {cur} {
573 pack $w.buttons.cancel -side right -padx 5
574 pack $w.buttons -side bottom -fill x -pady 10 -padx 10
575
576 - set fd [git_read rev-list --pretty=oneline $cur ^$new_hash]
576 + set fd [git_read [list rev-list --pretty=oneline $cur ^$new_hash]]
577 while {[gets $fd line] > 0} {
578 set abbr [string range $line 0 7]
579 set subj [string range $line 41 end]
lib/choose_repository.tcl
+4 -4
@@ -818,9 +818,9 @@ method _clone_refs {} {
818 error_popup [mc "Not a Git repository: %s" [file tail $origin_url]]
819 return 0
820 }
821 - set fd_in [git_read for-each-ref \
821 + set fd_in [git_read [list for-each-ref \
822 --tcl \
823 - {--format=list %(refname) %(objectname) %(*objectname)}]
823 + {--format=list %(refname) %(objectname) %(*objectname)}]]
824 cd $pwd
825
826 set fd [safe_open_file [gitdir packed-refs] w]
@@ -953,14 +953,14 @@ method _do_clone_checkout {HEAD} {
953 [mc "files"]]
954
955 set readtree_err {}
956 - set fd [git_read read-tree \
956 + set fd [git_read [list read-tree \
957 -m \
958 -u \
959 -v \
960 HEAD \
961 HEAD \
962 2>@1 \
963 - ]
963 + ]]
964 fconfigure $fd -blocking 0 -translation binary
965 fileevent $fd readable [cb _readtree_wait $fd]
966 }
lib/choose_rev.tcl
+3 -3
@@ -146,14 +146,14 @@ constructor _new {path unmerged_only title} {
146 append fmt { %(*subject)}
147 append fmt {]}
148 set all_refn [list]
149 - set fr_fd [git_read for-each-ref \
149 + set fr_fd [git_read [list for-each-ref \
150 --tcl \
151 --sort=-taggerdate \
152 --format=$fmt \
153 refs/heads \
154 refs/remotes \
155 refs/tags \
156 - ]
156 + ]]
157 fconfigure $fr_fd -translation lf -encoding utf-8
158 while {[gets $fr_fd line] > 0} {
159 set line [eval $line]
@@ -176,7 +176,7 @@ constructor _new {path unmerged_only title} {
176 close $fr_fd
177
178 if {$unmerged_only} {
179 - set fr_fd [git_read rev-list --all ^$::HEAD]
179 + set fr_fd [git_read [list rev-list --all ^$::HEAD]]
180 while {[gets $fr_fd sha1] > 0} {
181 if {[catch {set rlst $cmt_refn($sha1)}]} continue
182 foreach refn $rlst {
lib/commit.tcl
+3 -3
@@ -27,7 +27,7 @@ You are currently in the middle of a merge that has not been fully completed. Y
27 if {[catch {
28 set name ""
29 set email ""
30 - set fd [git_read cat-file commit $curHEAD]
30 + set fd [git_read [list cat-file commit $curHEAD]]
31 fconfigure $fd -encoding binary -translation lf
32 # By default commits are assumed to be in utf-8
33 set enc utf-8
@@ -325,7 +325,7 @@ proc commit_commitmsg_wait {fd_ph curHEAD msg_p} {
325
326 proc commit_writetree {curHEAD msg_p} {
327 ui_status [mc "Committing changes..."]
328 - set fd_wt [git_read write-tree]
328 + set fd_wt [git_read [list write-tree]]
329 fileevent $fd_wt readable \
330 [list commit_committree $fd_wt $curHEAD $msg_p]
331 }
@@ -350,7 +350,7 @@ proc commit_committree {fd_wt curHEAD msg_p} {
350 # -- Verify this wasn't an empty change.
351 #
352 if {$commit_type eq {normal}} {
353 - set fd_ot [git_read cat-file commit $PARENT]
353 + set fd_ot [git_read [list cat-file commit $PARENT]]
354 fconfigure $fd_ot -encoding binary -translation lf
355 set old_tree [gets $fd_ot]
356 close $fd_ot
lib/console.tcl
+1 -1
@@ -93,7 +93,7 @@ method _init {} {
93 method exec {cmd {after {}}} {
94 lappend cmd 2>@1
95 if {[lindex $cmd 0] eq {git}} {
96 - set fd_f [eval git_read [lrange $cmd 1 end]]
96 + set fd_f [git_read [lrange $cmd 1 end]]
97 } else {
98 set fd_f [_open_stdout_stderr $cmd]
99 }
lib/database.tcl
+1 -1
@@ -3,7 +3,7 @@
3
4 proc do_stats {} {
5 global use_ttk NS
6 - set fd [git_read count-objects -v]
6 + set fd [git_read [list count-objects -v]]
7 while {[gets $fd line] > 0} {
8 if {[regexp {^([^:]+): (\d+)$} $line _ name value]} {
9 set stats($name) $value
lib/diff.tcl
+4 -4
@@ -338,7 +338,7 @@ proc start_show_diff {cont_info {add_opts {}}} {
338 }
339 }
340
341 - if {[catch {set fd [eval git_read_nice $cmd]} err]} {
341 + if {[catch {set fd [git_read_nice $cmd]} err]} {
342 set diff_active 0
343 unlock_index
344 ui_status [mc "Unable to display %s" [escape_path $path]]
@@ -617,7 +617,7 @@ proc apply_or_revert_hunk {x y revert} {
617
618 if {[catch {
619 set enc [get_path_encoding $current_diff_path]
620 - set p [eval git_write $apply_cmd]
620 + set p [git_write $apply_cmd]
621 fconfigure $p -translation binary -encoding $enc
622 puts -nonewline $p $wholepatch
623 close $p} err]} {
@@ -853,7 +853,7 @@ proc apply_or_revert_range_or_line {x y revert} {
853
854 if {[catch {
855 set enc [get_path_encoding $current_diff_path]
856 - set p [eval git_write $apply_cmd]
856 + set p [git_write $apply_cmd]
857 fconfigure $p -translation binary -encoding $enc
858 puts -nonewline $p $current_diff_header
859 puts -nonewline $p $wholepatch
@@ -890,7 +890,7 @@ proc undo_last_revert {} {
890
891 if {[catch {
892 set enc $last_revert_enc
893 - set p [eval git_write $apply_cmd]
893 + set p [git_write $apply_cmd]
894 fconfigure $p -translation binary -encoding $enc
895 puts -nonewline $p $last_revert
896 close $p} err]} {
lib/index.tcl
+4 -4
@@ -75,7 +75,7 @@ proc update_indexinfo {msg path_list after} {
75 if {$batch > 25} {set batch 25}
76
77 set status_bar_operation [$::main_status start $msg [mc "files"]]
78 - set fd [git_write update-index -z --index-info]
78 + set fd [git_write [list update-index -z --index-info]]
79 fconfigure $fd \
80 -blocking 0 \
81 -buffering full \
@@ -144,7 +144,7 @@ proc update_index {msg path_list after} {
144 if {$batch > 25} {set batch 25}
145
146 set status_bar_operation [$::main_status start $msg [mc "files"]]
147 - set fd [git_write update-index --add --remove -z --stdin]
147 + set fd [git_write [list update-index --add --remove -z --stdin]]
148 fconfigure $fd \
149 -blocking 0 \
150 -buffering full \
@@ -218,13 +218,13 @@ proc checkout_index {msg path_list after capture_error} {
218 if {$batch > 25} {set batch 25}
219
220 set status_bar_operation [$::main_status start $msg [mc "files"]]
221 - set fd [git_write checkout-index \
221 + set fd [git_write [list checkout-index \
222 --index \
223 --quiet \
224 --force \
225 -z \
226 --stdin \
227 - ]
227 + ]]
228 fconfigure $fd \
229 -blocking 0 \
230 -buffering full \
lib/merge.tcl
+1 -1
@@ -239,7 +239,7 @@ Continue with resetting the current changes?"]
239 }
240
241 if {[ask_popup $op_question] eq {yes}} {
242 - set fd [git_read read-tree --reset -u -v HEAD 2>@1]
242 + set fd [git_read [list read-tree --reset -u -v HEAD 2>@1]]
243 fconfigure $fd -blocking 0 -translation binary
244 set status_bar_operation [$::main_status \
245 start \
lib/mergetool.tcl
+1 -1
@@ -88,7 +88,7 @@ proc merge_load_stages {path cont} {
88 set merge_stages(3) {}
89 set merge_stages_buf {}
90
91 - set merge_stages_fd [eval git_read ls-files -u -z -- {$path}]
91 + set merge_stages_fd [git_read [list ls-files -u -z -- $path]]
92
93 fconfigure $merge_stages_fd -blocking 0 -translation binary -encoding binary
94 fileevent $merge_stages_fd readable [list read_merge_stages $merge_stages_fd $cont]
lib/remote.tcl
+1 -1
@@ -32,7 +32,7 @@ proc all_tracking_branches {} {
32 }
33
34 if {$pat ne {}} {
35 - set fd [eval git_read for-each-ref --format=%(refname) $cmd]
35 + set fd [git_read [concat for-each-ref --format=%(refname) $cmd]]
36 while {[gets $fd n] > 0} {
37 foreach spec $pat {
38 set dst [string range [lindex $spec 0] 0 end-2]
lib/remote_branch_delete.tcl
+1 -1
@@ -308,7 +308,7 @@ method _load {cache uri} {
308 set full_list [list]
309 set head_cache($cache) [list]
310 set full_cache($cache) [list]
311 - set active_ls [git_read ls-remote $uri]
311 + set active_ls [git_read [list ls-remote $uri]]
312 fconfigure $active_ls \
313 -blocking 0 \
314 -translation lf \