git-gui / lib / index.tclon commit Merge branch 'jk/maint-add--interactive-delete' (9382587)
   1# git-gui index (add/remove) support
   2# Copyright (C) 2006, 2007 Shawn Pearce
   3
   4proc _delete_indexlock {} {
   5        if {[catch {file delete -- [gitdir index.lock]} err]} {
   6                error_popup [strcat [mc "Unable to unlock the index."] "\n\n$err"]
   7        }
   8}
   9
  10proc _close_updateindex {fd after} {
  11        global use_ttk NS
  12        fconfigure $fd -blocking 1
  13        if {[catch {close $fd} err]} {
  14                set w .indexfried
  15                Dialog $w
  16                wm withdraw $w
  17                wm title $w [strcat "[appname] ([reponame]): " [mc "Index Error"]]
  18                wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
  19                set s [mc "Updating the Git index failed.  A rescan will be automatically started to resynchronize git-gui."]
  20                text $w.msg -yscrollcommand [list $w.vs set] \
  21                        -width [string length $s] -relief flat \
  22                        -borderwidth 0 -highlightthickness 0 \
  23                        -background [get_bg_color $w]
  24                $w.msg tag configure bold -font font_uibold -justify center
  25                ${NS}::scrollbar $w.vs -command [list $w.msg yview]
  26                $w.msg insert end $s bold \n\n$err {}
  27                $w.msg configure -state disabled
  28
  29                ${NS}::button $w.continue \
  30                        -text [mc "Continue"] \
  31                        -command [list destroy $w]
  32                ${NS}::button $w.unlock \
  33                        -text [mc "Unlock Index"] \
  34                        -command "destroy $w; _delete_indexlock"
  35                grid $w.msg - $w.vs -sticky news
  36                grid $w.unlock $w.continue - -sticky se -padx 2 -pady 2
  37                grid columnconfigure $w 0 -weight 1
  38                grid rowconfigure $w 0 -weight 1
  39
  40                wm protocol $w WM_DELETE_WINDOW update
  41                bind $w.continue <Visibility> "
  42                        grab $w
  43                        focus %W
  44                "
  45                wm deiconify $w
  46                tkwait window $w
  47
  48                $::main_status stop
  49                unlock_index
  50                rescan $after 0
  51                return
  52        }
  53
  54        $::main_status stop
  55        unlock_index
  56        uplevel #0 $after
  57}
  58
  59proc update_indexinfo {msg pathList after} {
  60        global update_index_cp
  61
  62        if {![lock_index update]} return
  63
  64        set update_index_cp 0
  65        set pathList [lsort $pathList]
  66        set totalCnt [llength $pathList]
  67        set batch [expr {int($totalCnt * .01) + 1}]
  68        if {$batch > 25} {set batch 25}
  69
  70        $::main_status start $msg [mc "files"]
  71        set fd [git_write update-index -z --index-info]
  72        fconfigure $fd \
  73                -blocking 0 \
  74                -buffering full \
  75                -buffersize 512 \
  76                -encoding binary \
  77                -translation binary
  78        fileevent $fd writable [list \
  79                write_update_indexinfo \
  80                $fd \
  81                $pathList \
  82                $totalCnt \
  83                $batch \
  84                $after \
  85                ]
  86}
  87
  88proc write_update_indexinfo {fd pathList totalCnt batch after} {
  89        global update_index_cp
  90        global file_states current_diff_path
  91
  92        if {$update_index_cp >= $totalCnt} {
  93                _close_updateindex $fd $after
  94                return
  95        }
  96
  97        for {set i $batch} \
  98                {$update_index_cp < $totalCnt && $i > 0} \
  99                {incr i -1} {
 100                set path [lindex $pathList $update_index_cp]
 101                incr update_index_cp
 102
 103                set s $file_states($path)
 104                switch -glob -- [lindex $s 0] {
 105                A? {set new _O}
 106                M? {set new _M}
 107                T_ {set new _T}
 108                D_ {set new _D}
 109                D? {set new _?}
 110                ?? {continue}
 111                }
 112                set info [lindex $s 2]
 113                if {$info eq {}} continue
 114
 115                puts -nonewline $fd "$info\t[encoding convertto $path]\0"
 116                display_file $path $new
 117        }
 118
 119        $::main_status update $update_index_cp $totalCnt
 120}
 121
 122proc update_index {msg pathList after} {
 123        global update_index_cp
 124
 125        if {![lock_index update]} return
 126
 127        set update_index_cp 0
 128        set pathList [lsort $pathList]
 129        set totalCnt [llength $pathList]
 130        set batch [expr {int($totalCnt * .01) + 1}]
 131        if {$batch > 25} {set batch 25}
 132
 133        $::main_status start $msg [mc "files"]
 134        set fd [git_write update-index --add --remove -z --stdin]
 135        fconfigure $fd \
 136                -blocking 0 \
 137                -buffering full \
 138                -buffersize 512 \
 139                -encoding binary \
 140                -translation binary
 141        fileevent $fd writable [list \
 142                write_update_index \
 143                $fd \
 144                $pathList \
 145                $totalCnt \
 146                $batch \
 147                $after \
 148                ]
 149}
 150
 151proc write_update_index {fd pathList totalCnt batch after} {
 152        global update_index_cp
 153        global file_states current_diff_path
 154
 155        if {$update_index_cp >= $totalCnt} {
 156                _close_updateindex $fd $after
 157                return
 158        }
 159
 160        for {set i $batch} \
 161                {$update_index_cp < $totalCnt && $i > 0} \
 162                {incr i -1} {
 163                set path [lindex $pathList $update_index_cp]
 164                incr update_index_cp
 165
 166                switch -glob -- [lindex $file_states($path) 0] {
 167                AD {set new __}
 168                ?D {set new D_}
 169                _O -
 170                AM {set new A_}
 171                _T {set new T_}
 172                _U -
 173                U? {
 174                        if {[file exists $path]} {
 175                                set new M_
 176                        } else {
 177                                set new D_
 178                        }
 179                }
 180                ?M {set new M_}
 181                ?? {continue}
 182                }
 183                puts -nonewline $fd "[encoding convertto $path]\0"
 184                display_file $path $new
 185        }
 186
 187        $::main_status update $update_index_cp $totalCnt
 188}
 189
 190proc checkout_index {msg pathList after} {
 191        global update_index_cp
 192
 193        if {![lock_index update]} return
 194
 195        set update_index_cp 0
 196        set pathList [lsort $pathList]
 197        set totalCnt [llength $pathList]
 198        set batch [expr {int($totalCnt * .01) + 1}]
 199        if {$batch > 25} {set batch 25}
 200
 201        $::main_status start $msg [mc "files"]
 202        set fd [git_write checkout-index \
 203                --index \
 204                --quiet \
 205                --force \
 206                -z \
 207                --stdin \
 208                ]
 209        fconfigure $fd \
 210                -blocking 0 \
 211                -buffering full \
 212                -buffersize 512 \
 213                -encoding binary \
 214                -translation binary
 215        fileevent $fd writable [list \
 216                write_checkout_index \
 217                $fd \
 218                $pathList \
 219                $totalCnt \
 220                $batch \
 221                $after \
 222                ]
 223}
 224
 225proc write_checkout_index {fd pathList totalCnt batch after} {
 226        global update_index_cp
 227        global file_states current_diff_path
 228
 229        if {$update_index_cp >= $totalCnt} {
 230                _close_updateindex $fd $after
 231                return
 232        }
 233
 234        for {set i $batch} \
 235                {$update_index_cp < $totalCnt && $i > 0} \
 236                {incr i -1} {
 237                set path [lindex $pathList $update_index_cp]
 238                incr update_index_cp
 239                switch -glob -- [lindex $file_states($path) 0] {
 240                U? {continue}
 241                ?M -
 242                ?T -
 243                ?D {
 244                        puts -nonewline $fd "[encoding convertto $path]\0"
 245                        display_file $path ?_
 246                }
 247                }
 248        }
 249
 250        $::main_status update $update_index_cp $totalCnt
 251}
 252
 253proc unstage_helper {txt paths} {
 254        global file_states current_diff_path
 255
 256        if {![lock_index begin-update]} return
 257
 258        set pathList [list]
 259        set after {}
 260        foreach path $paths {
 261                switch -glob -- [lindex $file_states($path) 0] {
 262                A? -
 263                M? -
 264                T_ -
 265                D? {
 266                        lappend pathList $path
 267                        if {$path eq $current_diff_path} {
 268                                set after {reshow_diff;}
 269                        }
 270                }
 271                }
 272        }
 273        if {$pathList eq {}} {
 274                unlock_index
 275        } else {
 276                update_indexinfo \
 277                        $txt \
 278                        $pathList \
 279                        [concat $after [list ui_ready]]
 280        }
 281}
 282
 283proc do_unstage_selection {} {
 284        global current_diff_path selected_paths
 285
 286        if {[array size selected_paths] > 0} {
 287                unstage_helper \
 288                        {Unstaging selected files from commit} \
 289                        [array names selected_paths]
 290        } elseif {$current_diff_path ne {}} {
 291                unstage_helper \
 292                        [mc "Unstaging %s from commit" [short_path $current_diff_path]] \
 293                        [list $current_diff_path]
 294        }
 295}
 296
 297proc add_helper {txt paths} {
 298        global file_states current_diff_path
 299
 300        if {![lock_index begin-update]} return
 301
 302        set pathList [list]
 303        set after {}
 304        foreach path $paths {
 305                switch -glob -- [lindex $file_states($path) 0] {
 306                _U -
 307                U? {
 308                        if {$path eq $current_diff_path} {
 309                                unlock_index
 310                                merge_stage_workdir $path
 311                                return
 312                        }
 313                }
 314                _O -
 315                ?M -
 316                ?D -
 317                ?T {
 318                        lappend pathList $path
 319                        if {$path eq $current_diff_path} {
 320                                set after {reshow_diff;}
 321                        }
 322                }
 323                }
 324        }
 325        if {$pathList eq {}} {
 326                unlock_index
 327        } else {
 328                update_index \
 329                        $txt \
 330                        $pathList \
 331                        [concat $after {ui_status [mc "Ready to commit."]}]
 332        }
 333}
 334
 335proc do_add_selection {} {
 336        global current_diff_path selected_paths
 337
 338        if {[array size selected_paths] > 0} {
 339                add_helper \
 340                        {Adding selected files} \
 341                        [array names selected_paths]
 342        } elseif {$current_diff_path ne {}} {
 343                add_helper \
 344                        [mc "Adding %s" [short_path $current_diff_path]] \
 345                        [list $current_diff_path]
 346        }
 347}
 348
 349proc do_add_all {} {
 350        global file_states
 351
 352        set paths [list]
 353        foreach path [array names file_states] {
 354                switch -glob -- [lindex $file_states($path) 0] {
 355                U? {continue}
 356                ?M -
 357                ?T -
 358                ?D {lappend paths $path}
 359                }
 360        }
 361        add_helper {Adding all changed files} $paths
 362}
 363
 364proc revert_helper {txt paths} {
 365        global file_states current_diff_path
 366
 367        if {![lock_index begin-update]} return
 368
 369        set pathList [list]
 370        set after {}
 371        foreach path $paths {
 372                switch -glob -- [lindex $file_states($path) 0] {
 373                U? {continue}
 374                ?M -
 375                ?T -
 376                ?D {
 377                        lappend pathList $path
 378                        if {$path eq $current_diff_path} {
 379                                set after {reshow_diff;}
 380                        }
 381                }
 382                }
 383        }
 384
 385
 386        # Split question between singular and plural cases, because
 387        # such distinction is needed in some languages. Previously, the
 388        # code used "Revert changes in" for both, but that can't work
 389        # in languages where 'in' must be combined with word from
 390        # rest of string (in diffrent way for both cases of course).
 391        #
 392        # FIXME: Unfortunately, even that isn't enough in some languages
 393        # as they have quite complex plural-form rules. Unfortunately,
 394        # msgcat doesn't seem to support that kind of string translation.
 395        #
 396        set n [llength $pathList]
 397        if {$n == 0} {
 398                unlock_index
 399                return
 400        } elseif {$n == 1} {
 401                set query [mc "Revert changes in file %s?" [short_path [lindex $pathList]]]
 402        } else {
 403                set query [mc "Revert changes in these %i files?" $n]
 404        }
 405
 406        set reply [tk_dialog \
 407                .confirm_revert \
 408                "[appname] ([reponame])" \
 409                "$query
 410
 411[mc "Any unstaged changes will be permanently lost by the revert."]" \
 412                question \
 413                1 \
 414                [mc "Do Nothing"] \
 415                [mc "Revert Changes"] \
 416                ]
 417        if {$reply == 1} {
 418                checkout_index \
 419                        $txt \
 420                        $pathList \
 421                        [concat $after [list ui_ready]]
 422        } else {
 423                unlock_index
 424        }
 425}
 426
 427proc do_revert_selection {} {
 428        global current_diff_path selected_paths
 429
 430        if {[array size selected_paths] > 0} {
 431                revert_helper \
 432                        [mc "Reverting selected files"] \
 433                        [array names selected_paths]
 434        } elseif {$current_diff_path ne {}} {
 435                revert_helper \
 436                        [mc "Reverting %s" [short_path $current_diff_path]] \
 437                        [list $current_diff_path]
 438        }
 439}
 440
 441proc do_select_commit_type {} {
 442        global commit_type selected_commit_type
 443
 444        if {$selected_commit_type eq {new}
 445                && [string match amend* $commit_type]} {
 446                create_new_commit
 447        } elseif {$selected_commit_type eq {amend}
 448                && ![string match amend* $commit_type]} {
 449                load_last_commit
 450
 451                # The amend request was rejected...
 452                #
 453                if {![string match amend* $commit_type]} {
 454                        set selected_commit_type new
 455                }
 456        }
 457}