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