From fbc0e7ac1479b4262f6090098a4f68c3438aa94b Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 Jul 2008 00:12:28 +0400 Subject: [PATCH 01/36] Fix pre-commit hooks under MinGW/MSYS Apply the work-around for checking the executable permission of hook files not only on Cygwin, but on Windows in general. Signed-off-by: Alexander Gavrilov Signed-off-by: Shawn O. Pearce --- git-gui.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 940677cbd8..e3b6669880 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -473,10 +473,10 @@ proc githook_read {hook_name args} { set pchook [gitdir hooks $hook_name] lappend args 2>@1 - # On Cygwin [file executable] might lie so we need to ask + # On Windows [file executable] might lie so we need to ask # the shell if the hook is executable. Yes that's annoying. # - if {[is_Cygwin]} { + if {[is_Windows]} { upvar #0 _sh interp if {![info exists interp]} { set interp [_which sh] From 57cae87b77c93e8bdfd11293f11f140ff827269a Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 Jul 2008 00:43:48 +0400 Subject: [PATCH 02/36] Add options to control the search for copies in blame. On huge repositories, -C -C can be way too slow to be unconditionally enabled, and it can also be useful to control its precision. Signed-off-by: Alexander Gavrilov Signed-off-by: Shawn O. Pearce --- git-gui.sh | 2 ++ lib/blame.tcl | 20 ++++++++++++-------- lib/option.tcl | 2 ++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index e3b6669880..b1ed0ec568 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -642,6 +642,8 @@ set default_config(user.email) {} set default_config(gui.matchtrackingbranch) false set default_config(gui.pruneduringfetch) false set default_config(gui.trustmtime) false +set default_config(gui.fastcopyblame) false +set default_config(gui.copyblamethreshold) 40 set default_config(gui.diffcontext) 5 set default_config(gui.commitmsgwidth) 75 set default_config(gui.newbranchtemplate) {} diff --git a/lib/blame.tcl b/lib/blame.tcl index 92fac1bad4..192505d983 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -33,13 +33,6 @@ variable group_colors { #ececec } -# Switches for original location detection -# -variable original_options [list -C -C] -if {[git-version >= 1.5.3]} { - lappend original_options -w ; # ignore indentation changes -} - # Current blame data; cleared/reset on each load # field commit ; # input commit to blame @@ -511,7 +504,6 @@ method _exec_blame {cur_w cur_d options cur_s} { method _read_blame {fd cur_w cur_d} { upvar #0 $cur_d line_data variable group_colors - variable original_options if {$fd ne $current_fd} { catch {close $fd} @@ -684,6 +676,18 @@ method _read_blame {fd cur_w cur_d} { if {[eof $fd]} { close $fd if {$cur_w eq $w_asim} { + # Switches for original location detection + set threshold [get_config gui.copyblamethreshold] + set original_options [list "-C$threshold"] + + if {![is_config_true gui.fastcopyblame]} { + # thorough copy search; insert before the threshold + set original_options [linsert $original_options 0 -C] + } + if {[git-version >= 1.5.3]} { + lappend original_options -w ; # ignore indentation changes + } + _exec_blame $this $w_amov @amov_data \ $original_options \ [mc "Loading original location annotations..."] diff --git a/lib/option.tcl b/lib/option.tcl index 9270512582..ffb3f00ff0 100644 --- a/lib/option.tcl +++ b/lib/option.tcl @@ -123,6 +123,8 @@ proc do_options {} { {b gui.trustmtime {mc "Trust File Modification Timestamps"}} {b gui.pruneduringfetch {mc "Prune Tracking Branches During Fetch"}} {b gui.matchtrackingbranch {mc "Match Tracking Branches"}} + {b gui.fastcopyblame {mc "Blame Copy Only On Changed Files"}} + {i-20..200 gui.copyblamethreshold {mc "Minimum Letters To Blame Copy On"}} {i-0..99 gui.diffcontext {mc "Number of Diff Context Lines"}} {i-0..99 gui.commitmsgwidth {mc "Commit Message Text Width"}} {t gui.newbranchtemplate {mc "New Branch Name Template"}} From e6131d30c21d2c308571078729dc8d2e1b746285 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 Jul 2008 00:48:08 +0400 Subject: [PATCH 03/36] Kill the blame back-end on window close. Currently 'git-gui blame' does not kill its back-end process, hoping that it will die anyway when the pipe is closed. However, in some cases the process works for a long time without producing any output. This behavior results in a runaway CPU hog. Signed-off-by: Alexander Gavrilov Signed-off-by: Shawn O. Pearce --- git-gui.sh | 14 ++++++++++++++ lib/blame.tcl | 16 ++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index b1ed0ec568..83e2645714 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -497,6 +497,20 @@ proc githook_read {hook_name args} { return {} } +proc kill_file_process {fd} { + set process [pid $fd] + + catch { + if {[is_Windows]} { + # Use a Cygwin-specific flag to allow killing + # native Windows processes + exec kill -f $process + } else { + exec kill $process + } + } +} + proc sq {value} { regsub -all ' $value "'\\''" value return "'$value'" diff --git a/lib/blame.tcl b/lib/blame.tcl index 192505d983..2c190482df 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -326,19 +326,27 @@ constructor new {i_commit i_path} { bind $w.file_pane \ "if {{$w.file_pane} eq {%W}} {[cb _resize %h]}" + wm protocol $top WM_DELETE_WINDOW "destroy $top" + bind $top [cb _kill] + _load $this {} } +method _kill {} { + if {$current_fd ne {}} { + kill_file_process $current_fd + catch {close $current_fd} + set current_fd {} + } +} + method _load {jump} { variable group_colors _hide_tooltip $this if {$total_lines != 0 || $current_fd ne {}} { - if {$current_fd ne {}} { - catch {close $current_fd} - set current_fd {} - } + _kill $this foreach i $w_columns { $i conf -state normal From a01fe996a2f70b759b4d94bd3e9985a01d514ad7 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 Jul 2008 00:51:20 +0400 Subject: [PATCH 04/36] Add a menu item to invoke full copy detection in blame. Add a context menu item to invoke blame -C -C -C on a chunk of the file. The results are used to update the 'original location' column of the blame display. The chunk is computed as the smallest line range that covers both the 'last change' and 'original location' ranges of the line that was clicked to open the menu. Signed-off-by: Alexander Gavrilov Signed-off-by: Shawn O. Pearce --- lib/blame.tcl | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/lib/blame.tcl b/lib/blame.tcl index 2c190482df..b6e42cbc8f 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -256,6 +256,9 @@ constructor new {i_commit i_path} { $w.ctxm add command \ -label [mc "Copy Commit"] \ -command [cb _copycommit] + $w.ctxm add command \ + -label [mc "Do Full Copy Detection"] \ + -command [cb _fullcopyblame] foreach i $w_columns { for {set g 0} {$g < [llength $group_colors]} {incr g} { @@ -708,6 +711,72 @@ method _read_blame {fd cur_w cur_d} { } } ifdeleted { catch {close $fd} } +method _find_commit_bound {data_list start_idx delta} { + upvar #0 $data_list line_data + set pos $start_idx + set limit [expr {[llength $line_data] - 1}] + set base_commit [lindex $line_data $pos 0] + + while {$pos > 0 && $pos < $limit} { + set new_pos [expr {$pos + $delta}] + if {[lindex $line_data $new_pos 0] ne $base_commit} { + return $pos + } + + set pos $new_pos + } + + return $pos +} + +method _fullcopyblame {} { + if {$current_fd ne {}} { + tk_messageBox \ + -icon error \ + -type ok \ + -title [mc "Busy"] \ + -message [mc "Annotation process is already running."] + + return + } + + # Switches for original location detection + set threshold [get_config gui.copyblamethreshold] + set original_options [list -C -C "-C$threshold"] + + if {[git-version >= 1.5.3]} { + lappend original_options -w ; # ignore indentation changes + } + + # Find the line range + set pos @$::cursorX,$::cursorY + set lno [lindex [split [$::cursorW index $pos] .] 0] + set min_amov_lno [_find_commit_bound $this @amov_data $lno -1] + set max_amov_lno [_find_commit_bound $this @amov_data $lno 1] + set min_asim_lno [_find_commit_bound $this @asim_data $lno -1] + set max_asim_lno [_find_commit_bound $this @asim_data $lno 1] + + if {$min_asim_lno < $min_amov_lno} { + set min_amov_lno $min_asim_lno + } + + if {$max_asim_lno > $max_amov_lno} { + set max_amov_lno $max_asim_lno + } + + lappend original_options -L "$min_amov_lno,$max_amov_lno" + + # Clear lines + for {set i $min_amov_lno} {$i <= $max_amov_lno} {incr i} { + lset amov_data $i [list ] + } + + # Start the back-end process + _exec_blame $this $w_amov @amov_data \ + $original_options \ + [mc "Running thorough copy detection..."] +} + method _click {cur_w pos} { set lno [lindex [split [$cur_w index $pos] .] 0] _showcommit $this $cur_w $lno From 15430be5a1d17b888b45b608daab7573f24cf9f1 Mon Sep 17 00:00:00 2001 From: Abhijit Menon-Sen Date: Thu, 24 Jul 2008 18:58:53 +0530 Subject: [PATCH 05/36] git-gui: Look for gitk in $PATH, not $LIBEXEC/git-core Signed-off-by: Abhijit Menon-Sen Signed-off-by: Shawn O. Pearce --- git-gui.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 83e2645714..7c27a43a5d 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1686,10 +1686,10 @@ proc do_gitk {revs} { # -- Always start gitk through whatever we were loaded with. This # lets us bypass using shell process on Windows systems. # - set exe [file join [file dirname $::_git] gitk] + set exe [_which gitk] set cmd [list [info nameofexecutable] $exe] - if {! [file exists $exe]} { - error_popup [mc "Unable to start gitk:\n\n%s does not exist" $exe] + if {$exe eq {}} { + error_popup [mc "Couldn't find gitk in PATH"] } else { global env From 7838d3fb41ed7d738e2cbdf8e4f40f0367f4f46f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 25 Jul 2008 15:08:33 -0700 Subject: [PATCH 06/36] git-gui: Correct 'Visualize Branches' on Mac OS X to start gitk In Git 1.6 and later gitk is in $prefix/bin while git-gui and all of the other commands are in $gitexecdir, which is typically not the same as $prefix/bin. So we cannot launch $gitexecdir/gitk and expect it to actually start gitk properly. By allowing git-gui to locate the script via $PATH and then using exactly that path when we source it during the application start we can correctly run gitk on any Git 1.5 or later. Signed-off-by: Shawn O. Pearce --- macosx/AppMain.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macosx/AppMain.tcl b/macosx/AppMain.tcl index 41ca08e2b7..ddbe6334a2 100644 --- a/macosx/AppMain.tcl +++ b/macosx/AppMain.tcl @@ -7,7 +7,7 @@ if {[string first -psn [lindex $argv 0]] == 0} { } if {[file tail [lindex $argv 0]] eq {gitk}} { - set argv0 [file join $gitexecdir gitk] + set argv0 [lindex $argv 0] set AppMain_source $argv0 } else { set argv0 [file join $gitexecdir [file tail [lindex $argv 0]]] From fa6b5b3944b74f2ad2430f3dfec517f8f70a6663 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 15 Jul 2008 23:11:00 +0200 Subject: [PATCH 07/36] git-gui: Fix "Stage/Unstage Line" with one line of context. To "Stage/Unstage Line" we construct a patch that contains exactly one change (either addition or removal); the hunk header was forged by counting the old side and adjusting the count by +/-1 for the new side. But when we counted the context we never counted the changed line itself. If the hunk had only one removal line and one line of context, like this: @@ -1,3 +1,2 @@ context 1 -removal context 2 We had constructed this patch: @@ -1,2 +1,1 @@ context 1 -removal context 2 which does not apply because git apply deduces that it must apply at the end of the file. ("context 2" is considered garbage and ignored.) The fix is that removal lines must be counted towards the context of the old side. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- lib/diff.tcl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/diff.tcl b/lib/diff.tcl index 96ba94906c..ee7f391e2f 100644 --- a/lib/diff.tcl +++ b/lib/diff.tcl @@ -423,6 +423,9 @@ proc apply_line {x y} { # the line to stage/unstage set ln [$ui_diff get $i_l $next_l] set patch "$patch$ln" + if {$c1 eq {-}} { + set n [expr $n+1] + } } elseif {$c1 ne {-} && $c1 ne {+}} { # context line set ln [$ui_diff get $i_l $next_l] From c7f7457026dc2f6979842f81cc17098579fec8d8 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Thu, 17 Jul 2008 15:21:51 +0200 Subject: [PATCH 08/36] git-gui: "Stage Line": Treat independent changes in adjacent lines better Assume that we want to commit these states: Old state == HEAD Intermediate state New state -------------------------------------------------------- context before context before context before old 1 new 1 new 1 old 2 old 2 new 2 context after context after context after that is, want to commit two changes in this order: 1. transform "old 1" into "new 1" 2. transform "old 2" into "new 2" [This discussion and this patch is about this very case and one other case as outlined below; any other intermediate states that one could imagine are not affected by this patch.] Now assume further, that we have not staged and commited anything, but we have already changed the working file to the new state. Then we will see this hunk in the "Unstaged Changes": @@ -1,4 +1,4 @@ context before -old 1 -old 2 +new 1 +new 2 context after The obvious way to stage the intermediate state is to apply "Stage This Line" to "-old 1" and "+new 1". Unfortunately, this resulted in this intermediate state: context before old 2 new 1 context after which is not what we wanted. In fact, it was impossible to stage the intermediate state using "Stage Line". The crux was that if a "+" line was staged, then the "-" lines were converted to context lines and arranged *before* the "+" line in the forged hunk that we fed to 'git apply'. With this patch we now treat "+" lines that are staged differently. In particular, the "-" lines before the "+" block are moved *after* the staged "+" line. Now it is possible to get the correct intermediate state by staging "-old 1" and "+new 1". Problem solved. But there is a catch. Noticing that we didn't get the right intermediate state by staging "-old 1" and "+new 1", we could have had the idea to stage the complete hunk and to *unstage* "-old 2" and "+new 2". But... the result is the same. The reason is that there is the exact symmetric problem with unstaging the last "-" and "+" line that are in adjacent blocks of "-" and "+" lines. This patch does *not* change the way in which "-" lines are *unstaged*. Why? Because if we did (i.e. move "+" lines before the "-" line after converting them to context lines), then it would be impossible to stage this intermediate state: context before old 1 new 2 context after that is, it would be impossible to stage the two independet changes in the opposite order. Let's look at this case a bit further: The obvious way to get this intermediate state would be to apply "Stage This Line" to "-old 2" and "+new 2". Before this patch, this worked as expected. With this patch, it does not work as expected, but it can still be achieved by first staging the entire hunk, then *unstaging* "-old 1" and "+new 1". In summary, this patch makes a common case possible, at the expense that a less common case is made more complicated for the user. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- lib/diff.tcl | 61 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/lib/diff.tcl b/lib/diff.tcl index ee7f391e2f..77990c537c 100644 --- a/lib/diff.tcl +++ b/lib/diff.tcl @@ -411,6 +411,53 @@ proc apply_line {x y} { set hh [lindex [split $hh ,] 0] set hln [lindex [split $hh -] 1] + # There is a special situation to take care of. Consider this hunk: + # + # @@ -10,4 +10,4 @@ + # context before + # -old 1 + # -old 2 + # +new 1 + # +new 2 + # context after + # + # We used to keep the context lines in the order they appear in the + # hunk. But then it is not possible to correctly stage only + # "-old 1" and "+new 1" - it would result in this staged text: + # + # context before + # old 2 + # new 1 + # context after + # + # (By symmetry it is not possible to *un*stage "old 2" and "new 2".) + # + # We resolve the problem by introducing an asymmetry, namely, when + # a "+" line is *staged*, it is moved in front of the context lines + # that are generated from the "-" lines that are immediately before + # the "+" block. That is, we construct this patch: + # + # @@ -10,4 +10,5 @@ + # context before + # +new 1 + # old 1 + # old 2 + # context after + # + # But we do *not* treat "-" lines that are *un*staged in a special + # way. + # + # With this asymmetry it is possible to stage the change + # "old 1" -> "new 1" directly, and to stage the change + # "old 2" -> "new 2" by first staging the entire hunk and + # then unstaging the change "old 1" -> "new 1". + + # This is non-empty if and only if we are _staging_ changes; + # then it accumulates the consecutive "-" lines (after converting + # them to context lines) in order to be moved after the "+" change + # line. + set pre_context {} + set n 0 set i_l [$ui_diff index "$i_l + 1 lines"] set patch {} @@ -422,19 +469,27 @@ proc apply_line {x y} { [$ui_diff compare $the_l < $next_l]} { # the line to stage/unstage set ln [$ui_diff get $i_l $next_l] - set patch "$patch$ln" if {$c1 eq {-}} { set n [expr $n+1] + set patch "$patch$pre_context$ln" + } else { + set patch "$patch$ln$pre_context" } + set pre_context {} } elseif {$c1 ne {-} && $c1 ne {+}} { # context line set ln [$ui_diff get $i_l $next_l] - set patch "$patch$ln" + set patch "$patch$pre_context$ln" set n [expr $n+1] + set pre_context {} } elseif {$c1 eq $to_context} { # turn change line into context line set ln [$ui_diff get "$i_l + 1 chars" $next_l] - set patch "$patch $ln" + if {$c1 eq {-}} { + set pre_context "$pre_context $ln" + } else { + set patch "$patch $ln" + } set n [expr $n+1] } set i_l $next_l From 35faca1f99b5bb8a729a4e9718f5577432572332 Mon Sep 17 00:00:00 2001 From: Abhijit Menon-Sen Date: Sat, 26 Jul 2008 22:20:35 +0530 Subject: [PATCH 09/36] Clarify that "git log x.c y.h" lists commits that touch either file Signed-off-by: Abhijit Menon-Sen Signed-off-by: Junio C Hamano --- Documentation/git-log.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 5a58d5b03d..05cbac56ac 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -58,7 +58,7 @@ include::diff-options.txt[] its size is not included. ...:: - Show only commits that affect the specified paths. + Show only commits that affect any of the specified paths. include::rev-list-options.txt[] From cbb504c97437653540dc55430a6f64da9ddd24fd Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sat, 26 Jul 2008 12:26:56 +0200 Subject: [PATCH 10/36] bash completion: Add long options for 'git describe' Signed-off-by: Thomas Rast Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 40b3d99737..2fb88a8fef 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -667,6 +667,15 @@ _git_commit () _git_describe () { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --all --tags --contains --abbrev= --candidates= + --exact-match --debug --long --match --always + " + return + esac __gitcomp "$(__git_refs)" } From 6bb9e51b8ef2f397f9f44cfaeaa120ca0cc741e6 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Sun, 27 Jul 2008 13:12:15 +0200 Subject: [PATCH 11/36] Documentation: fix diff.external example The diff.external examples pass a flag to gnu-diff, but GNU diff does not follow the GIT_EXTERNAL_DIFF interface. Signed-off-by: Anders Melchiorsen Signed-off-by: Junio C Hamano --- Documentation/config.txt | 10 ++++++---- Documentation/git-config.txt | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 798b551514..1a13abc922 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -92,7 +92,7 @@ Example # Our diff algorithm [diff] - external = "/usr/local/bin/gnu-diff -u" + external = /usr/local/bin/diff-wrapper renames = true [branch "devel"] @@ -563,9 +563,11 @@ diff.autorefreshindex:: diff.external:: If this config variable is set, diff generation is not performed using the internal diff machinery, but using the - given command. Note: if you want to use an external diff - program only on a subset of your files, you might want to - use linkgit:gitattributes[5] instead. + given command. Can be overridden with the `GIT_EXTERNAL_DIFF' + environment variable. The command is called with parameters + as described under "git Diffs" in linkgit:git[1]. Note: if + you want to use an external diff program only on a subset of + your files, you might want to use linkgit:gitattributes[5] instead. diff.renameLimit:: The number of files to consider when performing the copy/rename diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 697824cbab..28e1861094 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -222,7 +222,7 @@ Given a .git/config like this: ; Our diff algorithm [diff] - external = "/usr/local/bin/gnu-diff -u" + external = /usr/local/bin/diff-wrapper renames = true ; Proxy settings From 1eb7e2f83472f49fda62cefe1d2d9b4c668c6771 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Wed, 23 Jul 2008 18:07:23 -0600 Subject: [PATCH 12/36] bash completion: Add completion for 'git help' Rename cached __git_commandlist to __git_porcelain_commandlist and add __git_all_commandlist that only filters out *--* helpers. Completions for 'git help' will use the __git_all_commandlist, while the __git_porcelain_commandlist is used for git command completion. Users who actually read man pages may want to see help for plumbing commands. Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 46 ++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2fb88a8fef..30d870187e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -349,14 +349,32 @@ __git_complete_revlist () esac } -__git_commands () +__git_all_commands () { - if [ -n "$__git_commandlist" ]; then - echo "$__git_commandlist" + if [ -n "$__git_all_commandlist" ]; then + echo "$__git_all_commandlist" return fi local i IFS=" "$'\n' for i in $(git help -a|egrep '^ ') + do + case $i in + *--*) : helper pattern;; + *) echo $i;; + esac + done +} +__git_all_commandlist= +__git_all_commandlist="$(__git_all_commands 2>/dev/null)" + +__git_porcelain_commands () +{ + if [ -n "$__git_porcelain_commandlist" ]; then + echo "$__git_porcelain_commandlist" + return + fi + local i IFS=" "$'\n' + for i in "help" $(__git_all_commands) do case $i in *--*) : helper pattern;; @@ -427,8 +445,8 @@ __git_commands () esac done } -__git_commandlist= -__git_commandlist="$(__git_commands 2>/dev/null)" +__git_porcelain_commandlist= +__git_porcelain_commandlist="$(__git_porcelain_commands 2>/dev/null)" __git_aliases () { @@ -778,6 +796,18 @@ _git_gc () COMPREPLY=() } +_git_help () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--all --info --man --web" + return + ;; + esac + __gitcomp "$(__git_all_commands)" +} + _git_ls_remote () { __gitcomp "$(__git_remotes)" @@ -1410,7 +1440,8 @@ _git () case "$i" in --git-dir=*) __git_dir="${i#--git-dir=}" ;; --bare) __git_dir="." ;; - --version|--help|-p|--paginate) ;; + --version|-p|--paginate) ;; + --help) command="help"; break ;; *) command="$i"; break ;; esac c=$((++c)) @@ -1430,7 +1461,7 @@ _git () --help " ;; - *) __gitcomp "$(__git_commands) $(__git_aliases)" ;; + *) __gitcomp "$(__git_porcelain_commands) $(__git_aliases)" ;; esac return fi @@ -1455,6 +1486,7 @@ _git () fetch) _git_fetch ;; format-patch) _git_format_patch ;; gc) _git_gc ;; + help) _git_help ;; log) _git_log ;; ls-remote) _git_ls_remote ;; ls-tree) _git_ls_tree ;; From 77ef80a83e652d65a26213bc276b33051524998f Mon Sep 17 00:00:00 2001 From: Cesar Eduardo Barros Date: Sat, 26 Jul 2008 01:17:42 -0300 Subject: [PATCH 13/36] Documentation/git-submodule.txt: fix doubled word Signed-off-by: Cesar Eduardo Barros Signed-off-by: Junio C Hamano --- Documentation/git-submodule.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 829b03201d..35efeefb30 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -63,7 +63,7 @@ COMMANDS add:: Add the given repository as a submodule at the given path to the changeset to be committed next to the current - project: the current project is termed termed the "superproject". + project: the current project is termed the "superproject". + This requires two arguments: and . + From b890fa33a41c0976171d80590286d2bc8bda3df6 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Sat, 26 Jul 2008 23:53:30 -0500 Subject: [PATCH 14/36] t6030 (bisect): work around Mac OS X "ls" t6030-bisect-porcelain.sh relies on "ls" exiting with nonzero status when asked to list nonexistent files. Unfortunately, /bin/ls on Mac OS X 10.3 exits with exit code 0. So look at its output instead. Signed-off-by: Jonathan Nieder Acked-by: Christian Couder Signed-off-by: Junio C Hamano --- t/t6030-bisect-porcelain.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 0626544823..244fda62a5 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -76,7 +76,7 @@ test_expect_success 'bisect fails if given any junk instead of revs' ' test_must_fail git bisect start foo $HASH1 -- && test_must_fail git bisect start $HASH4 $HASH1 bar -- && test -z "$(git for-each-ref "refs/bisect/*")" && - test_must_fail ls .git/BISECT_* && + test -z "$(ls .git/BISECT_* 2>/dev/null)" && git bisect start && test_must_fail git bisect good foo $HASH1 && test_must_fail git bisect good $HASH1 bar && From ef115e26f7d6859f679d122d6c2300db09015396 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 27 Jul 2008 01:23:46 +0200 Subject: [PATCH 15/36] gitweb: More about how gitweb gets 'owner' of repository Signed-off-by: Jakub Narebski Acked-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/README | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gitweb/README b/gitweb/README index 6908036402..825162a0b6 100644 --- a/gitweb/README +++ b/gitweb/README @@ -277,7 +277,8 @@ You can use the following files in repository: * gitweb.owner You can use the gitweb.owner repository configuration variable to set repository's owner. It is displayed in the project list and summary - page. If it's not set, filesystem directory's owner is used. + page. If it's not set, filesystem directory's owner is used + (via GECOS field / real name field from getpwiud(3)). * various gitweb.* config variables (in config) Read description of %feature hash for detailed list, and some descriptions. From 47c6ef1c8def9a20b4ff40825456b45f5e63b51f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 26 Jul 2008 23:15:51 -0700 Subject: [PATCH 16/36] make sure parsed wildcard refspec ends with slash A wildcard refspec is internally parsed into a refspec structure with src and dst strings. Many parts of the code assumed that these do not include the trailing "/*" when matching the wildcard pattern with an actual ref we see at the remote. What this meant was that we needed to make sure not just that the prefix matched, and also that a slash followed the part that matched. But a codepath that scans the result from ls-remote and finds matching refs forgot to check the "matching part must be followed by a slash" rule. This resulted in "refs/heads/b1" from the remote side to mistakenly match the source side of "refs/heads/b/*:refs/remotes/b/*" refspec. Worse, the refspec crafted internally by "git-clone", and a hardcoded preparsed refspec that is used to implement "git-fetch --tags", violated this "parsed widcard refspec does not end with slash" rule; simply adding the "matching part must be followed by a slash" rule then would have broken codepaths that use these refspecs. This commit changes the rule to require a trailing slash to parsed wildcard refspecs. IOW, "refs/heads/b/*:refs/remotes/b/*" is parsed as src = "refs/heads/b/" and dst = "refs/remotes/b/". This allows us to simplify the matching logic because we only need to do a prefixcmp() to notice "refs/heads/b/one" matches and "refs/heads/b1" does not. Acked-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- remote.c | 52 +++++++++++++++++++++++++++--------------- t/t5513-fetch-track.sh | 30 ++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 18 deletions(-) create mode 100755 t/t5513-fetch-track.sh diff --git a/remote.c b/remote.c index 0d6020beb8..f61a3ab399 100644 --- a/remote.c +++ b/remote.c @@ -427,6 +427,28 @@ static void read_config(void) alias_all_urls(); } +/* + * We need to make sure the tracking branches are well formed, but a + * wildcard refspec in "struct refspec" must have a trailing slash. We + * temporarily drop the trailing '/' while calling check_ref_format(), + * and put it back. The caller knows that a CHECK_REF_FORMAT_ONELEVEL + * error return is Ok for a wildcard refspec. + */ +static int verify_refname(char *name, int is_glob) +{ + int result, len = -1; + + if (is_glob) { + len = strlen(name); + assert(name[len - 1] == '/'); + name[len - 1] = '\0'; + } + result = check_ref_format(name); + if (is_glob) + name[len - 1] = '/'; + return result; +} + static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify) { int i; @@ -434,11 +456,11 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec); for (i = 0; i < nr_refspec; i++) { - size_t llen, rlen; + size_t llen; int is_glob; const char *lhs, *rhs; - llen = rlen = is_glob = 0; + llen = is_glob = 0; lhs = refspec[i]; if (*lhs == '+') { @@ -458,12 +480,9 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp } if (rhs) { - rhs++; - rlen = strlen(rhs); + size_t rlen = strlen(++rhs); is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*")); - if (is_glob) - rlen -= 2; - rs[i].dst = xstrndup(rhs, rlen); + rs[i].dst = xstrndup(rhs, rlen - is_glob); } llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); @@ -471,7 +490,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if ((rhs && !is_glob) || (!rhs && fetch)) goto invalid; is_glob = 1; - llen -= 2; + llen--; } else if (rhs && is_glob) { goto invalid; } @@ -488,7 +507,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if (!*rs[i].src) ; /* empty is ok */ else { - st = check_ref_format(rs[i].src); + st = verify_refname(rs[i].src, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } @@ -503,7 +522,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp } else if (!*rs[i].dst) { ; /* ok */ } else { - st = check_ref_format(rs[i].dst); + st = verify_refname(rs[i].dst, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } @@ -518,7 +537,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if (!*rs[i].src) ; /* empty is ok */ else if (is_glob) { - st = check_ref_format(rs[i].src); + st = verify_refname(rs[i].src, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } @@ -532,13 +551,13 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp * - otherwise it must be a valid looking ref. */ if (!rs[i].dst) { - st = check_ref_format(rs[i].src); + st = verify_refname(rs[i].src, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } else if (!*rs[i].dst) { goto invalid; } else { - st = check_ref_format(rs[i].dst); + st = verify_refname(rs[i].dst, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } @@ -687,8 +706,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec) if (!fetch->dst) continue; if (fetch->pattern) { - if (!prefixcmp(needle, key) && - needle[strlen(key)] == '/') { + if (!prefixcmp(needle, key)) { *result = xmalloc(strlen(value) + strlen(needle) - strlen(key) + 1); @@ -966,9 +984,7 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, continue; } - if (rs[i].pattern && - !prefixcmp(src->name, rs[i].src) && - src->name[strlen(rs[i].src)] == '/') + if (rs[i].pattern && !prefixcmp(src->name, rs[i].src)) return rs + i; } if (matching_refs != -1) diff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh new file mode 100755 index 0000000000..9e7486274b --- /dev/null +++ b/t/t5513-fetch-track.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +test_description='fetch follows remote tracking branches correctly' + +. ./test-lib.sh + +test_expect_success setup ' + >file && + git add . && + test_tick && + git commit -m Initial && + git branch b-0 && + git branch b1 && + git branch b/one && + test_create_repo other && + ( + cd other && + git config remote.origin.url .. && + git config remote.origin.fetch "+refs/heads/b/*:refs/remotes/b/*" + ) +' + +test_expect_success fetch ' + ( + cd other && git fetch origin && + test "$(git for-each-ref --format="%(refname)")" = refs/remotes/b/one + ) +' + +test_done From 2122f8b963d49a59762e121c2da571c2348dcce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Sat, 26 Jul 2008 18:37:56 +0200 Subject: [PATCH 17/36] rev-parse: Add support for the ^! and ^@ syntax MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those shorthands are explained in the rev-parse documentation but were not actually supported by rev-parse itself. gitk internally uses rev-parse to interpret its command line arguments, and being able to use these "limit with parents" syntax is handy there. Signed-off-by: Björn Steinbrink Signed-off-by: Junio C Hamano --- builtin-rev-parse.c | 32 ++++++++++++++++++++++++++++++++ t/t6101-rev-parse-parents.sh | 2 ++ 2 files changed, 34 insertions(+) diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index aa71f4a4fa..9aa049ec17 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -241,6 +241,36 @@ static int try_difference(const char *arg) return 0; } +static int try_parent_shorthands(const char *arg) +{ + char *dotdot; + unsigned char sha1[20]; + struct commit *commit; + struct commit_list *parents; + int parents_only; + + if ((dotdot = strstr(arg, "^!"))) + parents_only = 0; + else if ((dotdot = strstr(arg, "^@"))) + parents_only = 1; + + if (!dotdot || dotdot[2]) + return 0; + + *dotdot = 0; + if (get_sha1(arg, sha1)) + return 0; + + if (!parents_only) + show_rev(NORMAL, sha1, arg); + commit = lookup_commit_reference(sha1); + for (parents = commit->parents; parents; parents = parents->next) + show_rev(parents_only ? NORMAL : REVERSED, + parents->item->object.sha1, arg); + + return 1; +} + static int parseopt_dump(const struct option *o, const char *arg, int unset) { struct strbuf *parsed = o->value; @@ -573,6 +603,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) /* Not a flag argument */ if (try_difference(arg)) continue; + if (try_parent_shorthands(arg)) + continue; name = arg; type = NORMAL; if (*arg == '^') { diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh index efc831363e..919552a2fc 100755 --- a/t/t6101-rev-parse-parents.sh +++ b/t/t6101-rev-parse-parents.sh @@ -28,6 +28,8 @@ test_expect_success 'final^1^2 != final^1^1' "test $(git rev-parse final^1^2) != test_expect_success 'final^1^3 not valid' "if git rev-parse --verify final^1^3; then false; else :; fi" test_expect_success '--verify start2^1' 'test_must_fail git rev-parse --verify start2^1' test_expect_success '--verify start2^0' 'git rev-parse --verify start2^0' +test_expect_success 'final^1^@ = final^1^1 final^1^2' "test \"$(git rev-parse final^1^@)\" = \"$(git rev-parse final^1^1 final^1^2)\"" +test_expect_success 'final^1^! = final^1 ^final^1^1 ^final^1^2' "test \"$(git rev-parse final^1^\!)\" = \"$(git rev-parse final^1 ^final^1^1 ^final^1^2)\"" test_expect_success 'repack for next test' 'git repack -a -d' test_expect_success 'short SHA-1 works' ' From 346d437aab2496872f477bb759db10a43b5dfb5e Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Sat, 26 Jul 2008 12:27:23 +0200 Subject: [PATCH 18/36] builtin-branch: remove duplicated code The previous optimization to --[no-]merged ended up with some duplicated code which this patch removes. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- builtin-branch.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index 5db8ad836a..675a9b1637 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -214,7 +214,6 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, struct commit *commit; int kind; int len; - static struct commit_list branch; /* Detect kind */ if (!prefixcmp(refname, "refs/heads/")) { @@ -238,13 +237,9 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, if ((kind & ref_list->kinds) == 0) return 0; - if (merge_filter != NO_FILTER) { - branch.item = lookup_commit_reference_gently(sha1, 1); - if (!branch.item) - die("Unable to lookup tip of branch %s", refname); + if (merge_filter != NO_FILTER) add_pending_object(&ref_list->revs, - (struct object *)branch.item, refname); - } + (struct object *)commit, refname); /* Resize buffer */ if (ref_list->index >= ref_list->alloc) { From 7950cda782fc313569a8a09be0bfab234689627a Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Sat, 26 Jul 2008 12:27:24 +0200 Subject: [PATCH 19/36] builtin-branch: factor out merge_filter matching The logic for checking commits against merge_filter will be reused when we recalculate the maxwidth of refnames. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- builtin-branch.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index 675a9b1637..c0f1c2ede1 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -294,6 +294,17 @@ static void fill_tracking_info(char *stat, const char *branch_name) sprintf(stat, "[ahead %d, behind %d] ", ours, theirs); } +static int matches_merge_filter(struct commit *commit) +{ + int is_merged; + + if (merge_filter == NO_FILTER) + return 1; + + is_merged = !!(commit->object.flags & UNINTERESTING); + return (is_merged == (merge_filter == SHOW_MERGED)); +} + static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, int abbrev, int current) { @@ -301,11 +312,8 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, int color; struct commit *commit = item->commit; - if (merge_filter != NO_FILTER) { - int is_merged = !!(item->commit->object.flags & UNINTERESTING); - if (is_merged != (merge_filter == SHOW_MERGED)) - return; - } + if (!matches_merge_filter(commit)) + return; switch (item->kind) { case REF_LOCAL_BRANCH: From b6f637d199fed238179865a38f180fccc37ee8cc Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Sat, 26 Jul 2008 12:27:25 +0200 Subject: [PATCH 20/36] builtin-branch: fix -v for --[no-]merged After the optimization to --[no-]merged logic, the calculation of the width of the longest refname to be shown might become inaccurate (since the matching against merge_filter is performed after adding refs to ref_list). This patch forces a recalculation of maxwidth when it might be needed. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- builtin-branch.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/builtin-branch.c b/builtin-branch.c index c0f1c2ede1..b1a2ad7a6b 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -363,6 +363,19 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, } } +static int calc_maxwidth(struct ref_list *refs) +{ + int i, l, w = 0; + for (i = 0; i < refs->index; i++) { + if (!matches_merge_filter(refs->list[i].commit)) + continue; + l = strlen(refs->list[i].name); + if (l > w) + w = l; + } + return w; +} + static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit) { int i; @@ -383,6 +396,8 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str (struct object *) filter, ""); ref_list.revs.limited = 1; prepare_revision_walk(&ref_list.revs); + if (verbose) + ref_list.maxwidth = calc_maxwidth(&ref_list); } qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); From f5d600e2dd8721d7b9177f1c82fe54822d9f1d62 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 27 Jul 2008 22:34:14 +0200 Subject: [PATCH 21/36] Avoid chdir() in list_commands_in_dir() The function list_commands_in_dir() tried to be lazy and just chdir() to the directory which entries it listed, so that the check if the file is executable could be done on dir->d_name. However, there is no good reason to jump around wildly just to find all Git commands. Instead, have a strbuf and construct the full path dynamically. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- help.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/help.c b/help.c index bfc84aed10..3cb1962896 100644 --- a/help.c +++ b/help.c @@ -425,17 +425,24 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds, int prefix_len = strlen(prefix); DIR *dir = opendir(path); struct dirent *de; + struct strbuf buf = STRBUF_INIT; + int len; - if (!dir || chdir(path)) + if (!dir) return 0; + strbuf_addf(&buf, "%s/", path); + len = buf.len; + while ((de = readdir(dir)) != NULL) { int entlen; if (prefixcmp(de->d_name, prefix)) continue; - if (!is_executable(de->d_name)) + strbuf_setlen(&buf, len); + strbuf_addstr(&buf, de->d_name); + if (!is_executable(buf.buf)) continue; entlen = strlen(de->d_name) - prefix_len; @@ -448,6 +455,7 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds, add_cmdname(cmds, de->d_name + prefix_len, entlen); } closedir(dir); + strbuf_release(&buf); return longest; } From a08c53a119999df5b76e405195fa6c5e7d4b689f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 26 Jul 2008 21:33:00 -0500 Subject: [PATCH 22/36] fsck: Don't require tmp_obj_ file names are 14 bytes in length Not all temporary file creation routines will ensure 14 bytes are used to generate the temporary file name. In C Git this may be true, but alternate implementations such as jgit are not always able to generate a temporary file name with a specific prefix and also ensure the file name length is 14 bytes long. Since temporary files in a directory we are fsck'ing should be uncommon (as they are short lived only long enough for an active writer to finish writing the file and rename it) we shouldn't see these show up very often. Always using a prefixcmp() call and ignoring the length opens up room for other implementations to use different name generation schemes. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-fsck.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/builtin-fsck.c b/builtin-fsck.c index 7a4a4f144f..6eb7da88d3 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -377,10 +377,6 @@ static void fsck_dir(int i, char *path) if (de->d_name[0] != '.') break; continue; - case 14: - if (prefixcmp(de->d_name, "tmp_obj_")) - break; - continue; case 38: sprintf(name, "%02x", i); memcpy(name+2, de->d_name, len+1); @@ -389,6 +385,8 @@ static void fsck_dir(int i, char *path) add_sha1_list(sha1, DIRENT_SORT_HINT(de)); continue; } + if (prefixcmp(de->d_name, "tmp_obj_")) + continue; fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name); } closedir(dir); From f6c52fe4e871d1d07cb7d86692ccf8cc4de54579 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Wed, 16 Jul 2008 21:11:08 +0200 Subject: [PATCH 23/36] git-mv: Remove dead code branch The path list builder had a branch for the case the source is not in index, but this can happen only if the source was a directory. However, in that case we have already expanded the list to the directory contents and set mode to WORKING_DIRECTORY, which is tested earlier. The patch removes the superfluous branch and adds an assert() instead. git-mv testsuite still passes. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- builtin-mv.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/builtin-mv.c b/builtin-mv.c index 736a0b8bb1..e66fa54324 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -228,15 +228,13 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (mode == WORKING_DIRECTORY) continue; - if (cache_name_pos(src, strlen(src)) >= 0) { - string_list_insert(src, &deleted); + assert(cache_name_pos(src, strlen(src)) >= 0); - /* destination can be a directory with 1 file inside */ - if (string_list_has_string(&overwritten, dst)) - string_list_insert(dst, &changed); - else - string_list_insert(dst, &added); - } else + string_list_insert(src, &deleted); + /* destination can be a directory with 1 file inside */ + if (string_list_has_string(&overwritten, dst)) + string_list_insert(dst, &changed); + else string_list_insert(dst, &added); } From 81dc2307d0ad87a4da2e753a9d1b5586d6456eed Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Mon, 21 Jul 2008 02:25:56 +0200 Subject: [PATCH 24/36] git-mv: Keep moved index entries inact The rewrite of git-mv from a shell script to a builtin was perhaps a little too straightforward: the git add and git rm queues were emulated directly, which resulted in a rather complicated code and caused an inconsistent behaviour when moving dirty index entries; git mv would update the entry based on working tree state, except in case of overwrites, where the new entry would still have sha1 of the old file. This patch introduces rename_index_entry_at() into the index toolkit, which will rename an entry while removing any entries the new entry might render duplicate. This is then used in git mv instead of all the file queues, resulting in a major simplification of the code and an inevitable change in git mv -n output format. Also the code used to refuse renaming overwriting symlink with a regular file and vice versa; there is no need for that. A few new tests have been added to the testsuite to reflect this change. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- builtin-mv.c | 65 ++++++++------------------------------------------- cache.h | 2 ++ read-cache.c | 16 +++++++++++++ t/t7001-mv.sh | 52 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 55 deletions(-) diff --git a/builtin-mv.c b/builtin-mv.c index e66fa54324..4f65b5ae9b 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -36,18 +36,6 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec, return get_pathspec(prefix, result); } -static void show_list(const char *label, struct string_list *list) -{ - if (list->nr > 0) { - int i; - printf("%s", label); - for (i = 0; i < list->nr; i++) - printf("%s%s", i > 0 ? ", " : "", - list->items[i].string); - putchar('\n'); - } -} - static const char *add_slash(const char *path) { int len = strlen(path); @@ -76,11 +64,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) const char **source, **destination, **dest_path; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; - struct string_list overwritten = {NULL, 0, 0, 0}; struct string_list src_for_dst = {NULL, 0, 0, 0}; - struct string_list added = {NULL, 0, 0, 0}; - struct string_list deleted = {NULL, 0, 0, 0}; - struct string_list changed = {NULL, 0, 0, 0}; git_config(git_default_config, NULL); @@ -185,12 +169,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix) * only files can overwrite each other: * check both source and destination */ - if (S_ISREG(st.st_mode)) { + if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { fprintf(stderr, "Warning: %s;" " will overwrite!\n", bad); bad = NULL; - string_list_insert(dst, &overwritten); } else bad = "Cannot overwrite"; } @@ -219,6 +202,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; + int pos; if (show_only || verbose) printf("Renaming %s to %s\n", src, dst); if (!show_only && mode != INDEX && @@ -228,45 +212,16 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (mode == WORKING_DIRECTORY) continue; - assert(cache_name_pos(src, strlen(src)) >= 0); - - string_list_insert(src, &deleted); - /* destination can be a directory with 1 file inside */ - if (string_list_has_string(&overwritten, dst)) - string_list_insert(dst, &changed); - else - string_list_insert(dst, &added); + pos = cache_name_pos(src, strlen(src)); + assert(pos >= 0); + if (!show_only) + rename_cache_entry_at(pos, dst); } - if (show_only) { - show_list("Changed : ", &changed); - show_list("Adding : ", &added); - show_list("Deleting : ", &deleted); - } else { - for (i = 0; i < changed.nr; i++) { - const char *path = changed.items[i].string; - int j = cache_name_pos(path, strlen(path)); - struct cache_entry *ce = active_cache[j]; - - if (j < 0) - die ("Huh? Cache entry for %s unknown?", path); - refresh_cache_entry(ce, 0); - } - - for (i = 0; i < added.nr; i++) { - const char *path = added.items[i].string; - if (add_file_to_cache(path, verbose ? ADD_CACHE_VERBOSE : 0)) - die("updating index entries failed"); - } - - for (i = 0; i < deleted.nr; i++) - remove_file_from_cache(deleted.items[i].string); - - if (active_cache_changed) { - if (write_cache(newfd, active_cache, active_nr) || - commit_locked_index(&lock_file)) - die("Unable to write new index file"); - } + if (active_cache_changed) { + if (write_cache(newfd, active_cache, active_nr) || + commit_locked_index(&lock_file)) + die("Unable to write new index file"); } return 0; diff --git a/cache.h b/cache.h index 38985aa63e..4b6c0a6c59 100644 --- a/cache.h +++ b/cache.h @@ -260,6 +260,7 @@ static inline void remove_name_hash(struct cache_entry *ce) #define unmerged_cache() unmerged_index(&the_index) #define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen)) #define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option)) +#define rename_cache_entry_at(pos, new_name) rename_index_entry_at(&the_index, (pos), (new_name)) #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos)) #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path)) #define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags)) @@ -370,6 +371,7 @@ extern int index_name_pos(const struct index_state *, const char *name, int name #define ADD_CACHE_JUST_APPEND 8 /* Append only; tree.c::read_tree() */ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option); extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); +extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name); extern int remove_index_entry_at(struct index_state *, int pos); extern int remove_file_from_index(struct index_state *, const char *path); #define ADD_CACHE_VERBOSE 1 diff --git a/read-cache.c b/read-cache.c index a50a851125..6c0880337b 100644 --- a/read-cache.c +++ b/read-cache.c @@ -38,6 +38,22 @@ static void replace_index_entry(struct index_state *istate, int nr, struct cache istate->cache_changed = 1; } +void rename_index_entry_at(struct index_state *istate, int nr, const char *new_name) +{ + struct cache_entry *old = istate->cache[nr], *new; + int namelen = strlen(new_name); + + new = xmalloc(cache_entry_size(namelen)); + copy_cache_entry(new, old); + new->ce_flags &= ~(CE_STATE_MASK | CE_NAMEMASK); + new->ce_flags |= (namelen >= CE_NAMEMASK ? CE_NAMEMASK : namelen); + memcpy(new->name, new_name, namelen + 1); + + cache_tree_invalidate_path(istate->cache_tree, old->name); + remove_index_entry_at(istate, nr); + add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); +} + /* * This only updates the "non-critical" parts of the directory * cache, ie the parts that aren't tracked by GIT, and only used diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index 336cfaa1c5..b0fa407825 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -156,4 +156,56 @@ test_expect_success 'absolute pathname outside should fail' '( )' +test_expect_success 'git mv should not change sha1 of moved cache entry' ' + + rm -fr .git && + git init && + echo 1 >dirty && + git add dirty && + entry="$(git ls-files --stage dirty | cut -f 1)" + git mv dirty dirty2 && + [ "$entry" = "$(git ls-files --stage dirty2 | cut -f 1)" ] && + echo 2 >dirty2 && + git mv dirty2 dirty && + [ "$entry" = "$(git ls-files --stage dirty | cut -f 1)" ] + +' + +rm -f dirty dirty2 + +test_expect_success 'git mv should overwrite symlink to a file' ' + + rm -fr .git && + git init && + echo 1 >moved && + ln -s moved symlink && + git add moved symlink && + test_must_fail git mv moved symlink && + git mv -f moved symlink && + ! test -e moved && + test -f symlink && + test "$(cat symlink)" = 1 && + git diff-files --quiet + +' + +rm -f moved symlink + +test_expect_success 'git mv should overwrite file with a symlink' ' + + rm -fr .git && + git init && + echo 1 >moved && + ln -s moved symlink && + git add moved symlink && + test_must_fail git mv symlink moved && + git mv -f symlink moved && + ! test -e symlink && + test -h moved && + git diff-files --quiet + +' + +rm -f moved symlink + test_done From 8e1db3871c767cb17b5e0eeb7bea8d967821a055 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Jul 2008 12:37:51 -0700 Subject: [PATCH 25/36] GIT 1.6.0-rc1 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.0.txt | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Documentation/RelNotes-1.6.0.txt b/Documentation/RelNotes-1.6.0.txt index 7da62d08d6..2542cf53d2 100644 --- a/Documentation/RelNotes-1.6.0.txt +++ b/Documentation/RelNotes-1.6.0.txt @@ -15,6 +15,9 @@ release, but users are again strongly encouraged to adjust their scripts to use "git xyzzy" form, as we will stop installing "git-xyzzy" hardlinks for built-in commands in later releases. +An earlier change to page "git status" output was overwhelmingly unpopular +and has been reverted. + Source changes needed for porting to MinGW environment are now all in the main git.git codebase. @@ -179,6 +182,10 @@ Updates since v1.5.6 * "git rerere" can be told to update the index with auto-reused resolution with rerere.autoupdate configuration variable. +* git-rev-parse learned $commit^! and $commit^@ notations used in "log" + family. These notations are available in gitk as well, because the gitk + command internally uses rev-parse to interpret its arguments. + * git-rev-list learned --children option to show child commits it encountered during the traversal, instead of shoing parent commits. @@ -196,6 +203,9 @@ Updates since v1.5.6 * git-status gives the remote tracking statistics similar to the way git-checkout reports by how many commits your branch is ahead/behind. +* "git-svn dcommit" is now aware of auto-props setting the subversion user + has. + * You can tell "git status -u" to even more aggressively omit checking untracked files with --untracked-files=no. @@ -215,8 +225,15 @@ Fixes since v1.5.6 All of the fixes in v1.5.6 maintenance series are included in this release, unless otherwise noted. +* git-clone ignored its -u option; the fix needs to be backported to + 'maint'; + +* git-mv used to lose the distinction between changes that are staged + and that are only in the working tree, by staging both in the index + after moving such a path. + --- exec >/var/tmp/1 -O=v1.5.6.4-432-g6796399 +O=v1.6.0-rc0-104-g81dc230 echo O=$(git describe refs/heads/master) git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint From 172035f044ebae283f23e95e3ec4d119dc8057cb Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 28 Jul 2008 02:02:04 -0400 Subject: [PATCH 26/36] init: handle empty "template" parameter If a user passes "--template=", then our template parameter is blank. Unfortunately, copy_templates() assumes it has at least one character, and does all sorts of bad things like reading from template[-1] and then proceeding to link all of '/' into the .git directory. This patch just checks for that condition in copy_templates and aborts. As a side effect, this means that --template= now has the meaning "don't copy any templates." Signed-off-by: Junio C Hamano --- builtin-init-db.c | 2 ++ t/t0001-init.sh | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/builtin-init-db.c b/builtin-init-db.c index 38b4fcb6db..baf0d09ac4 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -117,6 +117,8 @@ static void copy_templates(const char *template_dir) template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT); if (!template_dir) template_dir = system_path(DEFAULT_GIT_TEMPLATE_DIR); + if (!template_dir[0]) + return; strcpy(template_path, template_dir); template_len = strlen(template_path); if (template_path[template_len-1] != '/') { diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 2a38d98cb4..620da5b320 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -141,4 +141,30 @@ test_expect_success 'reinit' ' test_cmp again/empty again/err2 ' +test_expect_success 'init with --template' ' + mkdir template-source && + echo content >template-source/file && + ( + mkdir template-custom && + cd template-custom && + git init --template=../template-source + ) && + test_cmp template-source/file template-custom/.git/file +' + +test_expect_success 'init with --template (blank)' ' + ( + mkdir template-plain && + cd template-plain && + git init + ) && + test -f template-plain/.git/info/exclude && + ( + mkdir template-blank && + cd template-blank && + git init --template= + ) && + ! test -f template-blank/.git/info/exclude +' + test_done From e84a063da86cbf4382fbfb6f85e75c2ca2a8b9c7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Jul 2008 00:02:48 -0700 Subject: [PATCH 27/36] Allow building without any git installed This is a follow-up patch to 49fa65a (Allow the built-in exec path to be relative to the command invocation path, 2008-07-23). Without specific gitexecdir passed from the command line, git-gui's build procedure would try to figure out the value for it by running an installed git. Signed-off-by: Junio C Hamano --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 798a2f2f77..7e30b30775 100644 --- a/Makefile +++ b/Makefile @@ -1067,7 +1067,7 @@ endif all:: ifndef NO_TCLTK - $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) all + $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) gitexecdir='$(gitexec_instdir_SQ)' all $(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all endif $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all From 1b65f38c06e277a13fd7bf77d3d70df604912c08 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Jul 2008 00:05:10 -0700 Subject: [PATCH 28/36] Allow installing in the traditional way In an earlier commit c70a8d9 (Makefile: Do not install a copy of 'git' in $(gitexecdir), 2008-07-21), we tried to avoid installing two git, one in /usr/bin/git and the other in /usr/libexec/git-core/git. It mistakenly removed the only copy of git when gitexecdir and bindir are set to the same directory, i.e. the traditional layout. Signed-off-by: Junio C Hamano --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7e30b30775..52c67c1a47 100644 --- a/Makefile +++ b/Makefile @@ -1362,7 +1362,10 @@ endif cp "$$bindir/git$X" "$$execdir/git$X"; \ fi && \ { $(foreach p,$(BUILT_INS), $(RM) "$$execdir/$p" && ln "$$execdir/git$X" "$$execdir/$p" ;) } && \ - $(RM) "$$execdir/git$X" && \ + if test "z$$bindir" != "z$$execdir"; \ + then \ + $(RM) "$$execdir/git$X"; \ + fi && \ ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X" install-doc: From 7ddea13af24d72deca06a779b1827a8ea2d6cfe6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Jul 2008 02:33:33 -0700 Subject: [PATCH 29/36] ls-tree documentation: enhance notes on subdirectory and pathspec behaviour When run in a working copy subdirectory, git-ls-tree will automagically add the prefix to the pathspec, which can result in an unexpected behavior when the tree object accessed is not the root tree object. Signed-off-by: Junio C Hamano --- Documentation/git-ls-tree.txt | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt index d7e73f568d..4c7262f1cd 100644 --- a/Documentation/git-ls-tree.txt +++ b/Documentation/git-ls-tree.txt @@ -16,10 +16,20 @@ SYNOPSIS DESCRIPTION ----------- Lists the contents of a given tree object, like what "/bin/ls -a" does -in the current working directory. Note that the usage is subtly different, -though - 'paths' denote just a list of patterns to match, e.g. so specifying -directory name (without '-r') will behave differently, and order of the -arguments does not matter. +in the current working directory. Note that: + + - the behaviour is slightly different from that of "/bin/ls" in that the + 'paths' denote just a list of patterns to match, e.g. so specifying + directory name (without '-r') will behave differently, and order of the + arguments does not matter. + + - the behaviour is similar to that of "/bin/ls" in that the 'paths' is + taken as relative to the current working directory. E.g. when you are + in a directory 'sub' that has a directory 'dir', you can run 'git + ls-tree -r HEAD dir' to list the contents of the tree (that is + 'sub/dir' in 'HEAD'). You don't want to give a tree that is not at the + root level (e.g. 'git ls-tree -r HEAD:sub dir') in this case, as that + would result in asking for 'sub/sub/dir' in the 'HEAD' commit. OPTIONS ------- From d2761895144b6fabcbe1f3bd40d6c6798914b518 Mon Sep 17 00:00:00 2001 From: Olivier Marin Date: Mon, 28 Jul 2008 12:48:44 +0200 Subject: [PATCH 30/36] builtin-verify-tag: fix -v option parsing Since the C rewrite, "git verify-tag -v" just does nothing instead of printing the usage message with an error. This patch fix the regression. Signed-off-by: Olivier Marin Signed-off-by: Junio C Hamano --- builtin-verify-tag.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c index 7d837f0f98..729a1593e6 100644 --- a/builtin-verify-tag.c +++ b/builtin-verify-tag.c @@ -92,14 +92,15 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); - if (argc == 1) - usage(builtin_verify_tag_usage); - - if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) { + if (argc > 1 && + (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))) { verbose = 1; i++; } + if (argc <= i) + usage(builtin_verify_tag_usage); + /* sometimes the program was terminated because this signal * was received in the process of writing the gpg input: */ signal(SIGPIPE, SIG_IGN); From 4933e5ebdefdb88841275132ef358e6649963751 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Mon, 28 Jul 2008 07:50:27 +0200 Subject: [PATCH 31/36] Refactor, adding prepare_git_cmd(const char **argv) prepare_git_cmd(const char **argv) adds a first entry "git" to the array argv. The new array is allocated on the heap. It's the caller's responsibility to release it with free(). The code was already present in execv_git_cmd() but could not be used from outside. Now it can also be called for preparing the command list in the MinGW codepath in run-command.c. Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano --- exec_cmd.c | 7 ++++++- exec_cmd.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/exec_cmd.c b/exec_cmd.c index 0ed768ddc0..ce6741eb68 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -78,7 +78,7 @@ void setup_path(void) strbuf_release(&new_path); } -int execv_git_cmd(const char **argv) +const char **prepare_git_cmd(const char **argv) { int argc; const char **nargv; @@ -91,6 +91,11 @@ int execv_git_cmd(const char **argv) for (argc = 0; argv[argc]; argc++) nargv[argc + 1] = argv[argc]; nargv[argc + 1] = NULL; + return nargv; +} + +int execv_git_cmd(const char **argv) { + const char **nargv = prepare_git_cmd(argv); trace_argv_printf(nargv, "trace: exec:"); /* execvp() can only ever return if it fails */ diff --git a/exec_cmd.h b/exec_cmd.h index 0c46cd5636..594f961387 100644 --- a/exec_cmd.h +++ b/exec_cmd.h @@ -5,6 +5,7 @@ extern void git_set_argv_exec_path(const char *exec_path); extern void git_set_argv0_path(const char *path); extern const char* git_exec_path(void); extern void setup_path(void); +extern const char **prepare_git_cmd(const char **argv); extern int execv_git_cmd(const char **argv); /* NULL terminated */ extern int execl_git_cmd(const char *cmd, ...); extern const char *system_path(const char *path); From 108ac313f73499572c69b1b80514661b22a2d62e Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Mon, 28 Jul 2008 07:50:28 +0200 Subject: [PATCH 32/36] run-command (Windows): Run dashless "git " We prefer running the dashless form, and POSIX side already does so; we should use it in MinGW's start_command(), too. Signed-off-by: Steffen Prohaska Acked-by: Johannes Sixt Signed-off-by: Junio C Hamano --- run-command.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/run-command.c b/run-command.c index 6e29fdf9e2..a3b28a64dc 100644 --- a/run-command.c +++ b/run-command.c @@ -119,9 +119,8 @@ int start_command(struct child_process *cmd) } #else int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */ - const char *sargv0 = cmd->argv[0]; + const char **sargv = cmd->argv; char **env = environ; - struct strbuf git_cmd; if (cmd->no_stdin) { s0 = dup(0); @@ -165,9 +164,7 @@ int start_command(struct child_process *cmd) } if (cmd->git_cmd) { - strbuf_init(&git_cmd, 0); - strbuf_addf(&git_cmd, "git-%s", cmd->argv[0]); - cmd->argv[0] = git_cmd.buf; + cmd->argv = prepare_git_cmd(cmd->argv); } cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env); @@ -175,9 +172,9 @@ int start_command(struct child_process *cmd) if (cmd->env) free_environ(env); if (cmd->git_cmd) - strbuf_release(&git_cmd); + free(cmd->argv); - cmd->argv[0] = sargv0; + cmd->argv = sargv; if (s0 >= 0) dup2(s0, 0), close(s0); if (s1 >= 0) From c297432d0d245af5505e0f5f99a5267119dd421e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Jul 2008 22:26:47 -0700 Subject: [PATCH 33/36] Documentation: clarify what is shown in "git-ls-files -s" output Signed-off-by: Junio C Hamano --- Documentation/git-ls-files.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index f43af41740..9f85d60b5f 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -53,7 +53,7 @@ OPTIONS -s:: --stage:: - Show stage files in the output + Show staged contents' object name, mode bits and stage number in the output. --directory:: If a whole directory is classified as "other", show just its From 65c35b2256ecbfaebcf04559fe6070807646afff Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Jul 2008 22:27:39 -0700 Subject: [PATCH 34/36] t7001: fix "git mv" test The test assumed that we can keep the cached stat information fresh across rename(2); many filesystems however update st_ctime (and POSIX allows them to do so), and that assumption does not hold. We can explicitly refresh the index for the purpose of these tests. The only thing we are interested in is the staged contents and the mode bits are preserved across "git mv". Signed-off-by: Junio C Hamano --- t/t7001-mv.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index b0fa407825..910a28c7e2 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -185,6 +185,7 @@ test_expect_success 'git mv should overwrite symlink to a file' ' ! test -e moved && test -f symlink && test "$(cat symlink)" = 1 && + git update-index --refresh && git diff-files --quiet ' @@ -202,6 +203,7 @@ test_expect_success 'git mv should overwrite file with a symlink' ' git mv -f symlink moved && ! test -e symlink && test -h moved && + git update-index --refresh && git diff-files --quiet ' From df57accb46695b50e2db94424e9d4ebfed0694c8 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 29 Jul 2008 07:42:53 +0200 Subject: [PATCH 35/36] merge-base: die with an error message if not passed a commit ref Before this patch "git merge-base" just exited with error code 1 and without an error message in case it was passed a ref to an object that is not a commit (for example a tree). This patch makes it "die" in this case with an error message. While at it, this patch also refactors the code to get the commit reference from an argument into a new "get_commit_reference" function. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin-merge-base.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/builtin-merge-base.c b/builtin-merge-base.c index 1cb2925d2f..3382b1382a 100644 --- a/builtin-merge-base.c +++ b/builtin-merge-base.c @@ -22,10 +22,23 @@ static int show_merge_base(struct commit *rev1, struct commit *rev2, int show_al static const char merge_base_usage[] = "git merge-base [--all] "; +static struct commit *get_commit_reference(const char *arg) +{ + unsigned char revkey[20]; + struct commit *r; + + if (get_sha1(arg, revkey)) + die("Not a valid object name %s", arg); + r = lookup_commit_reference(revkey); + if (!r) + die("Not a valid commit name %s", arg); + + return r; +} + int cmd_merge_base(int argc, const char **argv, const char *prefix) { struct commit *rev1, *rev2; - unsigned char rev1key[20], rev2key[20]; int show_all = 0; git_config(git_default_config, NULL); @@ -40,13 +53,8 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix) } if (argc != 3) usage(merge_base_usage); - if (get_sha1(argv[1], rev1key)) - die("Not a valid object name %s", argv[1]); - if (get_sha1(argv[2], rev2key)) - die("Not a valid object name %s", argv[2]); - rev1 = lookup_commit_reference(rev1key); - rev2 = lookup_commit_reference(rev2key); - if (!rev1 || !rev2) - return 1; + rev1 = get_commit_reference(argv[1]); + rev2 = get_commit_reference(argv[2]); + return show_merge_base(rev1, rev2, show_all); } From 1ce4790bf5eeceb212f003fffcb36069c0ebb45e Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 28 Jul 2008 08:31:28 +0200 Subject: [PATCH 36/36] Make use of stat.ctime configurable A new configuration variable 'core.trustctime' is introduced to allow ignoring st_ctime information when checking if paths in the working tree has changed, because there are situations where it produces too much false positives. Like when file system crawlers keep changing it when scanning and using the ctime for marking scanned files. The default is to notice ctime changes. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- Documentation/config.txt | 7 +++++++ Documentation/git-update-index.txt | 5 +++++ cache.h | 1 + config.c | 4 ++++ environment.c | 1 + read-cache.c | 2 +- 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 1a13abc922..61c376057c 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -117,6 +117,13 @@ core.fileMode:: the working copy are ignored; useful on broken filesystems like FAT. See linkgit:git-update-index[1]. True by default. +core.trustctime:: + If false, the ctime differences between the index and the + working copy are ignored; useful when the inode change time + is regularly modified by something outside Git (file system + crawlers and some backup systems). + See linkgit:git-update-index[1]. True by default. + core.quotepath:: The commands that output paths (e.g. 'ls-files', 'diff'), when not given the `-z` option, will quote diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index 6b930bc163..1d9d81a702 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -323,6 +323,11 @@ from symbolic link to regular file. The command looks at `core.ignorestat` configuration variable. See 'Using "assume unchanged" bit' section above. +The command also looks at `core.trustctime` configuration variable. +It can be useful when the inode change time is regularly modified by +something outside Git (file system crawlers and backup systems use +ctime for marking files processed) (see linkgit:git-config[1]). + SEE ALSO -------- diff --git a/cache.h b/cache.h index 4b6c0a6c59..2475de9fa8 100644 --- a/cache.h +++ b/cache.h @@ -423,6 +423,7 @@ extern int delete_ref(const char *, const unsigned char *sha1); /* Environment bits from configuration mechanism */ extern int trust_executable_bit; +extern int trust_ctime; extern int quote_path_fully; extern int has_symlinks; extern int ignore_case; diff --git a/config.c b/config.c index 1e066c71e0..53f04a076a 100644 --- a/config.c +++ b/config.c @@ -341,6 +341,10 @@ static int git_default_core_config(const char *var, const char *value) trust_executable_bit = git_config_bool(var, value); return 0; } + if (!strcmp(var, "core.trustctime")) { + trust_ctime = git_config_bool(var, value); + return 0; + } if (!strcmp(var, "core.quotepath")) { quote_path_fully = git_config_bool(var, value); diff --git a/environment.c b/environment.c index 4a88a17d54..0c6d11f6a0 100644 --- a/environment.c +++ b/environment.c @@ -13,6 +13,7 @@ char git_default_email[MAX_GITNAME]; char git_default_name[MAX_GITNAME]; int user_ident_explicitly_given; int trust_executable_bit = 1; +int trust_ctime = 1; int has_symlinks = 1; int ignore_case; int assume_unchanged; diff --git a/read-cache.c b/read-cache.c index 6c0880337b..1cae361c6c 100644 --- a/read-cache.c +++ b/read-cache.c @@ -197,7 +197,7 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) } if (ce->ce_mtime != (unsigned int) st->st_mtime) changed |= MTIME_CHANGED; - if (ce->ce_ctime != (unsigned int) st->st_ctime) + if (trust_ctime && ce->ce_ctime != (unsigned int) st->st_ctime) changed |= CTIME_CHANGED; if (ce->ce_uid != (unsigned int) st->st_uid ||