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