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