Merge branch 'bs/lock'
authorJunio C Hamano <gitster@pobox.com>
Mon, 30 Jul 2007 06:09:54 +0000 (23:09 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 30 Jul 2007 06:09:54 +0000 (23:09 -0700)
* bs/lock:
Add test for symlinked configuration file updates.
use lockfile.c routines in git_commit_set_multivar()
fully resolve symlinks when creating lockfiles

24 files changed:
Documentation/git-diff.txt
Makefile
builtin-add.c
builtin-diff.c
git-gui/git-gui.sh
git-gui/lib/blame.tcl
git-gui/lib/browser.tcl
git-gui/lib/checkout_op.tcl
git-gui/lib/choose_rev.tcl
git-gui/lib/commit.tcl
git-gui/lib/database.tcl
git-gui/lib/diff.tcl
git-gui/lib/encoding.tcl [new file with mode: 0644]
git-gui/lib/error.tcl
git-gui/lib/index.tcl
git-gui/lib/merge.tcl
git-gui/lib/remote.tcl
git-rebase--interactive.sh
git-stash.sh
gitk
gitweb/gitweb.perl
t/t3404-rebase-interactive.sh
t/t9300-fast-import.sh
t/t9500-gitweb-standalone-no-errors.sh
index 639b969315d4c9c6ece95cdeb10c2f92bd17adab..b1f5e7f93f4f394c42915e345f6b0f9d33fc41d9 100644 (file)
@@ -102,17 +102,14 @@ Limiting the diff output::
 +
 ------------
 $ git diff --diff-filter=MRC            <1>
-$ git diff --name-status -r             <2>
+$ git diff --name-status                <2>
 $ git diff arch/i386 include/asm-i386   <3>
 ------------
 +
 <1> show only modification, rename and copy, but not addition
 nor deletion.
 <2> show only names and the nature of change, but not actual
-diff output.  --name-status disables usual patch generation
-which in turn also disables recursive behavior, so without -r
-you would only see the directory name if there is a change in a
-file in a subdirectory.
+diff output.
 <3> limit diff output to named subtrees.
 
 Munging the diff output::
index 73b487fba9dc699fd8d2061113b8e94b3d4fa698..c58a4c2a09bf7824f8d68b34d218c077f1996865 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -176,6 +176,7 @@ CC = gcc
 AR = ar
 RM = rm -f
 TAR = tar
+FIND = find
 INSTALL = install
 RPMBUILD = rpmbuild
 TCL_PATH = tclsh
@@ -903,11 +904,11 @@ doc:
 
 TAGS:
        $(RM) TAGS
-       find . -name '*.[hcS]' -print | xargs etags -a
+       $(FIND) . -name '*.[hcS]' -print | xargs etags -a
 
 tags:
        $(RM) tags
-       find . -name '*.[hcS]' -print | xargs ctags -a
+       $(FIND) . -name '*.[hcS]' -print | xargs ctags -a
 
 ### Detect prefix changes
 TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
index 734547994f3a35e04fc7fdea10396c0c87bc6f0d..de5c108f8f5765e78c8ca15dcad58f3796015acc 100644 (file)
@@ -60,7 +60,7 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec,
                path = git_path("info/exclude");
                if (!access(path, R_OK))
                        add_excludes_from_file(dir, path);
-               if (!access(excludes_file, R_OK))
+               if (excludes_file != NULL && !access(excludes_file, R_OK))
                        add_excludes_from_file(dir, excludes_file);
        }
 
index 7f367b6b9d545ea760224fdacb68056261ce1617..b48121e6e21ddd29fbb725c813aa22d0bcee2ea2 100644 (file)
@@ -233,6 +233,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                        die("diff_setup_done failed");
        }
        rev.diffopt.allow_external = 1;
