5d5bc81ad00f8d5a1757c99e931d28e2b9d1d564
   1# git-gui Git repository chooser
   2# Copyright (C) 2007 Shawn Pearce
   3
   4class choose_repository {
   5
   6field top
   7field w
   8field w_body      ; # Widget holding the center content
   9field w_next      ; # Next button
  10field o_cons      ; # Console object (if active)
  11field w_types     ; # List of type buttons in clone
  12
  13field action          new ; # What action are we going to perform?
  14field done              0 ; # Finished picking the repository?
  15field local_path       {} ; # Where this repository is locally
  16field origin_url       {} ; # Where we are cloning from
  17field origin_name  origin ; # What we shall call 'origin'
  18field clone_type hardlink ; # Type of clone to construct
  19field readtree_err        ; # Error output from read-tree (if any)
  20
  21constructor pick {} {
  22        global M1T M1B
  23
  24        make_toplevel top w
  25        wm title $top [mc "Git Gui"]
  26
  27        if {$top eq {.}} {
  28                menu $w.mbar -tearoff 0
  29                $top configure -menu $w.mbar
  30
  31                $w.mbar add cascade \
  32                        -label [mc Repository] \
  33                        -menu $w.mbar.repository
  34                menu $w.mbar.repository
  35                $w.mbar.repository add command \
  36                        -label [mc Quit] \
  37                        -command exit \
  38                        -accelerator $M1T-Q
  39
  40                if {[is_MacOSX]} {
  41                        $w.mbar add cascade -label [mc Apple] -menu .mbar.apple
  42                        menu $w.mbar.apple
  43                        $w.mbar.apple add command \
  44                                -label [mc "About %s" [appname]] \
  45                                -command do_about
  46                } else {
  47                        $w.mbar add cascade -label [mc Help] -menu $w.mbar.help
  48                        menu $w.mbar.help
  49                        $w.mbar.help add command \
  50                                -label [mc "About %s" [appname]] \
  51                                -command do_about
  52                }
  53
  54                wm protocol $top WM_DELETE_WINDOW exit
  55                bind $top <$M1B-q> exit
  56                bind $top <$M1B-Q> exit
  57                bind $top <Key-Escape> exit
  58        } else {
  59                wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
  60                bind $top <Key-Escape> [list destroy $top]
  61        }
  62
  63        pack [git_logo $w.git_logo] -side left -fill y -padx 10 -pady 10
  64
  65        set w_body $w.body
  66        frame $w_body
  67        radiobutton $w_body.new \
  68                -anchor w \
  69                -text [mc "Create New Repository"] \
  70                -variable @action \
  71                -value new
  72        radiobutton $w_body.clone \
  73                -anchor w \
  74                -text [mc "Clone Existing Repository"] \
  75                -variable @action \
  76                -value clone
  77        radiobutton $w_body.open \
  78                -anchor w \
  79                -text [mc "Open Existing Repository"] \
  80                -variable @action \
  81                -value open
  82        pack $w_body.new -anchor w -fill x
  83        pack $w_body.clone -anchor w -fill x
  84        pack $w_body.open -anchor w -fill x
  85        pack $w_body -fill x -padx 10 -pady 10
  86
  87        frame $w.buttons
  88        set w_next $w.buttons.next
  89        button $w_next \
  90                -default active \
  91                -text [mc "Next >"] \
  92                -command [cb _next]
  93        pack $w_next -side right -padx 5
  94        button $w.buttons.quit \
  95                -text [mc "Quit"] \
  96                -command exit
  97        pack $w.buttons.quit -side right -padx 5
  98        pack $w.buttons -side bottom -fill x -padx 10 -pady 10
  99
 100        bind $top <Return> [cb _invoke_next]
 101        bind $top <Visibility> "
 102                [cb _center]
 103                grab $top
 104                focus $top
 105                bind $top <Visibility> {}
 106        "
 107        wm deiconify $top
 108        tkwait variable @done
 109
 110        if {$top eq {.}} {
 111                eval destroy [winfo children $top]
 112        }
 113}
 114
 115proc _home {} {
 116        if {[catch {set h $::env(HOME)}]
 117                || ![file isdirectory $h]} {
 118                set h .
 119        }
 120        return $h
 121}
 122
 123method _center {} {
 124        set nx [winfo reqwidth $top]
 125        set ny [winfo reqheight $top]
 126        set rx [expr {([winfo screenwidth  $top] - $nx) / 3}]
 127        set ry [expr {([winfo screenheight $top] - $ny) / 3}]
 128        wm geometry $top [format {+%d+%d} $rx $ry]
 129}
 130
 131method _invoke_next {} {
 132        if {[winfo exists $w_next]} {
 133                uplevel #0 [$w_next cget -command]
 134        }
 135}
 136
 137method _next {} {
 138        destroy $w_body
 139        _do_$action $this
 140}
 141
 142method _write_local_path {args} {
 143        if {$local_path eq {}} {
 144                $w_next conf -state disabled
 145        } else {
 146                $w_next conf -state normal
 147        }
 148}
 149
 150method _git_init {} {
 151        if {[file exists $local_path]} {
 152                error_popup [mc "Location %s already exists." $local_path]
 153                return 0
 154        }
 155
 156        if {[catch {file mkdir $local_path} err]} {
 157                error_popup [strcat \
 158                        [mc "Failed to create repository %s:" $local_path] \
 159                        "\n\n$err"]
 160                return 0
 161        }
 162
 163        if {[catch {cd $local_path} err]} {
 164                error_popup [strcat \
 165                        [mc "Failed to create repository %s:" $local_path] \
 166                        "\n\n$err"]
 167                return 0
 168        }
 169
 170        if {[catch {git init} err]} {
 171                error_popup [strcat \
 172                        [mc "Failed to create repository %s:" $local_path] \
 173                        "\n\n$err"]
 174                return 0
 175        }
 176
 177        set ::_gitdir .git
 178        set ::_prefix {}
 179        return 1
 180}
 181
 182proc _is_git {path} {
 183        if {[file exists [file join $path HEAD]]
 184         && [file exists [file join $path objects]]
 185         && [file exists [file join $path config]]} {
 186                return 1
 187        }
 188        return 0
 189}
 190
 191######################################################################
 192##
 193## Create New Repository
 194
 195method _do_new {} {
 196        $w_next conf \
 197                -state disabled \
 198                -command [cb _do_new2] \
 199                -text [mc "Create"]
 200
 201        frame $w_body
 202        label $w_body.h \
 203                -font font_uibold \
 204                -text [mc "Create New Repository"]
 205        pack $w_body.h -side top -fill x -pady 10
 206        pack $w_body -fill x -padx 10
 207
 208        frame $w_body.where
 209        label $w_body.where.l -text [mc "Directory:"]
 210        entry $w_body.where.t \
 211                -textvariable @local_path \
 212                -font font_diff \
 213                -width 50
 214        button $w_body.where.b \
 215                -text [mc "Browse"] \
 216                -command [cb _new_local_path]
 217
 218        pack $w_body.where.b -side right
 219        pack $w_body.where.l -side left
 220        pack $w_body.where.t -fill x
 221        pack $w_body.where -fill x
 222
 223        trace add variable @local_path write [cb _write_local_path]
 224        update
 225        focus $w_body.where.t
 226}
 227
 228method _new_local_path {} {
 229        if {$local_path ne {}} {
 230                set p [file dirname $local_path]
 231        } else {
 232                set p [_home]
 233        }
 234
 235        set p [tk_chooseDirectory \
 236                -initialdir $p \
 237                -parent $top \
 238                -title [mc "Git Repository"] \
 239                -mustexist false]
 240        if {$p eq {}} return
 241
 242        set p [file normalize $p]
 243        if {[file isdirectory $p]} {
 244                foreach i [glob \
 245                        -directory $p \
 246                        -tails \
 247                        -nocomplain \
 248                        * .*] {
 249                        switch -- $i {
 250                         . continue
 251                        .. continue
 252                        default {
 253                                error_popup [mc "Directory %s already exists." $p]
 254                                return
 255                        }
 256                        }
 257                }
 258                if {[catch {file delete $p} err]} {
 259                        error_popup [strcat \
 260                                [mc "Directory %s already exists." $p] \
 261                                "\n\n$err"]
 262                        return
 263                }
 264        } elseif {[file exists $p]} {
 265                error_popup [mc "File %s already exists." $p]
 266                return
 267        }
 268        set local_path $p
 269}
 270
 271method _do_new2 {} {
 272        if {![_git_init $this]} {
 273                return
 274        }
 275        set done 1
 276}
 277
 278######################################################################
 279##
 280## Clone Existing Repository
 281
 282method _do_clone {} {
 283        $w_next conf \
 284                -state disabled \
 285                -command [cb _do_clone2] \
 286                -text [mc "Clone"]
 287
 288        frame $w_body
 289        label $w_body.h \
 290                -font font_uibold \
 291                -text [mc "Clone Existing Repository"]
 292        pack $w_body.h -side top -fill x -pady 10
 293        pack $w_body -fill x -padx 10
 294
 295        set args $w_body.args
 296        frame $w_body.args
 297        pack $args -fill both
 298
 299        label $args.origin_l -text [mc "URL:"]
 300        entry $args.origin_t \
 301                -textvariable @origin_url \
 302                -font font_diff \
 303                -width 50
 304        button $args.origin_b \
 305                -text [mc "Browse"] \
 306                -command [cb _open_origin]
 307        grid $args.origin_l $args.origin_t $args.origin_b -sticky ew
 308
 309        label $args.where_l -text [mc "Directory:"]
 310        entry $args.where_t \
 311                -textvariable @local_path \
 312                -font font_diff \
 313                -width 50
 314        button $args.where_b \
 315                -text [mc "Browse"] \
 316                -command [cb _new_local_path]
 317        grid $args.where_l $args.where_t $args.where_b -sticky ew
 318
 319        label $args.type_l -text [mc "Clone Type:"]
 320        frame $args.type_f
 321        set w_types [list]
 322        lappend w_types [radiobutton $args.type_f.hardlink \
 323                -state disabled \
 324                -anchor w \
 325                -text [mc "Standard (Fast, Semi-Redundant, Hardlinks)"] \
 326                -variable @clone_type \
 327                -value hardlink]
 328        lappend w_types [radiobutton $args.type_f.full \
 329                -state disabled \
 330                -anchor w \
 331                -text [mc "Full Copy (Slower, Redundant Backup)"] \
 332                -variable @clone_type \
 333                -value full]
 334        lappend w_types [radiobutton $args.type_f.shared \
 335                -state disabled \
 336                -anchor w \
 337                -text [mc "Shared (Fastest, Not Recommended, No Backup)"] \
 338                -variable @clone_type \
 339                -value shared]
 340        foreach r $w_types {
 341                pack $r -anchor w
 342        }
 343        grid $args.type_l $args.type_f -sticky new
 344
 345        grid columnconfigure $args 1 -weight 1
 346
 347        trace add variable @local_path write [cb _update_clone]
 348        trace add variable @origin_url write [cb _update_clone]
 349        update
 350        focus $args.origin_t
 351}
 352
 353method _open_origin {} {
 354        if {$origin_url ne {} && [file isdirectory $origin_url]} {
 355                set p $origin_url
 356        } else {
 357                set p [_home]
 358        }
 359
 360        set p [tk_chooseDirectory \
 361                -initialdir $p \
 362                -parent $top \
 363                -title [mc "Git Repository"] \
 364                -mustexist true]
 365        if {$p eq {}} return
 366
 367        set p [file normalize $p]
 368        if {![_is_git [file join $p .git]] && ![_is_git $p]} {
 369                error_popup [mc "Not a Git repository: %s" [file tail $p]]
 370                return
 371        }
 372        set origin_url $p
 373}
 374
 375method _update_clone {args} {
 376        if {$local_path ne {} && $origin_url ne {}} {
 377                $w_next conf -state normal
 378        } else {
 379                $w_next conf -state disabled
 380        }
 381
 382        if {$origin_url ne {} &&
 383                (  [_is_git [file join $origin_url .git]]
 384                || [_is_git $origin_url])} {
 385                set e normal
 386                if {[[lindex $w_types 0] cget -state] eq {disabled}} {
 387                        set clone_type hardlink
 388                }
 389        } else {
 390                set e disabled
 391                set clone_type full
 392        }
 393
 394        foreach r $w_types {
 395                $r conf -state $e
 396        }
 397}
 398
 399method _do_clone2 {} {
 400        if {[file isdirectory $origin_url]} {
 401                set origin_url [file normalize $origin_url]
 402        }
 403
 404        if {$clone_type eq {hardlink} && ![file isdirectory $origin_url]} {
 405                error_popup [mc "Standard only available for local repository."]
 406                return
 407        }
 408        if {$clone_type eq {shared} && ![file isdirectory $origin_url]} {
 409                error_popup [mc "Shared only available for local repository."]
 410                return
 411        }
 412
 413        if {$clone_type eq {hardlink} || $clone_type eq {shared}} {
 414                set objdir [file join $origin_url .git objects]
 415                if {![file isdirectory $objdir]} {
 416                        set objdir [file join $origin_url objects]
 417                        if {![file isdirectory $objdir]} {
 418                                error_popup [mc "Not a Git repository: %s" [file tail $origin_url]]
 419                                return
 420                        }
 421                }
 422        }
 423
 424        set giturl $origin_url
 425        if {[is_Cygwin] && [file isdirectory $giturl]} {
 426                set giturl [exec cygpath --unix --absolute $giturl]
 427                if {$clone_type eq {shared}} {
 428                        set objdir [exec cygpath --unix --absolute $objdir]
 429                }
 430        }
 431
 432        if {![_git_init $this]} return
 433        set local_path [pwd]
 434
 435        if {[catch {
 436                        git config remote.$origin_name.url $giturl
 437                        git config remote.$origin_name.fetch +refs/heads/*:refs/remotes/$origin_name/*
 438                } err]} {
 439                error_popup [strcat [mc "Failed to configure origin"] "\n\n$err"]
 440                return
 441        }
 442
 443        destroy $w_body $w_next
 444
 445        switch -exact -- $clone_type {
 446        hardlink {
 447                set o_cons [status_bar::two_line $w_body]
 448                pack $w_body -fill x -padx 10 -pady 10
 449
 450                $o_cons start \
 451                        [mc "Counting objects"] \
 452                        [mc "buckets"]
 453                update
 454
 455                if {[file exists [file join $objdir info alternates]]} {
 456                        set pwd [pwd]
 457                        if {[catch {
 458                                file mkdir [gitdir objects info]
 459                                set f_in [open [file join $objdir info alternates] r]
 460                                set f_cp [open [gitdir objects info alternates] w]
 461                                fconfigure $f_in -translation binary -encoding binary
 462                                fconfigure $f_cp -translation binary -encoding binary
 463                                cd $objdir
 464                                while {[gets $f_in line] >= 0} {
 465                                        if {[is_Cygwin]} {
 466                                                puts $f_cp [exec cygpath --unix --absolute $line]
 467                                        } else {
 468                                                puts $f_cp [file normalize $line]
 469                                        }
 470                                }
 471                                close $f_in
 472                                close $f_cp
 473                                cd $pwd
 474                        } err]} {
 475                                catch {cd $pwd}
 476                                _clone_failed $this [mc "Unable to copy objects/info/alternates: %s" $err]
 477                                return
 478                        }
 479                }
 480
 481                set tolink  [list]
 482                set buckets [glob \
 483                        -tails \
 484                        -nocomplain \
 485                        -directory [file join $objdir] ??]
 486                set bcnt [expr {[llength $buckets] + 2}]
 487                set bcur 1
 488                $o_cons update $bcur $bcnt
 489                update
 490
 491                file mkdir [file join .git objects pack]
 492                foreach i [glob -tails -nocomplain \
 493                        -directory [file join $objdir pack] *] {
 494                        lappend tolink [file join pack $i]
 495                }
 496                $o_cons update [incr bcur] $bcnt
 497                update
 498
 499                foreach i $buckets {
 500                        file mkdir [file join .git objects $i]
 501                        foreach j [glob -tails -nocomplain \
 502                                -directory [file join $objdir $i] *] {
 503                                lappend tolink [file join $i $j]
 504                        }
 505                        $o_cons update [incr bcur] $bcnt
 506                        update
 507                }
 508                $o_cons stop
 509
 510                if {$tolink eq {}} {
 511                        info_popup [strcat \
 512                                [mc "Nothing to clone from %s." $origin_url] \
 513                                "\n" \
 514                                [mc "The 'master' branch has not been initialized."] \
 515                                ]
 516                        destroy $w_body
 517                        set done 1
 518                        return
 519                }
 520
 521                set i [lindex $tolink 0]
 522                if {[catch {
 523                                file link -hard \
 524                                        [file join .git objects $i] \
 525                                        [file join $objdir $i]
 526                        } err]} {
 527                        info_popup [mc "Hardlinks are unavailable.  Falling back to copying."]
 528                        set i [_copy_files $this $objdir $tolink]
 529                } else {
 530                        set i [_link_files $this $objdir [lrange $tolink 1 end]]
 531                }
 532                if {!$i} return
 533
 534                destroy $w_body
 535        }
 536        full {
 537                set o_cons [console::embed \
 538                        $w_body \
 539                        [mc "Cloning from %s" $origin_url]]
 540                pack $w_body -fill both -expand 1 -padx 10
 541                $o_cons exec \
 542                        [list git fetch --no-tags -k $origin_name] \
 543                        [cb _do_clone_tags]
 544        }
 545        shared {
 546                set fd [open [gitdir objects info alternates] w]
 547                fconfigure $fd -translation binary
 548                puts $fd $objdir
 549                close $fd
 550        }
 551        }
 552
 553        if {$clone_type eq {hardlink} || $clone_type eq {shared}} {
 554                if {![_clone_refs $this]} return
 555                set pwd [pwd]
 556                if {[catch {
 557                                cd $origin_url
 558                                set HEAD [git rev-parse --verify HEAD^0]
 559                        } err]} {
 560                        _clone_failed $this [mc "Not a Git repository: %s" [file tail $origin_url]]
 561                        return 0
 562                }
 563                cd $pwd
 564                _do_clone_checkout $this $HEAD
 565        }
 566}
 567
 568method _copy_files {objdir tocopy} {
 569        $o_cons start \
 570                [mc "Copying objects"] \
 571                [mc "KiB"]
 572        set tot 0
 573        set cmp 0
 574        foreach p $tocopy {
 575                incr tot [file size [file join $objdir $p]]
 576        }
 577        foreach p $tocopy {
 578                if {[catch {
 579                                set f_in [open [file join $objdir $p] r]
 580                                set f_cp [open [file join .git objects $p] w]
 581                                fconfigure $f_in -translation binary -encoding binary
 582                                fconfigure $f_cp -translation binary -encoding binary
 583
 584                                while {![eof $f_in]} {
 585                                        incr cmp [fcopy $f_in $f_cp -size 16384]
 586                                        $o_cons update \
 587                                                [expr {$cmp / 1024}] \
 588                                                [expr {$tot / 1024}]
 589                                        update
 590                                }
 591
 592                                close $f_in
 593                                close $f_cp
 594                        } err]} {
 595                        _clone_failed $this [mc "Unable to copy object: %s" $err]
 596                        return 0
 597                }
 598        }
 599        return 1
 600}
 601
 602method _link_files {objdir tolink} {
 603        set total [llength $tolink]
 604        $o_cons start \
 605                [mc "Linking objects"] \
 606                [mc "objects"]
 607        for {set i 0} {$i < $total} {} {
 608                set p [lindex $tolink $i]
 609                if {[catch {
 610                                file link -hard \
 611                                        [file join .git objects $p] \
 612                                        [file join $objdir $p]
 613                        } err]} {
 614                        _clone_failed $this [mc "Unable to hardlink object: %s" $err]
 615                        return 0
 616                }
 617
 618                incr i
 619                if {$i % 5 == 0} {
 620                        $o_cons update $i $total
 621                        update
 622                }
 623        }
 624        return 1
 625}
 626
 627method _clone_refs {} {
 628        set pwd [pwd]
 629        if {[catch {cd $origin_url} err]} {
 630                error_popup [mc "Not a Git repository: %s" [file tail $origin_url]]
 631                return 0
 632        }
 633        set fd_in [git_read for-each-ref \
 634                --tcl \
 635                {--format=list %(refname) %(objectname) %(*objectname)}]
 636        cd $pwd
 637
 638        set fd [open [gitdir packed-refs] w]
 639        fconfigure $fd -translation binary
 640        puts $fd "# pack-refs with: peeled"
 641        while {[gets $fd_in line] >= 0} {
 642                set line [eval $line]
 643                set refn [lindex $line 0]
 644                set robj [lindex $line 1]
 645                set tobj [lindex $line 2]
 646
 647                if {[regsub ^refs/heads/ $refn \
 648                        "refs/remotes/$origin_name/" refn]} {
 649                        puts $fd "$robj $refn"
 650                } elseif {[string match refs/tags/* $refn]} {
 651                        puts $fd "$robj $refn"
 652                        if {$tobj ne {}} {
 653                                puts $fd "^$tobj"
 654                        }
 655                }
 656        }
 657        close $fd_in
 658        close $fd
 659        return 1
 660}
 661
 662method _do_clone_tags {ok} {
 663        if {$ok} {
 664                $o_cons exec \
 665                        [list git fetch --tags -k $origin_name] \
 666                        [cb _do_clone_HEAD]
 667        } else {
 668                $o_cons done $ok
 669                _clone_failed $this [mc "Cannot fetch branches and objects.  See console output for details."]
 670        }
 671}
 672
 673method _do_clone_HEAD {ok} {
 674        if {$ok} {
 675                $o_cons exec \
 676                        [list git fetch $origin_name HEAD] \
 677                        [cb _do_clone_full_end]
 678        } else {
 679                $o_cons done $ok
 680                _clone_failed $this [mc "Cannot fetch tags.  See console output for details."]
 681        }
 682}
 683
 684method _do_clone_full_end {ok} {
 685        $o_cons done $ok
 686
 687        if {$ok} {
 688                destroy $w_body
 689
 690                set HEAD {}
 691                if {[file exists [gitdir FETCH_HEAD]]} {
 692                        set fd [open [gitdir FETCH_HEAD] r]
 693                        while {[gets $fd line] >= 0} {
 694                                if {[regexp "^(.{40})\t\t" $line line HEAD]} {
 695                                        break
 696                                }
 697                        }
 698                        close $fd
 699                }
 700
 701                catch {git pack-refs}
 702                _do_clone_checkout $this $HEAD
 703        } else {
 704                _clone_failed $this [mc "Cannot determine HEAD.  See console output for details."]
 705        }
 706}
 707
 708method _clone_failed {{why {}}} {
 709        if {[catch {file delete -force $local_path} err]} {
 710                set why [strcat \
 711                        $why \
 712                        "\n\n" \
 713                        [mc "Unable to cleanup %s" $local_path] \
 714                        "\n\n" \
 715                        $err]
 716        }
 717        if {$why ne {}} {
 718                update
 719                error_popup [strcat [mc "Clone failed."] "\n" $why]
 720        }
 721}
 722
 723method _do_clone_checkout {HEAD} {
 724        if {$HEAD eq {}} {
 725                info_popup [strcat \
 726                        [mc "No default branch obtained."] \
 727                        "\n" \
 728                        [mc "The 'master' branch has not been initialized."] \
 729                        ]
 730                set done 1
 731                return
 732        }
 733        if {[catch {
 734                        git update-ref HEAD $HEAD^0
 735                } err]} {
 736                info_popup [strcat \
 737                        [mc "Cannot resolve %s as a commit." $HEAD^0] \
 738                        "\n  $err" \
 739                        "\n" \
 740                        [mc "The 'master' branch has not been initialized."] \
 741                        ]
 742                set done 1
 743                return
 744        }
 745
 746        set o_cons [status_bar::two_line $w_body]
 747        pack $w_body -fill x -padx 10 -pady 10
 748        $o_cons start \
 749                [mc "Creating working directory"] \
 750                [mc "files"]
 751
 752        set readtree_err {}
 753        set fd [git_read --stderr read-tree \
 754                -m \
 755                -u \
 756                -v \
 757                HEAD \
 758                HEAD \
 759                ]
 760        fconfigure $fd -blocking 0 -translation binary
 761        fileevent $fd readable [cb _readtree_wait $fd]
 762}
 763
 764method _readtree_wait {fd} {
 765        set buf [read $fd]
 766        $o_cons update_meter $buf
 767        append readtree_err $buf
 768
 769        fconfigure $fd -blocking 1
 770        if {![eof $fd]} {
 771                fconfigure $fd -blocking 0
 772                return
 773        }
 774
 775        if {[catch {close $fd}]} {
 776                set err $readtree_err
 777                regsub {^fatal: } $err {} err
 778                error_popup [strcat \
 779                        [mc "Initial file checkout failed."] \
 780                        "\n\n$err"]
 781                return
 782        }
 783
 784        set done 1
 785}
 786
 787######################################################################
 788##
 789## Open Existing Repository
 790
 791method _do_open {} {
 792        $w_next conf \
 793                -state disabled \
 794                -command [cb _do_open2] \
 795                -text [mc "Open"]
 796
 797        frame $w_body
 798        label $w_body.h \
 799                -font font_uibold \
 800                -text [mc "Open Existing Repository"]
 801        pack $w_body.h -side top -fill x -pady 10
 802        pack $w_body -fill x -padx 10
 803
 804        frame $w_body.where
 805        label $w_body.where.l -text [mc "Repository:"]
 806        entry $w_body.where.t \
 807                -textvariable @local_path \
 808                -font font_diff \
 809                -width 50
 810        button $w_body.where.b \
 811                -text [mc "Browse"] \
 812                -command [cb _open_local_path]
 813
 814        pack $w_body.where.b -side right
 815        pack $w_body.where.l -side left
 816        pack $w_body.where.t -fill x
 817        pack $w_body.where -fill x
 818
 819        trace add variable @local_path write [cb _write_local_path]
 820        update
 821        focus $w_body.where.t
 822}
 823
 824method _open_local_path {} {
 825        if {$local_path ne {}} {
 826                set p $local_path
 827        } else {
 828                set p [_home]
 829        }
 830
 831        set p [tk_chooseDirectory \
 832                -initialdir $p \
 833                -parent $top \
 834                -title [mc "Git Repository"] \
 835                -mustexist true]
 836        if {$p eq {}} return
 837
 838        set p [file normalize $p]
 839        if {![_is_git [file join $p .git]]} {
 840                error_popup [mc "Not a Git repository: %s" [file tail $p]]
 841                return
 842        }
 843        set local_path $p
 844}
 845
 846method _do_open2 {} {
 847        if {![_is_git [file join $local_path .git]]} {
 848                error_popup [mc "Not a Git repository: %s" [file tail $local_path]]
 849                return
 850        }
 851
 852        if {[catch {cd $local_path} err]} {
 853                error_popup [strcat \
 854                        [mc "Failed to open repository %s:" $local_path] \
 855                        "\n\n$err"]
 856                return
 857        }
 858
 859        set ::_gitdir .git
 860        set ::_prefix {}
 861        set done 1
 862}
 863
 864}