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