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