git-gui / lib / index.tclon commit Merge branch 'en/merge-recursive' (ac9666f)
   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        foreach path [array names file_states] {
 360                switch -glob -- [lindex $file_states($path) 0] {
 361                U? {continue}
 362                ?M -
 363                ?T -
 364                ?D {lappend paths $path}
 365                }
 366        }
 367        add_helper {Adding all changed files} $paths
 368}
 369
 370proc revert_helper {txt paths} {
 371        global file_states current_diff_path
 372
 373        if {![lock_index begin-update]} return
 374
 375        set pathList [list]
 376        set after {}
 377        foreach path $paths {
 378                switch -glob -- [lindex $file_states($path) 0] {
 379                U? {continue}
 380                ?M -
 381                ?T -
 382                ?D {
 383                        lappend pathList $path
 384                        if {$path eq $current_diff_path} {
 385                                set after {reshow_diff;}
 386                        }
 387                }
 388                }
 389        }
 390
 391
 392        # Split question between singular and plural cases, because
 393        # such distinction is needed in some languages. Previously, the
 394        # code used "Revert changes in" for both, but that can't work
 395        # in languages where 'in' must be combined with word from
 396        # rest of string (in diffrent way for both cases of course).
 397        #
 398        # FIXME: Unfortunately, even that isn't enough in some languages
 399        # as they have quite complex plural-form rules. Unfortunately,
 400        # msgcat doesn't seem to support that kind of string translation.
 401        #
 402        set n [llength $pathList]
 403        if {$n == 0} {
 404                unlock_index
 405                return
 406        } elseif {$n == 1} {
 407                set query [mc "Revert changes in file %s?" [short_path [lindex $pathList]]]
 408        } else {
 409                set query [mc "Revert changes in these %i files?" $n]
 410        }
 411
 412        set reply [tk_dialog \
 413                .confirm_revert \
 414                "[appname] ([reponame])" \
 415                "$query
 416
 417[mc "Any unstaged changes will be permanently lost by the revert."]" \
 418                question \
 419                1 \
 420                [mc "Do Nothing"] \
 421                [mc "Revert Changes"] \
 422                ]
 423        if {$reply == 1} {
 424                checkout_index \
 425                        $txt \
 426                        $pathList \
 427                        [concat $after [list ui_ready]]
 428        } else {
 429                unlock_index
 430        }
 431}
 432
 433proc do_revert_selection {} {
 434        global current_diff_path selected_paths
 435
 436        if {[array size selected_paths] > 0} {
 437                revert_helper \
 438                        [mc "Reverting selected files"] \
 439                        [array names selected_paths]
 440        } elseif {$current_diff_path ne {}} {
 441                revert_helper \
 442                        [mc "Reverting %s" [short_path $current_diff_path]] \
 443                        [list $current_diff_path]
 444        }
 445}
 446
 447proc do_select_commit_type {} {
 448        global commit_type selected_commit_type
 449
 450        if {$selected_commit_type eq {new}
 451                && [string match amend* $commit_type]} {
 452                create_new_commit
 453        } elseif {$selected_commit_type eq {amend}
 454                && ![string match amend* $commit_type]} {
 455                load_last_commit
 456
 457                # The amend request was rejected...
 458                #
 459                if {![string match amend* $commit_type]} {
 460                        set selected_commit_type new
 461                }
 462        }
 463}