git-gui / lib / index.tclon commit Merge branch 'maint' (267123b)
   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                D_ {set new _D}
 103                D? {set new _?}
 104                ?? {continue}
 105                }
 106                set info [lindex $s 2]
 107                if {$info eq {}} continue
 108
 109                puts -nonewline $fd "$info\t[encoding convertto $path]\0"
 110                display_file $path $new
 111        }
 112
 113        $::main_status update $update_index_cp $totalCnt
 114}
 115
 116proc update_index {msg pathList after} {
 117        global update_index_cp
 118
 119        if {![lock_index update]} return
 120
 121        set update_index_cp 0
 122        set pathList [lsort $pathList]
 123        set totalCnt [llength $pathList]
 124        set batch [expr {int($totalCnt * .01) + 1}]
 125        if {$batch > 25} {set batch 25}
 126
 127        $::main_status start $msg [mc "files"]
 128        set fd [git_write update-index --add --remove -z --stdin]
 129        fconfigure $fd \
 130                -blocking 0 \
 131                -buffering full \
 132                -buffersize 512 \
 133                -encoding binary \
 134                -translation binary
 135        fileevent $fd writable [list \
 136                write_update_index \
 137                $fd \
 138                $pathList \
 139                $totalCnt \
 140                $batch \
 141                $after \
 142                ]
 143}
 144
 145proc write_update_index {fd pathList totalCnt batch after} {
 146        global update_index_cp
 147        global file_states current_diff_path
 148
 149        if {$update_index_cp >= $totalCnt} {
 150                _close_updateindex $fd $after
 151                return
 152        }
 153
 154        for {set i $batch} \
 155                {$update_index_cp < $totalCnt && $i > 0} \
 156                {incr i -1} {
 157                set path [lindex $pathList $update_index_cp]
 158                incr update_index_cp
 159
 160                switch -glob -- [lindex $file_states($path) 0] {
 161                AD {set new __}
 162                ?D {set new D_}
 163                _O -
 164                AM {set new A_}
 165                U? {
 166                        if {[file exists $path]} {
 167                                set new M_
 168                        } else {
 169                                set new D_
 170                        }
 171                }
 172                ?M {set new M_}
 173                ?? {continue}
 174                }
 175                puts -nonewline $fd "[encoding convertto $path]\0"
 176                display_file $path $new
 177        }
 178
 179        $::main_status update $update_index_cp $totalCnt
 180}
 181
 182proc checkout_index {msg pathList after} {
 183        global update_index_cp
 184
 185        if {![lock_index update]} return
 186
 187        set update_index_cp 0
 188        set pathList [lsort $pathList]
 189        set totalCnt [llength $pathList]
 190        set batch [expr {int($totalCnt * .01) + 1}]
 191        if {$batch > 25} {set batch 25}
 192
 193        $::main_status start $msg [mc "files"]
 194        set fd [git_write checkout-index \
 195                --index \
 196                --quiet \
 197                --force \
 198                -z \
 199                --stdin \
 200                ]
 201        fconfigure $fd \
 202                -blocking 0 \
 203                -buffering full \
 204                -buffersize 512 \
 205                -encoding binary \
 206                -translation binary
 207        fileevent $fd writable [list \
 208                write_checkout_index \
 209                $fd \
 210                $pathList \
 211                $totalCnt \
 212                $batch \
 213                $after \
 214                ]
 215}
 216
 217proc write_checkout_index {fd pathList totalCnt batch after} {
 218        global update_index_cp
 219        global file_states current_diff_path
 220
 221        if {$update_index_cp >= $totalCnt} {
 222                _close_updateindex $fd $after
 223                return
 224        }
 225
 226        for {set i $batch} \
 227                {$update_index_cp < $totalCnt && $i > 0} \
 228                {incr i -1} {
 229                set path [lindex $pathList $update_index_cp]
 230                incr update_index_cp
 231                switch -glob -- [lindex $file_states($path) 0] {
 232                U? {continue}
 233                ?M -
 234                ?D {
 235                        puts -nonewline $fd "[encoding convertto $path]\0"
 236                        display_file $path ?_
 237                }
 238                }
 239        }
 240
 241        $::main_status update $update_index_cp $totalCnt
 242}
 243
 244proc unstage_helper {txt paths} {
 245        global file_states current_diff_path
 246
 247        if {![lock_index begin-update]} return
 248
 249        set pathList [list]
 250        set after {}
 251        foreach path $paths {
 252                switch -glob -- [lindex $file_states($path) 0] {
 253                A? -
 254                M? -
 255                D? {
 256                        lappend pathList $path
 257                        if {$path eq $current_diff_path} {
 258                                set after {reshow_diff;}
 259                        }
 260                }
 261                }
 262        }
 263        if {$pathList eq {}} {
 264                unlock_index
 265        } else {
 266                update_indexinfo \
 267                        $txt \
 268                        $pathList \
 269                        [concat $after [list ui_ready]]
 270        }
 271}
 272
 273proc do_unstage_selection {} {
 274        global current_diff_path selected_paths
 275
 276        if {[array size selected_paths] > 0} {
 277                unstage_helper \
 278                        {Unstaging selected files from commit} \
 279                        [array names selected_paths]
 280        } elseif {$current_diff_path ne {}} {
 281                unstage_helper \
 282                        [mc "Unstaging %s from commit" [short_path $current_diff_path]] \
 283                        [list $current_diff_path]
 284        }
 285}
 286
 287proc add_helper {txt paths} {
 288        global file_states current_diff_path
 289
 290        if {![lock_index begin-update]} return
 291
 292        set pathList [list]
 293        set after {}
 294        foreach path $paths {
 295                switch -glob -- [lindex $file_states($path) 0] {
 296                _O -
 297                ?M -
 298                ?D -
 299                U? {
 300                        lappend pathList $path
 301                        if {$path eq $current_diff_path} {
 302                                set after {reshow_diff;}
 303                        }
 304                }
 305                }
 306        }
 307        if {$pathList eq {}} {
 308                unlock_index
 309        } else {
 310                update_index \
 311                        $txt \
 312                        $pathList \
 313                        [concat $after {ui_status [mc "Ready to commit."]}]
 314        }
 315}
 316
 317proc do_add_selection {} {
 318        global current_diff_path selected_paths
 319
 320        if {[array size selected_paths] > 0} {
 321                add_helper \
 322                        {Adding selected files} \
 323                        [array names selected_paths]
 324        } elseif {$current_diff_path ne {}} {
 325                add_helper \
 326                        [mc "Adding %s" [short_path $current_diff_path]] \
 327                        [list $current_diff_path]
 328        }
 329}
 330
 331proc do_add_all {} {
 332        global file_states
 333
 334        set paths [list]
 335        foreach path [array names file_states] {
 336                switch -glob -- [lindex $file_states($path) 0] {
 337                U? {continue}
 338                ?M -
 339                ?D {lappend paths $path}
 340                }
 341        }
 342        add_helper {Adding all changed files} $paths
 343}
 344
 345proc revert_helper {txt paths} {
 346        global file_states current_diff_path
 347
 348        if {![lock_index begin-update]} return
 349
 350        set pathList [list]
 351        set after {}
 352        foreach path $paths {
 353                switch -glob -- [lindex $file_states($path) 0] {
 354                U? {continue}
 355                ?M -
 356                ?D {
 357                        lappend pathList $path
 358                        if {$path eq $current_diff_path} {
 359                                set after {reshow_diff;}
 360                        }
 361                }
 362                }
 363        }
 364
 365
 366        # Split question between singular and plural cases, because
 367        # such distinction is needed in some languages. Previously, the
 368        # code used "Revert changes in" for both, but that can't work
 369        # in languages where 'in' must be combined with word from
 370        # rest of string (in diffrent way for both cases of course).
 371        #
 372        # FIXME: Unfortunately, even that isn't enough in some languages
 373        # as they have quite complex plural-form rules. Unfortunately,
 374        # msgcat doesn't seem to support that kind of string translation.
 375        #
 376        set n [llength $pathList]
 377        if {$n == 0} {
 378                unlock_index
 379                return
 380        } elseif {$n == 1} {
 381                set query [mc "Revert changes in file %s?" [short_path [lindex $pathList]]]
 382        } else {
 383                set query [mc "Revert changes in these %i files?" $n]
 384        }
 385
 386        set reply [tk_dialog \
 387                .confirm_revert \
 388                "[appname] ([reponame])" \
 389                "$query
 390
 391[mc "Any unstaged changes will be permanently lost by the revert."]" \
 392                question \
 393                1 \
 394                [mc "Do Nothing"] \
 395                [mc "Revert Changes"] \
 396                ]
 397        if {$reply == 1} {
 398                checkout_index \
 399                        $txt \
 400                        $pathList \
 401                        [concat $after [list ui_ready]]
 402        } else {
 403                unlock_index
 404        }
 405}
 406
 407proc do_revert_selection {} {
 408        global current_diff_path selected_paths
 409
 410        if {[array size selected_paths] > 0} {
 411                revert_helper \
 412                        {Reverting selected files} \
 413                        [array names selected_paths]
 414        } elseif {$current_diff_path ne {}} {
 415                revert_helper \
 416                        "Reverting [short_path $current_diff_path]" \
 417                        [list $current_diff_path]
 418        }
 419}
 420
 421proc do_select_commit_type {} {
 422        global commit_type selected_commit_type
 423
 424        if {$selected_commit_type eq {new}
 425                && [string match amend* $commit_type]} {
 426                create_new_commit
 427        } elseif {$selected_commit_type eq {amend}
 428                && ![string match amend* $commit_type]} {
 429                load_last_commit
 430
 431                # The amend request was rejected...
 432                #
 433                if {![string match amend* $commit_type]} {
 434                        set selected_commit_type new
 435                }
 436        }
 437}