git-gui / lib / index.tclon commit Merge branch 'jc/1.7.0-push-safety' (7ad9cec)
   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                _U -
 302                U? {
 303                        if {$path eq $current_diff_path} {
 304                                unlock_index
 305                                merge_stage_workdir $path
 306                                return
 307                        }
 308                }
 309                _O -
 310                ?M -
 311                ?D -
 312                ?T {
 313                        lappend pathList $path
 314                        if {$path eq $current_diff_path} {
 315                                set after {reshow_diff;}
 316                        }
 317                }
 318                }
 319        }
 320        if {$pathList eq {}} {
 321                unlock_index
 322        } else {
 323                update_index \
 324                        $txt \
 325                        $pathList \
 326                        [concat $after {ui_status [mc "Ready to commit."]}]
 327        }
 328}
 329
 330proc do_add_selection {} {
 331        global current_diff_path selected_paths
 332
 333        if {[array size selected_paths] > 0} {
 334                add_helper \
 335                        {Adding selected files} \
 336                        [array names selected_paths]
 337        } elseif {$current_diff_path ne {}} {
 338                add_helper \
 339                        [mc "Adding %s" [short_path $current_diff_path]] \
 340                        [list $current_diff_path]
 341        }
 342}
 343
 344proc do_add_all {} {
 345        global file_states
 346
 347        set paths [list]
 348        foreach path [array names file_states] {
 349                switch -glob -- [lindex $file_states($path) 0] {
 350                U? {continue}
 351                ?M -
 352                ?T -
 353                ?D {lappend paths $path}
 354                }
 355        }
 356        add_helper {Adding all changed files} $paths
 357}
 358
 359proc revert_helper {txt paths} {
 360        global file_states current_diff_path
 361
 362        if {![lock_index begin-update]} return
 363
 364        set pathList [list]
 365        set after {}
 366        foreach path $paths {
 367                switch -glob -- [lindex $file_states($path) 0] {
 368                U? {continue}
 369                ?M -
 370                ?T -
 371                ?D {
 372                        lappend pathList $path
 373                        if {$path eq $current_diff_path} {
 374                                set after {reshow_diff;}
 375                        }
 376                }
 377                }
 378        }
 379
 380
 381        # Split question between singular and plural cases, because
 382        # such distinction is needed in some languages. Previously, the
 383        # code used "Revert changes in" for both, but that can't work
 384        # in languages where 'in' must be combined with word from
 385        # rest of string (in diffrent way for both cases of course).
 386        #
 387        # FIXME: Unfortunately, even that isn't enough in some languages
 388        # as they have quite complex plural-form rules. Unfortunately,
 389        # msgcat doesn't seem to support that kind of string translation.
 390        #
 391        set n [llength $pathList]
 392        if {$n == 0} {
 393                unlock_index
 394                return
 395        } elseif {$n == 1} {
 396                set query [mc "Revert changes in file %s?" [short_path [lindex $pathList]]]
 397        } else {
 398                set query [mc "Revert changes in these %i files?" $n]
 399        }
 400
 401        set reply [tk_dialog \
 402                .confirm_revert \
 403                "[appname] ([reponame])" \
 404                "$query
 405
 406[mc "Any unstaged changes will be permanently lost by the revert."]" \
 407                question \
 408                1 \
 409                [mc "Do Nothing"] \
 410                [mc "Revert Changes"] \
 411                ]
 412        if {$reply == 1} {
 413                checkout_index \
 414                        $txt \
 415                        $pathList \
 416                        [concat $after [list ui_ready]]
 417        } else {
 418                unlock_index
 419        }
 420}
 421
 422proc do_revert_selection {} {
 423        global current_diff_path selected_paths
 424
 425        if {[array size selected_paths] > 0} {
 426                revert_helper \
 427                        [mc "Reverting selected files"] \
 428                        [array names selected_paths]
 429        } elseif {$current_diff_path ne {}} {
 430                revert_helper \
 431                        [mc "Reverting %s" [short_path $current_diff_path]] \
 432                        [list $current_diff_path]
 433        }
 434}
 435
 436proc do_select_commit_type {} {
 437        global commit_type selected_commit_type
 438
 439        if {$selected_commit_type eq {new}
 440                && [string match amend* $commit_type]} {
 441                create_new_commit
 442        } elseif {$selected_commit_type eq {amend}
 443                && ![string match amend* $commit_type]} {
 444                load_last_commit
 445
 446                # The amend request was rejected...
 447                #
 448                if {![string match amend* $commit_type]} {
 449                        set selected_commit_type new
 450                }
 451        }
 452}