+       rev.diffopt.recursive = 1;
 
        /* Do we have --cached and not have a pending object, then
         * default to HEAD by hand.  Eek.
index 2077261e647904d9871479411263d0fdcbb79662..671b8873f27e68f8ed2c93efd6734f583f8b7500 100755 (executable)
@@ -154,12 +154,10 @@ proc gitexec {args} {
 }
 
 proc reponame {} {
-       global _reponame
-       return $_reponame
+       return $::_reponame
 }
 
 proc is_MacOSX {} {
-       global tcl_platform tk_library
        if {[tk windowingsystem] eq {aqua}} {
                return 1
        }
@@ -167,17 +165,16 @@ proc is_MacOSX {} {
 }
 
 proc is_Windows {} {
-       global tcl_platform
-       if {$tcl_platform(platform) eq {windows}} {
+       if {$::tcl_platform(platform) eq {windows}} {
                return 1
        }
        return 0
 }
 
 proc is_Cygwin {} {
-       global tcl_platform _iscygwin
+       global _iscygwin
        if {$_iscygwin eq {}} {
-               if {$tcl_platform(platform) eq {windows}} {
+               if {$::tcl_platform(platform) eq {windows}} {
                        if {[catch {set p [exec cygpath --windir]} err]} {
                                set _iscygwin 0
                        } else {
@@ -367,16 +364,25 @@ proc _which {what} {
        return {}
 }
 
+proc _lappend_nice {cmd_var} {
+       global _nice
+       upvar $cmd_var cmd
+
+       if {![info exists _nice]} {
+               set _nice [_which nice]
+       }
+       if {$_nice ne {}} {
+               lappend cmd $_nice
+       }
+}
+
 proc git {args} {
        set opt [list exec]
 
        while {1} {
                switch -- [lindex $args 0] {
                --nice {
-                       global _nice
-                       if {$_nice ne {}} {
-                               lappend opt $_nice
-                       }
+                       _lappend_nice opt
                }
 
                default {
@@ -414,6 +420,7 @@ proc _open_stdout_stderr {cmd} {
                        error $err
                }
        }
+       fconfigure $fd -eofchar {}
        return $fd
 }
 
@@ -423,10 +430,7 @@ proc git_read {args} {
        while {1} {
                switch -- [lindex $args 0] {
                --nice {
-                       global _nice
-                       if {$_nice ne {}} {
-                               lappend opt $_nice
-                       }
+                       _lappend_nice opt
                }
 
                --stderr {
@@ -454,10 +458,7 @@ proc git_write {args} {
        while {1} {
                switch -- [lindex $args 0] {
                --nice {
-                       global _nice
-                       if {$_nice ne {}} {
-                               lappend opt $_nice
-                       }
+                       _lappend_nice opt
                }
 
                default {
@@ -524,7 +525,6 @@ if {$_git eq {}} {
        error_popup "Cannot find git in PATH."
        exit 1
 }
-set _nice [_which nice]
 
 ######################################################################
 ##
@@ -544,8 +544,34 @@ if {![regsub {^git version } $_git_version {} _git_version]} {
        error_popup "Cannot parse Git version string:\n\n$_git_version"
        exit 1
 }
+
+set _real_git_version $_git_version
+regsub -- {-dirty$} $_git_version {} _git_version
 regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
 regsub {\.rc[0-9]+$} $_git_version {} _git_version
+regsub {\.GIT$} $_git_version {} _git_version
+
+if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
+       catch {wm withdraw .}
+       if {[tk_messageBox \
+               -icon warning \
+               -type yesno \
+               -default no \
+               -title "[appname]: warning" \
+               -message "Git version cannot be determined.
+
+$_git claims it is version '$_real_git_version'.
+
+[appname] requires at least Git 1.5.0 or later.
+
+Assume '$_real_git_version' is version 1.5.0?
+"] eq {yes}} {
+               set _git_version 1.5.0
+       } else {
+               exit 1
+       }
+}
+unset _real_git_version
 
 proc git-version {args} {
        global _git_version
@@ -601,6 +627,46 @@ You are using [git-version]:
        exit 1
 }
 
+######################################################################
+##
+## feature option selection
+
+if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
+       unset _junk
+} else {
+       set subcommand gui
+}
+if {$subcommand eq {gui.sh}} {
+       set subcommand gui
+}
+if {$subcommand eq {gui} && [llength $argv] > 0} {
+       set subcommand [lindex $argv 0]
+       set argv [lrange $argv 1 end]
+}
+
+enable_option multicommit
+enable_option branch
+enable_option transport
+disable_option bare
+
+switch -- $subcommand {
+browser -
+blame {
+       enable_option bare
+
+       disable_option multicommit
+       disable_option branch
+       disable_option transport
+}
+citool {
+       enable_option singlecommit
+
+       disable_option multicommit
+       disable_option branch
+       disable_option transport
+}
+}
+
 ######################################################################
 ##
 ## repository setup
@@ -625,19 +691,24 @@ if {![file isdirectory $_gitdir]} {
        error_popup "Git directory not found:\n\n$_gitdir"
        exit 1
 }
-if {[lindex [file split $_gitdir] end] ne {.git}} {
-       catch {wm withdraw .}
-       error_popup "Cannot use funny .git directory:\n\n$_gitdir"
-       exit 1
+if {![is_enabled bare]} {
+       if {[lindex [file split $_gitdir] end] ne {.git}} {
+               catch {wm withdraw .}
+               error_popup "Cannot use funny .git directory:\n\n$_gitdir"
+               exit 1
+       }
+       if {[catch {cd [file dirname $_gitdir]} err]} {
+               catch {wm withdraw .}
+               error_popup "No working directory [file dirname $_gitdir]:\n\n$err"
+               exit 1
+       }
 }
-if {[catch {cd [file dirname $_gitdir]} err]} {
-       catch {wm withdraw .}
-       error_popup "No working directory [file dirname $_gitdir]:\n\n$err"
-       exit 1
+set _reponame [file split [file normalize $_gitdir]]
+if {[lindex $_reponame end] eq {.git}} {
+       set _reponame [lindex $_reponame end-1]
+} else {
+       set _reponame [lindex $_reponame end]
 }
-set _reponame [lindex [file split \
-       [file normalize [file dirname $_gitdir]]] \
-       end]
 
 ######################################################################
 ##
@@ -758,8 +829,9 @@ proc rescan {after {honor_trustmtime 1}} {
 
        array unset file_states
 
-       if {![$ui_comm edit modified]
-               || [string trim [$ui_comm get 0.0 end]] eq {}} {
+       if {!$::GITGUI_BCK_exists &&
+               (![$ui_comm edit modified]
+               || [string trim [$ui_comm get 0.0 end]] eq {})} {
                if {[string match amend* $commit_type]} {
                } elseif {[load_message GITGUI_MSG]} {
                } elseif {[load_message MERGE_MSG]} {
@@ -800,6 +872,10 @@ proc rescan_stage2 {fd after} {
        if {[file readable $info_exclude]} {
                lappend ls_others "--exclude-from=$info_exclude"
        }
+       set user_exclude [get_config core.excludesfile]
+       if {$user_exclude ne {} && [file readable $user_exclude]} {
+               lappend ls_others "--exclude-from=$user_exclude"
+       }
 
        set buf_rdi {}
        set buf_rdf {}
@@ -827,6 +903,7 @@ proc load_message {file} {
                if {[catch {set fd [open $f r]}]} {
                        return 0
                }
+               fconfigure $fd -eofchar {}
                set content [string trim [read $fd]]
                close $fd
                regsub -all -line {[ \r\t]+$} $content {} content
@@ -1219,32 +1296,6 @@ static unsigned char file_merge_bits[] = {
    0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
 } -maskdata $filemask
 
-set file_dir_data {
-#define file_width 18
-#define file_height 18
-static unsigned char file_bits[] = {
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
-  0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
-  0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
-  0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-}
-image create bitmap file_dir -background white -foreground blue \
-       -data $file_dir_data -maskdata $file_dir_data
-unset file_dir_data
-
-set file_uplevel_data {
-#define up_width 15
-#define up_height 15
-static unsigned char up_bits[] = {
-  0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
-  0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
-  0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
-}
-image create bitmap file_uplevel -background white -foreground red \
-       -data $file_uplevel_data -maskdata $file_uplevel_data
-unset file_uplevel_data
-
 set ui_index .vpane.files.index.list
 set ui_workdir .vpane.files.workdir.list
 
@@ -1345,6 +1396,7 @@ set is_quitting 0
 
 proc do_quit {} {
        global ui_comm is_quitting repo_config commit_type
+       global GITGUI_BCK_exists GITGUI_BCK_i
 
        if {$is_quitting} return
        set is_quitting 1
@@ -1353,18 +1405,30 @@ proc do_quit {} {
                # -- Stash our current commit buffer.
                #
                set save [gitdir GITGUI_MSG]
-               set msg [string trim [$ui_comm get 0.0 end]]
-               regsub -all -line {[ \r\t]+$} $msg {} msg
-               if {(![string match amend* $commit_type]
-                       || [$ui_comm edit modified])
-                       && $msg ne {}} {
-                       catch {
-                               set fd [open $save w]
-                               puts -nonewline $fd $msg
-                               close $fd
-                       }
+               if {$GITGUI_BCK_exists && ![$ui_comm edit modified]} {
+                       file rename -force [gitdir GITGUI_BCK] $save
+                       set GITGUI_BCK_exists 0
                } else {
-                       catch {file delete $save}
+                       set msg [string trim [$ui_comm get 0.0 end]]
+                       regsub -all -line {[ \r\t]+$} $msg {} msg
+                       if {(![string match amend* $commit_type]
+                               || [$ui_comm edit modified])
+                               && $msg ne {}} {
+                               catch {
+                                       set fd [open $save w]
+                                       puts -nonewline $fd $msg
+                                       close $fd
+                               }
+                       } else {
+                               catch {file delete $save}
+                       }
+               }
+
+               # -- Remove our editor backup, its not needed.
+               #
+               after cancel $GITGUI_BCK_i
+               if {$GITGUI_BCK_exists} {
+                       catch {file delete [gitdir GITGUI_BCK]}
                }
 
                # -- Stash our current window geometry into this repository.
@@ -1566,43 +1630,6 @@ set font_descs {
 load_config 0
 apply_config
 
-######################################################################
-##
-## feature option selection
-
-if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
-       unset _junk
-} else {
-       set subcommand gui
-}
-if {$subcommand eq {gui.sh}} {
-       set subcommand gui
-}
-if {$subcommand eq {gui} && [llength $argv] > 0} {
-       set subcommand [lindex $argv 0]
-       set argv [lrange $argv 1 end]
-}
-
-enable_option multicommit
-enable_option branch
-enable_option transport
-
-switch -- $subcommand {
-browser -
-blame {
-       disable_option multicommit
-       disable_option branch
-       disable_option transport
-}
-citool {
-       enable_option singlecommit
-
-       disable_option multicommit
-       disable_option branch
-       disable_option transport
-}
-}
-
 ######################################################################
 ##
 ## ui construction
@@ -1632,20 +1659,32 @@ if {[is_enabled transport]} {
 menu .mbar.repository
 
 .mbar.repository add command \
-       -label {Browse Current Branch} \
+       -label {Browse Current Branch's Files} \
        -command {browser::new $current_branch}
-trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
+set ui_browse_current [.mbar.repository index last]
+.mbar.repository add command \
+       -label {Browse Branch Files...} \
+       -command browser_open::dialog
 .mbar.repository add separator
 
 .mbar.repository add command \
-       -label {Visualize Current Branch} \
+       -label {Visualize Current Branch's History} \
        -command {do_gitk $current_branch}
-trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#"
+set ui_visualize_current [.mbar.repository index last]
 .mbar.repository add command \
-       -label {Visualize All Branches} \
+       -label {Visualize All Branch History} \
        -command {do_gitk --all}
 .mbar.repository add separator
 
+proc current_branch_write {args} {
+       global current_branch
+       .mbar.repository entryconf $::ui_browse_current \
+               -label "Browse $current_branch's Files"
+       .mbar.repository entryconf $::ui_visualize_current \
+               -label "Visualize $current_branch's History"
+}
+trace add variable current_branch write current_branch_write
+
 if {[is_enabled multicommit]} {
        .mbar.repository add command -label {Database Statistics} \
                -command do_stats
@@ -1766,12 +1805,12 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
        lappend disable_on_lock \
                [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
-       .mbar.commit add command -label {Add To Commit} \
+       .mbar.commit add command -label {Stage To Commit} \
                -command do_add_selection
        lappend disable_on_lock \
                [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
-       .mbar.commit add command -label {Add Existing To Commit} \
+       .mbar.commit add command -label {Stage Changed Files To Commit} \
                -command do_add_all \
                -accelerator $M1T-I
        lappend disable_on_lock \
@@ -1805,14 +1844,14 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
 if {[is_enabled branch]} {
        menu .mbar.merge
        .mbar.merge add command -label {Local Merge...} \
-               -command merge::dialog
+               -command merge::dialog \
+               -accelerator $M1T-M
        lappend disable_on_lock \
                [list .mbar.merge entryconf [.mbar.merge index last] -state]
        .mbar.merge add command -label {Abort Merge...} \
                -command merge::reset_hard
        lappend disable_on_lock \
                [list .mbar.merge entryconf [.mbar.merge index last] -state]
-
 }
 
 # -- Transport Menu
@@ -1844,33 +1883,6 @@ if {[is_MacOSX]} {
        .mbar.edit add separator
        .mbar.edit add command -label {Options...} \
                -command do_options
-
-       # -- Tools Menu
-       #
-       if {[is_Cygwin] && [file exists /usr/local/miga/lib/gui-miga]} {
-       proc do_miga {} {
-               if {![lock_index update]} return
-               set cmd [list sh --login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""]
-               set miga_fd [open "|$cmd" r]
-               fconfigure $miga_fd -blocking 0
-               fileevent $miga_fd readable [list miga_done $miga_fd]
-               ui_status {Running miga...}
-       }
-       proc miga_done {fd} {
-               read $fd 512
-               if {[eof $fd]} {
-                       close $fd
-                       unlock_index
-                       rescan ui_ready
-               }
-       }
-       .mbar add cascade -label Tools -menu .mbar.tools
-       menu .mbar.tools
-       .mbar.tools add command -label "Migrate" \
-               -command do_miga
-       lappend disable_on_lock \
-               [list .mbar.tools entryconf [.mbar.tools index last] -state]
-       }
 }
 
 # -- Help Menu
@@ -1938,29 +1950,10 @@ proc usage {} {
 # -- Not a normal commit type invocation?  Do that instead!
 #
 switch -- $subcommand {
-browser {
-       set subcommand_args {rev?}
-       switch [llength $argv] {
-       0 { load_current_branch }
-       1 {
-               set current_branch [lindex $argv 0]
-               if {[regexp {^[0-9a-f]{1,39}$} $current_branch]} {
-                       if {[catch {
-                                       set current_branch \
-                                       [git rev-parse --verify $current_branch]
-                               } err]} {
-                               puts stderr $err
-                               exit 1
-                       }
-               }
-       }
-       default usage
-       }
-       browser::new $current_branch
-       return
-}
+browser -
 blame {
-       set subcommand_args {rev? path?}
+       set subcommand_args {rev? path}
+       if {$argv eq {}} usage
        set head {}
        set path {}
        set is_path 0
@@ -1979,12 +1972,18 @@ blame {
                } elseif {$head eq {}} {
                        if {$head ne {}} usage
                        set head $a
+                       set is_path 1
                } else {
                        usage
                }
        }
        unset is_path
 
+       if {$head ne {} && $path eq {}} {
+               set path $_prefix$head
+               set head {}
+       }
+
        if {$head eq {}} {
                load_current_branch
        } else {
@@ -1999,8 +1998,26 @@ blame {
                set current_branch $head
        }
 
-       if {$path eq {}} usage
-       blame::new $head $path
+       switch -- $subcommand {
+       browser {
+               if {$head eq {}} {
+                       if {$path ne {} && [file isdirectory $path]} {
+                               set head $current_branch
+                       } else {
+                               set head $path
+                               set path {}
+                       }
+               }
+               browser::new $head $path
+       }
+       blame   {
+               if {$head eq {} && ![file exists $path]} {
+                       puts stderr "fatal: cannot stat path $path: No such file or directory"
+                       exit 1
+               }
+               blame::new $head $path
+       }
+       }
        return
 }
 citool -
@@ -2115,7 +2132,7 @@ pack .vpane.lower.commarea.buttons.rescan -side top -fill x
 lappend disable_on_lock \
        {.vpane.lower.commarea.buttons.rescan conf -state}
 
-button .vpane.lower.commarea.buttons.incall -text {Add Existing} \
+button .vpane.lower.commarea.buttons.incall -text {Stage Changed} \
        -command do_add_all
 pack .vpane.lower.commarea.buttons.incall -side top -fill x
 lappend disable_on_lock \
@@ -2389,17 +2406,25 @@ lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
 $ctxm add separator
 $ctxm add command -label {Options...} \
        -command do_options
-bind_button3 $ui_diff "
-       set cursorX %x
-       set cursorY %y
-       if {\$ui_index eq \$current_diff_side} {
-               $ctxm entryconf $ui_diff_applyhunk -label {Unstage Hunk From Commit}
+proc popup_diff_menu {ctxm x y X Y} {
+       set ::cursorX $x
+       set ::cursorY $y
+       if {$::ui_index eq $::current_diff_side} {
+               $ctxm entryconf $::ui_diff_applyhunk \
+                       -state normal \
+                       -label {Unstage Hunk From Commit}
+       } elseif {{_O} eq [lindex $::file_states($::current_diff_path) 0]} {
+               $ctxm entryconf $::ui_diff_applyhunk \
+                       -state disabled \
+                       -label {Stage Hunk For Commit}
        } else {
-               $ctxm entryconf $ui_diff_applyhunk -label {Stage Hunk For Commit}
+               $ctxm entryconf $::ui_diff_applyhunk \
+                       -state normal \
+                       -label {Stage Hunk For Commit}
        }
-       tk_popup $ctxm %X %Y
-"
-unset ui_diff_applyhunk
+       tk_popup $ctxm $X $Y
+}
+bind_button3 $ui_diff [list popup_diff_menu $ctxm %x %y %X %Y]
 
 # -- Status Bar
 #
@@ -2460,6 +2485,8 @@ if {[is_enabled branch]} {
        bind . <$M1B-Key-N> branch_create::dialog
        bind . <$M1B-Key-o> branch_checkout::dialog
        bind . <$M1B-Key-O> branch_checkout::dialog
+       bind . <$M1B-Key-m> merge::dialog
+       bind . <$M1B-Key-M> merge::dialog
 }
 if {[is_enabled transport]} {
        bind . <$M1B-Key-p> do_push_anywhere
@@ -2551,24 +2578,64 @@ if {[is_enabled transport]} {
        populate_push_menu
 }
 
-# -- Only suggest a gc run if we are going to stay running.
-#
-if {[is_enabled multicommit]} {
-       set object_limit 2000
-       if {[is_Windows]} {set object_limit 200}
-       regexp {^([0-9]+) objects,} [git count-objects] _junk objects_current
-       if {$objects_current >= $object_limit} {
-               if {[ask_popup \
-                       "This repository currently has $objects_current loose objects.
+if {[winfo exists $ui_comm]} {
+       set GITGUI_BCK_exists [load_message GITGUI_BCK]
+
+       # -- If both our backup and message files exist use the
+       #    newer of the two files to initialize the buffer.
+       #
+       if {$GITGUI_BCK_exists} {
+               set m [gitdir GITGUI_MSG]
+               if {[file isfile $m]} {
+                       if {[file mtime [gitdir GITGUI_BCK]] > [file mtime $m]} {
+                               catch {file delete [gitdir GITGUI_MSG]}
+                       } else {
+                               $ui_comm delete 0.0 end
+                               $ui_comm edit reset
+                               $ui_comm edit modified false
+                               catch {file delete [gitdir GITGUI_BCK]}
+                               set GITGUI_BCK_exists 0
+                       }
+               }
+               unset m
+       }
 
-To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
+       proc backup_commit_buffer {} {
+               global ui_comm GITGUI_BCK_exists
 
-Compress the database now?"] eq yes} {
-                       do_gc
+               set m [$ui_comm edit modified]
+               if {$m || $GITGUI_BCK_exists} {
+                       set msg [string trim [$ui_comm get 0.0 end]]
+                       regsub -all -line {[ \r\t]+$} $msg {} msg
+
+                       if {$msg eq {}} {
+                               if {$GITGUI_BCK_exists} {
+                                       catch {file delete [gitdir GITGUI_BCK]}
+                                       set GITGUI_BCK_exists 0
+                               }
+                       } elseif {$m} {
+                               catch {
+                                       set fd [open [gitdir GITGUI_BCK] w]
+                                       puts -nonewline $fd $msg
+                                       close $fd
+                                       set GITGUI_BCK_exists 1
+                               }
+                       }
+
+                       $ui_comm edit modified false
                }
+
+               set ::GITGUI_BCK_i [after 2000 backup_commit_buffer]
        }
-       unset object_limit _junk objects_current
+
+       backup_commit_buffer
 }
 
 lock_index begin-read
+if {![winfo ismapped .]} {
+       wm deiconify .
+}
 after 1 do_rescan
+if {[is_enabled multicommit]} {
+       after 1000 hint_gc
+}
index 4bdb9a27a3dbcc60957f1a4eaed123faf3952bac..96072847a2ffeec814f499657744e5ed4f8988c0 100644 (file)
@@ -370,6 +370,7 @@ method _load {jump} {
        $w_path conf -text [escape_path $path]
        if {$commit eq {}} {
                set fd [open $path r]
+               fconfigure $fd -eofchar {}
        } else {
                set fd [git_read cat-file blob "$commit:$path"]
        }
@@ -770,15 +771,20 @@ method _showcommit {cur_w lno} {
                                                set enc [string tolower [string range $line 9 end]]
                                        }
                                }
-                               set msg [encoding convertfrom $enc [read $fd]]
-                               set msg [string trim $msg]
+                               set msg [read $fd]
                                close $fd
 
-                               set author_name [encoding convertfrom $enc $author_name]
-                               set committer_name [encoding convertfrom $enc $committer_name]
-
-                               set header($cmit,author) $author_name
-                               set header($cmit,committer) $committer_name
+                               set enc [tcl_encoding $enc]
+                               if {$enc ne {}} {
+                                       set msg [encoding convertfrom $enc $msg]
+                                       set author_name [encoding convertfrom $enc $author_name]
+                                       set committer_name [encoding convertfrom $enc $committer_name]
+                                       set header($cmit,author) $author_name
+                                       set header($cmit,committer) $committer_name
+                                       set header($cmit,summary) \
+                                       [encoding convertfrom $enc $header($cmit,summary)]
+                               }
+                               set msg [string trim $msg]
                        }
                        set header($cmit,message) $msg
                }
@@ -873,6 +879,11 @@ method _open_tooltip {cur_w} {
                set org [lindex $amov_data $lno]
        }
 
+       if {$dat eq {}} {
+               _hide_tooltip $this
+               return
+       }
+
        set cmit [lindex $dat 0]
        set tooltip_commit [list $cmit]
 
index 911e5af7f42f4059636a1a2c95234520f397331e..888db3c889fabbeb221aa9d8c6aab044105f039b 100644 (file)
@@ -3,6 +3,13 @@
 
 class browser {
 
+image create photo ::browser::img_parent  -data {R0lGODlhEAAQAIUAAPwCBBxSHBxOHMTSzNzu3KzCtBRGHCSKFIzCjLzSxBQ2FAxGHDzCLCyeHBQ+FHSmfAwuFBxKLDSCNMzizISyjJzOnDSyLAw+FAQSDAQeDBxWJAwmDAQOBKzWrDymNAQaDAQODAwaDDyKTFSyXFTGTEy6TAQCBAQKDAwiFBQyHAwSFAwmHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAZ1QIBwSCwaj0hiQCBICpcDQsFgGAaIguhhi0gohIsrQEDYMhiNrRfgeAQC5fMCAolIDhD2hFI5WC4YRBkaBxsOE2l/RxsHHA4dHmkfRyAbIQ4iIyQlB5NFGCAACiakpSZEJyinTgAcKSesACorgU4mJ6uxR35BACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs=}
+image create photo ::browser::img_rblob   -data {R0lGODlhEAAQAIUAAPwCBFxaXNze3Ly2rJSWjPz+/Ozq7GxqbJyanPT29HRydMzOzDQyNIyKjERCROTi3Pz69PTy7Pzy7PTu5Ozm3LyqlJyWlJSSjJSOhOzi1LyulPz27PTq3PTm1OzezLyqjIyKhJSKfOzaxPz29OzizLyidIyGdIyCdOTOpLymhOzavOTStMTCtMS+rMS6pMSynMSulLyedAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAaQQIAQECgajcNkQMBkDgKEQFK4LFgLhkMBIVUKroWEYlEgMLxbBKLQUBwc52HgAQ4LBo049atWQyIPA3pEdFcQEhMUFYNVagQWFxgZGoxfYRsTHB0eH5UJCJAYICEinUoPIxIcHCQkIiIllQYEGCEhJicoKYwPmiQeKisrKLFKLCwtLi8wHyUlMYwM0tPUDH5BACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs=}
+image create photo ::browser::img_xblob   -data {R0lGODlhEAAQAIYAAPwCBFRWVFxaXNza3OTi3Nze3Ly2tJyanPz+/Ozq7GxubNzSxMzOzMTGxHRybDQyNLy+vHRydHx6fKSipISChIyKjGxqbERCRCwuLLy6vGRiZExKTCQiJAwKDLSytLy2rJSSlHx+fDw6PKyqrBQWFPTu5Ozm3LyulLS2tCQmJAQCBPTq3Ozi1MSynCwqLAQGBOTazOzizOzezLyqjBweHNzSvOzaxKyurHRuZNzOtLymhDw+PIyCdOzWvOTOpLyidNzKtOTStLyifMTCtMS+rLyedAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAfZgACCAAEChYeGg4oCAwQFjgYBBwGKggEECJkICQoIkwADCwwNDY2mDA4Lng8QDhESsLARExQVDhYXGBkWExIaGw8cHR4SCQQfFQ8eFgUgIQEiwiMSBMYfGB4atwEXDyQd0wQlJicPKAHoFyIpJCoeDgMrLC0YKBsX6i4kL+4OMDEyZijr5oLGNxUqUCioEcPGDAwjPNyI6MEDChQjcOSwsUDHgw07RIgI4KCkAgs8cvTw8eOBogAxQtXIASTISiEuBwUYMoRIixYnZggpUgTDywdIkWJIitRPIAAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7}
+image create photo ::browser::img_tree    -data {R0lGODlhEAAQAIYAAPwCBAQCBExKTBwWHMzKzOzq7ERCRExGTCwqLARqnAQ+ZHR2dKyqrNTOzHx2fCQiJMTi9NTu9HzC3AxmnAQ+XPTm7Dy67DymzITC3IzG5AxypHRydKymrMzOzOzu7BweHByy9AyGtFyy1IzG3NTu/ARupFRSVByazBR6rAyGvFyuzJTK3MTm9BR+tAxWhHS61MTi7Pz+/IymvCxulBRelAx2rHS63Pz6/PTy9PTu9Nza3ISitBRupFSixNTS1CxqnDQyNMzGzOTi5MTCxMTGxGxubGxqbLy2vLSutGRiZLy6vLSytKyurDQuNFxaXKSipDw6PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAfDgACCAAECg4eIAAMEBQYHCImDBgkKCwwNBQIBBw4Bhw8QERITFJYEFQUFnoIPFhcYoRkaFBscHR4Ggh8gIRciEiMQJBkltCa6JyUoKSkXKhIrLCQYuQAPLS4TEyUhKb0qLzDVAjEFMjMuNBMoNcw21QY3ODkFOjs82RM1PfDzFRU3fOggcM7Fj2pAgggRokOHDx9DhhAZUqQaISBGhjwMEvEIkiIHEgUAkgSJkiNLmFSMJChAEydPGBSBwvJQgAc0/QQCACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs=}
+image create photo ::browser::img_symlink -data {R0lGODlhEAAQAIQAAPwCBCwqLLSytLy+vERGRFRWVDQ2NKSmpAQCBKyurMTGxISChJyanHR2dIyKjGxubHRydGRmZIyOjFxeXHx6fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAVbICACwWieY1CibCCsrBkMb0zchSEcNYskCtqBBzshFkOGQFk0IRqOxqPBODRHCMhCQKteRc9FI/KQWGOIyFYgkDC+gPR4snCcfRGKOIKIgSMQE31+f4OEYCZ+IQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7}
+image create photo ::browser::img_unknown -data {R0lGODlhEAAQAIUAAPwCBFxaXIyKjNTW1Nze3LS2tJyanER2RGS+VPz+/PTu5GxqbPz69BQ6BCxeLFSqRPT29HRydMzOzDQyNERmPKSypCRWHIyKhERCRDyGPKz2nESiLBxGHCyCHGxubPz6/PTy7Ozi1Ly2rKSipOzm3LyqlKSWhCRyFOzizLymhNTKtNzOvOzaxOTStPz27OzWvOTOpLSupLyedMS+rMS6pMSulLyqjLymfLyifAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAamQIAQECgajcOkYEBoDgoBQyAJOCCuiENCsWBIh9aGw9F4HCARiXciRDQoBUnlYRlcIgsMG5CxXAgMGhscBRAEBRd7AB0eBBoIgxUfICEiikSPgyMMIAokJZcBkBybJgomIaBJAZoMpyCmqkMBFCcVCrgKKAwpoSorKqchKCwtvasIFBIhLiYvLzDHsxQNMcMKLDAwMqEz3jQ1NTY3ONyrE+jp6hN+QQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7}
+
 field w
 field browser_commit
 field browser_path
@@ -13,13 +20,13 @@ field browser_busy   1
 
 field ls_buf     {}; # Buffered record output from ls-tree
 
-constructor new {commit} {
+constructor new {commit {path {}}} {
        global cursor_ptr M1B
        make_toplevel top w
        wm title $top "[appname] ([reponame]): File Browser"
 
        set browser_commit $commit
-       set browser_path $browser_commit:
+       set browser_path $browser_commit:$path
 
        label $w.path \
                -textvariable @browser_path \
@@ -73,7 +80,11 @@ constructor new {commit} {
 
        bind $w_list <Visibility> [list focus $w_list]
        set w $w_list
-       _ls $this $browser_commit
+       if {$path ne {}} {
+               _ls $this $browser_commit:$path $path
+       } else {
+               _ls $this $browser_commit $path
+       }
        return $this
 }
 
@@ -173,7 +184,7 @@ method _ls {tree_id {name {}}} {
                $w image create end \
                        -align center -padx 5 -pady 1 \
                        -name icon0 \
-                       -image file_uplevel
+                       -image ::browser::img_parent
                $w insert end {[Up To Parent]}
                lappend browser_files parent
        }
@@ -203,14 +214,21 @@ method _read {fd} {
 
                switch -- $type {
                blob {
-                       set image file_mod
+                       scan [lindex $info 0] %o mode
+                       if {$mode == 0120000} {
+                               set image ::browser::img_symlink
+                       } elseif {($mode & 0100) != 0} {
+                               set image ::browser::img_xblob
+                       } else {
+                               set image ::browser::img_rblob
+                       }
                }
                tree {
-                       set image file_dir
+                       set image ::browser::img_tree
                        append path /
                }
                default {
-                       set image file_question
+                       set image ::browser::img_unknown
                }
                }
 
@@ -239,3 +257,56 @@ method _read {fd} {
 }
 
 }
+
+class browser_open {
+
+field w              ; # widget path
+field w_rev          ; # mega-widget to pick the initial revision
+
+constructor dialog {} {
+       make_toplevel top w
+       wm title $top "[appname] ([reponame]): Browse Branch Files"
+       if {$top ne {.}} {
+               wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+       }
+
+       label $w.header \
+               -text {Browse Branch Files} \
+               -font font_uibold
+       pack $w.header -side top -fill x
+
+       frame $w.buttons
+       button $w.buttons.browse -text Browse \
+               -default active \
+               -command [cb _open]
+       pack $w.buttons.browse -side right
+       button $w.buttons.cancel -text {Cancel} \
+               -command [list destroy $w]
+       pack $w.buttons.cancel -side right -padx 5
+       pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+       set w_rev [::choose_rev::new $w.rev {Revision}]
+       $w_rev bind_listbox <Double-Button-1> [cb _open]
+       pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5
+
+       bind $w <Visibility> [cb _visible]
+       bind $w <Key-Escape> [list destroy $w]
+       bind $w <Key-Return> [cb _open]\;break
+       tkwait window $w
+}
+
+method _open {} {
+       if {[catch {$w_rev commit_or_die} err]} {
+               return
+       }
+       set name [$w_rev get]
+       destroy $w
+       browser::new $name
+}
+
+method _visible {} {
+       grab $w
+       $w_rev focus_filter
+}
+
+}
index 00a994be120edaa1024733cb94e3f1fe4e494039..170f737f611b48fa901e1789ec6d4f4d64834537 100644 (file)
@@ -12,6 +12,7 @@ field new_ref    ; # ref we are updating/creating
 
 field parent_w      .; # window that started us
 field merge_type none; # type of merge to apply to existing branch
+field merge_base   {}; # merge base if we have another ref involved
 field fetch_spec   {}; # refetch tracking branch if used?
 field checkout      1; # actually checkout the branch?
 field create        0; # create the branch if it doesn't exist?
@@ -65,14 +66,19 @@ method run {} {
                set r_head [lindex $fetch_spec 2]
                regsub ^refs/heads/ $r_head {} r_name
 
+               set cmd [list git fetch $remote]
+               if {$l_trck ne {}} {
+                       lappend cmd +$r_head:$l_trck
+               } else {
+                       lappend cmd $r_head
+               }
+
                _toplevel $this {Refreshing Tracking Branch}
                set w_cons [::console::embed \
                        $w.console \
                        "Fetching $r_name from $remote"]
                pack $w.console -fill both -expand 1
-               $w_cons exec \
-                       [list git fetch $remote +$r_head:$l_trck] \
-                       [cb _finish_fetch]
+               $w_cons exec $cmd [cb _finish_fetch]
 
                bind $w <$M1B-Key-w> break
                bind $w <$M1B-Key-W> break
@@ -113,6 +119,9 @@ method _noop {} {}
 method _finish_fetch {ok} {
        if {$ok} {
                set l_trck [lindex $fetch_spec 0]
+               if {$l_trck eq {}} {
+                       set l_trck FETCH_HEAD
+               }
                if {[catch {set new_hash [git rev-parse --verify "$l_trck^0"]} err]} {
                        set ok 0
                        $w_cons insert "fatal: Cannot resolve $l_trck"
@@ -180,29 +189,25 @@ method _update_ref {} {
                # No merge would be required, don't compute anything.
                #
        } else {
-               set mrb {}
-               catch {set mrb [git merge-base $new $cur]}
-               switch -- $merge_type {
-               ff {
-                       if {$mrb eq $new} {
-                               # The current branch is actually newer.
-                               #
-                               set new $cur
-                       } elseif {$mrb eq $cur} {
-                               # The current branch is older.
-                               #
-                               set reflog_msg "merge $new_expr: Fast-forward"
-                       } else {
-                               _error $this "Branch '$newbranch' already exists.\n\nIt cannot fast-forward to $new_expr.\nA merge is required."
-                               return 0
+               catch {set merge_base [git merge-base $new $cur]}
+               if {$merge_base eq $cur} {
+                       # The current branch is older.
+                       #
+                       set reflog_msg "merge $new_expr: Fast-forward"
+               } else {
+                       switch -- $merge_type {
+                       ff {
+                               if {$merge_base eq $new} {
+                                       # The current branch is actually newer.
+                                       #
+                                       set new $cur
+                                       set new_hash $cur
+                               } else {
+                                       _error $this "Branch '$newbranch' already exists.\n\nIt cannot fast-forward to $new_expr.\nA merge is required."
+                                       return 0
+                               }
                        }
-               }
-               reset {
-                       if {$mrb eq $cur} {
-                               # The current branch is older.
-                               #
-                               set reflog_msg "merge $new_expr: Fast-forward"
-                       } else {
+                       reset {
                                # The current branch will lose things.
                                #
                                if {[_confirm_reset $this $cur]} {
@@ -211,11 +216,11 @@ method _update_ref {} {
                                        return 0
                                }
                        }
-               }
-               default {
-                       _error $this "Only 'ff' and 'reset' merge is currently supported."
-                       return 0
-               }
+                       default {
+                               _error $this "Merge strategy '$merge_type' not supported."
+                               return 0
+                       }
+                       }
                }
        }
 
@@ -243,7 +248,7 @@ method _checkout {} {
        if {[lock_index checkout_op]} {
                after idle [cb _start_checkout]
        } else {
-               _error $this "Index is already locked."
+               _error $this "Staging area (index) is already locked."
                delete_this
        }
 }
@@ -270,7 +275,9 @@ The rescan will be automatically started now.
                return
        }
 
-       if {[is_config_true gui.trustmtime]} {
+       if {$curHEAD eq $new_hash} {
+               _after_readtree $this
+       } elseif {[is_config_true gui.trustmtime]} {
                _readtree $this
        } else {
                ui_status {Refreshing file status...}
@@ -378,22 +385,24 @@ method _after_readtree {} {
        set rn [string length $rh]
        if {[string equal -length $rn $rh $new_ref]} {
                set new_branch [string range $new_ref $rn end]
-               append log " to $new_branch"
-
-               if {[catch {
-                               git symbolic-ref -m $log HEAD $new_ref
-                       } err]} {
-                       _fatal $this $err
+               if {$is_detached || $current_branch ne $new_branch} {
+                       append log " to $new_branch"
+                       if {[catch {
+                                       git symbolic-ref -m $log HEAD $new_ref
+                               } err]} {
+                               _fatal $this $err
+                       }
+                       set current_branch $new_branch
+                       set is_detached 0
                }
-               set current_branch $new_branch
-               set is_detached 0
        } else {
-               append log " to $new_expr"
-
-               if {[catch {
-                               _detach_HEAD $log $new_hash
-                       } err]} {
-                       _fatal $this $err
+               if {$new_hash ne $HEAD} {
+                       append log " to $new_expr"
+                       if {[catch {
+                                       _detach_HEAD $log $new_hash
+                               } err]} {
+                               _fatal $this $err
+                       }
                }
                set current_branch HEAD
                set is_detached 1
index afd81707ceb08ccc106e76b082f892c824dfb828..ec064b3e13a6b2e6ab0e3ee05ab7a55cab8aa4bc 100644 (file)
@@ -16,10 +16,28 @@ field cur_specs [list]; # list of specs for $revtype
 field spec_head       ; # list of all head specs
 field spec_trck       ; # list of all tracking branch specs
 field spec_tag        ; # list of all tag specs
+field tip_data        ; # array of tip commit info by refname
+field log_last        ; # array of reflog date by refname
 
-constructor new {path {title {}}} {
+field tooltip_wm        {} ; # Current tooltip toplevel, if open
+field tooltip_t         {} ; # Text widget in $tooltip_wm
+field tooltip_timer     {} ; # Current timer event for our tooltip
+
+proc new {path {title {}}} {
+       return [_new $path 0 $title]
+}
+
+proc new_unmerged {path {title {}}} {
+       return [_new $path 1 $title]
+}
+
+constructor _new {path unmerged_only title} {
        global current_branch is_detached
 
+       if {![info exists ::all_remotes]} {
+               load_all_remotes
+       }
+
        set w $path
 
        if {$title ne {}} {
@@ -86,13 +104,17 @@ constructor new {path {title {}}} {
        listbox $w_list \
                -font font_diff \
                -width 50 \
-               -height 5 \
+               -height 10 \
                -selectmode browse \
                -exportselection false \
                -xscrollcommand [cb _sb_set $w.list.sbx h] \
                -yscrollcommand [cb _sb_set $w.list.sby v]
        pack $w_list -fill both -expand 1
        grid $w.list -sticky nswe -padx {20 5} -columnspan 2
+       bind $w_list <Any-Motion>  [cb _show_tooltip @%x,%y]
+       bind $w_list <Any-Enter>   [cb _hide_tooltip]
+       bind $w_list <Any-Leave>   [cb _hide_tooltip]
+       bind $w_list <Destroy>     [cb _hide_tooltip]
 
        grid columnconfigure $w 1 -weight 1
        if {$is_detached} {
@@ -105,21 +127,89 @@ constructor new {path {title {}}} {
        bind $w_filter <Key-Return> [list focus $w_list]\;break
        bind $w_filter <Key-Down>   [list focus $w_list]
 
+       set fmt list
+       append fmt { %(refname)}
+       append fmt { [list}
+       append fmt { %(objecttype)}
+       append fmt { %(objectname)}
+       append fmt { [concat %(taggername) %(authorname)]}
+       append fmt { [concat %(taggerdate) %(authordate)]}
+       append fmt { %(subject)}
+       append fmt {] [list}
+       append fmt { %(*objecttype)}
+       append fmt { %(*objectname)}
+       append fmt { %(*authorname)}
+       append fmt { %(*authordate)}
+       append fmt { %(*subject)}
+       append fmt {]}
+       set all_refn [list]
+       set fr_fd [git_read for-each-ref \
+               --tcl \
+               --sort=-taggerdate \
+               --format=$fmt \
+               refs/heads \
+               refs/remotes \
+               refs/tags \
+               ]
+       fconfigure $fr_fd -translation lf -encoding utf-8
+       while {[gets $fr_fd line] > 0} {
+               set line [eval $line]
+               if {[lindex $line 1 0] eq {tag}} {
+                       if {[lindex $line 2 0] eq {commit}} {
+                               set sha1 [lindex $line 2 1]
+                       } else {
+                               continue
+                       }
+               } elseif {[lindex $line 1 0] eq {commit}} {
+                       set sha1 [lindex $line 1 1]
+               } else {
+                       continue
+               }
+               set refn [lindex $line 0]
+               set tip_data($refn) [lrange $line 1 end]
+               lappend cmt_refn($sha1) $refn
+               lappend all_refn $refn
+       }
+       close $fr_fd
+
+       if {$unmerged_only} {
+               set fr_fd [git_read rev-list --all ^$::HEAD]
+               while {[gets $fr_fd sha1] > 0} {
+                       if {[catch {set rlst $cmt_refn($sha1)}]} continue
+                       foreach refn $rlst {
+                               set inc($refn) 1
+                       }
+               }
+               close $fr_fd
+       } else {
+               foreach refn $all_refn {
+                       set inc($refn) 1
+               }
+       }
+
        set spec_head [list]
        foreach name [load_all_heads] {
-               lappend spec_head [list $name refs/heads/$name]
+               set refn refs/heads/$name
+               if {[info exists inc($refn)]} {
+                       lappend spec_head [list $name $refn]
+               }
        }
 
        set spec_trck [list]
        foreach spec [all_tracking_branches] {
-               set name [lindex $spec 0]
-               regsub ^refs/(heads|remotes)/ $name {} name
-               lappend spec_trck [concat $name $spec]
+               set refn [lindex $spec 0]
+               if {[info exists inc($refn)]} {
+                       regsub ^refs/(heads|remotes)/ $refn {} name
+                       lappend spec_trck [concat $name $spec]
+               }
        }
 
        set spec_tag [list]
        foreach name [load_all_tags] {
-               lappend spec_tag [list $name refs/tags/$name]
+               set refn refs/tags/$name
+               if {[info exists inc($refn)]} {
+                       lappend spec_tag [list $name $refn]
+               }
        }
 
                  if {$is_detached}             { set revtype HEAD
@@ -364,4 +454,174 @@ method _sb_set {sb orient first last} {
        $sb set $first $last
 }
 
+method _show_tooltip {pos} {
+       if {$tooltip_wm ne {}} {
+               _open_tooltip $this
+       } elseif {$tooltip_timer eq {}} {
+               set tooltip_timer [after 1000 [cb _open_tooltip]]
+       }
+}
+
+method _open_tooltip {} {
+       global remote_url
+
+       set tooltip_timer {}
+       set pos_x [winfo pointerx $w_list]
+       set pos_y [winfo pointery $w_list]
+       if {[winfo containing $pos_x $pos_y] ne $w_list} {
+               _hide_tooltip $this
+               return
+       }
+
+       set pos @[join [list \
+               [expr {$pos_x - [winfo rootx $w_list]}] \
+               [expr {$pos_y - [winfo rooty $w_list]}]] ,]
+       set lno [$w_list index $pos]
+       if {$lno eq {}} {
+               _hide_tooltip $this
+               return
+       }
+
+       set spec [lindex $cur_specs $lno]
+       set refn [lindex $spec 1]
+       if {$refn eq {}} {
+               _hide_tooltip $this
+               return
+       }
+
+       if {$tooltip_wm eq {}} {
+               set tooltip_wm [toplevel $w_list.tooltip -borderwidth 1]
+               wm overrideredirect $tooltip_wm 1
+               wm transient $tooltip_wm [winfo toplevel $w_list]
+               set tooltip_t $tooltip_wm.label
+               text $tooltip_t \
+                       -takefocus 0 \
+                       -highlightthickness 0 \
+                       -relief flat \
+                       -borderwidth 0 \
+                       -wrap none \
+                       -background lightyellow \
+                       -foreground black
+               $tooltip_t tag conf section_header -font font_uibold
+               bind $tooltip_wm <Escape> [cb _hide_tooltip]
+               pack $tooltip_t
+       } else {
+               $tooltip_t conf -state normal
+               $tooltip_t delete 0.0 end
+       }
+
+       set data $tip_data($refn)
+       if {[lindex $data 0 0] eq {tag}} {
+               set tag  [lindex $data 0]
+               if {[lindex $data 1 0] eq {commit}} {
+                       set cmit [lindex $data 1]
+               } else {
+                       set cmit {}
+               }
+       } elseif {[lindex $data 0 0] eq {commit}} {
+               set tag  {}
+               set cmit [lindex $data 0]
+       }
+
+       $tooltip_t insert end [lindex $spec 0]
+       set last [_reflog_last $this [lindex $spec 1]]
+       if {$last ne {}} {
+               $tooltip_t insert end "\n"
+               $tooltip_t insert end "updated"
+               $tooltip_t insert end " $last"
+       }
+       $tooltip_t insert end "\n"
+
+       if {$tag ne {}} {
+               $tooltip_t insert end "\n"
+               $tooltip_t insert end "tag" section_header
+               $tooltip_t insert end "  [lindex $tag 1]\n"
+               $tooltip_t insert end [lindex $tag 2]
+               $tooltip_t insert end " ([lindex $tag 3])\n"
+               $tooltip_t insert end [lindex $tag 4]
+               $tooltip_t insert end "\n"
+       }
+
+       if {$cmit ne {}} {
+               $tooltip_t insert end "\n"
+               $tooltip_t insert end "commit" section_header
+               $tooltip_t insert end "  [lindex $cmit 1]\n"
+               $tooltip_t insert end [lindex $cmit 2]
+               $tooltip_t insert end " ([lindex $cmit 3])\n"
+               $tooltip_t insert end [lindex $cmit 4]
+       }
+
+       if {[llength $spec] > 2} {
+               $tooltip_t insert end "\n"
+               $tooltip_t insert end "remote" section_header
+               $tooltip_t insert end "  [lindex $spec 2]\n"
+               $tooltip_t insert end "url"
+               $tooltip_t insert end " $remote_url([lindex $spec 2])\n"
+               $tooltip_t insert end "branch"
+               $tooltip_t insert end " [lindex $spec 3]"
+       }
+
+       $tooltip_t conf -state disabled
+       _position_tooltip $this
+}
+
+method _reflog_last {name} {
+       if {[info exists reflog_last($name)]} {
+               return reflog_last($name)
+       }
+
+       set last {}
+       if {[catch {set last [file mtime [gitdir $name]]}]
+       && ![catch {set g [open [gitdir logs $name] r]}]} {
+               fconfigure $g -translation binary
+               while {[gets $g line] >= 0} {
+                       if {[regexp {> ([1-9][0-9]*) } $line line when]} {
+                               set last $when
+                       }
+               }
+               close $g
+       }
+
+       if {$last ne {}} {
+               set last [clock format $last -format {%a %b %e %H:%M:%S %Y}]
+       }
+       set reflog_last($name) $last
+       return $last
+}
+
+method _position_tooltip {} {
+       set max_h [lindex [split [$tooltip_t index end] .] 0]
+       set max_w 0
+       for {set i 1} {$i <= $max_h} {incr i} {
+               set c [lindex [split [$tooltip_t index "$i.0 lineend"] .] 1]
+               if {$c > $max_w} {set max_w $c}
+       }
+       $tooltip_t conf -width $max_w -height $max_h
+
+       set req_w [winfo reqwidth  $tooltip_t]
+       set req_h [winfo reqheight $tooltip_t]
+       set pos_x [expr {[winfo pointerx .] +  5}]
+       set pos_y [expr {[winfo pointery .] + 10}]
+
+       set g "${req_w}x${req_h}"
+       if {$pos_x >= 0} {append g +}
+       append g $pos_x
+       if {$pos_y >= 0} {append g +}
+       append g $pos_y
+
+       wm geometry $tooltip_wm $g
+       raise $tooltip_wm
+}
+
+method _hide_tooltip {} {
+       if {$tooltip_wm ne {}} {
+               destroy $tooltip_wm
+               set tooltip_wm {}
+       }
+       if {$tooltip_timer ne {}} {
+               after cancel $tooltip_timer
+               set tooltip_timer {}
+       }
+}
+
 }
index 46a78c158f53372a481061e9a5a6072c40e54e58..f857a2ff5bb3d0e151ea471a16a1b233117afc70 100644 (file)
@@ -37,9 +37,14 @@ You are currently in the middle of a merge that has not been fully completed.  Y
                                        set enc [string tolower [string range $line 9 end]]
                                }
                        }
-                       set msg [encoding convertfrom $enc [read $fd]]
-                       set msg [string trim $msg]
+                       set msg [read $fd]
                        close $fd
+
+                       set enc [tcl_encoding $enc]
+                       if {$enc ne {}} {
+                               set msg [encoding convertfrom $enc $msg]
+                       }
+                       set msg [string trim $msg]
                } err]} {
                error_popup "Error loading commit data for amend:\n\n$err"
                return
@@ -148,7 +153,7 @@ The rescan will be automatically started now.
                U? {
                        error_popup "Unmerged files cannot be committed.
 
-File [short_path $path] has merge conflicts.  You must resolve them and add the file before committing.
+File [short_path $path] has merge conflicts.  You must resolve them and stage the file before committing.
 "
                        unlock_index
                        return
@@ -164,7 +169,7 @@ File [short_path $path] cannot be committed by this program.
        if {!$files_ready && ![string match *merge $curType]} {
                info_popup {No changes to commit.
 
-You must add at least 1 file before you can commit.
+You must stage at least 1 file before you can commit.
 }
                unlock_index
                return
@@ -209,7 +214,7 @@ A good commit message has the following format:
        ui_status {Calling pre-commit hook...}
        set pch_error {}
        set fd_ph [open "| $pchook" r]
-       fconfigure $fd_ph -blocking 0 -translation binary
+       fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
        fileevent $fd_ph readable \
                [list commit_prehook_wait $fd_ph $curHEAD $msg]
 }
@@ -287,11 +292,18 @@ A rescan will be automatically started now.
        #
        set msg_p [gitdir COMMIT_EDITMSG]
        set msg_wt [open $msg_p w]
+       fconfigure $msg_wt -translation lf
        if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
                set enc utf-8
        }
-       fconfigure $msg_wt -encoding binary -translation binary
-       puts -nonewline $msg_wt [encoding convertto $enc $msg]
+       set use_enc [tcl_encoding $enc]
+       if {$use_enc ne {}} {
+               fconfigure $msg_wt -encoding $use_enc
+       } else {
+               puts stderr "warning: Tcl does not support encoding '$enc'."
+               fconfigure $msg_wt -encoding utf-8
+       }
+       puts -nonewline $msg_wt $msg
        close $msg_wt
 
        # -- Create the commit.
@@ -367,6 +379,10 @@ A rescan will be automatically started now.
        $ui_comm delete 0.0 end
        $ui_comm edit reset
        $ui_comm edit modified false
+       if {$::GITGUI_BCK_exists} {
+               catch {file delete [gitdir GITGUI_BCK]}
+               set ::GITGUI_BCK_exists 0
+       }
 
        if {[is_enabled singlecommit]} do_quit
 
index 87c815d7ac4f64d4d837f950f6f60e141a4433d5..0657cc2245cec67bbb6d3399935a40247bd0c402 100644 (file)
@@ -87,3 +87,30 @@ proc do_fsck_objects {} {
        lappend cmd --strict
        console::exec $w $cmd
 }
+
+proc hint_gc {} {
+       set object_limit 8
+       if {[is_Windows]} {
+               set object_limit 1
+       }
+
+       set objects_current [llength [glob \
+               -directory [gitdir objects 42] \
+               -nocomplain \
+               -tails \
+               -- \
+               *]]
+
+       if {$objects_current >= $object_limit} {
+               set objects_current [expr {$objects_current * 256}]
+               set object_limit    [expr {$object_limit    * 256}]
+               if {[ask_popup \
+                       "This repository currently has approximately $objects_current loose objects.
+
+To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
+
+Compress the database now?"] eq yes} {
+                       do_gc
+               }
+       }
+}
index 9cb9d0604a4905337dc0c57d8d846753a3157c8a..e09e1257e1be299caceb0c6f0074c1b43b566974 100644 (file)
@@ -86,6 +86,7 @@ proc show_diff {path w {lno {}}} {
                set max_sz [expr {128 * 1024}]
                if {[catch {
                                set fd [open $path r]
+                               fconfigure $fd -eofchar {}
                                set content [read $fd $max_sz]
                                close $fd
                                set sz [file size $path]
diff --git a/git-gui/lib/encoding.tcl b/git-gui/lib/encoding.tcl
new file mode 100644 (file)
index 0000000..7f06b0d
--- /dev/null
@@ -0,0 +1,276 @@
+# git-gui encoding support
+# Copyright (C) 2005 Paul Mackerras <paulus@samba.org>
+# (Copied from gitk, commit fd8ccbec4f0161)
+
+# This list of encoding names and aliases is distilled from
+# http://www.iana.org/assignments/character-sets.
+# Not all of them are supported by Tcl.
+set encoding_aliases {
+    { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
+      ISO646-US US-ASCII us IBM367 cp367 csASCII }
+    { ISO-10646-UTF-1 csISO10646UTF1 }
+    { ISO_646.basic:1983 ref csISO646basic1983 }
+    { INVARIANT csINVARIANT }
+    { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
+    { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
+    { NATS-SEFI iso-ir-8-1 csNATSSEFI }
+    { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
+    { NATS-DANO iso-ir-9-1 csNATSDANO }
+    { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
+    { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
+    { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
+    { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
+    { ISO-2022-KR csISO2022KR }
+    { EUC-KR csEUCKR }
+    { ISO-2022-JP csISO2022JP }
+    { ISO-2022-JP-2 csISO2022JP2 }
+    { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
+      csISO13JISC6220jp }
+    { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
+    { IT iso-ir-15 ISO646-IT csISO15Italian }
+    { PT iso-ir-16 ISO646-PT csISO16Portuguese }
+    { ES iso-ir-17 ISO646-ES csISO17Spanish }
+    { greek7-old iso-ir-18 csISO18Greek7Old }
+    { latin-greek iso-ir-19 csISO19LatinGreek }
+    { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
+    { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
+    { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
+    { ISO_5427 iso-ir-37 csISO5427Cyrillic }
+    { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
+    { BS_viewdata iso-ir-47 csISO47BSViewdata }
+    { INIS iso-ir-49 csISO49INIS }
+    { INIS-8 iso-ir-50 csISO50INIS8 }
+    { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
+    { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
+    { ISO_5428:1980 iso-ir-55 csISO5428Greek }
+    { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
+    { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
+    { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
+      csISO60Norwegian1 }
+    { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
+    { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
+    { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
+    { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
+    { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
+    { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
+    { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
+    { greek7 iso-ir-88 csISO88Greek7 }
+    { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
+    { iso-ir-90 csISO90 }
+    { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
+    { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
+      csISO92JISC62991984b }
+    { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
+    { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
+    { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
+      csISO95JIS62291984handadd }
+    { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
+    { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
+    { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
+    { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
+      CP819 csISOLatin1 }
+    { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
+    { T.61-7bit iso-ir-102 csISO102T617bit }
+    { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
+    { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
+    { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
+    { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
+    { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
+    { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
+    { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
+    { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
+      arabic csISOLatinArabic }
+    { ISO_8859-6-E csISO88596E ISO-8859-6-E }
+    { ISO_8859-6-I csISO88596I ISO-8859-6-I }
+    { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
+      greek greek8 csISOLatinGreek }
+    { T.101-G2 iso-ir-128 csISO128T101G2 }
+    { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
+      csISOLatinHebrew }
+    { ISO_8859-8-E csISO88598E ISO-8859-8-E }
+    { ISO_8859-8-I csISO88598I ISO-8859-8-I }
+    { CSN_369103 iso-ir-139 csISO139CSN369103 }
+    { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
+    { ISO_6937-2-add iso-ir-142 csISOTextComm }
+    { IEC_P27-1 iso-ir-143 csISO143IECP271 }
+    { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
+      csISOLatinCyrillic }
+    { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
+    { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
+    { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
+    { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
+    { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
+    { ISO_6937-2-25 iso-ir-152 csISO6937Add }
+    { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
+    { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
+    { ISO_10367-box iso-ir-155 csISO10367Box }
+    { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
+    { latin-lap lap iso-ir-158 csISO158Lap }
+    { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
+    { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
+    { us-dk csUSDK }
+    { dk-us csDKUS }
+    { JIS_X0201 X0201 csHalfWidthKatakana }
+    { KSC5636 ISO646-KR csKSC5636 }
+    { ISO-10646-UCS-2 csUnicode }
+    { ISO-10646-UCS-4 csUCS4 }
+    { DEC-MCS dec csDECMCS }
+    { hp-roman8 roman8 r8 csHPRoman8 }
+    { macintosh mac csMacintosh }
+    { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
+      csIBM037 }
+    { IBM038 EBCDIC-INT cp038 csIBM038 }
+    { IBM273 CP273 csIBM273 }
+    { IBM274 EBCDIC-BE CP274 csIBM274 }
+    { IBM275 EBCDIC-BR cp275 csIBM275 }
+    { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
+    { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
+    { IBM280 CP280 ebcdic-cp-it csIBM280 }
+    { IBM281 EBCDIC-JP-E cp281 csIBM281 }
+    { IBM284 CP284 ebcdic-cp-es csIBM284 }
+    { IBM285 CP285 ebcdic-cp-gb csIBM285 }
+    { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
+    { IBM297 cp297 ebcdic-cp-fr csIBM297 }
+    { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
+    { IBM423 cp423 ebcdic-cp-gr csIBM423 }
+    { IBM424 cp424 ebcdic-cp-he csIBM424 }
+    { IBM437 cp437 437 csPC8CodePage437 }
+    { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
+    { IBM775 cp775 csPC775Baltic }
+    { IBM850 cp850 850 csPC850Multilingual }
+    { IBM851 cp851 851 csIBM851 }
+    { IBM852 cp852 852 csPCp852 }
+    { IBM855 cp855 855 csIBM855 }
+    { IBM857 cp857 857 csIBM857 }
+    { IBM860 cp860 860 csIBM860 }
+    { IBM861 cp861 861 cp-is csIBM861 }
+    { IBM862 cp862 862 csPC862LatinHebrew }
+    { IBM863 cp863 863 csIBM863 }
+    { IBM864 cp864 csIBM864 }
+    { IBM865 cp865 865 csIBM865 }
+    { IBM866 cp866 866 csIBM866 }
+    { IBM868 CP868 cp-ar csIBM868 }
+    { IBM869 cp869 869 cp-gr csIBM869 }
+    { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
+    { IBM871 CP871 ebcdic-cp-is csIBM871 }
+    { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
+    { IBM891 cp891 csIBM891 }
+    { IBM903 cp903 csIBM903 }
+    { IBM904 cp904 904 csIBBM904 }
+    { IBM905 CP905 ebcdic-cp-tr csIBM905 }
+    { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
+    { IBM1026 CP1026 csIBM1026 }
+    { EBCDIC-AT-DE csIBMEBCDICATDE }
+    { EBCDIC-AT-DE-A csEBCDICATDEA }
+    { EBCDIC-CA-FR csEBCDICCAFR }
+    { EBCDIC-DK-NO csEBCDICDKNO }
+    { EBCDIC-DK-NO-A csEBCDICDKNOA }
+    { EBCDIC-FI-SE csEBCDICFISE }
+    { EBCDIC-FI-SE-A csEBCDICFISEA }
+    { EBCDIC-FR csEBCDICFR }
+    { EBCDIC-IT csEBCDICIT }
+    { EBCDIC-PT csEBCDICPT }
+    { EBCDIC-ES csEBCDICES }
+    { EBCDIC-ES-A csEBCDICESA }
+    { EBCDIC-ES-S csEBCDICESS }
+    { EBCDIC-UK csEBCDICUK }
+    { EBCDIC-US csEBCDICUS }
+    { UNKNOWN-8BIT csUnknown8BiT }
+    { MNEMONIC csMnemonic }
+    { MNEM csMnem }
+    { VISCII csVISCII }
+    { VIQR csVIQR }
+    { KOI8-R csKOI8R }
+    { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
+    { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
+    { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
+    { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
+    { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
+    { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
+    { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
+    { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
+    { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
+    { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
+    { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
+    { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
+    { IBM1047 IBM-1047 }
+    { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
+    { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
+    { UNICODE-1-1 csUnicode11 }
+    { CESU-8 csCESU-8 }
+    { BOCU-1 csBOCU-1 }
+    { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
+    { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
+      l8 }
+    { ISO-8859-15 ISO_8859-15 Latin-9 }
+    { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
+    { GBK CP936 MS936 windows-936 }
+    { JIS_Encoding csJISEncoding }
+    { Shift_JIS MS_Kanji csShiftJIS }
+    { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
+      EUC-JP }
+    { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
+    { ISO-10646-UCS-Basic csUnicodeASCII }
+    { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
+    { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
+    { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
+    { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
+    { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
+    { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
+    { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
+    { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
+    { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
+    { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
+    { Adobe-Standard-Encoding csAdobeStandardEncoding }
+    { Ventura-US csVenturaUS }
+    { Ventura-International csVenturaInternational }
+    { PC8-Danish-Norwegian csPC8DanishNorwegian }
+    { PC8-Turkish csPC8Turkish }
+    { IBM-Symbols csIBMSymbols }
+    { IBM-Thai csIBMThai }
+    { HP-Legal csHPLegal }
+    { HP-Pi-font csHPPiFont }
+    { HP-Math8 csHPMath8 }
+    { Adobe-Symbol-Encoding csHPPSMath }
+    { HP-DeskTop csHPDesktop }
+    { Ventura-Math csVenturaMath }
+    { Microsoft-Publishing csMicrosoftPublishing }
+    { Windows-31J csWindows31J }
+    { GB2312 csGB2312 }
+    { Big5 csBig5 }
+}
+
+proc tcl_encoding {enc} {
+    global encoding_aliases
+    set names [encoding names]
+    set lcnames [string tolower $names]
+    set enc [string tolower $enc]
+    set i [lsearch -exact $lcnames $enc]
+    if {$i < 0} {
+       # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
+       if {[regsub {^iso[-_]} $enc iso encx]} {
+           set i [lsearch -exact $lcnames $encx]
+       }
+    }
+    if {$i < 0} {
+       foreach l $encoding_aliases {
+           set ll [string tolower $l]
+           if {[lsearch -exact $ll $enc] < 0} continue
+           # look through the aliases for one that tcl knows about
+           foreach e $ll {
+               set i [lsearch -exact $lcnames $e]
+               if {$i < 0} {
+                   if {[regsub {^iso[-_]} $e iso ex]} {
+                       set i [lsearch -exact $lcnames $ex]
+                   }
+               }
+               if {$i >= 0} break
+           }
+           break
+       }
+    }
+    if {$i >= 0} {
+       return [lindex $names $i]
+    }
+    return {}
+}
index d0253ae2ffa72e491784434ff6ad98ed675ff10c..16a22187b26760963069bef14673b1791b311c12 100644 (file)
@@ -51,12 +51,15 @@ proc ask_popup {msg} {
        if {[reponame] ne {}} {
                append title " ([reponame])"
        }
-       return [tk_messageBox \
-               -parent . \
+       set cmd [list tk_messageBox \
                -icon question \
                -type yesno \
                -title $title \
                -message $msg]
+       if {[winfo ismapped .]} {
+               lappend cmd -parent .
+       }
+       eval $cmd
 }
 
 proc hook_failed_popup {hook msg} {
index 3ea72e1ec9807477e72e48306d7e3ad47a393abb..f47f9290c8f82b2c899c839c26d9921e85b9fa0d 100644 (file)
@@ -360,7 +360,7 @@ proc revert_helper {txt paths} {
                "[appname] ([reponame])" \
                "Revert changes in $s?
 
-Any unadded changes will be permanently lost by the revert." \
+Any unstaged changes will be permanently lost by the revert." \
                question \
                1 \
                {Do Nothing} \
index 288d7ac8894fbaf2e756add9b2b9a56fdd00b75e..5de0d82b1486ef6ff8ce0eaea4221fa393a87d5b 100644 (file)
@@ -1,9 +1,12 @@
 # git-gui branch merge support
 # Copyright (C) 2006, 2007 Shawn Pearce
 
-namespace eval merge {
+class merge {
+
+field w         ; # top level window
+field w_rev     ; # mega-widget to pick the revision to merge
 
-proc _can_merge {} {
+method _can_merge {} {
        global HEAD commit_type file_states
 
        if {[string match amend* $commit_type]} {
@@ -42,7 +45,7 @@ The rescan will be automatically started now.
 
 File [short_path $path] has merge conflicts.
 
-You must resolve them, add the file, and commit to complete the current merge.  Only then can you begin another merge.
+You must resolve them, stage the file, and commit to complete the current merge.  Only then can you begin another merge.
 "
                        unlock_index
                        return 0
@@ -63,147 +66,93 @@ You should complete the current commit before starting a merge.  Doing so will h
        return 1
 }
 
-proc _refs {w list} {
-       set r {}
-       foreach i [$w.source.l curselection] {
-               lappend r [lindex [lindex $list $i] 0]
+method _rev {} {
+       if {[catch {$w_rev commit_or_die}]} {
+               return {}
        }
-       return $r
+       return [$w_rev get]
 }
 
-proc _visualize {w list} {
-       set revs [_refs $w $list]
-       if {$revs eq {}} return
-       lappend revs --not HEAD
-       do_gitk $revs
+method _visualize {} {
+       set rev [_rev $this]
+       if {$rev ne {}} {
+               do_gitk [list $rev --not HEAD]
+       }
 }
 
-proc _start {w list} {
-       global HEAD current_branch
+method _start {} {
+       global HEAD current_branch remote_url
 
-       set cmd [list git merge]
-       set names [_refs $w $list]
-       set revcnt [llength $names]
-       append cmd { } $names
-
-       if {$revcnt == 0} {
+       set name [_rev $this]
+       if {$name eq {}} {
                return
-       } elseif {$revcnt == 1} {
-               set unit branch
-       } elseif {$revcnt <= 15} {
-               set unit branches
-
-               if {[tk_dialog \
-               $w.confirm_octopus \
-               [wm title $w] \
-               "Use octopus merge strategy?
-
-You are merging $revcnt branches at once.  This requires using the octopus merge driver, which may not succeed if there are file-level conflicts.
-" \
-               question \
-               0 \
-               {Cancel} \
-               {Use octopus} \
-               ] != 1} return
-       } else {
-               tk_messageBox \
-                       -icon error \
-                       -type ok \
-                       -title [wm title $w] \
-                       -parent $w \
-                       -message "Too many branches selected.
+       }
 
-You have requested to merge $revcnt branches in an octopus merge.  This exceeds Git's internal limit of 15 branches per merge.
+       set spec [$w_rev get_tracking_branch]
+       set cmit [$w_rev get_commit]
 
-Please select fewer branches.  To merge more than 15 branches, merge the branches in batches.
-"
-               return
+       set fh [open [gitdir FETCH_HEAD] w]
+       fconfigure $fh -translation lf
+       if {$spec eq {}} {
+               set remote .
+               set branch $name
+               set stitle $branch
+       } else {
+               set remote $remote_url([lindex $spec 1])
+               if {[regexp {^[^:@]*@[^:]*:/} $remote]} {
+                       regsub {^[^:@]*@} $remote {} remote
+               }
+               set branch [lindex $spec 2]
+               set stitle "$branch of $remote"
        }
-
-       set msg "Merging $current_branch, [join $names {, }]"
+       regsub ^refs/heads/ $branch {} branch
+       puts $fh "$cmit\t\tbranch '$branch' of $remote"
+       close $fh
+
+       set cmd [list git]
+       lappend cmd merge
+       lappend cmd --strategy=recursive
+       lappend cmd [git fmt-merge-msg <[gitdir FETCH_HEAD]]
+       lappend cmd HEAD
+       lappend cmd $cmit
+
+       set msg "Merging $current_branch and $stitle"
        ui_status "$msg..."
-       set cons [console::new "Merge" $msg]
-       console::exec $cons $cmd \
-               [namespace code [list _finish $revcnt $cons]]
+       set cons [console::new "Merge" "merge $stitle"]
+       console::exec $cons $cmd [cb _finish $cons]
 
        wm protocol $w WM_DELETE_WINDOW {}
        destroy $w
 }
 
-proc _finish {revcnt w ok} {
-       console::done $w $ok
+method _finish {cons ok} {
+       console::done $cons $ok
        if {$ok} {
                set msg {Merge completed successfully.}
        } else {
-               if {$revcnt != 1} {
-                       info_popup "Octopus merge failed.
-
-Your merge of $revcnt branches has failed.
-
-There are file-level conflicts between the branches which must be resolved manually.
-
-The working directory will now be reset.
-
-You can attempt this merge again by merging only one branch at a time." $w
-
-                       set fd [git_read read-tree --reset -u HEAD]
-                       fconfigure $fd -blocking 0 -translation binary
-                       fileevent $fd readable \
-                               [namespace code [list _reset_wait $fd]]
-                       ui_status {Aborting... please wait...}
-                       return
-               }
-
                set msg {Merge failed.  Conflict resolution is required.}
        }
        unlock_index
        rescan [list ui_status $msg]
+       delete_this
 }
 
-proc dialog {} {
+constructor dialog {} {
        global current_branch
        global M1B
 
-       if {![_can_merge]} return
-
-       set fmt {list %(objectname) %(*objectname) %(refname) %(subject)}
-       set fr_fd [git_read for-each-ref \
-               --tcl \
-               --format=$fmt \
-               refs/heads \
-               refs/remotes \
-               refs/tags \
-               ]
-       fconfigure $fr_fd -translation binary
-       while {[gets $fr_fd line] > 0} {
-               set line [eval $line]
-               set ref [lindex $line 2]
-               regsub ^refs/(heads|remotes|tags)/ $ref {} ref
-               set subj($ref) [lindex $line 3]
-               lappend sha1([lindex $line 0]) $ref
-               if {[lindex $line 1] ne {}} {
-                       lappend sha1([lindex $line 1]) $ref
-               }
-       }
-       close $fr_fd
-
-       set to_show {}
-       set fr_fd [git_read rev-list --all --not HEAD]
-       while {[gets $fr_fd line] > 0} {
-               if {[catch {set ref $sha1($line)}]} continue
-               foreach n $ref {
-                       lappend to_show [list $n $line]
-               }
+       if {![_can_merge $this]} {
+               delete_this
+               return
        }
-       close $fr_fd
-       set to_show [lsort -unique $to_show]
 
-       set w .merge_setup
-       toplevel $w
-       wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
+       make_toplevel top w
+       wm title $top "[appname] ([reponame]): Merge"
+       if {$top ne {.}} {
+               wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+       }
 
-       set _visualize [namespace code [list _visualize $w $to_show]]
-       set _start [namespace code [list _start $w $to_show]]
+       set _start [cb _start]
 
        label $w.header \
                -text "Merge Into $current_branch" \
@@ -211,55 +160,51 @@ proc dialog {} {
        pack $w.header -side top -fill x
 
        frame $w.buttons
-       button $w.buttons.visualize -text Visualize -command $_visualize
+       button $w.buttons.visualize \
+               -text Visualize \
+               -command [cb _visualize]
        pack $w.buttons.visualize -side left
-       button $w.buttons.create -text Merge -command $_start
-       pack $w.buttons.create -side right
+       button $w.buttons.merge \
+               -text Merge \
+               -command $_start
+       pack $w.buttons.merge -side right
        button $w.buttons.cancel \
                -text {Cancel} \
-               -command "unlock_index;destroy $w"
+               -command [cb _cancel]
        pack $w.buttons.cancel -side right -padx 5
        pack $w.buttons -side bottom -fill x -pady 10 -padx 10
 
-       labelframe $w.source -text {Source Branches}
-       listbox $w.source.l \
-               -height 10 \
-               -width 70 \
-               -font font_diff \
-               -selectmode extended \
-               -yscrollcommand [list $w.source.sby set]
-       scrollbar $w.source.sby -command [list $w.source.l yview]
-       pack $w.source.sby -side right -fill y
-       pack $w.source.l -side left -fill both -expand 1
-       pack $w.source -fill both -expand 1 -pady 5 -padx 5
-
-       foreach ref $to_show {
-               set n [lindex $ref 0]
-               if {[string length $n] > 20} {
-                       set n "[string range $n 0 16]..."
-               }
-               $w.source.l insert end [format {%s %-20s %s} \
-                       [string range [lindex $ref 1] 0 5] \
-                       $n \
-                       $subj([lindex $ref 0])]
-       }
-
-       bind $w.source.l <Key-K> [list event generate %W <Shift-Key-Up>]
-       bind $w.source.l <Key-J> [list event generate %W <Shift-Key-Down>]
-       bind $w.source.l <Key-k> [list event generate %W <Key-Up>]
-       bind $w.source.l <Key-j> [list event generate %W <Key-Down>]
-       bind $w.source.l <Key-h> [list event generate %W <Key-Left>]
-       bind $w.source.l <Key-l> [list event generate %W <Key-Right>]
-       bind $w.source.l <Key-v> $_visualize
+       set w_rev [::choose_rev::new_unmerged $w.rev {Revision To Merge}]
+       pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5
 
        bind $w <$M1B-Key-Return> $_start
-       bind $w <Visibility> "grab $w; focus $w.source.l"
-       bind $w <Key-Escape> "unlock_index;destroy $w"
-       wm protocol $w WM_DELETE_WINDOW "unlock_index;destroy $w"
-       wm title $w "[appname] ([reponame]): Merge"
+       bind $w <Key-Return> $_start
+       bind $w <Key-Escape> [cb _cancel]
+       wm protocol $w WM_DELETE_WINDOW [cb _cancel]
+
+       bind $w.buttons.merge <Visibility> [cb _visible]
        tkwait window $w
 }
 
+method _visible {} {
+       grab $w
+       if {[is_config_true gui.matchtrackingbranch]} {
+               $w_rev pick_tracking_branch
+       }
+       $w_rev focus_filter
+}
+
+method _cancel {} {
+       wm protocol $w WM_DELETE_WINDOW {}
+       unlock_index
+       destroy $w
+       delete_this
+}
+
+}
+
+namespace eval merge {
+
 proc reset_hard {} {
        global HEAD commit_type file_states
 
@@ -274,20 +219,24 @@ You must finish amending this commit.
        if {![lock_index abort]} return
 
        if {[string match *merge* $commit_type]} {
-               set op merge
+               set op_question "Abort merge?
+
+Aborting the current merge will cause *ALL* uncommitted changes to be lost.
+
+Continue with aborting the current merge?"
        } else {
-               set op commit
-       }
+               set op_question "Reset changes?
 
-       if {[ask_popup "Abort $op?
+Resetting the changes will cause *ALL* uncommitted changes to be lost.
 
-Aborting the current $op will cause *ALL* uncommitted changes to be lost.
+Continue with resetting the current changes?"
+       }
 
-Continue with aborting the current $op?"] eq {yes}} {
-               set fd [git_read read-tree --reset -u HEAD]
+       if {[ask_popup $op_question] eq {yes}} {
+               set fd [git_read --stderr read-tree --reset -u -v HEAD]
                fconfigure $fd -blocking 0 -translation binary
                fileevent $fd readable [namespace code [list _reset_wait $fd]]
-               ui_status {Aborting... please wait...}
+               $::main_status start {Aborting} {files reset}
        } else {
                unlock_index
        }
@@ -296,9 +245,12 @@ Continue with aborting the current $op?"] eq {yes}} {
 proc _reset_wait {fd} {
        global ui_comm
 
-       read $fd
+       $::main_status update_meter [read $fd]
+
+       fconfigure $fd -blocking 1
        if {[eof $fd]} {
-               close $fd
+               set fail [catch {close $fd} err]
+               $::main_status stop
                unlock_index
 
                $ui_comm delete 0.0 end
@@ -310,7 +262,12 @@ proc _reset_wait {fd} {
                catch {file delete [gitdir MERGE_MSG]}
                catch {file delete [gitdir GITGUI_MSG]}
 
+               if {$fail} {
+                       warn_popup "Abort failed.\n\n$err"
+               }
                rescan {ui_status {Abort completed.  Ready.}}
+       } else {
+               fconfigure $fd -blocking 0
        }
 }
 
index e235ca88765090e08707f63096369d56da76d196..cf9b9d582959e62c805a92a86c33e0f3ae7f304e 100644 (file)
@@ -57,6 +57,7 @@ proc all_tracking_branches {} {
 proc load_all_remotes {} {
        global repo_config
        global all_remotes tracking_branches some_heads_tracking
+       global remote_url
 
        set some_heads_tracking 0
        set all_remotes [list]
@@ -76,6 +77,10 @@ proc load_all_remotes {} {
                        catch {
                                set fd [open [file join $rm_dir $name] r]
                                while {[gets $fd line] >= 0} {
+                                       if {[regexp {^URL:[     ]*(.+)$} $line line url]} {
+                                               set remote_url($name) $url
+                                               continue
+                                       }
                                        if {![regexp {^Pull:[   ]*([^:]+):(.+)$} \
                                                $line line src dst]} continue
                                        if {[string index $src 0] eq {+}} {
@@ -100,6 +105,7 @@ proc load_all_remotes {} {
        foreach line [array names repo_config remote.*.url] {
                if {![regexp ^remote\.(.*)\.url\$ $line line name]} continue
                lappend all_remotes $name
+               set remote_url($name) $repo_config(remote.$name.url)
 
                if {[catch {set fl $repo_config(remote.$name.fetch)}]} {
                        set fl {}
index 9a88335c5f7c99f05a76cccbf1badad6903596f9..061cd0a69e22a51a73488b89ffb99a934fd58ce1 100755 (executable)
@@ -17,7 +17,7 @@ USAGE='(--continue | --abort | --skip | [--preserve-merges] [--verbose]
 require_work_tree
 
 DOTEST="$GIT_DIR/.dotest-merge"
-TODO="$DOTEST"/todo
+TODO="$DOTEST"/git-rebase-todo
 DONE="$DOTEST"/done
 MSG="$DOTEST"/message
 SQUASH_MSG="$DOTEST"/message-squash
@@ -260,8 +260,8 @@ do_next () {
                esac
 
                failed=f
-               pick_one -n $sha1 || failed=t
                output git reset --soft HEAD^
+               pick_one -n $sha1 || failed=t
                author_script=$(get_author_ident_from_commit $sha1)
                echo "$author_script" > "$DOTEST"/author-script
                case $failed in
index f90dffd4caf88d649a9998a43aee18e57d3f594f..30425ce6df8b8da9b105fa7d8e958936420983ed 100755 (executable)
@@ -36,6 +36,9 @@ save_stash () {
        test -f "$GIT_DIR/logs/$ref_stash" ||
                clear_stash || die "Cannot initialize stash"
 
+       # Make sure the reflog for stash is kept.
+       : >>"$GIT_DIR/logs/$ref_stash"
+
        # state of the base commit
        if b_commit=$(git rev-parse --verify HEAD)
        then
@@ -125,19 +128,24 @@ apply_stash () {
        c_tree=$(git write-tree) ||
                die 'Cannot apply a stash in the middle of a merge'
 
+       # stash records the work tree, and is a merge between the
+       # base commit (first parent) and the index tree (second parent).
        s=$(git rev-parse --revs-only --no-flags --default $ref_stash "$@") &&
        w_tree=$(git rev-parse --verify "$s:") &&
-       b_tree=$(git rev-parse --verify "$s^:") ||
+       b_tree=$(git rev-parse --verify "$s^1:") &&
+       i_tree=$(git rev-parse --verify "$s^2:") ||
                die "$*: no valid stashed state found"
 
-       test -z "$unstash_index" || {
+       unstashed_index_tree=
+       if test -n "$unstash_index" && test "$b_tree" != "$i_tree"
+       then
                git diff --binary $s^2^..$s^2 | git apply --cached
                test $? -ne 0 &&
                        die 'Conflicts in index. Try without --index.'
                unstashed_index_tree=$(git-write-tree) ||
                        die 'Could not save index tree'
                git reset
-       }
+       fi
 
        eval "
                GITHEAD_$w_tree='Stashed changes' &&
@@ -149,18 +157,25 @@ apply_stash () {
        if git-merge-recursive $b_tree -- $c_tree $w_tree
        then
                # No conflict
-               a="$TMP-added" &&
-               git diff --cached --name-only --diff-filter=A $c_tree >"$a" &&
-               git read-tree --reset $c_tree &&
-               git update-index --add --stdin <"$a" ||
-                       die "Cannot unstage modified files"
-               git-status
-               rm -f "$a"
-               test -z "$unstash_index" || git read-tree $unstashed_index_tree
+               if test -n "$unstashed_index_tree"
+               then
+                       git read-tree "$unstashed_index_tree"
+               else
+                       a="$TMP-added" &&
+                       git diff --cached --name-only --diff-filter=A $c_tree >"$a" &&
+                       git read-tree --reset $c_tree &&
+                       git update-index --add --stdin <"$a" ||
+                               die "Cannot unstage modified files"
+                       rm -f "$a"
+               fi
+               git status || :
        else
                # Merge conflict; keep the exit status from merge-recursive
                status=$?
-               test -z "$unstash_index" || echo 'Index was not unstashed.' >&2
+               if test -n "$unstash_index"
+               then
+                       echo >&2 'Index was not unstashed.'
+               fi
                exit $status
        fi
 }
diff --git a/gitk b/gitk
index 39e452aba96ec0c15fcdefcfb10bff05d6631eb9..f74ce513795bb90fe3a96dd7da0dc98a2e6e1aa6 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -101,7 +101,7 @@ proc start_rev_list {view} {
     set commfd($view) $fd
     set leftover($view) {}
     set lookingforhead $showlocalchanges
-    fconfigure $fd -blocking 0 -translation lf
+    fconfigure $fd -blocking 0 -translation lf -eofchar {}
     if {$tclencoding != {}} {
        fconfigure $fd -encoding $tclencoding
     }
@@ -139,6 +139,10 @@ proc getcommitlines {fd view}  {
     global vparentlist vdisporder vcmitlisted
 
     set stuff [read $fd 500000]
+    # git log doesn't terminate the last commit with a null...
+    if {$stuff == {} && $leftover($view) ne {} && [eof $fd]} {
+       set stuff "\0"
+    }
     if {$stuff == {}} {
        if {![eof $fd]} {
            return 1
@@ -262,11 +266,11 @@ proc chewcommits {view} {
        set tlimit [expr {[clock clicks -milliseconds] + 50}]
        set more [layoutmore $tlimit $allread]
        if {$allread && !$more} {
-           global displayorder nullid commitidx phase
+           global displayorder commitidx phase
            global numcommits startmsecs
 
            if {[info exists pending_select]} {
-               set row [expr {[lindex $displayorder 0] eq $nullid}]
+               set row [first_real_row]
                selectline $row 1
            }
            if {$commitidx($curview) > 0} {
@@ -437,6 +441,19 @@ proc readrefs {} {
     }
 }
 
+# skip over fake commits
+proc first_real_row {} {
+    global nullid nullid2 displayorder numcommits
+
+    for {set row 0} {$row < $numcommits} {incr row} {
+       set id [lindex $displayorder $row]
+       if {$id ne $nullid && $id ne $nullid2} {
+           break
+       }
+    }
+    return $row
+}
+
 # update things for a head moved to a child of its previous location
 proc movehead {id name} {
     global headids idheads
@@ -796,6 +813,12 @@ proc makewindow {} {
         wm geometry . "$geometry(main)"
     }
 
+    if {[tk windowingsystem] eq {aqua}} {
+        set M1B M1
+    } else {
+        set M1B Control
+    }
+
     bind .pwbottom <Configure> {resizecdetpanes %W %w}
     pack .ctop -fill both -expand 1
     bindall <1> {selcanvline %W %x %y}
@@ -814,12 +837,12 @@ proc makewindow {} {
     bindkey <Key-Left> "goback"
     bind . <Key-Prior> "selnextpage -1"
     bind . <Key-Next> "selnextpage 1"
-    bind . <Control-Home> "allcanvs yview moveto 0.0"
-    bind . <Control-End> "allcanvs yview moveto 1.0"
-    bind . <Control-Key-Up> "allcanvs yview scroll -1 units"
-    bind . <Control-Key-Down> "allcanvs yview scroll 1 units"
-    bind . <Control-Key-Prior> "allcanvs yview scroll -1 pages"
-    bind . <Control-Key-Next> "allcanvs yview scroll 1 pages"
+    bind . <$M1B-Home> "allcanvs yview moveto 0.0"
+    bind . <$M1B-End> "allcanvs yview moveto 1.0"
+    bind . <$M1B-Key-Up> "allcanvs yview scroll -1 units"
+    bind . <$M1B-Key-Down> "allcanvs yview scroll 1 units"
+    bind . <$M1B-Key-Prior> "allcanvs yview scroll -1 pages"
+    bind . <$M1B-Key-Next> "allcanvs yview scroll 1 pages"
     bindkey <Key-Delete> "$ctext yview scroll -1 pages"
     bindkey <Key-BackSpace> "$ctext yview scroll -1 pages"
     bindkey <Key-space> "$ctext yview scroll 1 pages"
@@ -839,15 +862,15 @@ proc makewindow {} {
     bindkey ? findprev
     bindkey f nextfile
     bindkey <F5> updatecommits
-    bind . <Control-q> doquit
-    bind . <Control-f> dofind
-    bind . <Control-g> {findnext 0}
-    bind . <Control-r> dosearchback
-    bind . <Control-s> dosearch
-    bind . <Control-equal> {incrfont 1}
-    bind . <Control-KP_Add> {incrfont 1}
-    bind . <Control-minus> {incrfont -1}
-    bind . <Control-KP_Subtract> {incrfont -1}
+    bind . <$M1B-q> doquit
+    bind . <$M1B-f> dofind
+    bind . <$M1B-g> {findnext 0}
+    bind . <$M1B-r> dosearchback
+    bind . <$M1B-s> dosearch
+    bind . <$M1B-equal> {incrfont 1}
+    bind . <$M1B-KP_Add> {incrfont 1}
+    bind . <$M1B-minus> {incrfont -1}
+    bind . <$M1B-KP_Subtract> {incrfont -1}
     wm protocol . WM_DELETE_WINDOW doquit
     bind . <Button-1> "click %W"
     bind $fstring <Key-Return> dofind
@@ -1088,12 +1111,17 @@ proc keys {} {
        raise $w
        return
     }
+    if {[tk windowingsystem] eq {aqua}} {
+       set M1T Cmd
+    } else {
+       set M1T Ctrl
+    }
     toplevel $w
     wm title $w "Gitk key bindings"
-    message $w.m -text {
+    message $w.m -text "
 Gitk key bindings:
 
-<Ctrl-Q>               Quit
+<$M1T-Q>               Quit
 <Home>         Move to first commit
 <End>          Move to last commit
 <Up>, p, i     Move up one commit
@@ -1102,12 +1130,12 @@ Gitk key bindings:
 <Right>, x, l  Go forward in history list
 <PageUp>       Move up one page in commit list
 <PageDown>     Move down one page in commit list
-<Ctrl-Home>    Scroll to top of commit list
-<Ctrl-End>     Scroll to bottom of commit list
-<Ctrl-Up>      Scroll commit list up one line
-<Ctrl-Down>    Scroll commit list down one line
-<Ctrl-PageUp>  Scroll commit list up one page
-<Ctrl-PageDown>        Scroll commit list down one page
+<$M1T-Home>    Scroll to top of commit list
+<$M1T-End>     Scroll to bottom of commit list
+<$M1T-Up>      Scroll commit list up one line
+<$M1T-Down>    Scroll commit list down one line
+<$M1T-PageUp>  Scroll commit list up one page
+<$M1T-PageDown>        Scroll commit list down one page
 <Shift-Up>     Move to previous highlighted line
 <Shift-Down>   Move to next highlighted line
 <Delete>, b    Scroll diff view up one page
@@ -1115,20 +1143,20 @@ Gitk key bindings:
 <Space>                Scroll diff view down one page
 u              Scroll diff view up 18 lines
 d              Scroll diff view down 18 lines
-<Ctrl-F>               Find
-<Ctrl-G>               Move to next find hit
+<$M1T-F>               Find
+<$M1T-G>               Move to next find hit
 <Return>       Move to next find hit
 /              Move to next find hit, or redo find
 ?              Move to previous find hit
 f              Scroll diff view to next file
-<Ctrl-S>               Search for next hit in diff view
-<Ctrl-R>               Search for previous hit in diff view
-<Ctrl-KP+>     Increase font size
-<Ctrl-plus>    Increase font size
-<Ctrl-KP->     Decrease font size
-<Ctrl-minus>   Decrease font size
+<$M1T-S>               Search for next hit in diff view
+<$M1T-R>               Search for previous hit in diff view
+<$M1T-KP+>     Increase font size
+<$M1T-plus>    Increase font size
+<$M1T-KP->     Decrease font size
+<$M1T-minus>   Decrease font size
 <F5>           Update
-} \
+" \
            -justify left -bg white -border 2 -relief groove
     pack $w.m -side top -fill both -padx 2 -pady 2
     $w.m configure -font $uifont
@@ -1871,7 +1899,7 @@ proc showview {n} {
     } elseif {$selid ne {}} {
        set pending_select $selid
     } else {
-       set row [expr {[lindex $displayorder 0] eq $nullid}]
+       set row [first_real_row]
        if {$row < $numcommits} {
            selectline $row 0
        } else {
@@ -2133,7 +2161,7 @@ proc readfhighlight {} {
 
 proc find_change {name ix op} {
     global nhighlights mainfont boldnamerows
-    global findstring findpattern findtype markingmatches
+    global findstring findpattern findtype
 
     # delete previous highlights, if any
     foreach row $boldnamerows {
@@ -2148,7 +2176,6 @@ proc find_change {name ix op} {
                   $findstring]
        set findpattern "*$e*"
     }
-    set markingmatches [expr {$findstring ne {}}]
     drawvisible
 }
 
@@ -2194,26 +2221,32 @@ proc askfindhighlight {row id} {
            }
        }
        if {$markingmatches} {
-           markrowmatches $row [lindex $info 0] [lindex $info 1]
+           markrowmatches $row $id
        }
     }
     set nhighlights($row) $isbold
 }
 
-proc markrowmatches {row headline author} {
-    global canv canv2 linehtag linentag
+proc markrowmatches {row id} {
+    global canv canv2 linehtag linentag commitinfo findloc
 
+    set headline [lindex $commitinfo($id) 0]
+    set author [lindex $commitinfo($id) 1]
     $canv delete match$row
     $canv2 delete match$row
-    set m [findmatches $headline]
-    if {$m ne {}} {
-       markmatches $canv $row $headline $linehtag($row) $m \
-           [$canv itemcget $linehtag($row) -font]
+    if {$findloc eq "All fields" || $findloc eq "Headline"} {
+       set m [findmatches $headline]
+       if {$m ne {}} {
+           markmatches $canv $row $headline $linehtag($row) $m \
+               [$canv itemcget $linehtag($row) -font] $row
+       }
     }
-    set m [findmatches $author]
-    if {$m ne {}} {
-       markmatches $canv2 $row $author $linentag($row) $m \
-           [$canv2 itemcget $linentag($row) -font]
+    if {$findloc eq "All fields" || $findloc eq "Author"} {
+       set m [findmatches $author]
+       if {$m ne {}} {
+           markmatches $canv2 $row $author $linentag($row) $m \
+               [$canv2 itemcget $linentag($row) -font] $row
+       }
     }
 }
 
@@ -2643,7 +2676,7 @@ proc layoutmore {tmax allread} {
 
 proc showstuff {canshow last} {
     global numcommits commitrow pending_select selectedline curview
-    global lookingforhead mainheadid displayorder nullid selectfirst
+    global lookingforhead mainheadid displayorder selectfirst
     global lastscrollset
 
     if {$numcommits == 0} {
@@ -2676,7 +2709,7 @@ proc showstuff {canshow last} {
        if {[info exists selectedline] || [info exists pending_select]} {
            set selectfirst 0
        } else {
-           set l [expr {[lindex $displayorder 0] eq $nullid}]
+           set l [first_real_row]
            selectline $l 1
            set selectfirst 0
        }
@@ -2700,48 +2733,93 @@ proc doshowlocalchanges {} {
 }
 
 proc dohidelocalchanges {} {
-    global lookingforhead localrow lserial
+    global lookingforhead localfrow localirow lserial
 
     set lookingforhead 0
-    if {$localrow >= 0} {
-       removerow $localrow
-       set localrow -1
+    if {$localfrow >= 0} {
+       removerow $localfrow
+       set localfrow -1
+       if {$localirow > 0} {
+           incr localirow -1
+       }
+    }
+    if {$localirow >= 0} {
+       removerow $localirow
+       set localirow -1
     }
     incr lserial
 }
 
-# spawn off a process to do git diff-index HEAD
+# spawn off a process to do git diff-index --cached HEAD
 proc dodiffindex {} {
-    global localrow lserial
+    global localirow localfrow lserial
 
     incr lserial
-    set localrow -1
-    set fd [open "|git diff-index HEAD" r]
+    set localfrow -1
+    set localirow -1
+    set fd [open "|git diff-index --cached HEAD" r]
     fconfigure $fd -blocking 0
     filerun $fd [list readdiffindex $fd $lserial]
 }
 
 proc readdiffindex {fd serial} {
-    global localrow commitrow mainheadid nullid curview
+    global localirow commitrow mainheadid nullid2 curview
     global commitinfo commitdata lserial
 
+    set isdiff 1
     if {[gets $fd line] < 0} {
-       if {[eof $fd]} {
-           close $fd
-           return 0
+       if {![eof $fd]} {
+           return 1
        }
-       return 1
+       set isdiff 0
     }
     # we only need to see one line and we don't really care what it says...
     close $fd
 
-    if {$serial == $lserial && $localrow == -1} {
+    # now see if there are any local changes not checked in to the index
+    if {$serial == $lserial} {
+       set fd [open "|git diff-files" r]
+       fconfigure $fd -blocking 0
+       filerun $fd [list readdifffiles $fd $serial]
+    }
+
+    if {$isdiff && $serial == $lserial && $localirow == -1} {
+       # add the line for the changes in the index to the graph
+       set localirow $commitrow($curview,$mainheadid)
+       set hl "Local changes checked in to index but not committed"
+       set commitinfo($nullid2) [list  $hl {} {} {} {} "    $hl\n"]
+       set commitdata($nullid2) "\n    $hl\n"
+       insertrow $localirow $nullid2
+    }
+    return 0
+}
+
+proc readdifffiles {fd serial} {
+    global localirow localfrow commitrow mainheadid nullid curview
+    global commitinfo commitdata lserial
+
+    set isdiff 1
+    if {[gets $fd line] < 0} {
+       if {![eof $fd]} {
+           return 1
+       }
+       set isdiff 0
+    }
+    # we only need to see one line and we don't really care what it says...
+    close $fd
+
+    if {$isdiff && $serial == $lserial && $localfrow == -1} {
        # add the line for the local diff to the graph
-       set localrow $commitrow($curview,$mainheadid)
-       set hl "Local uncommitted changes"
+       if {$localirow >= 0} {
+           set localfrow $localirow
+           incr localirow
+       } else {
+           set localfrow $commitrow($curview,$mainheadid)
+       }
+       set hl "Local uncommitted changes, not checked in to index"
        set commitinfo($nullid) [list  $hl {} {} {} {} "    $hl\n"]
        set commitdata($nullid) "\n    $hl\n"
-       insertrow $localrow $nullid
+       insertrow $localfrow $nullid
     }
     return 0
 }
@@ -3337,13 +3415,15 @@ proc drawcmittext {id row col} {
     global linespc canv canv2 canv3 canvy0 fgcolor curview
     global commitlisted commitinfo rowidlist parentlist
     global rowtextx idpos idtags idheads idotherrefs
-    global linehtag linentag linedtag markingmatches
-    global mainfont canvxmax boldrows boldnamerows fgcolor nullid
+    global linehtag linentag linedtag
+    global mainfont canvxmax boldrows boldnamerows fgcolor nullid nullid2
 
     # listed is 0 for boundary, 1 for normal, 2 for left, 3 for right
     set listed [lindex $commitlisted $row]
     if {$id eq $nullid} {
        set ofill red
+    } elseif {$id eq $nullid2} {
+       set ofill green
     } else {
        set ofill [expr {$listed != 0? "blue": "white"}]
     }
@@ -3412,9 +3492,6 @@ proc drawcmittext {id row col} {
     set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
                            -text $date -font $mainfont -tags text]
     set xr [expr {$xt + [font measure $mainfont $headline]}]
-    if {$markingmatches} {
-       markrowmatches $row $headline $name
-    }
     if {$xr > $canvxmax} {
        set canvxmax $xr
        setcanvscroll
@@ -3423,7 +3500,7 @@ proc drawcmittext {id row col} {
 
 proc drawcmitrow {row} {
     global displayorder rowidlist
-    global iddrawn
+    global iddrawn markingmatches
     global commitinfo parentlist numcommits
     global filehighlight fhighlights findstring nhighlights
     global hlview vhighlights
@@ -3444,18 +3521,22 @@ proc drawcmitrow {row} {
     if {$highlight_related ne "None" && ![info exists rhighlights($row)]} {
        askrelhighlight $row $id
     }
-    if {[info exists iddrawn($id)]} return
-    set col [lsearch -exact [lindex $rowidlist $row] $id]
-    if {$col < 0} {
-       puts "oops, row $row id $id not in list"
-       return
+    if {![info exists iddrawn($id)]} {
+       set col [lsearch -exact [lindex $rowidlist $row] $id]
+       if {$col < 0} {
+           puts "oops, row $row id $id not in list"
+           return
+       }
+       if {![info exists commitinfo($id)]} {
+           getcommit $id
+       }
+       assigncolor $id
+       drawcmittext $id $row $col
+       set iddrawn($id) 1
     }
-    if {![info exists commitinfo($id)]} {
-       getcommit $id
+    if {$markingmatches} {
+       markrowmatches $row $id
     }
-    assigncolor $id
-    drawcmittext $id $row $col
-    set iddrawn($id) 1
 }
 
 proc drawcommits {row {endrow {}}} {
@@ -3973,7 +4054,6 @@ proc dofind {{rev 0}} {
     if {!$rev} {
        run findmore
     } else {
-       set findcurline $findstartline
        if {$findcurline == 0} {
            set findcurline $numcommits
        }
@@ -4008,7 +4088,7 @@ proc findprev {} {
 
 proc findmore {} {
     global commitdata commitinfo numcommits findstring findpattern findloc
-    global findstartline findcurline markingmatches displayorder
+    global findstartline findcurline displayorder
 
     set fldtypes {Headline Author Date Committer CDate Comments}
     set l [expr {$findcurline + 1}]
@@ -4026,6 +4106,8 @@ proc findmore {} {
     set last 0
     for {} {$l < $lim} {incr l} {
        set id [lindex $displayorder $l]
+       # shouldn't happen unless git log doesn't give all the commits...
+       if {![info exists commitdata($id)]} continue
        if {![doesmatch $commitdata($id)]} continue
        if {![info exists commitinfo($id)]} {
            getcommit $id
@@ -4034,7 +4116,6 @@ proc findmore {} {
        foreach f $info ty $fldtypes {
            if {($findloc eq "All fields" || $findloc eq $ty) &&
                [doesmatch $f]} {
-               set markingmatches 1
                findselectline $l
                notbusy finding
                return 0
@@ -4053,7 +4134,7 @@ proc findmore {} {
 
 proc findmorerev {} {
     global commitdata commitinfo numcommits findstring findpattern findloc
-    global findstartline findcurline markingmatches displayorder
+    global findstartline findcurline displayorder
 
     set fldtypes {Headline Author Date Committer CDate Comments}
     set l $findcurline
@@ -4080,7 +4161,6 @@ proc findmorerev {} {
        foreach f $info ty $fldtypes {
            if {($findloc eq "All fields" || $findloc eq $ty) &&
                [doesmatch $f]} {
-               set markingmatches 1
                findselectline $l
                notbusy finding
                return 0
@@ -4098,7 +4178,10 @@ proc findmorerev {} {
 }
 
 proc findselectline {l} {
-    global findloc commentend ctext
+    global findloc commentend ctext findcurline markingmatches
+
+    set markingmatches 1
+    set findcurline $l
     selectline $l 1
     if {$findloc == "All fields" || $findloc == "Comments"} {
        # highlight the matches in the comments
@@ -4110,10 +4193,13 @@ proc findselectline {l} {
            $ctext tag add found "1.0 + $start c" "1.0 + $end c"
        }
     }
+    drawvisible
 }
 
 # mark the bits of a headline or author that match a find string
-proc markmatches {canv l str tag matches font} {
+proc markmatches {canv l str tag matches font row} {
+    global selectedline
+
     set bbox [$canv bbox $tag]
     set x0 [lindex $bbox 0]
     set y0 [lindex $bbox 1]
@@ -4128,6 +4214,9 @@ proc markmatches {canv l str tag matches font} {
                   [expr {$x0+$xlen+2}] $y1 \
                   -outline {} -tags [list match$l matches] -fill yellow]
        $canv lower $t
+       if {[info exists selectedline] && $row == $selectedline} {
+           $canv raise $t secsel
+       }
     }
 }
 
@@ -4582,16 +4671,19 @@ proc goforw {} {
 }
 
 proc gettree {id} {
-    global treefilelist treeidlist diffids diffmergeid treepending nullid
+    global treefilelist treeidlist diffids diffmergeid treepending
+    global nullid nullid2
 
     set diffids $id
     catch {unset diffmergeid}
     if {![info exists treefilelist($id)]} {
        if {![info exists treepending]} {
-           if {$id ne $nullid} {
-               set cmd [concat | git ls-tree -r $id]
+           if {$id eq $nullid} {
+               set cmd [list | git ls-files]
+           } elseif {$id eq $nullid2} {
+               set cmd [list | git ls-files --stage -t]
            } else {
-               set cmd [concat | git ls-files]
+               set cmd [list | git ls-tree -r $id]
            }
            if {[catch {set gtf [open $cmd r]}]} {
                return
@@ -4608,12 +4700,14 @@ proc gettree {id} {
 }
 
 proc gettreeline {gtf id} {
-    global treefilelist treeidlist treepending cmitmode diffids nullid
+    global treefilelist treeidlist treepending cmitmode diffids nullid nullid2
 
     set nl 0
     while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
-       if {$diffids ne $nullid} {
-           if {[lindex $line 1] ne "blob"} continue
+       if {$diffids eq $nullid} {
+           set fname $line
+       } else {
+           if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
            set i [string first "\t" $line]
            if {$i < 0} continue
            set sha1 [lindex $line 2]
@@ -4622,8 +4716,6 @@ proc gettreeline {gtf id} {
                set fname [lindex $fname 0]
            }
            lappend treeidlist($id) $sha1
-       } else {
-           set fname $line
        }
        lappend treefilelist($id) $fname
     }
@@ -4645,7 +4737,7 @@ proc gettreeline {gtf id} {
 }
 
 proc showfile {f} {
-    global treefilelist treeidlist diffids nullid
+    global treefilelist treeidlist diffids nullid nullid2
     global ctext commentend
 
     set i [lsearch -exact $treefilelist($diffids) $f]
@@ -4653,15 +4745,15 @@ proc showfile {f} {
        puts "oops, $f not in list for id $diffids"
        return
     }
-    if {$diffids ne $nullid} {
-       set blob [lindex $treeidlist($diffids) $i]
-       if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
-           puts "oops, error reading blob $blob: $err"
+    if {$diffids eq $nullid} {
+       if {[catch {set bf [open $f r]} err]} {
+           puts "oops, can't read $f: $err"
            return
        }
     } else {
-       if {[catch {set bf [open $f r]} err]} {
-           puts "oops, can't read $f: $err"
+       set blob [lindex $treeidlist($diffids) $i]
+       if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
+           puts "oops, error reading blob $blob: $err"
            return
        }
     }
@@ -4789,11 +4881,13 @@ proc getmergediffline {mdf id np} {
 }
 
 proc startdiff {ids} {
-    global treediffs diffids treepending diffmergeid nullid
+    global treediffs diffids treepending diffmergeid nullid nullid2
 
     set diffids $ids
     catch {unset diffmergeid}
-    if {![info exists treediffs($ids)] || [lsearch -exact $ids $nullid] >= 0} {
+    if {![info exists treediffs($ids)] ||
+       [lsearch -exact $ids $nullid] >= 0 ||
+       [lsearch -exact $ids $nullid2] >= 0} {
        if {![info exists treepending]} {
            gettreediffs $ids
        }
@@ -4809,22 +4903,41 @@ proc addtocflist {ids} {
 }
 
 proc diffcmd {ids flags} {
-    global nullid
+    global nullid nullid2
 
     set i [lsearch -exact $ids $nullid]
+    set j [lsearch -exact $ids $nullid2]
     if {$i >= 0} {
-       set cmd [concat | git diff-index $flags]
+       if {[llength $ids] > 1 && $j < 0} {
+           # comparing working directory with some specific revision
+           set cmd [concat | git diff-index $flags]
+           if {$i == 0} {
+               lappend cmd -R [lindex $ids 1]
+           } else {
+               lappend cmd [lindex $ids 0]
+           }
+       } else {
+           # comparing working directory with index
+           set cmd [concat | git diff-files $flags]
+           if {$j == 1} {
+               lappend cmd -R
+           }
+       }
+    } elseif {$j >= 0} {
+       set cmd [concat | git diff-index --cached $flags]
        if {[llength $ids] > 1} {
+           # comparing index with specific revision
            if {$i == 0} {
                lappend cmd -R [lindex $ids 1]
            } else {
                lappend cmd [lindex $ids 0]
            }
        } else {
+           # comparing index with HEAD
            lappend cmd HEAD
        }
     } else {
-       set cmd [concat | git diff-tree --no-commit-id -r $flags $ids]
+       set cmd [concat | git diff-tree -r $flags $ids]
     }
     return $cmd
 }
@@ -4834,7 +4947,7 @@ proc gettreediffs {ids} {
 
     set treepending $ids
     set treediff {}
-    if {[catch {set gdtf [open [diffcmd $ids {}] r]}]} return
+    if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
     fconfigure $gdtf -blocking 0
     filerun $gdtf [list gettreediffline $gdtf $ids]
 }
@@ -4877,7 +4990,7 @@ proc getblobdiffs {ids} {
     global diffinhdr treediffs
 
     set env(GIT_DIFF_OPTS) $diffopts
-    if {[catch {set bdf [open [diffcmd $ids {-p -C}] r]} err]} {
+    if {[catch {set bdf [open [diffcmd $ids {-p -C --no-commit-id}] r]} err]} {
        puts "error getting diffs: $err"
        return
     }
@@ -5468,7 +5581,7 @@ proc mstime {} {
 
 proc rowmenu {x y id} {
     global rowctxmenu commitrow selectedline rowmenuid curview
-    global nullid fakerowmenu mainhead
+    global nullid nullid2 fakerowmenu mainhead
 
     set rowmenuid $id
     if {![info exists selectedline]
@@ -5477,7 +5590,7 @@ proc rowmenu {x y id} {
     } else {
        set state normal
     }
-    if {$id ne $nullid} {
+    if {$id ne $nullid && $id ne $nullid2} {
        set menu $rowctxmenu
        $menu entryconfigure 7 -label "Reset $mainhead branch to here"
     } else {
@@ -5596,18 +5709,12 @@ proc mkpatchrev {} {
 }
 
 proc mkpatchgo {} {
-    global patchtop nullid
+    global patchtop nullid nullid2
 
     set oldid [$patchtop.fromsha1 get]
     set newid [$patchtop.tosha1 get]
     set fname [$patchtop.fname get]
-    if {$newid eq $nullid} {
-       set cmd [list git diff-index -p $oldid]
-    } elseif {$oldid eq $nullid} {
-       set cmd [list git diff-index -p -R $newid]
-    } else {
-       set cmd [list git diff-tree -p $oldid $newid]
-    }
+    set cmd [diffcmd [list $oldid $newid] -p]
     lappend cmd >$fname &
     if {[catch {eval exec $cmd} err]} {
        error_popup "Error creating patch: $err"
@@ -7522,6 +7629,8 @@ if {$i >= [llength $argv] && $revtreeargs ne {}} {
 }
 
 set nullid "0000000000000000000000000000000000000000"
+set nullid2 "0000000000000000000000000000000000000001"
+
 
 set runq {}
 set history {}
@@ -7550,10 +7659,13 @@ set stopped 0
 set stuffsaved 0
 set patchnum 0
 set lookingforhead 0
-set localrow -1
+set localirow -1
+set localfrow -1
 set lserial 0
 setcoords
 makewindow
+# wait for the window to become visible
+tkwait visibility .
 wm title . "[file tail $argv0]: [file tail [pwd]]"
 readrefs
 
index b3816921192ed6983337e60e95a9db77337d9fde..498b936dd48bef9a788b56c2ddbbacb63d1e29b6 100755 (executable)
@@ -436,12 +436,11 @@ sub filter_snapshot_fmts {
 
 our @extra_options = $cgi->param('opt');
 if (defined @extra_options) {
-       foreach(@extra_options)
-       {
-               if (not grep(/^$_$/, keys %allowed_options)) {
+       foreach my $opt (@extra_options) {
+               if (not exists $allowed_options{$opt}) {
                        die_error(undef, "Invalid option parameter");
                }
-               if (not grep(/^$action$/, @{$allowed_options{$_}})) {
+               if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
                        die_error(undef, "Invalid option parameter for this action");
                }
        }
@@ -598,7 +597,6 @@ (%)
                action => "a",
                file_name => "f",
                file_parent => "fp",
-               extra_options => "opt",
                hash => "h",
                hash_parent => "hp",
                hash_base => "hb",
@@ -608,6 +606,7 @@ (%)
                searchtext => "s",
                searchtype => "st",
                snapshot_format => "sf",
+               extra_options => "opt",
        );
        my %mapping = @mapping;
 
@@ -630,7 +629,13 @@ (%)
        for (my $i = 0; $i < @mapping; $i += 2) {
                my ($name, $symbol) = ($mapping[$i], $mapping[$i+1]);
                if (defined $params{$name}) {
-                       push @result, $symbol . "=" . esc_param($params{$name});
+                       if (ref($params{$name}) eq "ARRAY") {
+                               foreach my $par (@{$params{$name}}) {
+                                       push @result, $symbol . "=" . esc_param($par);
+                               }
+                       } else {
+                               push @result, $symbol . "=" . esc_param($params{$name});
+                       }
                }
        }
        $href .= "?" . join(';', @result) if scalar @result;
@@ -890,11 +895,25 @@ sub age_string {
        return $age_str;
 }
 
+use constant {
+       S_IFINVALID => 0030000,
+       S_IFGITLINK => 0160000,
+};
+
+# submodule/subproject, a commit object reference
+sub S_ISGITLINK($) {
+       my $mode = shift;
+
+       return (($mode & S_IFMT) == S_IFGITLINK)
+}
+
 # convert file mode in octal to symbolic file mode string
 sub mode_str {
        my $mode = oct shift;
 
-       if (S_ISDIR($mode & S_IFMT)) {
+       if (S_ISGITLINK($mode)) {
+               return 'm---------';
+       } elsif (S_ISDIR($mode & S_IFMT)) {
                return 'drwxr-xr-x';
        } elsif (S_ISLNK($mode)) {
                return 'lrwxrwxrwx';
@@ -920,7 +939,9 @@ sub file_type {
                $mode = oct $mode;
        }
 
-       if (S_ISDIR($mode & S_IFMT)) {
+       if (S_ISGITLINK($mode)) {
+               return "submodule";
+       } elsif (S_ISDIR($mode & S_IFMT)) {
                return "directory";
        } elsif (S_ISLNK($mode)) {
                return "symlink";
@@ -941,7 +962,9 @@ sub file_type_long {
                $mode = oct $mode;
        }
 
-       if (S_ISDIR($mode & S_IFMT)) {
+       if (S_ISGITLINK($mode)) {
+               return "submodule";
+       } elsif (S_ISDIR($mode & S_IFMT)) {
                return "directory";
        } elsif (S_ISLNK($mode)) {
                return "symlink";
@@ -2267,9 +2290,17 @@ sub git_header_html {
                printf('<link rel="alternate" title="%s log RSS feed" '.
                       'href="%s" type="application/rss+xml" />'."\n",
                       esc_param($project), href(action=>"rss"));
+               printf('<link rel="alternate" title="%s log RSS feed (no merges)" '.
+                      'href="%s" type="application/rss+xml" />'."\n",
+                      esc_param($project), href(action=>"rss",
+                                                extra_options=>"--no-merges"));
                printf('<link rel="alternate" title="%s log Atom feed" '.
                       'href="%s" type="application/atom+xml" />'."\n",
                       esc_param($project), href(action=>"atom"));
+               printf('<link rel="alternate" title="%s log Atom feed (no merges)" '.
+                      'href="%s" type="application/atom+xml" />'."\n",
+                      esc_param($project), href(action=>"atom",
+                                                extra_options=>"--no-merges"));
        } else {
                printf('<link rel="alternate" title="%s projects list" '.
                       'href="%s" type="text/plain; charset=utf-8"/>'."\n",
@@ -2707,6 +2738,20 @@ sub git_print_tree_entry {
                                      "history");
                }
                print "</td>\n";
+       } else {
+               # unknown object: we can only present history for it
+               # (this includes 'commit' object, i.e. submodule support)
+               print "<td class=\"list\">" .
+                     esc_path($t->{'name'}) .
+                     "</td>\n";
+               print "<td class=\"link\">";
+               if (defined $hash_base) {
+                       print $cgi->a({-href => href(action=>"history",
+                                                    hash_base=>$hash_base,
+                                                    file_name=>"$basedir$t->{'name'}")},
+                                     "history");
+               }
+               print "</td>\n";
        }
 }
 
index 8206436cc732f809556f957b04f8f9292ac82b3b..817f614cde29f12e01724901e1f0b966a28d04b3 100755 (executable)
@@ -221,4 +221,34 @@ test_expect_success 'multi-squash only fires up editor once' '
        test 1 = $(git show | grep ONCE | wc -l)
 '
 
+test_expect_success 'squash works as expected' '
+       for n in one two three four
+       do
+               echo $n >> file$n &&
+               git add file$n &&
+               git commit -m $n
+       done &&
+       one=$(git rev-parse HEAD~3) &&
+       FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
+       test $one = $(git rev-parse HEAD~2)
+'
+
+test_expect_success 'interrupted squash works as expected' '
+       for n in one two three four
+       do
+               echo $n >> conflict &&
+               git add conflict &&
+               git commit -m $n
+       done &&
+       one=$(git rev-parse HEAD~3) &&
+       ! FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
+       (echo one; echo two; echo four) > conflict &&
+       git add conflict &&
+       ! git rebase --continue &&
+       echo resolved > conflict &&
+       git add conflict &&
+       git rebase --continue &&
+       test $one = $(git rev-parse HEAD~2)
+'
+
 test_done
index 4b920be331faa0b1ad211517da218170cabd92b2..6f95305bf461c2659392d3ee6a77a5360c833c37 100755 (executable)
@@ -577,7 +577,7 @@ EXPECT_END
 test_expect_success \
     'L: verify internal tree sorting' \
        'git-fast-import <input &&
-        git diff --raw L^ L >output &&
+        git diff-tree --abbrev --raw L^ L >output &&
         git diff expect output'
 
 ###
index d948724566b2185d4934beb1bc82157e893611fc..fa32598b0c32429b54e5f72013dd272e9325be57 100755 (executable)
@@ -521,4 +521,32 @@ test_expect_success \
        'gitweb_run "p=.git;a=log"'
 test_debug 'cat gitweb.log'
 
+# ----------------------------------------------------------------------
+# extra options
+
+test_expect_success \
+       'opt: log --no-merges' \
+       'gitweb_run "p=.git;a=log;opt=--no-merges"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'opt: atom --no-merges' \
+       'gitweb_run "p=.git;a=log;opt=--no-merges"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'opt: "file" history --no-merges' \
+       'gitweb_run "p=.git;a=history;f=file;opt=--no-merges"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'opt: log --no-such-option (invalid option)' \
+       'gitweb_run "p=.git;a=log;opt=--no-such-option"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'opt: tree --no-merges (invalid option for action)' \
+       'gitweb_run "p=.git;a=tree;opt=--no-merges"'
+test_debug 'cat gitweb.log'
+
 test_done