lib / index.tclon commit git-gui: Work around bad interaction between Tcl and cmd.exe on ^{tree} (20f1a10)
   1# git-gui index (add/remove) support
   2# Copyright (C) 2006, 2007 Shawn Pearce
   3
   4proc update_indexinfo {msg pathList after} {
   5        global update_index_cp ui_status_value
   6
   7        if {![lock_index update]} return
   8
   9        set update_index_cp 0
  10        set pathList [lsort $pathList]
  11        set totalCnt [llength $pathList]
  12        set batch [expr {int($totalCnt * .01) + 1}]
  13        if {$batch > 25} {set batch 25}
  14
  15        set ui_status_value [format \
  16                "$msg... %i/%i files (%.2f%%)" \
  17                $update_index_cp \
  18                $totalCnt \
  19                0.0]
  20        set fd [open "| git update-index -z --index-info" w]
  21        fconfigure $fd \
  22                -blocking 0 \
  23                -buffering full \
  24                -buffersize 512 \
  25                -encoding binary \
  26                -translation binary
  27        fileevent $fd writable [list \
  28                write_update_indexinfo \
  29                $fd \
  30                $pathList \
  31                $totalCnt \
  32                $batch \
  33                $msg \
  34                $after \
  35                ]
  36}
  37
  38proc write_update_indexinfo {fd pathList totalCnt batch msg after} {
  39        global update_index_cp ui_status_value
  40        global file_states current_diff_path
  41
  42        if {$update_index_cp >= $totalCnt} {
  43                close $fd
  44                unlock_index
  45                uplevel #0 $after
  46                return
  47        }
  48
  49        for {set i $batch} \
  50                {$update_index_cp < $totalCnt && $i > 0} \
  51                {incr i -1} {
  52                set path [lindex $pathList $update_index_cp]
  53                incr update_index_cp
  54
  55                set s $file_states($path)
  56                switch -glob -- [lindex $s 0] {
  57                A? {set new _O}
  58                M? {set new _M}
  59                D_ {set new _D}
  60                D? {set new _?}
  61                ?? {continue}
  62                }
  63                set info [lindex $s 2]
  64                if {$info eq {}} continue
  65
  66                puts -nonewline $fd "$info\t[encoding convertto $path]\0"
  67                display_file $path $new
  68        }
  69
  70        set ui_status_value [format \
  71                "$msg... %i/%i files (%.2f%%)" \
  72                $update_index_cp \
  73                $totalCnt \
  74                [expr {100.0 * $update_index_cp / $totalCnt}]]
  75}
  76
  77proc update_index {msg pathList after} {
  78        global update_index_cp ui_status_value
  79
  80        if {![lock_index update]} return
  81
  82        set update_index_cp 0
  83        set pathList [lsort $pathList]
  84        set totalCnt [llength $pathList]
  85        set batch [expr {int($totalCnt * .01) + 1}]
  86        if {$batch > 25} {set batch 25}
  87
  88        set ui_status_value [format \
  89                "$msg... %i/%i files (%.2f%%)" \
  90                $update_index_cp \
  91                $totalCnt \
  92                0.0]
  93        set fd [open "| git update-index --add --remove -z --stdin" w]
  94        fconfigure $fd \
  95                -blocking 0 \
  96                -buffering full \
  97                -buffersize 512 \
  98                -encoding binary \
  99                -translation binary
 100        fileevent $fd writable [list \
 101                write_update_index \
 102                $fd \
 103                $pathList \
 104                $totalCnt \
 105                $batch \
 106                $msg \
 107                $after \
 108                ]
 109}
 110
 111proc write_update_index {fd pathList totalCnt batch msg after} {
 112        global update_index_cp ui_status_value
 113        global file_states current_diff_path
 114
 115        if {$update_index_cp >= $totalCnt} {
 116                close $fd
 117                unlock_index
 118                uplevel #0 $after
 119                return
 120        }
 121
 122        for {set i $batch} \
 123                {$update_index_cp < $totalCnt && $i > 0} \
 124                {incr i -1} {
 125                set path [lindex $pathList $update_index_cp]
 126                incr update_index_cp
 127
 128                switch -glob -- [lindex $file_states($path) 0] {
 129                AD {set new __}
 130                ?D {set new D_}
 131                _O -
 132                AM {set new A_}
 133                U? {
 134                        if {[file exists $path]} {
 135                                set new M_
 136                        } else {
 137                                set new D_
 138                        }
 139                }
 140                ?M {set new M_}
 141                ?? {continue}
 142                }
 143                puts -nonewline $fd "[encoding convertto $path]\0"
 144                display_file $path $new
 145        }
 146
 147        set ui_status_value [format \
 148                "$msg... %i/%i files (%.2f%%)" \
 149                $update_index_cp \
 150                $totalCnt \
 151                [expr {100.0 * $update_index_cp / $totalCnt}]]
 152}
 153
 154proc checkout_index {msg pathList after} {
 155        global update_index_cp ui_status_value
 156
 157        if {![lock_index update]} return
 158
 159        set update_index_cp 0
 160        set pathList [lsort $pathList]
 161        set totalCnt [llength $pathList]
 162        set batch [expr {int($totalCnt * .01) + 1}]
 163        if {$batch > 25} {set batch 25}
 164
 165        set ui_status_value [format \
 166                "$msg... %i/%i files (%.2f%%)" \
 167                $update_index_cp \
 168                $totalCnt \
 169                0.0]
 170        set cmd [list git checkout-index]
 171        lappend cmd --index
 172        lappend cmd --quiet
 173        lappend cmd --force
 174        lappend cmd -z
 175        lappend cmd --stdin
 176        set fd [open "| $cmd " w]
 177        fconfigure $fd \
 178                -blocking 0 \
 179                -buffering full \
 180                -buffersize 512 \
 181                -encoding binary \
 182                -translation binary
 183        fileevent $fd writable [list \
 184                write_checkout_index \
 185                $fd \
 186                $pathList \
 187                $totalCnt \
 188                $batch \
 189                $msg \
 190                $after \
 191                ]
 192}
 193
 194proc write_checkout_index {fd pathList totalCnt batch msg after} {
 195        global update_index_cp ui_status_value
 196        global file_states current_diff_path
 197
 198        if {$update_index_cp >= $totalCnt} {
 199                close $fd
 200                unlock_index
 201                uplevel #0 $after
 202                return
 203        }
 204
 205        for {set i $batch} \
 206                {$update_index_cp < $totalCnt && $i > 0} \
 207                {incr i -1} {
 208                set path [lindex $pathList $update_index_cp]
 209                incr update_index_cp
 210                switch -glob -- [lindex $file_states($path) 0] {
 211                U? {continue}
 212                ?M -
 213                ?D {
 214                        puts -nonewline $fd "[encoding convertto $path]\0"
 215                        display_file $path ?_
 216                }
 217                }
 218        }
 219
 220        set ui_status_value [format \
 221                "$msg... %i/%i files (%.2f%%)" \
 222                $update_index_cp \
 223                $totalCnt \
 224                [expr {100.0 * $update_index_cp / $totalCnt}]]
 225}
 226
 227proc unstage_helper {txt paths} {
 228        global file_states current_diff_path
 229
 230        if {![lock_index begin-update]} return
 231
 232        set pathList [list]
 233        set after {}
 234        foreach path $paths {
 235                switch -glob -- [lindex $file_states($path) 0] {
 236                A? -
 237                M? -
 238                D? {
 239                        lappend pathList $path
 240                        if {$path eq $current_diff_path} {
 241                                set after {reshow_diff;}
 242                        }
 243                }
 244                }
 245        }
 246        if {$pathList eq {}} {
 247                unlock_index
 248        } else {
 249                update_indexinfo \
 250                        $txt \
 251                        $pathList \
 252                        [concat $after {set ui_status_value {Ready.}}]
 253        }
 254}
 255
 256proc do_unstage_selection {} {
 257        global current_diff_path selected_paths
 258
 259        if {[array size selected_paths] > 0} {
 260                unstage_helper \
 261                        {Unstaging selected files from commit} \
 262                        [array names selected_paths]
 263        } elseif {$current_diff_path ne {}} {
 264                unstage_helper \
 265                        "Unstaging [short_path $current_diff_path] from commit" \
 266                        [list $current_diff_path]
 267        }
 268}
 269
 270proc add_helper {txt paths} {
 271        global file_states current_diff_path
 272
 273        if {![lock_index begin-update]} return
 274
 275        set pathList [list]
 276        set after {}
 277        foreach path $paths {
 278                switch -glob -- [lindex $file_states($path) 0] {
 279                _O -
 280                ?M -
 281                ?D -
 282                U? {
 283                        lappend pathList $path
 284                        if {$path eq $current_diff_path} {
 285                                set after {reshow_diff;}
 286                        }
 287                }
 288                }
 289        }
 290        if {$pathList eq {}} {
 291                unlock_index
 292        } else {
 293                update_index \
 294                        $txt \
 295                        $pathList \
 296                        [concat $after {set ui_status_value {Ready to commit.}}]
 297        }
 298}
 299
 300proc do_add_selection {} {
 301        global current_diff_path selected_paths
 302
 303        if {[array size selected_paths] > 0} {
 304                add_helper \
 305                        {Adding selected files} \
 306                        [array names selected_paths]
 307        } elseif {$current_diff_path ne {}} {
 308                add_helper \
 309                        "Adding [short_path $current_diff_path]" \
 310                        [list $current_diff_path]
 311        }
 312}
 313
 314proc do_add_all {} {
 315        global file_states
 316
 317        set paths [list]
 318        foreach path [array names file_states] {
 319                switch -glob -- [lindex $file_states($path) 0] {
 320                U? {continue}
 321                ?M -
 322                ?D {lappend paths $path}
 323                }
 324        }
 325        add_helper {Adding all changed files} $paths
 326}
 327
 328proc revert_helper {txt paths} {
 329        global file_states current_diff_path
 330
 331        if {![lock_index begin-update]} return
 332
 333        set pathList [list]
 334        set after {}
 335        foreach path $paths {
 336                switch -glob -- [lindex $file_states($path) 0] {
 337                U? {continue}
 338                ?M -
 339                ?D {
 340                        lappend pathList $path
 341                        if {$path eq $current_diff_path} {
 342                                set after {reshow_diff;}
 343                        }
 344                }
 345                }
 346        }
 347
 348        set n [llength $pathList]
 349        if {$n == 0} {
 350                unlock_index
 351                return
 352        } elseif {$n == 1} {
 353                set s "[short_path [lindex $pathList]]"
 354        } else {
 355                set s "these $n files"
 356        }
 357
 358        set reply [tk_dialog \
 359                .confirm_revert \
 360                "[appname] ([reponame])" \
 361                "Revert changes in $s?
 362
 363Any unadded changes will be permanently lost by the revert." \
 364                question \
 365                1 \
 366                {Do Nothing} \
 367                {Revert Changes} \
 368                ]
 369        if {$reply == 1} {
 370                checkout_index \
 371                        $txt \
 372                        $pathList \
 373                        [concat $after {set ui_status_value {Ready.}}]
 374        } else {
 375                unlock_index
 376        }
 377}
 378
 379proc do_revert_selection {} {
 380        global current_diff_path selected_paths
 381
 382        if {[array size selected_paths] > 0} {
 383                revert_helper \
 384                        {Reverting selected files} \
 385                        [array names selected_paths]
 386        } elseif {$current_diff_path ne {}} {
 387                revert_helper \
 388                        "Reverting [short_path $current_diff_path]" \
 389                        [list $current_diff_path]
 390        }
 391}
 392
 393proc do_select_commit_type {} {
 394        global commit_type selected_commit_type
 395
 396        if {$selected_commit_type eq {new}
 397                && [string match amend* $commit_type]} {
 398                create_new_commit
 399        } elseif {$selected_commit_type eq {amend}
 400                && ![string match amend* $commit_type]} {
 401                load_last_commit
 402
 403                # The amend request was rejected...
 404                #
 405                if {![string match amend* $commit_type]} {
 406                        set selected_commit_type new
 407                }
 408        }
 409}