From dd87558f58d989e47de09eafefa8d2f2ea4de27d Mon Sep 17 00:00:00 2001 From: Gustaf Hendeby Date: Thu, 25 Sep 2008 23:31:22 +0200 Subject: [PATCH 001/100] git-gui: Help identify aspell version on Windows too On windows, git gui fails to correctly extract the aspell version (experienced with aspell version 0.50.3) due to scilent white space at the end of the version string. Trim the obtained version string to work around this. Signed-off-by: Gustaf Hendeby Signed-off-by: Shawn O. Pearce --- lib/spellcheck.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/spellcheck.tcl b/lib/spellcheck.tcl index 78f344f08f..a479b2f285 100644 --- a/lib/spellcheck.tcl +++ b/lib/spellcheck.tcl @@ -80,7 +80,7 @@ method _connect {pipe_fd} { error_popup [strcat [mc "Unrecognized spell checker"] ":\n\n$s_version"] return } - set s_version [string range $s_version 5 end] + set s_version [string range [string trim $s_version] 5 end] regexp \ {International Ispell Version .* \(but really (Aspell .*?)\)$} \ $s_version _junk s_version From ed70e4d7db69a66d833d1efe56606213bab807e1 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 26 Sep 2008 07:44:40 -0700 Subject: [PATCH 002/100] git-gui: Show/hide "Sign Off" based on nocommitmsg option If citool --nocommit is invoked we hide the Sign Off features, as the commit message area is not editable. But we really want the selection tied to the message area's editing ability. Suggested-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 4085e8fea5..09ce4101f1 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2413,7 +2413,7 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} { .mbar.commit add separator - if {![is_enabled nocommit]} { + if {![is_enabled nocommitmsg]} { .mbar.commit add command -label [mc "Sign Off"] \ -command do_signoff \ -accelerator $M1T-S @@ -2743,7 +2743,7 @@ pack .vpane.lower.commarea.buttons.incall -side top -fill x lappend disable_on_lock \ {.vpane.lower.commarea.buttons.incall conf -state} -if {![is_enabled nocommit]} { +if {![is_enabled nocommitmsg]} { button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \ -command do_signoff pack .vpane.lower.commarea.buttons.signoff -side top -fill x From d3bcf55d675a255c00d2743c00978e1f42c93572 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Wed, 24 Sep 2008 21:08:01 +0200 Subject: [PATCH 003/100] git-gui: Do not automatically stage file after merge tool finishes If a merge tool was invoked on a conflicted file and the tool completed, then the conflicted file was staged automatically. However, the fact that the user closed the merge tool cannot be understood as the unequivocal sign that the conflict was completely resolved. For example, the user could have decided to postpone the resolution of the conflict, or could have accidentally closed the tool. We better leave the file unstaged and let the user stage it explicitly. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- lib/mergetool.tcl | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/mergetool.tcl b/lib/mergetool.tcl index 6ab5701d93..8d1ee5b11d 100644 --- a/lib/mergetool.tcl +++ b/lib/mergetool.tcl @@ -375,14 +375,6 @@ proc merge_tool_finish {fd} { } } - # Check the modification time of the target file - if {!$failed && [file mtime $mtool_target] eq $mtool_mtime} { - if {[ask_popup [mc "File %s unchanged, still accept as resolved?" \ - [short_path $mtool_target]]] ne {yes}} { - set failed 1 - } - } - # Finish if {$failed} { file rename -force -- $backup $mtool_target @@ -395,6 +387,6 @@ proc merge_tool_finish {fd} { delete_temp_files $mtool_tmpfiles - merge_add_resolution $mtool_target + reshow_diff } } From 0aea2842d9d556afa4a3e8120e465479ad7368ca Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 30 Sep 2008 12:12:16 +0400 Subject: [PATCH 004/100] git-gui: Make Ctrl-T safe to use for conflicting files. A previous patch added a check for conflict markers, which is done when the file is about to be staged due to a click on the icon. However, pressing Ctrl-T still immediately stages the file without confirmation. This patch fixes it. The check requires a loaded diff, so staging multiple files at once won't work if they are unmerged. Signed-off-by: Alexander Gavrilov Signed-off-by: Shawn O. Pearce --- git-gui.sh | 4 +++- lib/index.tcl | 11 +++++++++-- lib/merge.tcl | 1 + lib/mergetool.tcl | 7 ++++--- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 09ce4101f1..4d52f02dce 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2090,7 +2090,9 @@ proc toggle_or_diff {w x y} { if {$col == 0 && $y > 1} { # Conflicts need special handling if {[string first {U} $state] >= 0} { - merge_stage_workdir $path $w $lno + # $w must always be $ui_workdir, but... + if {$w ne $ui_workdir} { set lno {} } + merge_stage_workdir $path $lno return } diff --git a/lib/index.tcl b/lib/index.tcl index b045219a1c..d33896a0ce 100644 --- a/lib/index.tcl +++ b/lib/index.tcl @@ -298,11 +298,18 @@ proc add_helper {txt paths} { set after {} foreach path $paths { switch -glob -- [lindex $file_states($path) 0] { + _U - + U? { + if {$path eq $current_diff_path} { + unlock_index + merge_stage_workdir $path + return + } + } _O - ?M - ?D - - ?T - - U? { + ?T { lappend pathList $path if {$path eq $current_diff_path} { set after {reshow_diff;} diff --git a/lib/merge.tcl b/lib/merge.tcl index 5c01875b05..ac4c7ded14 100644 --- a/lib/merge.tcl +++ b/lib/merge.tcl @@ -40,6 +40,7 @@ The rescan will be automatically started now. _O { continue; # and pray it works! } + _U U? { error_popup [mc "You are in the middle of a conflicted merge. diff --git a/lib/mergetool.tcl b/lib/mergetool.tcl index 8d1ee5b11d..eb2b4b56a4 100644 --- a/lib/mergetool.tcl +++ b/lib/mergetool.tcl @@ -23,13 +23,14 @@ This operation can be undone only by restarting the merge." \ } } -proc merge_stage_workdir {path w lno} { +proc merge_stage_workdir {path {lno {}}} { global current_diff_path diff_active + global current_diff_side ui_workdir if {$diff_active} return - if {$path ne $current_diff_path} { - show_diff $path $w $lno {} [list do_merge_stage_workdir $path] + if {$path ne $current_diff_path || $ui_workdir ne $current_diff_side} { + show_diff $path $ui_workdir $lno {} [list do_merge_stage_workdir $path] } else { do_merge_stage_workdir $path } From 34785f8ccabb12f684352902b7f201135b9ccd51 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 30 Sep 2008 08:39:29 +0200 Subject: [PATCH 005/100] git-gui: Remove space from the end of aspell's reply before processing When git gui processes a reply from aspell it explicitly ignores an empty line. The Windows version of aspell, however, terminates lines with CRLF, but TCL's 'gets' does not remove CR, hence, a "visibly" empty line was not actually recognized as empty. With this change we explicitly trim off whitespace before the line is further processed. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- lib/spellcheck.tcl | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/spellcheck.tcl b/lib/spellcheck.tcl index a479b2f285..e6120303e9 100644 --- a/lib/spellcheck.tcl +++ b/lib/spellcheck.tcl @@ -314,6 +314,7 @@ method _run {} { method _read {} { while {[gets $s_fd line] >= 0} { set lineno [lindex $s_pending 0 0] + set line [string trim $line] if {$s_clear} { $w_text tag remove misspelled "$lineno.0" "$lineno.end" From 3c1c2a00b2cba9dfeee6e2a3c44a0ad52fcff4a8 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Wed, 24 Sep 2008 22:43:59 +0200 Subject: [PATCH 006/100] git-gui: Clarify the Remote -> Delete... action Currently, it was not really clear what all does this perform. We rename "Delete..." to "Delete Branch..." (since this does not delete the remote as a whole) and relabel the window from "Delete Remote Branch" to "Delete Branch Remotely" (since the action also involves pushing the delete out). Signed-off-by: Petr Baudis Signed-off-by: Shawn O. Pearce --- git-gui.sh | 2 +- lib/remote_branch_delete.tcl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 4d52f02dce..078636f23f 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2453,7 +2453,7 @@ if {[is_enabled transport]} { -command do_push_anywhere \ -accelerator $M1T-P .mbar.remote add command \ - -label [mc "Delete..."] \ + -label [mc "Delete Branch..."] \ -command remote_branch_delete::dialog } diff --git a/lib/remote_branch_delete.tcl b/lib/remote_branch_delete.tcl index c7b8148698..fbcfb27157 100644 --- a/lib/remote_branch_delete.tcl +++ b/lib/remote_branch_delete.tcl @@ -26,12 +26,12 @@ constructor dialog {} { global all_remotes M1B make_toplevel top w - wm title $top [append "[appname] ([reponame]): " [mc "Delete Remote Branch"]] + wm title $top [append "[appname] ([reponame]): " [mc "Delete Branch Remotely"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - label $w.header -text [mc "Delete Remote Branch"] -font font_uibold + label $w.header -text [mc "Delete Branch Remotely"] -font font_uibold pack $w.header -side top -fill x frame $w.buttons From 8329bd07250e0b26b76105d299b6153a68c3c404 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Wed, 24 Sep 2008 22:44:00 +0200 Subject: [PATCH 007/100] git-gui: Squash populate_{push,fetch}_menu to populate_remotes_menu The meat of the routines is now separated to add_fetch_entry() and add_push_entry(). This refactoring will allow easy implementation of adding individual remotes later. Signed-off-by: Petr Baudis Signed-off-by: Shawn O. Pearce --- git-gui.sh | 3 +- lib/remote.tcl | 143 +++++++++++++++++++++++++------------------------ 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 078636f23f..ecc3b630ed 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3263,8 +3263,7 @@ if {[is_enabled transport]} { load_all_remotes set n [.mbar.remote index end] - populate_push_menu - populate_fetch_menu + populate_remotes_menu set n [expr {[.mbar.remote index end] - $n}] if {$n > 0} { if {[.mbar.remote type 0] eq "tearoff"} { incr n } diff --git a/lib/remote.tcl b/lib/remote.tcl index 0e86ddac09..d97c851110 100644 --- a/lib/remote.tcl +++ b/lib/remote.tcl @@ -132,91 +132,92 @@ proc load_all_remotes {} { set all_remotes [lsort -unique $all_remotes] } -proc populate_fetch_menu {} { - global all_remotes repo_config - +proc add_fetch_entry {r} { + global repo_config set remote_m .mbar.remote set fetch_m $remote_m.fetch set prune_m $remote_m.prune - - foreach r $all_remotes { - set enable 0 - if {![catch {set a $repo_config(remote.$r.url)}]} { - if {![catch {set a $repo_config(remote.$r.fetch)}]} { - set enable 1 - } - } else { - catch { - set fd [open [gitdir remotes $r] r] - while {[gets $fd n] >= 0} { - if {[regexp {^Pull:[ \t]*([^:]+):} $n]} { - set enable 1 - break - } + set enable 0 + if {![catch {set a $repo_config(remote.$r.url)}]} { + if {![catch {set a $repo_config(remote.$r.fetch)}]} { + set enable 1 + } + } else { + catch { + set fd [open [gitdir remotes $r] r] + while {[gets $fd n] >= 0} { + if {[regexp {^Pull:[ \t]*([^:]+):} $n]} { + set enable 1 + break } - close $fd } + close $fd + } + } + + if {$enable} { + if {![winfo exists $fetch_m]} { + menu $prune_m + $remote_m insert 0 cascade \ + -label [mc "Prune from"] \ + -menu $prune_m + + menu $fetch_m + $remote_m insert 0 cascade \ + -label [mc "Fetch from"] \ + -menu $fetch_m } - if {$enable} { - if {![winfo exists $fetch_m]} { - menu $prune_m - $remote_m insert 0 cascade \ - -label [mc "Prune from"] \ - -menu $prune_m - - menu $fetch_m - $remote_m insert 0 cascade \ - -label [mc "Fetch from"] \ - -menu $fetch_m - } - - $fetch_m add command \ - -label $r \ - -command [list fetch_from $r] - $prune_m add command \ - -label $r \ - -command [list prune_from $r] - } + $fetch_m add command \ + -label $r \ + -command [list fetch_from $r] + $prune_m add command \ + -label $r \ + -command [list prune_from $r] } } -proc populate_push_menu {} { - global all_remotes repo_config - +proc add_push_entry {r} { + global repo_config set remote_m .mbar.remote set push_m $remote_m.push - - foreach r $all_remotes { - set enable 0 - if {![catch {set a $repo_config(remote.$r.url)}]} { - if {![catch {set a $repo_config(remote.$r.push)}]} { - set enable 1 - } - } else { - catch { - set fd [open [gitdir remotes $r] r] - while {[gets $fd n] >= 0} { - if {[regexp {^Push:[ \t]*([^:]+):} $n]} { - set enable 1 - break - } - } - close $fd - } + set enable 0 + if {![catch {set a $repo_config(remote.$r.url)}]} { + if {![catch {set a $repo_config(remote.$r.push)}]} { + set enable 1 } - - if {$enable} { - if {![winfo exists $push_m]} { - menu $push_m - $remote_m insert 0 cascade \ - -label [mc "Push to"] \ - -menu $push_m + } else { + catch { + set fd [open [gitdir remotes $r] r] + while {[gets $fd n] >= 0} { + if {[regexp {^Push:[ \t]*([^:]+):} $n]} { + set enable 1 + break + } } - - $push_m add command \ - -label $r \ - -command [list push_to $r] + close $fd } } + + if {$enable} { + if {![winfo exists $push_m]} { + menu $push_m + $remote_m insert 0 cascade \ + -label [mc "Push to"] \ + -menu $push_m + } + + $push_m add command \ + -label $r \ + -command [list push_to $r] + } +} + +proc populate_remotes_menu {} { + global all_remotes + + foreach r $all_remotes { + add_fetch_entry $r + add_push_entry $r + } } From ba6485e05d43abf66e6b651a41b1ddc511b0e5eb Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Wed, 24 Sep 2008 22:44:01 +0200 Subject: [PATCH 008/100] git-gui: Add support for adding remotes When a remote is being added, it can also be automatically either fetched or initialized and pushed; this patch adds capability for initializing of local and ssh repositories. This also of course leaves a lot of space for further customization features, like individually turning the initialization phase on/off or tuning attributes of the remote repository; I consider that out of scope of this patch, however. Signed-off-by: Petr Baudis Signed-off-by: Shawn O. Pearce --- git-gui.sh | 4 + lib/remote.tcl | 15 ++++ lib/remote_add.tcl | 190 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 lib/remote_add.tcl diff --git a/git-gui.sh b/git-gui.sh index ecc3b630ed..ae9832c879 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2448,6 +2448,10 @@ if {[is_enabled branch]} { if {[is_enabled transport]} { menu .mbar.remote + .mbar.remote add command \ + -label [mc "Add..."] \ + -command remote_add::dialog \ + -accelerator $M1T-A .mbar.remote add command \ -label [mc "Push..."] \ -command do_push_anywhere \ diff --git a/lib/remote.tcl b/lib/remote.tcl index d97c851110..643f0bc543 100644 --- a/lib/remote.tcl +++ b/lib/remote.tcl @@ -221,3 +221,18 @@ proc populate_remotes_menu {} { add_push_entry $r } } + +proc add_single_remote {name location} { + global all_remotes repo_config + lappend all_remotes $name + + git remote add $name $location + + # XXX: Better re-read the config so that we will never get out + # of sync with git remote implementation? + set repo_config(remote.$name.url) $location + set repo_config(remote.$name.fetch) "+refs/heads/*:refs/remotes/$name/*" + + add_fetch_entry $name + add_push_entry $name +} diff --git a/lib/remote_add.tcl b/lib/remote_add.tcl new file mode 100644 index 0000000000..89e88eeb49 --- /dev/null +++ b/lib/remote_add.tcl @@ -0,0 +1,190 @@ +# git-gui remote adding support +# Copyright (C) 2008 Petr Baudis + +class remote_add { + +field w ; # widget path +field w_name ; # new remote name widget +field w_loc ; # new remote location widget + +field name {}; # name of the remote the user has chosen +field location {}; # location of the remote the user has chosen + +field opt_action fetch; # action to do after registering the remote locally + +constructor dialog {} { + global repo_config + + make_toplevel top w + wm title $top [append "[appname] ([reponame]): " [mc "Add Remote"]] + if {$top ne {.}} { + wm geometry $top "+[winfo rootx .]+[winfo rooty .]" + } + + label $w.header -text [mc "Add New Remote"] -font font_uibold + pack $w.header -side top -fill x + + frame $w.buttons + button $w.buttons.create -text [mc Add] \ + -default active \ + -command [cb _add] + pack $w.buttons.create -side right + button $w.buttons.cancel -text [mc Cancel] \ + -command [list destroy $w] + pack $w.buttons.cancel -side right -padx 5 + pack $w.buttons -side bottom -fill x -pady 10 -padx 10 + + labelframe $w.desc -text [mc "Remote Details"] + + label $w.desc.name_l -text [mc "Name:"] + set w_name $w.desc.name_t + entry $w_name \ + -borderwidth 1 \ + -relief sunken \ + -width 40 \ + -textvariable @name \ + -validate key \ + -validatecommand [cb _validate_name %d %S] + grid $w.desc.name_l $w_name -sticky we -padx {0 5} + + label $w.desc.loc_l -text [mc "Location:"] + set w_loc $w.desc.loc_t + entry $w_loc \ + -borderwidth 1 \ + -relief sunken \ + -width 40 \ + -textvariable @location + grid $w.desc.loc_l $w_loc -sticky we -padx {0 5} + + grid columnconfigure $w.desc 1 -weight 1 + pack $w.desc -anchor nw -fill x -pady 5 -padx 5 + + labelframe $w.action -text [mc "Further Action"] + + radiobutton $w.action.fetch \ + -text [mc "Fetch Immediately"] \ + -value fetch \ + -variable @opt_action + pack $w.action.fetch -anchor nw + + radiobutton $w.action.push \ + -text [mc "Initialize Remote Repository and Push"] \ + -value push \ + -variable @opt_action + pack $w.action.push -anchor nw + + radiobutton $w.action.none \ + -text [mc "Do Nothing Else Now"] \ + -value none \ + -variable @opt_action + pack $w.action.none -anchor nw + + grid columnconfigure $w.action 1 -weight 1 + pack $w.action -anchor nw -fill x -pady 5 -padx 5 + + bind $w [cb _visible] + bind $w [list destroy $w] + bind $w [cb _add]\;break + tkwait window $w +} + +method _add {} { + global repo_config env + global M1B + + if {$name eq {}} { + tk_messageBox \ + -icon error \ + -type ok \ + -title [wm title $w] \ + -parent $w \ + -message [mc "Please supply a remote name."] + focus $w_name + return + } + + # XXX: We abuse check-ref-format here, but + # that should be ok. + if {[catch {git check-ref-format "remotes/$name"}]} { + tk_messageBox \ + -icon error \ + -type ok \ + -title [wm title $w] \ + -parent $w \ + -message [mc "'%s' is not an acceptable remote name." $name] + focus $w_name + return + } + + if {[catch {add_single_remote $name $location}]} { + tk_messageBox \ + -icon error \ + -type ok \ + -title [wm title $w] \ + -parent $w \ + -message [mc "Failed to add remote '%s' of location '%s'." $name $location] + focus $w_name + return + } + + switch -- $opt_action { + fetch { + set c [console::new \ + [mc "fetch %s" $remote] \ + [mc "Fetching the %s" $remote]] + console::exec $c [list git fetch --all $name] + } + push { + set cmds [list] + + # Parse the location + if { [regexp {(?:git\+)?ssh://([^/]+)(/.+)} $location xx host path] + || [regexp {([^:][^:]+):(.+)} $location xx host path]} { + set ssh ssh + if {[info exists env(GIT_SSH)]} { + set ssh $env(GIT_SSH) + } + lappend cmds [list exec $ssh $host git --git-dir=$path init --bare] + } elseif { ! [regexp {://} $location xx] } { + lappend cmds [list exec git --git-dir=$location init --bare] + } else { + tk_messageBox \ + -icon error \ + -type ok \ + -title [wm title $w] \ + -parent $w \ + -message [mc "Do not know how to initialize repository at location '%s'." $location] + destroy $w + return + } + + set c [console::new \ + [mc "push %s" $name] \ + [mc "Setting up the %s (at %s)" $name $location]] + + lappend cmds [list exec git push -v --all $name] + console::chain $c $cmds + } + none { + } + } + + destroy $w +} + +method _validate_name {d S} { + if {$d == 1} { + if {[regexp {[~^:?*\[\0- ]} $S]} { + return 0 + } + } + return 1 +} + +method _visible {} { + grab $w + $w_name icursor end + focus $w_name +} + +} From 0d4044123cb081bd690dece6505ffbcb8476e7ef Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Wed, 24 Sep 2008 22:44:02 +0200 Subject: [PATCH 009/100] git-gui: Add support for removing remotes We introduce new submenu Remote -> Remove Remote, allowing to remove remotes. In the future, we might consider a confirmation popup to avoid misclicks, but removing a remote is not very lossy operation. Signed-off-by: Petr Baudis Signed-off-by: Shawn O. Pearce --- lib/remote.tcl | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/lib/remote.tcl b/lib/remote.tcl index 643f0bc543..1852247358 100644 --- a/lib/remote.tcl +++ b/lib/remote.tcl @@ -137,6 +137,7 @@ proc add_fetch_entry {r} { set remote_m .mbar.remote set fetch_m $remote_m.fetch set prune_m $remote_m.prune + set remove_m $remote_m.remove set enable 0 if {![catch {set a $repo_config(remote.$r.url)}]} { if {![catch {set a $repo_config(remote.$r.fetch)}]} { @@ -157,6 +158,11 @@ proc add_fetch_entry {r} { if {$enable} { if {![winfo exists $fetch_m]} { + menu $remove_m + $remote_m insert 0 cascade \ + -label [mc "Remove Remote"] \ + -menu $remove_m + menu $prune_m $remote_m insert 0 cascade \ -label [mc "Prune from"] \ @@ -174,6 +180,9 @@ proc add_fetch_entry {r} { $prune_m add command \ -label $r \ -command [list prune_from $r] + $remove_m add command \ + -label $r \ + -command [list remove_remote $r] } } @@ -236,3 +245,31 @@ proc add_single_remote {name location} { add_fetch_entry $name add_push_entry $name } + +proc delete_from_menu {menu name} { + if {[winfo exists $menu]} { + $menu delete $name + } +} + +proc remove_remote {name} { + global all_remotes repo_config + + git remote rm $name + + catch { + # Missing values are ok + unset repo_config(remote.$name.url) + unset repo_config(remote.$name.fetch) + unset repo_config(remote.$name.push) + } + + set i [lsearch -exact all_remotes $name] + lreplace all_remotes $i $i + + set remote_m .mbar.remote + delete_from_menu $remote_m.fetch $name + delete_from_menu $remote_m.prune $name + delete_from_menu $remote_m.remove $name + delete_from_menu $remote_m.push $name +} From adcbd431e7ae3c6356c50fa2559ac06ddb970008 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Wed, 24 Sep 2008 22:44:03 +0200 Subject: [PATCH 010/100] git-gui: mkdir -p when initializing new remote repository This allows the user to create repositories with arbitrary paths on the server. The downside is that errorneously typed paths are not caught but instead created remotely; YMMV. Signed-off-by: Petr Baudis Signed-off-by: Shawn O. Pearce --- lib/remote_add.tcl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/remote_add.tcl b/lib/remote_add.tcl index 89e88eeb49..8e3ad166b7 100644 --- a/lib/remote_add.tcl +++ b/lib/remote_add.tcl @@ -144,8 +144,9 @@ method _add {} { if {[info exists env(GIT_SSH)]} { set ssh $env(GIT_SSH) } - lappend cmds [list exec $ssh $host git --git-dir=$path init --bare] + lappend cmds [list exec $ssh $host mkdir -p $location && git --git-dir=$path init --bare] } elseif { ! [regexp {://} $location xx] } { + lappend cmds [list exec mkdir -p $location] lappend cmds [list exec git --git-dir=$location init --bare] } else { tk_messageBox \ From 2db21e709a1345d84178e53079e861232c243bd1 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Wed, 24 Sep 2008 23:57:16 +0200 Subject: [PATCH 011/100] git-gui: Use git web--browser for web browsing This patch removes git-gui specific webbrowser guessing and instead relies on git web--browser to do the right thing, removing unnecessary code duplication. New function start_browser encapsulates the browser execution, for usage from other parts of code. This will also make git-gui show the documentation menu item even in cases it might not be able to start up a browser, these cases should be however only very rare. Signed-off-by: Petr Baudis Signed-off-by: Shawn O. Pearce --- git-gui.sh | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index ae9832c879..fffb1e9967 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2493,8 +2493,7 @@ if {![is_MacOSX]} { -command do_about } -set browser {} -catch {set browser $repo_config(instaweb.browser)} + set doc_path [file dirname [gitexec]] set doc_path [file join $doc_path Documentation index.html] @@ -2502,34 +2501,19 @@ if {[is_Cygwin]} { set doc_path [exec cygpath --mixed $doc_path] } -if {$browser eq {}} { - if {[is_MacOSX]} { - set browser open - } elseif {[is_Cygwin]} { - set program_files [file dirname [exec cygpath --windir]] - set program_files [file join $program_files {Program Files}] - set firefox [file join $program_files {Mozilla Firefox} firefox.exe] - set ie [file join $program_files {Internet Explorer} IEXPLORE.EXE] - if {[file exists $firefox]} { - set browser $firefox - } elseif {[file exists $ie]} { - set browser $ie - } - unset program_files firefox ie - } -} - if {[file isfile $doc_path]} { set doc_url "file:$doc_path" } else { set doc_url {http://www.kernel.org/pub/software/scm/git/docs/} } -if {$browser ne {}} { - .mbar.help add command -label [mc "Online Documentation"] \ - -command [list exec $browser $doc_url &] +proc start_browser {url} { + git "web--browse" $url } -unset browser doc_path doc_url + +.mbar.help add command -label [mc "Online Documentation"] \ + -command [list start_browser $doc_url] +unset doc_path doc_url # -- Standard bindings # From afd5424085151990b5ac87da09e5cea76809d7c0 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Thu, 25 Sep 2008 00:05:53 +0200 Subject: [PATCH 012/100] git-gui: Add Explore Working Copy to the Repository menu Especially when cloning is finished, the Git GUI window pops up, but there is not really much one can do within it - there needs to be a way to easily start exploring and working with the new working copy using the standard system interface: explorer.exe on Windows, open on MacOS/X and xdg-open as a fallback (all modern Linux desktops). This might be also a post-clone option instead (possibly opening the window automagically) but I believe that this might be useful also in other situations, e.g. you don't have to keep the working copy window around if you work in multiple repositories. This operation will not make sense on bare repositories. Signed-off-by: Petr Baudis Signed-off-by: Shawn O. Pearce --- git-gui.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index fffb1e9967..c4c66c0d87 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1869,6 +1869,19 @@ proc do_gitk {revs} { } } +proc do_explore {} { + set explorer {} + if {[is_Cygwin] || [is_Windows]} { + set explorer "explorer.exe" + } elseif {[is_MacOSX]} { + set explorer "open" + } else { + # freedesktop.org-conforming system is our best shot + set explorer "xdg-open" + } + eval exec $explorer [file dirname [gitdir]] & +} + set is_quitting 0 set ret_code 1 @@ -2220,6 +2233,11 @@ if {[is_enabled transport]} { # menu .mbar.repository +.mbar.repository add command \ + -label [mc "Explore Working Copy"] \ + -command {do_explore} +.mbar.repository add separator + .mbar.repository add command \ -label [mc "Browse Current Branch's Files"] \ -command {browser::new $current_branch} From bb4812bc0ae6e14b422ce3c45504965b523fdf84 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Thu, 25 Sep 2008 00:07:02 +0200 Subject: [PATCH 013/100] git-gui: gui.autoexplore makes explorer to pop up automatically after picking Especially for Windows users used to work with the Windows Explorer, it is very useful when after picking a repository (either opening a local one or initializing/cloning a new one) in the "intro" window, the explorer view of the working copy pops up along the standard Git GUI window, so that the users can, well, actually work with the repository. Signed-off-by: Petr Baudis Signed-off-by: Shawn O. Pearce --- git-gui.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index c4c66c0d87..79a108dbaf 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -995,6 +995,7 @@ citool { ## ## repository setup +set picked 0 if {[catch { set _gitdir $env(GIT_DIR) set _prefix {} @@ -1006,6 +1007,7 @@ if {[catch { load_config 1 apply_config choose_repository::pick + set picked 1 } if {![file isdirectory $_gitdir] && [is_Cygwin]} { catch {set _gitdir [exec cygpath --windows $_gitdir]} @@ -3376,3 +3378,6 @@ if {[is_enabled multicommit]} { if {[is_enabled retcode]} { bind . {+terminate_me %W} } +if {$picked && [is_config_true gui.autoexplore]} { + do_explore +} From 4259568d722d0a46993ad945ffdba0f084448b37 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Thu, 25 Sep 2008 00:12:50 +0200 Subject: [PATCH 014/100] git-gui: Avoid using the term URL when specifying repositories Instead, 'Location' is used to label such inputs; in the Clone dialog, 'Source' and 'Target' are also introduced to further clarify the situation. The intent is to increase GUI consistency in the case location templates (upcoming) are used - then, other locators than URL may be used. Signed-off-by: Petr Baudis Signed-off-by: Shawn O. Pearce --- lib/choose_repository.tcl | 4 ++-- lib/remote_branch_delete.tcl | 2 +- lib/transport.tcl | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/choose_repository.tcl b/lib/choose_repository.tcl index 3180786158..f54d88af16 100644 --- a/lib/choose_repository.tcl +++ b/lib/choose_repository.tcl @@ -463,7 +463,7 @@ method _do_clone {} { frame $w_body.args pack $args -fill both - label $args.origin_l -text [mc "URL:"] + label $args.origin_l -text [mc "Source Location:"] entry $args.origin_t \ -textvariable @origin_url \ -font font_diff \ @@ -473,7 +473,7 @@ method _do_clone {} { -command [cb _open_origin] grid $args.origin_l $args.origin_t $args.origin_b -sticky ew - label $args.where_l -text [mc "Directory:"] + label $args.where_l -text [mc "Target Directory:"] entry $args.where_t \ -textvariable @local_path \ -font font_diff \ diff --git a/lib/remote_branch_delete.tcl b/lib/remote_branch_delete.tcl index fbcfb27157..89eb0f70f2 100644 --- a/lib/remote_branch_delete.tcl +++ b/lib/remote_branch_delete.tcl @@ -63,7 +63,7 @@ constructor dialog {} { set urltype url } radiobutton $w.dest.url_r \ - -text [mc "Arbitrary URL:"] \ + -text [mc "Arbitrary Location:"] \ -value url \ -variable @urltype entry $w.dest.url_t \ diff --git a/lib/transport.tcl b/lib/transport.tcl index 8e6a9d0a60..e419d7810a 100644 --- a/lib/transport.tcl +++ b/lib/transport.tcl @@ -135,7 +135,7 @@ proc do_push_anywhere {} { set push_urltype url } radiobutton $w.dest.url_r \ - -text [mc "Arbitrary URL:"] \ + -text [mc "Arbitrary Location:"] \ -value url \ -variable push_urltype entry $w.dest.url_t \ From 902e2bb5b7473765acc388d051f5c1c7d42015d0 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Thu, 25 Sep 2008 00:12:51 +0200 Subject: [PATCH 015/100] git-gui: Make input boxes in init/clone/open dialogs consistent Before, the input boxes would not be sunken and would have larger border, which is inconsistent with the rest of the inputboxes for repository locations in the git-gui UI. Signed-off-by: Petr Baudis Signed-off-by: Shawn O. Pearce --- lib/choose_repository.tcl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/choose_repository.tcl b/lib/choose_repository.tcl index f54d88af16..909131689e 100644 --- a/lib/choose_repository.tcl +++ b/lib/choose_repository.tcl @@ -381,7 +381,8 @@ method _do_new {} { label $w_body.where.l -text [mc "Directory:"] entry $w_body.where.t \ -textvariable @local_path \ - -font font_diff \ + -borderwidth 1 \ + -relief sunken \ -width 50 button $w_body.where.b \ -text [mc "Browse"] \ @@ -466,7 +467,8 @@ method _do_clone {} { label $args.origin_l -text [mc "Source Location:"] entry $args.origin_t \ -textvariable @origin_url \ - -font font_diff \ + -borderwidth 1 \ + -relief sunken \ -width 50 button $args.origin_b \ -text [mc "Browse"] \ @@ -476,7 +478,8 @@ method _do_clone {} { label $args.where_l -text [mc "Target Directory:"] entry $args.where_t \ -textvariable @local_path \ - -font font_diff \ + -borderwidth 1 \ + -relief sunken \ -width 50 button $args.where_b \ -text [mc "Browse"] \ @@ -979,7 +982,8 @@ method _do_open {} { label $w_body.where.l -text [mc "Repository:"] entry $w_body.where.t \ -textvariable @local_path \ - -font font_diff \ + -borderwidth 1 \ + -relief sunken \ -width 50 button $w_body.where.b \ -text [mc "Browse"] \ From 2243ffcc6a445d1953297949e8e944fdc3df6ed7 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Thu, 25 Sep 2008 01:32:47 +0200 Subject: [PATCH 016/100] git-gui: Fix removing non-pushable remotes Git-gui does not add most of the remotes to the 'push' menu since they are missing the "Push" line in their remotespec. In that case, removing the remote would end up with an error. Signed-off-by: Petr Baudis Signed-off-by: Shawn O. Pearce --- lib/remote.tcl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/remote.tcl b/lib/remote.tcl index 1852247358..b92b429cf7 100644 --- a/lib/remote.tcl +++ b/lib/remote.tcl @@ -271,5 +271,6 @@ proc remove_remote {name} { delete_from_menu $remote_m.fetch $name delete_from_menu $remote_m.prune $name delete_from_menu $remote_m.remove $name - delete_from_menu $remote_m.push $name + # Not all remotes are in the push menu + catch { delete_from_menu $remote_m.push $name } } From 0b32cab933e0d3d1e85d4cb86669e33ef478ef66 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Thu, 25 Sep 2008 01:39:13 +0200 Subject: [PATCH 017/100] git-gui: Fix fetching from remotes when adding them As you can see, this particular code branch did not see a lot of testing for some time now. Apologies for that. Signed-off-by: Petr Baudis Signed-off-by: Shawn O. Pearce --- lib/remote_add.tcl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/remote_add.tcl b/lib/remote_add.tcl index 8e3ad166b7..fb29422aa7 100644 --- a/lib/remote_add.tcl +++ b/lib/remote_add.tcl @@ -130,9 +130,9 @@ method _add {} { switch -- $opt_action { fetch { set c [console::new \ - [mc "fetch %s" $remote] \ - [mc "Fetching the %s" $remote]] - console::exec $c [list git fetch --all $name] + [mc "fetch %s" $name] \ + [mc "Fetching the %s" $name]] + console::exec $c [list git fetch $name] } push { set cmds [list] From a910898e86be79c580d1e643cdbee8a19a7cd691 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Wed, 8 Oct 2008 10:03:54 +0200 Subject: [PATCH 018/100] git-gui: Fix switch statement in lib/merge.tcl 0aea2842 (Make Ctrl-T safe to use for conflicting files) introduced a new case, but forgot the '-' to indicate that it shares the body with the subsequent case label. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- lib/merge.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/merge.tcl b/lib/merge.tcl index ac4c7ded14..283e4915e9 100644 --- a/lib/merge.tcl +++ b/lib/merge.tcl @@ -40,7 +40,7 @@ The rescan will be automatically started now. _O { continue; # and pray it works! } - _U + _U - U? { error_popup [mc "You are in the middle of a conflicted merge. From d4d992562e8c6d27ac584ff0907ff840030a98d9 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 3 Oct 2008 11:36:52 +0400 Subject: [PATCH 019/100] git-gui: Fix the blame window shape. On modern high-resolution monitors the blame viewer window is very high, yet too narrow. This patch makes it gravitate to a more sane resolution, which takes the font size into account. It also changes the default text view size to 80% of the window, and slightly modifies the border decorations for better appearance. Signed-off-by: Alexander Gavrilov Signed-off-by: Shawn O. Pearce --- lib/blame.tcl | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/blame.tcl b/lib/blame.tcl index eb61374d2d..221313c768 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -69,6 +69,8 @@ constructor new {i_commit i_path i_jump} { make_toplevel top w wm title $top [append "[appname] ([reponame]): " [mc "File Viewer"]] + set font_w [font measure font_diff "0"] + frame $w.header -background gold label $w.header.commit_l \ -text [mc "Commit:"] \ @@ -114,9 +116,9 @@ constructor new {i_commit i_path i_jump} { pack $w_path -fill x -side right pack $w.header.path_l -side right - panedwindow $w.file_pane -orient vertical - frame $w.file_pane.out - frame $w.file_pane.cm + panedwindow $w.file_pane -orient vertical -borderwidth 0 -sashwidth 3 + frame $w.file_pane.out -relief flat -borderwidth 1 + frame $w.file_pane.cm -relief sunken -borderwidth 1 $w.file_pane add $w.file_pane.out \ -sticky nsew \ -minsize 100 \ @@ -328,9 +330,14 @@ constructor new {i_commit i_path i_jump} { set req_w [winfo reqwidth $top] set req_h [winfo reqheight $top] - set scr_h [expr {[winfo screenheight $top] - 100}] - if {$req_w < 600} {set req_w 600} + set scr_w [expr {[winfo screenwidth $top] - 40}] + set scr_h [expr {[winfo screenheight $top] - 120}] + set opt_w [expr {$font_w * (80 + 5*3 + 3)}] + if {$req_w < $opt_w} {set req_w $opt_w} + if {$req_w > $scr_w} {set req_w $scr_w} + set opt_h [expr {$req_w*4/3}] if {$req_h < $scr_h} {set req_h $scr_h} + if {$req_h > $opt_h} {set req_h $opt_h} set g "${req_w}x${req_h}" wm geometry $top $g update @@ -338,7 +345,7 @@ constructor new {i_commit i_path i_jump} { set old_height [winfo height $w.file_pane] $w.file_pane sash place 0 \ [lindex [$w.file_pane sash coord 0] 0] \ - [expr {int($old_height * 0.70)}] + [expr {int($old_height * 0.80)}] bind $w.file_pane \ "if {{$w.file_pane} eq {%W}} {[cb _resize %h]}" From f10d5b064ae5c9bcd6e353211f128f5c4072ed4f Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 3 Oct 2008 11:36:53 +0400 Subject: [PATCH 020/100] git-gui: Add a search command to the blame viewer. One of the largest deficiencies in the blame viewer at the moment is the impossibility to search for a text string. This commit fixes it by adding a Firefox-like search panel to the viewer. The panel can be shown by pressing F7 or clicking a menu entry, and is hidden by pressing Esc. Find Next is available through the F3 key. Implementation is based on the gitk code, but heavily refactored. It now also supports case-insensitive searches, and uses the text box background color to signal success or failure of the search. Signed-off-by: Alexander Gavrilov Signed-off-by: Shawn O. Pearce --- git-gui.sh | 3 + lib/blame.tcl | 37 +++++++++- lib/search.tcl | 190 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 lib/search.tcl diff --git a/git-gui.sh b/git-gui.sh index 79a108dbaf..4f951399ff 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -591,6 +591,7 @@ bind . { if {[is_Windows]} { wm iconbitmap . -default $oguilib/git-gui.ico + set ::tk::AlwaysShowSelection 1 } ###################################################################### @@ -1067,6 +1068,8 @@ set selected_commit_type new set nullid "0000000000000000000000000000000000000000" set nullid2 "0000000000000000000000000000000000000001" +set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}] + ###################################################################### ## ## task management diff --git a/lib/blame.tcl b/lib/blame.tcl index 221313c768..a45784c057 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -21,9 +21,11 @@ field w_amov ; # text column: annotations + move tracking field w_asim ; # text column: annotations (simple computation) field w_file ; # text column: actual file data field w_cviewer ; # pane showing commit message +field finder ; # find mini-dialog frame field status ; # status mega-widget instance field old_height ; # last known height of $w.file_pane + # Tk UI colors # variable active_color #c0edc5 @@ -59,7 +61,7 @@ field tooltip_timer {} ; # Current timer event for our tooltip field tooltip_commit {} ; # Commit(s) in tooltip constructor new {i_commit i_path i_jump} { - global cursor_ptr + global cursor_ptr M1B M1T have_tk85 variable active_color variable group_colors @@ -199,6 +201,11 @@ constructor new {i_commit i_path i_jump} { -width 80 \ -xscrollcommand [list $w.file_pane.out.sbx set] \ -font font_diff + if {$have_tk85} { + $w_file configure -inactiveselectbackground darkblue + } + $w_file tag conf found \ + -background yellow set w_columns [list $w_amov $w_asim $w_line $w_file] @@ -219,6 +226,11 @@ constructor new {i_commit i_path i_jump} { -weight 1 grid rowconfigure $w.file_pane.out 0 -weight 1 + set finder [::searchbar::new \ + $w.file_pane.out.ff $w_file \ + -column [expr {[llength $w_columns] - 1}] \ + ] + set w_cviewer $w.file_pane.cm.t text $w_cviewer \ -background white \ @@ -259,6 +271,10 @@ constructor new {i_commit i_path i_jump} { -label [mc "Copy Commit"] \ -command [cb _copycommit] $w.ctxm add separator + $w.ctxm add command \ + -label [mc "Find Text..."] \ + -accelerator F7 \ + -command [list searchbar::show $finder] menu $w.ctxm.enc build_encoding_menu $w.ctxm.enc [cb _setencoding] $w.ctxm add cascade \ @@ -280,9 +296,15 @@ constructor new {i_commit i_path i_jump} { $i tag conf color$g -background [lindex $group_colors $g] } + if {$i eq $w_file} { + $w_file tag raise found + } + $i tag raise sel + $i conf -cursor $cursor_ptr - $i conf -yscrollcommand [list many2scrollbar \ - $w_columns yview $w.file_pane.out.sby] + $i conf -yscrollcommand \ + "[list ::searchbar::scrolled $finder] + [list many2scrollbar $w_columns yview $w.file_pane.out.sby]" bind $i " [cb _hide_tooltip] [cb _click $i @%x,%y] @@ -319,6 +341,11 @@ constructor new {i_commit i_path i_jump} { bind $w_cviewer "[list focus $w_file];break" bind $w_cviewer [list focus $w_cviewer] bind $w_file [list focus $w_file] + bind $top [list searchbar::show $finder] + bind $top [list searchbar::hide $finder] + bind $top [list searchbar::find_next $finder] + bind $top [list searchbar::find_prev $finder] + catch { bind $top [list searchbar::find_prev $finder] } grid configure $w.header -sticky ew grid configure $w.file_pane -sticky nsew @@ -873,6 +900,10 @@ method _showcommit {cur_w lno} { foreach i $w_columns { $i tag conf g$cmit -background $active_color $i tag raise g$cmit + if {$i eq $w_file} { + $w_file tag raise found + } + $i tag raise sel } set author_name {} diff --git a/lib/search.tcl b/lib/search.tcl new file mode 100644 index 0000000000..d292f20f66 --- /dev/null +++ b/lib/search.tcl @@ -0,0 +1,190 @@ +# incremental search panel +# based on code from gitk, Copyright (C) Paul Mackerras + +class searchbar { + +field w +field ctext + +field searchstring {} +field casesensitive 1 +field searchdirn -forwards + +field smarktop +field smarkbot + +constructor new {i_w i_text args} { + set w $i_w + set ctext $i_text + + frame $w + label $w.l -text [mc Find:] + button $w.bn -text [mc Next] -command [cb find_next] + button $w.bp -text [mc Prev] -command [cb find_prev] + checkbutton $w.cs -text [mc Case-Sensitive] \ + -variable ${__this}::casesensitive -command [cb _incrsearch] + entry $w.ent -textvariable ${__this}::searchstring -background lightgreen + pack $w.l -side left + pack $w.cs -side right + pack $w.bp -side right + pack $w.bn -side right + pack $w.ent -side left -expand 1 -fill x + + eval grid conf $w -sticky we $args + grid remove $w + + trace add variable searchstring write [cb _incrsearch_cb] + + bind $w [cb delete_this] + return $this +} + +method show {} { + if {![winfo ismapped $w]} { + grid $w + } + focus -force $w.ent +} + +method hide {} { + if {[winfo ismapped $w]} { + focus $ctext + grid remove $w + } +} + +method _get_new_anchor {} { + # use start of selection if it is visible, + # or the bounds of the visible area + set top [$ctext index @0,0] + set bottom [$ctext index @0,[winfo height $ctext]] + set sel [$ctext tag ranges sel] + if {$sel ne {}} { + set spos [lindex $sel 0] + if {[lindex $spos 0] >= [lindex $top 0] && + [lindex $spos 0] <= [lindex $bottom 0]} { + return $spos + } + } + if {$searchdirn eq "-forwards"} { + return $top + } else { + return $bottom + } +} + +method _get_wrap_anchor {dir} { + if {$dir eq "-forwards"} { + return 1.0 + } else { + return end + } +} + +method _do_search {start {mlenvar {}} {dir {}} {endbound {}}} { + set cmd [list $ctext search] + if {$mlenvar ne {}} { + upvar $mlenvar mlen + lappend cmd -count mlen + } + if {!$casesensitive} { + lappend cmd -nocase + } + if {$dir eq {}} { + set dir $searchdirn + } + lappend cmd $dir -- $searchstring + if {$endbound ne {}} { + set here [eval $cmd [list $start] [list $endbound]] + } else { + set here [eval $cmd [list $start]] + if {$here eq {}} { + set here [eval $cmd [_get_wrap_anchor $this $dir]] + } + } + return $here +} + +method _incrsearch_cb {name ix op} { + after idle [cb _incrsearch] +} + +method _incrsearch {} { + $ctext tag remove found 1.0 end + if {[catch {$ctext index anchor}]} { + $ctext mark set anchor [_get_new_anchor $this] + } + if {$searchstring ne {}} { + set here [_do_search $this anchor mlen] + if {$here ne {}} { + $ctext see $here + $ctext tag remove sel 1.0 end + $ctext tag add sel $here "$here + $mlen c" + $w.ent configure -background lightgreen + _set_marks $this 1 + } else { + $w.ent configure -background lightpink + } + } +} + +method find_prev {} { + find_next $this -backwards +} + +method find_next {{dir -forwards}} { + focus $w.ent + $w.ent icursor end + set searchdirn $dir + $ctext mark unset anchor + if {$searchstring ne {}} { + set start [_get_new_anchor $this] + if {$dir eq "-forwards"} { + set start "$start + 1c" + } + set match [_do_search $this $start mlen] + $ctext tag remove sel 1.0 end + if {$match ne {}} { + $ctext see $match + $ctext tag add sel $match "$match + $mlen c" + } + } +} + +method _mark_range {first last} { + set mend $first.0 + while {1} { + set match [_do_search $this $mend mlen -forwards $last.end] + if {$match eq {}} break + set mend "$match + $mlen c" + $ctext tag add found $match $mend + } +} + +method _set_marks {doall} { + set topline [lindex [split [$ctext index @0,0] .] 0] + set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0] + if {$doall || $botline < $smarktop || $topline > $smarkbot} { + # no overlap with previous + _mark_range $this $topline $botline + set smarktop $topline + set smarkbot $botline + } else { + if {$topline < $smarktop} { + _mark_range $this $topline [expr {$smarktop-1}] + set smarktop $topline + } + if {$botline > $smarkbot} { + _mark_range $this [expr {$smarkbot+1}] $botline + set smarkbot $botline + } + } +} + +method scrolled {} { + if {$searchstring ne {}} { + after idle [cb _set_marks 0] + } +} + +} \ No newline at end of file From 5c91cb5d0dd06aa5574234a3d651098393adb4fa Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 3 Oct 2008 11:36:54 +0400 Subject: [PATCH 021/100] git-gui: Fix the blame viewer destroy handler. It did not delete the object, which is not very good. Also, destroy may be fired up for subwindows, so we should check %W. Signed-off-by: Alexander Gavrilov Signed-off-by: Shawn O. Pearce --- lib/blame.tcl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/blame.tcl b/lib/blame.tcl index a45784c057..765d08c004 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -377,11 +377,18 @@ constructor new {i_commit i_path i_jump} { "if {{$w.file_pane} eq {%W}} {[cb _resize %h]}" wm protocol $top WM_DELETE_WINDOW "destroy $top" - bind $top [cb _kill] + bind $top [cb _handle_destroy %W] _load $this $i_jump } +method _handle_destroy {win} { + if {$win eq $w} { + _kill $this + delete_this + } +} + method _kill {} { if {$current_fd ne {}} { kill_file_process $current_fd From f2df8a5bfb6b766b0362a2cb545d5226a268bf37 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 3 Oct 2008 10:28:49 +0200 Subject: [PATCH 022/100] git-gui: Show a round number of bytes of large untracked text files If an untracked text file is selected, then its contents are displayed instead of a diff. If the file is large, then the following hint is inserted at the top: * Untracked file is 14774881 bytes. * Showing only first 131072 bytes. Why exactly 131072 bytes? With this patch it is 100000 bytes. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- lib/diff.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/diff.tcl b/lib/diff.tcl index abe502d971..6e08704dbb 100644 --- a/lib/diff.tcl +++ b/lib/diff.tcl @@ -164,7 +164,7 @@ proc show_other_diff {path w m cont_info} { # - Git won't give us the diff, there's nothing to compare to! # if {$m eq {_O}} { - set max_sz [expr {128 * 1024}] + set max_sz 100000 set type unknown if {[catch { set type [file type $path] From 7f15b0027392155f5b3260bb584ba37126232bf1 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 3 Oct 2008 13:13:42 +0200 Subject: [PATCH 023/100] git-gui: Mark-up strings in show_{other,unmerged}_diff() for localization Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- lib/diff.tcl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/diff.tcl b/lib/diff.tcl index 6e08704dbb..bdcbbf86e9 100644 --- a/lib/diff.tcl +++ b/lib/diff.tcl @@ -117,22 +117,22 @@ proc show_unmerged_diff {cont_info} { if {$merge_stages(2) eq {}} { set is_conflict_diff 1 lappend current_diff_queue \ - [list "LOCAL: deleted\nREMOTE:\n" d======= \ + [list [mc "LOCAL: deleted\nREMOTE:\n"] d======= \ [list ":1:$current_diff_path" ":3:$current_diff_path"]] } elseif {$merge_stages(3) eq {}} { set is_conflict_diff 1 lappend current_diff_queue \ - [list "REMOTE: deleted\nLOCAL:\n" d======= \ + [list [mc "REMOTE: deleted\nLOCAL:\n"] d======= \ [list ":1:$current_diff_path" ":2:$current_diff_path"]] } elseif {[lindex $merge_stages(1) 0] eq {120000} || [lindex $merge_stages(2) 0] eq {120000} || [lindex $merge_stages(3) 0] eq {120000}} { set is_conflict_diff 1 lappend current_diff_queue \ - [list "LOCAL:\n" d======= \ + [list [mc "LOCAL:\n"] d======= \ [list ":1:$current_diff_path" ":2:$current_diff_path"]] lappend current_diff_queue \ - [list "REMOTE:\n" d======= \ + [list [mc "REMOTE:\n"] d======= \ [list ":1:$current_diff_path" ":3:$current_diff_path"]] } else { start_show_diff $cont_info @@ -218,17 +218,17 @@ proc show_other_diff {path w m cont_info} { d_@ } else { if {$sz > $max_sz} { - $ui_diff insert end \ -"* Untracked file is $sz bytes. -* Showing only first $max_sz bytes. -" d_@ + $ui_diff insert end [mc \ +"* Untracked file is %d bytes. +* Showing only first %d bytes. +" $sz $max_sz] d_@ } $ui_diff insert end $content if {$sz > $max_sz} { - $ui_diff insert end " -* Untracked file clipped here by [appname]. + $ui_diff insert end [mc " +* Untracked file clipped here by %s. * To see the entire file, use an external editor. -" d_@ +" [appname]] d_@ } } $ui_diff conf -state disabled From eee0184da8457c1dbd4418f95d158917540da094 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 14 Oct 2008 21:27:12 -0700 Subject: [PATCH 024/100] Fix reading of cloud tags The projectroot path could have SP in it, in which case iterating over <$git_dir/ctags/*> does not correctly enumerate the cloud tags files at all. This can be observed by creating an empty t/trash directory and running t9500 test. The $projectroot ends with "trash directory.t9500-gitweb-/" and <$glob> would give "trash", which can be opened and reading from it immediately yields undef, which in turn gives an undef value warning to the standard error stream upon attempt to chomp it. Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 11168006cf..41b68668e8 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1805,7 +1805,10 @@ sub git_get_project_ctags { my $ctags = {}; $git_dir = "$projectroot/$path"; - foreach (<$git_dir/ctags/*>) { + unless (opendir D, "$git_dir/ctags") { + return $ctags; + } + foreach (grep { -f $_ } map { "$git_dir/ctags/$_" } readdir(D)) { open CT, $_ or next; my $val = ; chomp $val; @@ -1813,6 +1816,7 @@ sub git_get_project_ctags { my $ctag = $_; $ctag =~ s#.*/##; $ctags->{$ctag} = $val; } + closedir D; $ctags; } From 42f939e4735bc144e1767395e84cffb1bd805a1c Mon Sep 17 00:00:00 2001 From: Stephen Haberman Date: Wed, 15 Oct 2008 02:44:34 -0500 Subject: [PATCH 025/100] rebase-i-p: test to exclude commits from todo based on its parents The first case was based off a script from Avi Kivity . The second case includes a merge-of-a-merge to ensure both are included in todo. Signed-off-by: Stephen Haberman Signed-off-by: Junio C Hamano --- t/t3411-rebase-preserve-around-merges.sh | 135 +++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100755 t/t3411-rebase-preserve-around-merges.sh diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh new file mode 100755 index 0000000000..f9c549b3b8 --- /dev/null +++ b/t/t3411-rebase-preserve-around-merges.sh @@ -0,0 +1,135 @@ +#!/bin/sh +# +# Copyright (c) 2008 Stephen Haberman +# + +test_description='git rebase preserve merges + +This test runs git rebase with and tries to squash a commit from after a merge +to before the merge. +' +. ./test-lib.sh + +# Copy/paste from t3404-rebase-interactive.sh +echo "#!$SHELL_PATH" >fake-editor.sh +cat >> fake-editor.sh <<\EOF +case "$1" in +*/COMMIT_EDITMSG) + test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1" + test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1" + exit + ;; +esac +test -z "$EXPECT_COUNT" || + test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) || + exit +test -z "$FAKE_LINES" && exit +grep -v '^#' < "$1" > "$1".tmp +rm -f "$1" +cat "$1".tmp +action=pick +for line in $FAKE_LINES; do + case $line in + squash|edit) + action="$line";; + *) + echo sed -n "${line}s/^pick/$action/p" + sed -n "${line}p" < "$1".tmp + sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1" + action=pick;; + esac +done +EOF + +test_set_editor "$(pwd)/fake-editor.sh" +chmod a+x fake-editor.sh + +# set up two branches like this: +# +# A1 - B1 - D1 - E1 - F1 +# \ / +# -- C1 -- + +test_expect_success 'setup' ' + touch a && + touch b && + git add a && + git commit -m A1 && + git tag A1 + git add b && + git commit -m B1 && + git tag B1 && + git checkout -b branch && + touch c && + git add c && + git commit -m C1 && + git checkout master && + touch d && + git add d && + git commit -m D1 && + git merge branch && + touch f && + git add f && + git commit -m F1 && + git tag F1 +' + +# Should result in: +# +# A1 - B1 - D2 - E2 +# \ / +# -- C1 -- +# +test_expect_failure 'squash F1 into D1' ' + FAKE_LINES="1 squash 3 2" git rebase -i -p B1 && + test "$(git rev-parse HEAD^2)" = "$(git rev-parse branch)" && + test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" && + git tag E2 +' + +# Start with: +# +# A1 - B1 - D2 - E2 +# \ +# G1 ---- L1 ---- M1 +# \ / +# H1 -- J1 -- K1 +# \ / +# -- I1 -- +# +# And rebase G1..M1 onto E2 + +test_expect_failure 'rebase two levels of merge' ' + git checkout -b branch2 A1 && + touch g && + git add g && + git commit -m G1 && + git checkout -b branch3 && + touch h + git add h && + git commit -m H1 && + git checkout -b branch4 && + touch i && + git add i && + git commit -m I1 && + git tag I1 && + git checkout branch3 && + touch j && + git add j && + git commit -m J1 && + git merge I1 --no-commit && + git commit -m K1 && + git tag K1 && + git checkout branch2 && + touch l && + git add l && + git commit -m L1 && + git merge K1 --no-commit && + git commit -m M1 && + GIT_EDITOR=: git rebase -i -p E2 && + test "$(git rev-parse HEAD~3)" = "$(git rev-parse E2)" && + test "$(git rev-parse HEAD~2)" = "$(git rev-parse HEAD^2^2~2)" && + test "$(git rev-parse HEAD^2^1^1)" = "$(git rev-parse HEAD^2^2^1)" +' + +test_done From 72583e6c685a85b9354ee2310cec3d9240df3c0f Mon Sep 17 00:00:00 2001 From: Stephen Haberman Date: Wed, 15 Oct 2008 02:44:35 -0500 Subject: [PATCH 026/100] rebase-i-p: use HEAD for updating the ref instead of mapping OLDHEAD If OLDHEAD was reordered in the todo, and its mapped NEWHEAD was used to set the ref, commits reordered after OLDHEAD in the todo would should up as un-committed changes. Signed-off-by: Stephen Haberman Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 30e45237a2..c9681178f7 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -376,20 +376,7 @@ do_next () { HEADNAME=$(cat "$DOTEST"/head-name) && OLDHEAD=$(cat "$DOTEST"/head) && SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) && - if test -d "$REWRITTEN" - then - test -f "$DOTEST"/current-commit && - current_commit=$(cat "$DOTEST"/current-commit) && - git rev-parse HEAD > "$REWRITTEN"/$current_commit - if test -f "$REWRITTEN"/$OLDHEAD - then - NEWHEAD=$(cat "$REWRITTEN"/$OLDHEAD) - else - NEWHEAD=$OLDHEAD - fi - else - NEWHEAD=$(git rev-parse HEAD) - fi && + NEWHEAD=$(git rev-parse HEAD) && case $HEADNAME in refs/*) message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" && From bb645071645240a23ede4e93c9425252a0eaaefb Mon Sep 17 00:00:00 2001 From: Stephen Haberman Date: Wed, 15 Oct 2008 02:44:36 -0500 Subject: [PATCH 027/100] rebase-i-p: delay saving current-commit to REWRITTEN if squashing If the current-commit was dumped to REWRITTEN, but then we squash the next commit in to it, we have invalidated the HEAD was just written to REWRITTEN. Instead, append the squash hash to current-commit and save both of them the next time around. Signed-off-by: Stephen Haberman Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index c9681178f7..23cf7a5d68 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -170,13 +170,18 @@ pick_one_preserving_merges () { if test -f "$DOTEST"/current-commit then - current_commit=$(cat "$DOTEST"/current-commit) && - git rev-parse HEAD > "$REWRITTEN"/$current_commit && - rm "$DOTEST"/current-commit || - die "Cannot write current commit's replacement sha1" + if [ "$fast_forward" == "t" ] + then + cat "$DOTEST"/current-commit | while read current_commit + do + git rev-parse HEAD > "$REWRITTEN"/$current_commit + done + rm "$DOTEST"/current-commit || + die "Cannot write current commit's replacement sha1" + fi fi - echo $sha1 > "$DOTEST"/current-commit + echo $sha1 >> "$DOTEST"/current-commit # rewrite parents; if none were rewritten, we can fast-forward. new_parents= From a4f25e368247108e89e5ee1a02c2f1b6fc31c56f Mon Sep 17 00:00:00 2001 From: Stephen Haberman Date: Wed, 15 Oct 2008 02:44:37 -0500 Subject: [PATCH 028/100] rebase-i-p: fix 'no squashing merges' tripping up non-merges Also only check out the first parent if this commit if not a squash--if it is a squash, we want to explicitly ignore the parent and leave the wc as is, as cherry-pick will apply the squash on top of it. Signed-off-by: Stephen Haberman Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 23cf7a5d68..274251f697 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -219,15 +219,19 @@ pick_one_preserving_merges () { die "Cannot fast forward to $sha1" ;; f) - test "a$1" = a-n && die "Refusing to squash a merge: $sha1" - first_parent=$(expr "$new_parents" : ' \([^ ]*\)') - # detach HEAD to current parent - output git checkout $first_parent 2> /dev/null || - die "Cannot move HEAD to $first_parent" + + if [ "$1" != "-n" ] + then + # detach HEAD to current parent + output git checkout $first_parent 2> /dev/null || + die "Cannot move HEAD to $first_parent" + fi case "$new_parents" in ' '*' '*) + test "a$1" = a-n && die "Refusing to squash a merge: $sha1" + # redo merge author_script=$(get_author_ident_from_commit $sha1) eval "$author_script" From acc8559aa33b7a1ea49b0841e0759ecf05c6f26e Mon Sep 17 00:00:00 2001 From: Stephen Haberman Date: Wed, 15 Oct 2008 02:44:38 -0500 Subject: [PATCH 029/100] rebase-i-p: only list commits that require rewriting in todo This is heavily based on Stephan Beyer's git sequencer rewrite of rebase-i-p. Each commit is still found by rev-list UPSTREAM..HEAD, but a commit is only included in todo if at least one its parents has been marked for rewriting. Signed-off-by: Stephen Haberman Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 77 +++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 274251f697..851066f0f4 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -579,18 +579,67 @@ first and then run 'git rebase --continue' again." echo $ONTO > "$REWRITTEN"/$c || die "Could not init rewritten commits" done + # No cherry-pick because our first pass is to determine + # parents to rewrite and skipping dropped commits would + # prematurely end our probe MERGES_OPTION= else - MERGES_OPTION=--no-merges + MERGES_OPTION="--no-merges --cherry-pick" fi SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) SHORTHEAD=$(git rev-parse --short $HEAD) SHORTONTO=$(git rev-parse --short $ONTO) git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \ - --abbrev=7 --reverse --left-right --cherry-pick \ + --abbrev=7 --reverse --left-right --topo-order \ $UPSTREAM...$HEAD | \ - sed -n "s/^>/pick /p" > "$TODO" + sed -n "s/^>//p" | while read shortsha1 rest + do + if test t != "$PRESERVE_MERGES" + then + echo "pick $shortsha1 $rest" >> "$TODO" + else + sha1=$(git rev-parse $shortsha1) + preserve=t + for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-) + do + if test -f "$REWRITTEN"/$p + then + preserve=f + fi + done + if test f = "$preserve" + then + touch "$REWRITTEN"/$sha1 + echo "pick $shortsha1 $rest" >> "$TODO" + fi + fi + done + + # Watch for commits that been dropped by --cherry-pick + if test t = "$PRESERVE_MERGES" + then + mkdir "$DROPPED" + # Save all non-cherry-picked changes + git rev-list $UPSTREAM...$HEAD --left-right --cherry-pick | \ + sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks + # Now all commits and note which ones are missing in + # not-cherry-picks and hence being dropped + git rev-list $UPSTREAM...$HEAD --left-right | \ + sed -n "s/^>//p" | while read rev + do + if test -f "$REWRITTEN"/$rev -a "$(grep "$rev" "$DOTEST"/not-cherry-picks)" = "" + then + # Use -f2 because if rev-list is telling us this commit is + # not worthwhile, we don't want to track its multiple heads, + # just the history of its first-parent for others that will + # be rebasing on top of it + git rev-list --parents -1 $rev | cut -d' ' -f2 > "$DROPPED"/$rev + cat "$TODO" | grep -v "${rev:0:7}" > "${TODO}2" ; mv "${TODO}2" "$TODO" + rm "$REWRITTEN"/$rev + fi + done + fi test -s "$TODO" || echo noop >> "$TODO" cat >> "$TODO" << EOF @@ -606,28 +655,6 @@ first and then run 'git rebase --continue' again." # EOF - # Watch for commits that been dropped by --cherry-pick - if test t = "$PRESERVE_MERGES" - then - mkdir "$DROPPED" - # drop the --cherry-pick parameter this time - git rev-list $MERGES_OPTION --abbrev-commit \ - --abbrev=7 $UPSTREAM...$HEAD --left-right | \ - sed -n "s/^>//p" | while read rev - do - grep --quiet "$rev" "$TODO" - if [ $? -ne 0 ] - then - # Use -f2 because if rev-list is telling this commit is not - # worthwhile, we don't want to track its multiple heads, - # just the history of its first-parent for others that will - # be rebasing on top of us - full=$(git rev-parse $rev) - git rev-list --parents -1 $rev | cut -d' ' -f2 > "$DROPPED"/$full - fi - done - fi - has_action "$TODO" || die_abort "Nothing to do" From d80d6bc146232d81f1bb4bc58e5d89263fd228d4 Mon Sep 17 00:00:00 2001 From: Stephen Haberman Date: Wed, 15 Oct 2008 02:44:39 -0500 Subject: [PATCH 030/100] rebase-i-p: do not include non-first-parent commits touching UPSTREAM This covers an odd boundary case found by Avi Kivity's script where a branch coming off of UPSTREAM is merged into HEAD. Initially it show up in UPSTREAM..HEAD, but technically UPSTREAM is not moving, the rest of head is, so we should not need to rewrite the merge. This adds a check saying we can keep `preserve=t` if `p=UPSTREAM`...unless this is the first first-parent commit in our UPSTREAM..HEAD rev-list, which could very well point to UPSTREAM, but we still need to consider it as rewritten so we start pulling in the rest of the UPSTREAM..HEAD commits that point to it. Signed-off-by: Stephen Haberman Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 851066f0f4..495f554b65 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -583,6 +583,7 @@ first and then run 'git rebase --continue' again." # parents to rewrite and skipping dropped commits would # prematurely end our probe MERGES_OPTION= + first_after_upstream="$(git rev-list --reverse --first-parent $UPSTREAM..$HEAD | head -n 1)" else MERGES_OPTION="--no-merges --cherry-pick" fi @@ -603,7 +604,7 @@ first and then run 'git rebase --continue' again." preserve=t for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-) do - if test -f "$REWRITTEN"/$p + if test -f "$REWRITTEN"/$p -a \( $p != $UPSTREAM -o $sha1 = $first_after_upstream \) then preserve=f fi From 80fe82e4eb365773ba6518c4539c9235ea9a8b2e Mon Sep 17 00:00:00 2001 From: Stephen Haberman Date: Wed, 15 Oct 2008 02:44:40 -0500 Subject: [PATCH 031/100] rebase-i-p: if todo was reordered use HEAD as the rewritten parent This seems like the best guess we can make until git sequencer marks are available. That being said, within the context of re-ordering a commit before its parent in todo, I think applying it on top of the current commit seems like a reasonable assumption of what the user intended. Signed-off-by: Stephen Haberman Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 9 +++++++++ t/t3411-rebase-preserve-around-merges.sh | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) mode change 100755 => 100644 t/t3411-rebase-preserve-around-merges.sh diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 495f554b65..848fbe7d59 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -194,6 +194,15 @@ pick_one_preserving_merges () { if test -f "$REWRITTEN"/$p then new_p=$(cat "$REWRITTEN"/$p) + + # If the todo reordered commits, and our parent is marked for + # rewriting, but hasn't been gotten to yet, assume the user meant to + # drop it on top of the current HEAD + if test -z "$new_p" + then + new_p=$(git rev-parse HEAD) + fi + test $p != $new_p && fast_forward=f case "$new_parents" in *$new_p*) diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh old mode 100755 new mode 100644 index f9c549b3b8..aacfaae843 --- a/t/t3411-rebase-preserve-around-merges.sh +++ b/t/t3411-rebase-preserve-around-merges.sh @@ -80,7 +80,7 @@ test_expect_success 'setup' ' # \ / # -- C1 -- # -test_expect_failure 'squash F1 into D1' ' +test_expect_success 'squash F1 into D1' ' FAKE_LINES="1 squash 3 2" git rebase -i -p B1 && test "$(git rev-parse HEAD^2)" = "$(git rev-parse branch)" && test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" && @@ -99,7 +99,7 @@ test_expect_failure 'squash F1 into D1' ' # # And rebase G1..M1 onto E2 -test_expect_failure 'rebase two levels of merge' ' +test_expect_success 'rebase two levels of merge' ' git checkout -b branch2 A1 && touch g && git add g && From 9441b61dc5c3f1f984114ec8bd470dc20c55dfe0 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 17 Oct 2008 15:57:57 -0400 Subject: [PATCH 032/100] index-pack: rationalize delta resolution code Instead of having strange loops for walking unresolved deltas with the same base duplicated in many places, let's rework the code so this is done in a single place instead. This simplifies callers quite a bit too. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- index-pack.c | 123 ++++++++++++++++++++++++--------------------------- 1 file changed, 58 insertions(+), 65 deletions(-) diff --git a/index-pack.c b/index-pack.c index d3a4d31b4e..82843e7e8d 100644 --- a/index-pack.c +++ b/index-pack.c @@ -244,7 +244,8 @@ static void link_base_data(struct base_data *base, struct base_data *c) c->base = base; c->child = NULL; - base_cache_used += c->size; + if (c->data) + base_cache_used += c->size; prune_base_data(c); } @@ -494,8 +495,10 @@ static void *get_base_data(struct base_data *c) free(raw); if (!c->data) bad_object(obj->idx.offset, "failed to apply delta"); - } else + } else { c->data = get_data_from_pack(obj); + c->size = obj->size; + } base_cache_used += c->size; prune_base_data(c); @@ -504,49 +507,73 @@ static void *get_base_data(struct base_data *c) } static void resolve_delta(struct object_entry *delta_obj, - struct base_data *base_obj, enum object_type type) + struct base_data *base, struct base_data *result) { void *delta_data; unsigned long delta_size; - union delta_base delta_base; - int j, first, last; - struct base_data result; - delta_obj->real_type = type; + delta_obj->real_type = base->obj->type; delta_data = get_data_from_pack(delta_obj); delta_size = delta_obj->size; - result.data = patch_delta(get_base_data(base_obj), base_obj->size, - delta_data, delta_size, - &result.size); + result->obj = delta_obj; + result->data = patch_delta(get_base_data(base), base->obj->size, + delta_data, delta_size, &result->size); free(delta_data); - if (!result.data) + if (!result->data) bad_object(delta_obj->idx.offset, "failed to apply delta"); - sha1_object(result.data, result.size, type, delta_obj->idx.sha1); + sha1_object(result->data, result->size, delta_obj->real_type, + delta_obj->idx.sha1); nr_resolved_deltas++; +} - result.obj = delta_obj; - link_base_data(base_obj, &result); +static void find_unresolved_deltas(struct base_data *base, + struct base_data *prev_base) +{ + int i, ref, ref_first, ref_last, ofs, ofs_first, ofs_last; - hashcpy(delta_base.sha1, delta_obj->idx.sha1); - if (!find_delta_children(&delta_base, &first, &last)) { - for (j = first; j <= last; j++) { - struct object_entry *child = objects + deltas[j].obj_no; - if (child->real_type == OBJ_REF_DELTA) - resolve_delta(child, &result, type); + /* + * This is a recursive function. Those brackets should help reducing + * stack usage by limiting the scope of the delta_base union. + */ + { + union delta_base base_spec; + + hashcpy(base_spec.sha1, base->obj->idx.sha1); + ref = !find_delta_children(&base_spec, &ref_first, &ref_last); + + memset(&base_spec, 0, sizeof(base_spec)); + base_spec.offset = base->obj->idx.offset; + ofs = !find_delta_children(&base_spec, &ofs_first, &ofs_last); + } + + if (!ref && !ofs) + return; + + link_base_data(prev_base, base); + + if (ref) { + for (i = ref_first; i <= ref_last; i++) { + struct object_entry *child = objects + deltas[i].obj_no; + if (child->real_type == OBJ_REF_DELTA) { + struct base_data result; + resolve_delta(child, base, &result); + find_unresolved_deltas(&result, base); + } } } - memset(&delta_base, 0, sizeof(delta_base)); - delta_base.offset = delta_obj->idx.offset; - if (!find_delta_children(&delta_base, &first, &last)) { - for (j = first; j <= last; j++) { - struct object_entry *child = objects + deltas[j].obj_no; - if (child->real_type == OBJ_OFS_DELTA) - resolve_delta(child, &result, type); + if (ofs) { + for (i = ofs_first; i <= ofs_last; i++) { + struct object_entry *child = objects + deltas[i].obj_no; + if (child->real_type == OBJ_OFS_DELTA) { + struct base_data result; + resolve_delta(child, base, &result); + find_unresolved_deltas(&result, base); + } } } - unlink_base_data(&result); + unlink_base_data(base); } static int compare_delta_entry(const void *a, const void *b) @@ -622,37 +649,13 @@ static void parse_pack_objects(unsigned char *sha1) progress = start_progress("Resolving deltas", nr_deltas); for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; - union delta_base base; - int j, ref, ref_first, ref_last, ofs, ofs_first, ofs_last; struct base_data base_obj; if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) continue; - hashcpy(base.sha1, obj->idx.sha1); - ref = !find_delta_children(&base, &ref_first, &ref_last); - memset(&base, 0, sizeof(base)); - base.offset = obj->idx.offset; - ofs = !find_delta_children(&base, &ofs_first, &ofs_last); - if (!ref && !ofs) - continue; - base_obj.data = get_data_from_pack(obj); - base_obj.size = obj->size; base_obj.obj = obj; - link_base_data(NULL, &base_obj); - - if (ref) - for (j = ref_first; j <= ref_last; j++) { - struct object_entry *child = objects + deltas[j].obj_no; - if (child->real_type == OBJ_REF_DELTA) - resolve_delta(child, &base_obj, obj->type); - } - if (ofs) - for (j = ofs_first; j <= ofs_last; j++) { - struct object_entry *child = objects + deltas[j].obj_no; - if (child->real_type == OBJ_OFS_DELTA) - resolve_delta(child, &base_obj, obj->type); - } - unlink_base_data(&base_obj); + base_obj.data = NULL; + find_unresolved_deltas(&base_obj, NULL); display_progress(progress, nr_resolved_deltas); } } @@ -745,7 +748,6 @@ static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved) for (i = 0; i < n; i++) { struct delta_entry *d = sorted_by_pos[i]; enum object_type type; - int j, first, last; struct base_data base_obj; if (objects[d->obj_no].real_type != OBJ_REF_DELTA) @@ -759,16 +761,7 @@ static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved) die("local object %s is corrupt", sha1_to_hex(d->base.sha1)); base_obj.obj = append_obj_to_pack(f, d->base.sha1, base_obj.data, base_obj.size, type); - link_base_data(NULL, &base_obj); - - find_delta_children(&d->base, &first, &last); - for (j = first; j <= last; j++) { - struct object_entry *child = objects + deltas[j].obj_no; - if (child->real_type == OBJ_REF_DELTA) - resolve_delta(child, &base_obj, type); - } - - unlink_base_data(&base_obj); + find_unresolved_deltas(&base_obj, NULL); display_progress(progress, nr_resolved_deltas); } free(sorted_by_pos); From 6a87ed972c9c7a4b4378b6e34670d3aa2ac7ccc8 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 17 Oct 2008 15:57:58 -0400 Subject: [PATCH 033/100] index-pack: smarter memory usage during delta resolution There is no need to keep the base object data around after its last delta has been resolved. This also means that long delta chains with only one delta per base won't grow the cache size unnecessarily as the base will be freed before recursing down. To make it easy, find_delta_children() is modified so the first and last indices are initialized in all cases. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- index-pack.c | 73 ++++++++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/index-pack.c b/index-pack.c index 82843e7e8d..9def64133e 100644 --- a/index-pack.c +++ b/index-pack.c @@ -221,17 +221,23 @@ static void bad_object(unsigned long offset, const char *format, ...) die("pack has bad object at offset %lu: %s", offset, buf); } +static void free_base_data(struct base_data *c) +{ + if (c->data) { + free(c->data); + c->data = NULL; + base_cache_used -= c->size; + } +} + static void prune_base_data(struct base_data *retain) { struct base_data *b = base_cache; for (b = base_cache; base_cache_used > delta_base_cache_limit && b; b = b->child) { - if (b->data && b != retain) { - free(b->data); - b->data = NULL; - base_cache_used -= b->size; - } + if (b->data && b != retain) + free_base_data(b); } } @@ -256,10 +262,7 @@ static void unlink_base_data(struct base_data *c) base->child = NULL; else base_cache = NULL; - if (c->data) { - free(c->data); - base_cache_used -= c->size; - } + free_base_data(c); } static void *unpack_entry_data(unsigned long offset, unsigned long size) @@ -409,22 +412,24 @@ static int find_delta(const union delta_base *base) return -first-1; } -static int find_delta_children(const union delta_base *base, - int *first_index, int *last_index) +static void find_delta_children(const union delta_base *base, + int *first_index, int *last_index) { int first = find_delta(base); int last = first; int end = nr_deltas - 1; - if (first < 0) - return -1; + if (first < 0) { + *first_index = 0; + *last_index = -1; + return; + } while (first > 0 && !memcmp(&deltas[first - 1].base, base, UNION_BASE_SZ)) --first; while (last < end && !memcmp(&deltas[last + 1].base, base, UNION_BASE_SZ)) ++last; *first_index = first; *last_index = last; - return 0; } static void sha1_object(const void *data, unsigned long size, @@ -529,7 +534,7 @@ static void resolve_delta(struct object_entry *delta_obj, static void find_unresolved_deltas(struct base_data *base, struct base_data *prev_base) { - int i, ref, ref_first, ref_last, ofs, ofs_first, ofs_last; + int i, ref_first, ref_last, ofs_first, ofs_last; /* * This is a recursive function. Those brackets should help reducing @@ -539,37 +544,37 @@ static void find_unresolved_deltas(struct base_data *base, union delta_base base_spec; hashcpy(base_spec.sha1, base->obj->idx.sha1); - ref = !find_delta_children(&base_spec, &ref_first, &ref_last); + find_delta_children(&base_spec, &ref_first, &ref_last); memset(&base_spec, 0, sizeof(base_spec)); base_spec.offset = base->obj->idx.offset; - ofs = !find_delta_children(&base_spec, &ofs_first, &ofs_last); + find_delta_children(&base_spec, &ofs_first, &ofs_last); } - if (!ref && !ofs) + if (ref_last == -1 && ofs_last == -1) return; link_base_data(prev_base, base); - if (ref) { - for (i = ref_first; i <= ref_last; i++) { - struct object_entry *child = objects + deltas[i].obj_no; - if (child->real_type == OBJ_REF_DELTA) { - struct base_data result; - resolve_delta(child, base, &result); - find_unresolved_deltas(&result, base); - } + for (i = ref_first; i <= ref_last; i++) { + struct object_entry *child = objects + deltas[i].obj_no; + if (child->real_type == OBJ_REF_DELTA) { + struct base_data result; + resolve_delta(child, base, &result); + if (i == ref_last && ofs_last == -1) + free_base_data(base); + find_unresolved_deltas(&result, base); } } - if (ofs) { - for (i = ofs_first; i <= ofs_last; i++) { - struct object_entry *child = objects + deltas[i].obj_no; - if (child->real_type == OBJ_OFS_DELTA) { - struct base_data result; - resolve_delta(child, base, &result); - find_unresolved_deltas(&result, base); - } + for (i = ofs_first; i <= ofs_last; i++) { + struct object_entry *child = objects + deltas[i].obj_no; + if (child->real_type == OBJ_OFS_DELTA) { + struct base_data result; + resolve_delta(child, base, &result); + if (i == ofs_last) + free_base_data(base); + find_unresolved_deltas(&result, base); } } From a6c7db1b1945d656cc0dd559d93c7b2129c65d86 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sat, 18 Oct 2008 03:17:23 +0200 Subject: [PATCH 034/100] parse-opt: migrate builtin-checkout-index. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- builtin-checkout-index.c | 152 +++++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 69 deletions(-) diff --git a/builtin-checkout-index.c b/builtin-checkout-index.c index 4ba27024c5..0d534bc023 100644 --- a/builtin-checkout-index.c +++ b/builtin-checkout-index.c @@ -40,6 +40,7 @@ #include "cache.h" #include "quote.h" #include "cache-tree.h" +#include "parse-options.h" #define CHECKOUT_ALL 4 static int line_termination = '\n'; @@ -153,11 +154,58 @@ static void checkout_all(const char *prefix, int prefix_length) exit(128); } -static const char checkout_cache_usage[] = -"git checkout-index [-u] [-q] [-a] [-f] [-n] [--stage=[123]|all] [--prefix=] [--temp] [--] ..."; +static const char * const builtin_checkout_index_usage[] = { + "git checkout-index [options] [--] ...", + NULL +}; static struct lock_file lock_file; +static int option_parse_u(const struct option *opt, + const char *arg, int unset) +{ + int *newfd = opt->value; + + state.refresh_cache = 1; + if (*newfd < 0) + *newfd = hold_locked_index(&lock_file, 1); + return 0; +} + +static int option_parse_z(const struct option *opt, + const char *arg, int unset) +{ + if (unset) + line_termination = '\n'; + else + line_termination = 0; + return 0; +} + +static int option_parse_prefix(const struct option *opt, + const char *arg, int unset) +{ + state.base_dir = arg; + state.base_dir_len = strlen(arg); + return 0; +} + +static int option_parse_stage(const struct option *opt, + const char *arg, int unset) +{ + if (!strcmp(arg, "all")) { + to_tempfile = 1; + checkout_stage = CHECKOUT_ALL; + } else { + int ch = arg[0]; + if ('1' <= ch && ch <= '3') + checkout_stage = arg[0] - '0'; + else + die("stage should be between 1 and 3 or all"); + } + return 0; +} + int cmd_checkout_index(int argc, const char **argv, const char *prefix) { int i; @@ -165,6 +213,33 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) int all = 0; int read_from_stdin = 0; int prefix_length; + int force = 0, quiet = 0, not_new = 0; + struct option builtin_checkout_index_options[] = { + OPT_BOOLEAN('a', "all", &all, + "checks out all files in the index"), + OPT_BOOLEAN('f', "force", &force, + "forces overwrite of existing files"), + OPT__QUIET(&quiet), + OPT_BOOLEAN('n', "no-create", ¬_new, + "don't checkout new files"), + { OPTION_CALLBACK, 'u', "index", &newfd, NULL, + "update stat information in the index file", + PARSE_OPT_NOARG, option_parse_u }, + { OPTION_CALLBACK, 'z', NULL, NULL, NULL, + "paths are separated with NUL character", + PARSE_OPT_NOARG, option_parse_z }, + OPT_BOOLEAN(0, "stdin", &read_from_stdin, + "read list of paths from the standard input"), + OPT_BOOLEAN(0, "temp", &to_tempfile, + "write the content to temporary files"), + OPT_CALLBACK(0, "prefix", NULL, "string", + "when creating files, prepend ", + option_parse_prefix), + OPT_CALLBACK(0, "stage", NULL, NULL, + "copy out the files from named stage", + option_parse_stage), + OPT_END() + }; git_config(git_default_config, NULL); state.base_dir = ""; @@ -174,72 +249,11 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) die("invalid cache"); } - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!strcmp(arg, "-a") || !strcmp(arg, "--all")) { - all = 1; - continue; - } - if (!strcmp(arg, "-f") || !strcmp(arg, "--force")) { - state.force = 1; - continue; - } - if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) { - state.quiet = 1; - continue; - } - if (!strcmp(arg, "-n") || !strcmp(arg, "--no-create")) { - state.not_new = 1; - continue; - } - if (!strcmp(arg, "-u") || !strcmp(arg, "--index")) { - state.refresh_cache = 1; - if (newfd < 0) - newfd = hold_locked_index(&lock_file, 1); - continue; - } - if (!strcmp(arg, "-z")) { - line_termination = 0; - continue; - } - if (!strcmp(arg, "--stdin")) { - if (i != argc - 1) - die("--stdin must be at the end"); - read_from_stdin = 1; - i++; /* do not consider arg as a file name */ - break; - } - if (!strcmp(arg, "--temp")) { - to_tempfile = 1; - continue; - } - if (!prefixcmp(arg, "--prefix=")) { - state.base_dir = arg+9; - state.base_dir_len = strlen(state.base_dir); - continue; - } - if (!prefixcmp(arg, "--stage=")) { - if (!strcmp(arg + 8, "all")) { - to_tempfile = 1; - checkout_stage = CHECKOUT_ALL; - } else { - int ch = arg[8]; - if ('1' <= ch && ch <= '3') - checkout_stage = arg[8] - '0'; - else - die("stage should be between 1 and 3 or all"); - } - continue; - } - if (arg[0] == '-') - usage(checkout_cache_usage); - break; - } + argc = parse_options(argc, argv, builtin_checkout_index_options, + builtin_checkout_index_usage, 0); + state.force = force; + state.quiet = quiet; + state.not_new = not_new; if (state.base_dir_len || to_tempfile) { /* when --prefix is specified we do not @@ -253,7 +267,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) } /* Check out named files first */ - for ( ; i < argc; i++) { + for (i = 0; i < argc; i++) { const char *arg = argv[i]; const char *p; From d8b24b930f5b7150e63d989de39eb71bd37e8e63 Mon Sep 17 00:00:00 2001 From: Christian Jaeger Date: Sat, 18 Oct 2008 20:25:12 +0200 Subject: [PATCH 035/100] Git.pm: do not break inheritance Make it possible to write subclasses of Git.pm Signed-off-by: Christian Jaeger Signed-off-by: Junio C Hamano --- perl/Git.pm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/perl/Git.pm b/perl/Git.pm index 6aab712e6a..ba94453781 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -1203,8 +1203,7 @@ either version 2, or (at your option) any later version. # the method was called upon an instance and (undef, @args) if # it was called directly. sub _maybe_self { - # This breaks inheritance. Oh well. - ref $_[0] eq 'Git' ? @_ : (undef, @_); + UNIVERSAL::isa($_[0], 'Git') ? @_ : (undef, @_); } # Check if the command id is something reasonable. From ce3f6dc655392803a59c9b2f2ec9509fd8061b33 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 20 Oct 2008 16:46:19 -0400 Subject: [PATCH 036/100] fix multiple issues in index-pack Since commit 9441b61dc5, two issues affected correct behavior of index-pack: 1) The real_type of a delta object is the 'real_type' of its base, not the 'type' which can be a "delta type". Consequence of this is a corrupted pack index file which only needs to be recreated with a good index-pack command ('git verify-pack' will flag those). 2) The code sequence: result->data = patch_delta(get_base_data(base), base->obj->size, delta_data, delta_size, &result->size); has two issues of its own since base->obj->size should instead be base->size as we want the size of the actual object data and not the size of the delta object it is represented by. Except that simply replacing base->obj->size with base->size won't make the code more correct as the C language doesn't enforce a particular ordering for the evaluation of needed arguments for a function call, hence base->size could be pushed on the stack before get_base_data() which initializes base->size is called. Signed-off-by: Nicolas Pitre Tested-by: Jeff King Signed-off-by: Junio C Hamano --- index-pack.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/index-pack.c b/index-pack.c index 9def64133e..f109a00270 100644 --- a/index-pack.c +++ b/index-pack.c @@ -514,15 +514,14 @@ static void *get_base_data(struct base_data *c) static void resolve_delta(struct object_entry *delta_obj, struct base_data *base, struct base_data *result) { - void *delta_data; - unsigned long delta_size; + void *base_data, *delta_data; - delta_obj->real_type = base->obj->type; + delta_obj->real_type = base->obj->real_type; delta_data = get_data_from_pack(delta_obj); - delta_size = delta_obj->size; + base_data = get_base_data(base); result->obj = delta_obj; - result->data = patch_delta(get_base_data(base), base->obj->size, - delta_data, delta_size, &result->size); + result->data = patch_delta(base_data, base->size, + delta_data, delta_obj->size, &result->size); free(delta_data); if (!result->data) bad_object(delta_obj->idx.offset, "failed to apply delta"); From e249044c67d347dcffff247c72a503a9dd592294 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 20 Oct 2008 16:36:38 -0700 Subject: [PATCH 037/100] rebase-i-p: minimum fix to obvious issues Jeff King noticed that this series uses non-portable ${var:0:7} syntax to splice a string, which is not even in POSIX, in the script. A quick look at around the offending part revealed a few issues, which this commit fixes: * Why filter output from "rev-list --left-right A...B" and look for the ones that begin with ">"? Wouldn't "rev-list A..B" give that? * The abbreviated SHA-1 are made with "rev-list --abbrev=7" into $TODO in an earlier invocation, and it can be more than 7 letters to avoid ambiguity. Not just that "${r:0:7} is not even in POSIX", but use of it here is actively wrong. * There is no point in catting a single file and piping it into grep. Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 848fbe7d59..a563dea9b5 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -635,8 +635,8 @@ first and then run 'git rebase --continue' again." sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks # Now all commits and note which ones are missing in # not-cherry-picks and hence being dropped - git rev-list $UPSTREAM...$HEAD --left-right | \ - sed -n "s/^>//p" | while read rev + git rev-list $UPSTREAM..$HEAD | + while read rev do if test -f "$REWRITTEN"/$rev -a "$(grep "$rev" "$DOTEST"/not-cherry-picks)" = "" then @@ -645,7 +645,8 @@ first and then run 'git rebase --continue' again." # just the history of its first-parent for others that will # be rebasing on top of it git rev-list --parents -1 $rev | cut -d' ' -f2 > "$DROPPED"/$rev - cat "$TODO" | grep -v "${rev:0:7}" > "${TODO}2" ; mv "${TODO}2" "$TODO" + short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev) + grep -v "^[a-z][a-z]* $short" <"$TODO" > "${TODO}2" ; mv "${TODO}2" "$TODO" rm "$REWRITTEN"/$rev fi done From 69cd8f63427c65c65d9cd66bb8c05ca93dc3edef Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 22 Oct 2008 00:55:57 +0400 Subject: [PATCH 038/100] builtin-blame: Reencode commit messages according to git-log rules. Currently git-blame outputs text from the commit messages (e.g. the author name and the summary string) as-is, without even providing any information about the encoding used for the data. It makes interpreting the data in multilingual environment very difficult. This commit changes the blame implementation to recode the messages using the rules used by other commands like git-log. Namely, the target encoding can be specified through the i18n.commitEncoding or i18n.logOutputEncoding options, or directly on the command line using the --encoding parameter. Converting the encoding before output seems to be more friendly to the porcelain tools than simply providing the value of the encoding header, and does not require changing the output format. If anybody needs the old behavior, it is possible to achieve it by specifying --encoding=none. Signed-off-by: Alexander Gavrilov Signed-off-by: Junio C Hamano --- Documentation/blame-options.txt | 7 +++ Documentation/i18n.txt | 6 +-- builtin-blame.c | 16 ++++-- commit.h | 2 + pretty.c | 21 +++++--- t/t8005-blame-i18n.sh | 92 +++++++++++++++++++++++++++++++++ t/t8005/cp1251.txt | 2 + t/t8005/sjis.txt | 2 + t/t8005/utf8.txt | 2 + 9 files changed, 136 insertions(+), 14 deletions(-) create mode 100755 t/t8005-blame-i18n.sh create mode 100644 t/t8005/cp1251.txt create mode 100644 t/t8005/sjis.txt create mode 100644 t/t8005/utf8.txt diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt index 5428111d73..1ab1b96cf9 100644 --- a/Documentation/blame-options.txt +++ b/Documentation/blame-options.txt @@ -49,6 +49,13 @@ of lines before or after the line given by . Show the result incrementally in a format designed for machine consumption. +--encoding=:: + Specifies the encoding used to output author names + and commit summaries. Setting it to `none` makes blame + output unconverted data. For more information see the + discussion about encoding in the linkgit:git-log[1] + manual page. + --contents :: When is not specified, the command annotates the changes starting backwards from the working tree copy. diff --git a/Documentation/i18n.txt b/Documentation/i18n.txt index d2970f8357..2cdacd94cd 100644 --- a/Documentation/i18n.txt +++ b/Documentation/i18n.txt @@ -37,9 +37,9 @@ of `i18n.commitencoding` in its `encoding` header. This is to help other people who look at them later. Lack of this header implies that the commit log message is encoded in UTF-8. -. 'git-log', 'git-show' and friends looks at the `encoding` - header of a commit object, and tries to re-code the log - message into UTF-8 unless otherwise specified. You can +. 'git-log', 'git-show', 'git-blame' and friends look at the + `encoding` header of a commit object, and try to re-code the + log message into UTF-8 unless otherwise specified. You can specify the desired output encoding with `i18n.logoutputencoding` in `.git/config` file, like this: + diff --git a/builtin-blame.c b/builtin-blame.c index 48cc0c175d..2457e71fc0 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -1431,7 +1431,7 @@ static void get_commit_info(struct commit *commit, int detailed) { int len; - char *tmp, *endp; + char *tmp, *endp, *reencoded, *message; static char author_buf[1024]; static char committer_buf[1024]; static char summary_buf[1024]; @@ -1449,24 +1449,29 @@ static void get_commit_info(struct commit *commit, die("Cannot read commit %s", sha1_to_hex(commit->object.sha1)); } + reencoded = reencode_commit_message(commit, NULL); + message = reencoded ? reencoded : commit->buffer; ret->author = author_buf; - get_ac_line(commit->buffer, "\nauthor ", + get_ac_line(message, "\nauthor ", sizeof(author_buf), author_buf, &ret->author_mail, &ret->author_time, &ret->author_tz); - if (!detailed) + if (!detailed) { + free(reencoded); return; + } ret->committer = committer_buf; - get_ac_line(commit->buffer, "\ncommitter ", + get_ac_line(message, "\ncommitter ", sizeof(committer_buf), committer_buf, &ret->committer_mail, &ret->committer_time, &ret->committer_tz); ret->summary = summary_buf; - tmp = strstr(commit->buffer, "\n\n"); + tmp = strstr(message, "\n\n"); if (!tmp) { error_out: sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1)); + free(reencoded); return; } tmp += 2; @@ -1478,6 +1483,7 @@ static void get_commit_info(struct commit *commit, goto error_out; memcpy(summary_buf, tmp, len); summary_buf[len] = 0; + free(reencoded); } /* diff --git a/commit.h b/commit.h index 4c05864fb4..3a7b06a828 100644 --- a/commit.h +++ b/commit.h @@ -65,6 +65,8 @@ enum cmit_fmt { extern int non_ascii(int); struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */ +extern char *reencode_commit_message(const struct commit *commit, + const char **encoding_p); extern void get_commit_format(const char *arg, struct rev_info *); extern void format_commit_message(const struct commit *commit, const void *format, struct strbuf *sb, diff --git a/pretty.c b/pretty.c index 1e79943339..f6ff31264b 100644 --- a/pretty.c +++ b/pretty.c @@ -783,6 +783,20 @@ void pp_remainder(enum cmit_fmt fmt, } } +char *reencode_commit_message(const struct commit *commit, const char **encoding_p) +{ + const char *encoding; + + encoding = (git_log_output_encoding + ? git_log_output_encoding + : git_commit_encoding); + if (!encoding) + encoding = "utf-8"; + if (encoding_p) + *encoding_p = encoding; + return logmsg_reencode(commit, encoding); +} + void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, struct strbuf *sb, int abbrev, const char *subject, const char *after_subject, @@ -799,12 +813,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, return; } - encoding = (git_log_output_encoding - ? git_log_output_encoding - : git_commit_encoding); - if (!encoding) - encoding = "utf-8"; - reencoded = logmsg_reencode(commit, encoding); + reencoded = reencode_commit_message(commit, &encoding); if (reencoded) { msg = reencoded; } diff --git a/t/t8005-blame-i18n.sh b/t/t8005-blame-i18n.sh new file mode 100755 index 0000000000..4470a92bb2 --- /dev/null +++ b/t/t8005-blame-i18n.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +test_description='git blame encoding conversion' +. ./test-lib.sh + +. "$TEST_DIRECTORY"/t8005/utf8.txt +. "$TEST_DIRECTORY"/t8005/cp1251.txt +. "$TEST_DIRECTORY"/t8005/sjis.txt + +test_expect_success 'setup the repository' ' + # Create the file + echo "UTF-8 LINE" > file && + git add file && + git commit --author "$UTF8_NAME " -m "$UTF8_MSG" && + + echo "CP1251 LINE" >> file && + git add file && + git config i18n.commitencoding cp1251 && + git commit --author "$CP1251_NAME " -m "$CP1251_MSG" && + + echo "SJIS LINE" >> file && + git add file && + git config i18n.commitencoding shift-jis && + git commit --author "$SJIS_NAME " -m "$SJIS_MSG" +' + +cat >expected < actual && + test_cmp actual expected +' + +cat >expected < actual && + test_cmp actual expected +' + +cat >expected < actual && + test_cmp actual expected +' + +cat >expected < actual && + test_cmp actual expected +' + +test_done diff --git a/t/t8005/cp1251.txt b/t/t8005/cp1251.txt new file mode 100644 index 0000000000..ce41e98b81 --- /dev/null +++ b/t/t8005/cp1251.txt @@ -0,0 +1,2 @@ +CP1251_NAME=" " +CP1251_MSG=" " diff --git a/t/t8005/sjis.txt b/t/t8005/sjis.txt new file mode 100644 index 0000000000..2ccfbad207 --- /dev/null +++ b/t/t8005/sjis.txt @@ -0,0 +1,2 @@ +SJIS_NAME="Irp~ Pury Rytr" +SJIS_MSG="Suru qu~yu" diff --git a/t/t8005/utf8.txt b/t/t8005/utf8.txt new file mode 100644 index 0000000000..f46cfc56d8 --- /dev/null +++ b/t/t8005/utf8.txt @@ -0,0 +1,2 @@ +UTF8_NAME="Иван Петрович Сидоров" +UTF8_MSG="Тестовое сообщение" From 4c1360f472ca5706a3dd1eed0b88603cb05d0827 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 22 Oct 2008 11:59:30 -0700 Subject: [PATCH 039/100] git-rebase--interactive.sh: comparision with == is bashism Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index a563dea9b5..0cae3be6f6 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -170,7 +170,7 @@ pick_one_preserving_merges () { if test -f "$DOTEST"/current-commit then - if [ "$fast_forward" == "t" ] + if test "$fast_forward" = t then cat "$DOTEST"/current-commit | while read current_commit do From 2b5c208f5bd371ea5527046384f9955dbbceb188 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 22 Oct 2008 20:59:22 -0400 Subject: [PATCH 040/100] improve index-pack tests Commit 9441b61dc5 introduced serious bugs in index-pack which are described and fixed by commit ce3f6dc655. However, despite the boldness of those bugs, the test suite still passed. This improves t5302-pack-index.sh so to ensure a much better code path coverage. With commit ce3f6dc655 reverted, 17 of the 26 tests do fail now. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- t/t5302-pack-index.sh | 92 ++++++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 27 deletions(-) diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh index 6424db1f28..0cb945fc80 100755 --- a/t/t5302-pack-index.sh +++ b/t/t5302-pack-index.sh @@ -11,13 +11,18 @@ test_expect_success \ 'rm -rf .git git init && i=1 && - while test $i -le 100 + while test $i -le 100 do - i=`printf '%03i' $i` - echo $i >file_$i && - test-genrandom "$i" 8192 >>file_$i && - git update-index --add file_$i && - i=`expr $i + 1` || return 1 + iii=`printf '%03i' $i` + test-genrandom "bar" 200 > wide_delta_$iii && + test-genrandom "baz $iii" 50 >> wide_delta_$iii && + test-genrandom "foo"$i 100 > deep_delta_$iii && + test-genrandom "foo"`expr $i + 1` 100 >> deep_delta_$iii && + test-genrandom "foo"`expr $i + 2` 100 >> deep_delta_$iii && + echo $iii >file_$iii && + test-genrandom "$iii" 8192 >>file_$iii && + git update-index --add file_$iii deep_delta_$iii wide_delta_$iii && + i=`expr $i + 1` || return 1 done && { echo 101 && test-genrandom 100 8192; } >file_101 && git update-index --add file_101 && @@ -92,6 +97,31 @@ test_expect_success \ '64-bit offsets: index-pack result should match pack-objects one' \ 'cmp "test-3-${pack3}.idx" "3.idx"' +# returns the object number for given object in given pack index +index_obj_nr() +{ + idx_file=$1 + object_sha1=$2 + nr=0 + git show-index < $idx_file | + while read offs sha1 extra + do + nr=$(($nr + 1)) + test "$sha1" = "$object_sha1" || continue + echo "$(($nr - 1))" + break + done +} + +# returns the pack offset for given object as found in given pack index +index_obj_offset() +{ + idx_file=$1 + object_sha1=$2 + git show-index < $idx_file | grep $object_sha1 | + ( read offs extra && echo "$offs" ) +} + test_expect_success \ '[index v1] 1) stream pack to repository' \ 'git index-pack --index-version=1 --stdin < "test-1-${pack1}.pack" && @@ -102,19 +132,22 @@ test_expect_success \ test_expect_success \ '[index v1] 2) create a stealth corruption in a delta base reference' \ - '# this test assumes a delta smaller than 16 bytes at the end of the pack - git show-index <1.idx | sort -n | sed -ne \$p | ( - read delta_offs delta_sha1 && - git cat-file blob "$delta_sha1" > blob_1 && - chmod +w ".git/objects/pack/pack-${pack1}.pack" && - dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($delta_offs + 1)) \ - if=".git/objects/pack/pack-${pack1}.idx" skip=$((256 * 4 + 4)) \ - bs=1 count=20 conv=notrunc && - git cat-file blob "$delta_sha1" > blob_2 )' + '# This test assumes file_101 is a delta smaller than 16 bytes. + # It should be against file_100 but we substitute its base for file_099 + sha1_101=`git hash-object file_101` && + sha1_099=`git hash-object file_099` && + offs_101=`index_obj_offset 1.idx $sha1_101` && + nr_099=`index_obj_nr 1.idx $sha1_099` && + chmod +w ".git/objects/pack/pack-${pack1}.pack" && + dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($offs_101 + 1)) \ + if=".git/objects/pack/pack-${pack1}.idx" \ + skip=$((4 + 256 * 4 + $nr_099 * 24)) \ + bs=1 count=20 conv=notrunc && + git cat-file blob $sha1_101 > file_101_foo1' test_expect_success \ '[index v1] 3) corrupted delta happily returned wrong data' \ - '! cmp blob_1 blob_2' + 'test -f file_101_foo1 && ! cmp file_101 file_101_foo1' test_expect_success \ '[index v1] 4) confirm that the pack is actually corrupted' \ @@ -140,19 +173,22 @@ test_expect_success \ test_expect_success \ '[index v2] 2) create a stealth corruption in a delta base reference' \ - '# this test assumes a delta smaller than 16 bytes at the end of the pack - git show-index <1.idx | sort -n | sed -ne \$p | ( - read delta_offs delta_sha1 delta_crc && - git cat-file blob "$delta_sha1" > blob_3 && - chmod +w ".git/objects/pack/pack-${pack1}.pack" && - dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($delta_offs + 1)) \ - if=".git/objects/pack/pack-${pack1}.idx" skip=$((8 + 256 * 4)) \ - bs=1 count=20 conv=notrunc && - git cat-file blob "$delta_sha1" > blob_4 )' + '# This test assumes file_101 is a delta smaller than 16 bytes. + # It should be against file_100 but we substitute its base for file_099 + sha1_101=`git hash-object file_101` && + sha1_099=`git hash-object file_099` && + offs_101=`index_obj_offset 1.idx $sha1_101` && + nr_099=`index_obj_nr 1.idx $sha1_099` && + chmod +w ".git/objects/pack/pack-${pack1}.pack" && + dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($offs_101 + 1)) \ + if=".git/objects/pack/pack-${pack1}.idx" \ + skip=$((8 + 256 * 4 + $nr_099 * 20)) \ + bs=1 count=20 conv=notrunc && + git cat-file blob $sha1_101 > file_101_foo2' test_expect_success \ '[index v2] 3) corrupted delta happily returned wrong data' \ - '! cmp blob_3 blob_4' + 'test -f file_101_foo2 && ! cmp file_101 file_101_foo2' test_expect_success \ '[index v2] 4) confirm that the pack is actually corrupted' \ @@ -167,9 +203,11 @@ test_expect_success \ 'rm -f .git/objects/pack/* && git index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" && git verify-pack ".git/objects/pack/pack-${pack1}.pack" && + obj=`git hash-object file_001` && + nr=`index_obj_nr ".git/objects/pack/pack-${pack1}.idx" $obj` && chmod +w ".git/objects/pack/pack-${pack1}.idx" && dd if=/dev/zero of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \ - bs=1 count=4 seek=$((8 + 256 * 4 + `wc -l /dev/null || exit 1 done Date: Thu, 23 Oct 2008 15:05:59 -0400 Subject: [PATCH 041/100] index-pack: don't leak leaf delta result Another (but minor this time) fallout from commit 9441b61 (index-pack: rationalize delta resolution code, 2008-10-17). Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- index-pack.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index-pack.c b/index-pack.c index f109a00270..7db7fbb56b 100644 --- a/index-pack.c +++ b/index-pack.c @@ -550,8 +550,10 @@ static void find_unresolved_deltas(struct base_data *base, find_delta_children(&base_spec, &ofs_first, &ofs_last); } - if (ref_last == -1 && ofs_last == -1) + if (ref_last == -1 && ofs_last == -1) { + free(base->data); return; + } link_base_data(prev_base, base); From 225f1d0c6af36722e6a52ab0563a19e86e51933d Mon Sep 17 00:00:00 2001 From: Deskin Miller Date: Thu, 23 Oct 2008 15:21:34 -0400 Subject: [PATCH 042/100] git-svn: change dashed git-config to git config Signed-off-by: Deskin Miller Acked-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-svn.perl b/git-svn.perl index 33e1b503c4..2e68c68d49 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1126,7 +1126,7 @@ sub read_repo_config { my $v = $opts->{$o}; my ($key) = ($o =~ /^([a-zA-Z\-]+)/); $key =~ s/-//g; - my $arg = 'git-config'; + my $arg = 'git config'; $arg .= ' --int' if ($o =~ /[:=]i$/); $arg .= ' --bool' if ($o !~ /[:=][sfi]$/); if (ref $v eq 'ARRAY') { From 53ffb878a94c3eae7c7f57e05568aedcfb77e57f Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 24 Oct 2008 01:48:50 -0400 Subject: [PATCH 043/100] git-daemon: set REMOTE_ADDR to client address This allows hooks like pre-receive to look at the client's IP address. Of course the IP address can't be used to get strong security; git-daemon isn't the right thing to use if you need that. However, basic IP address checking can be good enough in some situations. REMOTE_ADDR is the same environment variable used to communicate the client's address to CGI scripts. Signed-off-by: Joey Hess Signed-off-by: Junio C Hamano --- Documentation/git-daemon.txt | 9 +++++++++ daemon.c | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt index b08a08cd95..f1a570a874 100644 --- a/Documentation/git-daemon.txt +++ b/Documentation/git-daemon.txt @@ -270,6 +270,15 @@ selectively enable/disable services per repository:: ---------------------------------------------------------------- +ENVIRONMENT +----------- +'git-daemon' will set REMOTE_ADDR to the IP address of the client +that connected to it, if the IP address is available. REMOTE_ADDR will +be available in the environment of hooks called when +services are performed. + + + Author ------ Written by Linus Torvalds , YOSHIFUJI Hideaki diff --git a/daemon.c b/daemon.c index 3e5582d289..b9ba44c582 100644 --- a/daemon.c +++ b/daemon.c @@ -537,6 +537,10 @@ static int execute(struct sockaddr *addr) #endif } loginfo("Connection from %s:%d", addrbuf, port); + setenv("REMOTE_ADDR", addrbuf, 1); + } + else { + unsetenv("REMOTE_ADDR"); } alarm(init_timeout ? init_timeout : timeout); From d8c2882254f0f30e4f44de593c3b3db6a8fccef9 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Tue, 21 Oct 2008 21:34:50 +0200 Subject: [PATCH 044/100] gitweb: parse project/action/hash_base:filename PATH_INFO This patch enables gitweb to parse URLs with more information embedded in PATH_INFO, reducing the need for CGI parameters. The typical gitweb path is now $project/$action/$hash_base:$file_name or $project/$action/$hash This is mostly backwards compatible with the old-style gitweb paths, $project/$branch[:$filename], except when it was used to access a branch whose name matches a gitweb action. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index c5254afa7f..d09cf0a520 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -534,23 +534,55 @@ sub evaluate_path_info { return if $input_params{'action'}; $path_info =~ s,^\Q$project\E/*,,; + # next, check if we have an action + my $action = $path_info; + $action =~ s,/.*$,,; + if (exists $actions{$action}) { + $path_info =~ s,^$action/*,,; + $input_params{'action'} = $action; + } + + # list of actions that want hash_base instead of hash, but can have no + # pathname (f) parameter + my @wants_base = ( + 'tree', + 'history', + ); + my ($refname, $pathname) = split(/:/, $path_info, 2); if (defined $pathname) { - # we got "project.git/branch:filename" or "project.git/branch:dir/" - # we could use git_get_type(branch:pathname), but it needs $git_dir + # we got "branch:filename" or "branch:dir/" + # we could use git_get_type(branch:pathname), but: + # - it needs $git_dir + # - it does a git() call + # - the convention of terminating directories with a slash + # makes it superfluous + # - embedding the action in the PATH_INFO would make it even + # more superfluous $pathname =~ s,^/+,,; if (!$pathname || substr($pathname, -1) eq "/") { - $input_params{'action'} = "tree"; + $input_params{'action'} ||= "tree"; $pathname =~ s,/$,,; } else { - $input_params{'action'} = "blob_plain"; + $input_params{'action'} ||= "blob_plain"; } $input_params{'hash_base'} ||= $refname; $input_params{'file_name'} ||= $pathname; } elsif (defined $refname) { - # we got "project.git/branch" - $input_params{'action'} = "shortlog"; - $input_params{'hash'} ||= $refname; + # we got "branch". In this case we have to choose if we have to + # set hash or hash_base. + # + # Most of the actions without a pathname only want hash to be + # set, except for the ones specified in @wants_base that want + # hash_base instead. It should also be noted that hand-crafted + # links having 'history' as an action and no pathname or hash + # set will fail, but that happens regardless of PATH_INFO. + $input_params{'action'} ||= "shortlog"; + if (grep { $_ eq $input_params{'action'} } @wants_base) { + $input_params{'hash_base'} ||= $refname; + } else { + $input_params{'hash'} ||= $refname; + } } } evaluate_path_info(); From b02bd7a6323a85f9feedd9c8cd7d7401021dfb11 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Tue, 21 Oct 2008 21:34:51 +0200 Subject: [PATCH 045/100] gitweb: generate project/action/hash URLs When generating path info URLs, reduce the number of CGI parameters by embedding action and hash_parent:filename or hash in the path. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index d09cf0a520..50604e0a0c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -731,14 +731,41 @@ sub href (%) { my ($use_pathinfo) = gitweb_check_feature('pathinfo'); if ($use_pathinfo) { - # use PATH_INFO for project name + # try to put as many parameters as possible in PATH_INFO: + # - project name + # - action + # - hash or hash_base:filename + + # When the script is the root DirectoryIndex for the domain, + # $href here would be something like http://gitweb.example.com/ + # Thus, we strip any trailing / from $href, to spare us double + # slashes in the final URL + $href =~ s,/$,,; + + # Then add the project name, if present $href .= "/".esc_url($params{'project'}) if defined $params{'project'}; delete $params{'project'}; - # Summary just uses the project path URL - if (defined $params{'action'} && $params{'action'} eq 'summary') { + # Summary just uses the project path URL, any other action is + # added to the URL + if (defined $params{'action'}) { + $href .= "/".esc_url($params{'action'}) unless $params{'action'} eq 'summary'; delete $params{'action'}; } + + # Finally, we put either hash_base:file_name or hash + if (defined $params{'hash_base'}) { + $href .= "/".esc_url($params{'hash_base'}); + if (defined $params{'file_name'}) { + $href .= ":".esc_url($params{'file_name'}); + delete $params{'file_name'}; + } + delete $params{'hash'}; + delete $params{'hash_base'}; + } elsif (defined $params{'hash'}) { + $href .= "/".esc_url($params{'hash'}); + delete $params{'hash'}; + } } # now encode the parameters explicitly From 3550ea71f566b6958ffedf1573806d5fe891f344 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Tue, 21 Oct 2008 21:34:52 +0200 Subject: [PATCH 046/100] gitweb: use_pathinfo filenames start with / Generate PATH_INFO URLs in the form project/action/hash_base:/filename rather than project/action/hash_base:filename (the latter form is still accepted in input). This minimal change allows relative navigation to work properly when viewing HTML files in raw ('blob_plain') mode. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 50604e0a0c..f8021da967 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -734,7 +734,7 @@ sub href (%) { # try to put as many parameters as possible in PATH_INFO: # - project name # - action - # - hash or hash_base:filename + # - hash or hash_base:/filename # When the script is the root DirectoryIndex for the domain, # $href here would be something like http://gitweb.example.com/ @@ -753,11 +753,11 @@ sub href (%) { delete $params{'action'}; } - # Finally, we put either hash_base:file_name or hash + # Finally, we put either hash_base:/file_name or hash if (defined $params{'hash_base'}) { $href .= "/".esc_url($params{'hash_base'}); if (defined $params{'file_name'}) { - $href .= ":".esc_url($params{'file_name'}); + $href .= ":/".esc_url($params{'file_name'}); delete $params{'file_name'}; } delete $params{'hash'}; From b0be3838bb75e8b3be04310cc379142c3ef65703 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Tue, 21 Oct 2008 21:34:53 +0200 Subject: [PATCH 047/100] gitweb: parse parent..current syntax from PATH_INFO This patch makes it possible to use an URL such as project/action/somebranch..otherbranch:/filename to get a diff between different version of a file. Paths like project/action/somebranch:/somefile..otherbranch:/otherfile are parsed as well. All '*diff' actions and in general actions that use $hash_parent[_base] and $file_parent (e.g. 'shortlog') can now get all of their parameters from PATH_INFO Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index f8021da967..3d62019e1f 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -549,7 +549,12 @@ sub evaluate_path_info { 'history', ); - my ($refname, $pathname) = split(/:/, $path_info, 2); + # we want to catch + # [$hash_parent_base[:$file_parent]..]$hash_parent[:$file_name] + my ($parentrefname, $parentpathname, $refname, $pathname) = + ($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?(.+?)(?::(.+))?$/); + + # first, analyze the 'current' part if (defined $pathname) { # we got "branch:filename" or "branch:dir/" # we could use git_get_type(branch:pathname), but: @@ -564,7 +569,13 @@ sub evaluate_path_info { $input_params{'action'} ||= "tree"; $pathname =~ s,/$,,; } else { - $input_params{'action'} ||= "blob_plain"; + # the default action depends on whether we had parent info + # or not + if ($parentrefname) { + $input_params{'action'} ||= "blobdiff_plain"; + } else { + $input_params{'action'} ||= "blob_plain"; + } } $input_params{'hash_base'} ||= $refname; $input_params{'file_name'} ||= $pathname; @@ -584,6 +595,27 @@ sub evaluate_path_info { $input_params{'hash'} ||= $refname; } } + + # next, handle the 'parent' part, if present + if (defined $parentrefname) { + # a missing pathspec defaults to the 'current' filename, allowing e.g. + # someproject/blobdiff/oldrev..newrev:/filename + if ($parentpathname) { + $parentpathname =~ s,^/+,,; + $parentpathname =~ s,/$,,; + $input_params{'file_parent'} ||= $parentpathname; + } else { + $input_params{'file_parent'} ||= $input_params{'file_name'}; + } + # we assume that hash_parent_base is wanted if a path was specified, + # or if the action wants hash_base instead of hash + if (defined $input_params{'file_parent'} || + grep { $_ eq $input_params{'action'} } @wants_base) { + $input_params{'hash_parent_base'} ||= $parentrefname; + } else { + $input_params{'hash_parent'} ||= $parentrefname; + } + } } evaluate_path_info(); From 8db49a7f6f272ecb72c75a172e9753f3981488ce Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Tue, 21 Oct 2008 21:34:54 +0200 Subject: [PATCH 048/100] gitweb: generate parent..current URLs If use_pathinfo is enabled, href now creates links that contain paths in the form $project/$action/oldhash:/oldname..newhash:/newname for actions that use hash_parent etc. If any of the filename contains two consecutive dots, it's kept as a CGI parameter since the resulting path would otherwise be ambiguous. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 3d62019e1f..63c793ec39 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -766,6 +766,7 @@ sub href (%) { # try to put as many parameters as possible in PATH_INFO: # - project name # - action + # - hash_parent or hash_parent_base:/file_parent # - hash or hash_base:/filename # When the script is the root DirectoryIndex for the domain, @@ -785,17 +786,36 @@ sub href (%) { delete $params{'action'}; } - # Finally, we put either hash_base:/file_name or hash + # Next, we put hash_parent_base:/file_parent..hash_base:/file_name, + # stripping nonexistent or useless pieces + $href .= "/" if ($params{'hash_base'} || $params{'hash_parent_base'} + || $params{'hash_parent'} || $params{'hash'}); if (defined $params{'hash_base'}) { - $href .= "/".esc_url($params{'hash_base'}); - if (defined $params{'file_name'}) { + if (defined $params{'hash_parent_base'}) { + $href .= esc_url($params{'hash_parent_base'}); + # skip the file_parent if it's the same as the file_name + delete $params{'file_parent'} if $params{'file_parent'} eq $params{'file_name'}; + if (defined $params{'file_parent'} && $params{'file_parent'} !~ /\.\./) { + $href .= ":/".esc_url($params{'file_parent'}); + delete $params{'file_parent'}; + } + $href .= ".."; + delete $params{'hash_parent'}; + delete $params{'hash_parent_base'}; + } elsif (defined $params{'hash_parent'}) { + $href .= esc_url($params{'hash_parent'}). ".."; + delete $params{'hash_parent'}; + } + + $href .= esc_url($params{'hash_base'}); + if (defined $params{'file_name'} && $params{'file_name'} !~ /\.\./) { $href .= ":/".esc_url($params{'file_name'}); delete $params{'file_name'}; } delete $params{'hash'}; delete $params{'hash_base'}; } elsif (defined $params{'hash'}) { - $href .= "/".esc_url($params{'hash'}); + $href .= esc_url($params{'hash'}); delete $params{'hash'}; } } From b8492539f9c078162ee3ae4987ceaa7e7f510c9b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 26 Oct 2008 11:07:18 -0700 Subject: [PATCH 049/100] receive-pack: fix "borrowing from alternate object store" implementation In the alternate_object_database structure, ent->base[] is a buffer the users can use to form pathnames to loose objects, and ent->name is a pointer into that buffer (it points at one beyond ".git/objects/"). If you get a call to add_refs_from_alternate() after somebody used the entry (has_loose_object() has been called, for example), *ent->name would not be NUL, and ent->base[] won't be the path to the object store. This caller is expecting to read the path to the object store in ent->base[]; it needs to NUL terminate the buffer if it wants to. Signed-off-by: Junio C Hamano --- builtin-receive-pack.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c index 45e3cd90fd..9f60f31c2b 100644 --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@ -466,12 +466,17 @@ static int delete_only(struct command *cmd) static int add_refs_from_alternate(struct alternate_object_database *e, void *unused) { - char *other = xstrdup(make_absolute_path(e->base)); - size_t len = strlen(other); + char *other; + size_t len; struct remote *remote; struct transport *transport; const struct ref *extra; + e->name[-1] = '\0'; + other = xstrdup(make_absolute_path(e->base)); + e->name[-1] = '/'; + len = strlen(other); + while (other[len-1] == '/') other[--len] = '\0'; if (len < 8 || memcmp(other + len - 8, "/objects", 8)) From eca35a25a92a1ad725af2a549fc9158488c4cc43 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sun, 26 Oct 2008 03:33:56 +0100 Subject: [PATCH 050/100] Fix git branch -m for symrefs. This had two problems with symrefs. First, it copied the actual sha1 instead of the "pointer", second it failed to remove the old ref after a successful rename. Given that till now delete_ref() always dereferenced symrefs, a new parameters has been introduced to delete_ref() to allow deleting refs without a dereference. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- builtin-branch.c | 2 +- builtin-remote.c | 4 +-- builtin-reset.c | 2 +- builtin-send-pack.c | 2 +- builtin-tag.c | 2 +- builtin-update-ref.c | 2 +- cache.h | 2 +- receive-pack.c | 2 +- refs.c | 59 +++++++++++++++++++++++++++----------------- t/t3200-branch.sh | 9 +++++++ 10 files changed, 55 insertions(+), 31 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index b1a2ad7a6b..4b4abfd363 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -160,7 +160,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) continue; } - if (delete_ref(name, sha1)) { + if (delete_ref(name, sha1, 0)) { error("Error deleting %sbranch '%s'", remote, argv[i]); ret = 1; diff --git a/builtin-remote.c b/builtin-remote.c index 90a4e35828..584280fbf5 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -340,7 +340,7 @@ static int remove_branches(struct string_list *branches) const char *refname = item->string; unsigned char *sha1 = item->util; - if (delete_ref(refname, sha1)) + if (delete_ref(refname, sha1, 0)) result |= error("Could not remove branch %s", refname); } return result; @@ -570,7 +570,7 @@ static int prune(int argc, const char **argv) const char *refname = states.stale.items[i].util; if (!dry_run) - result |= delete_ref(refname, NULL); + result |= delete_ref(refname, NULL, 0); printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned", abbrev_ref(refname, "refs/remotes/")); diff --git a/builtin-reset.c b/builtin-reset.c index 16e6bb20f1..9514b77f8c 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -279,7 +279,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR); } else if (old_orig) - delete_ref("ORIG_HEAD", old_orig); + delete_ref("ORIG_HEAD", old_orig, 0); prepend_reflog_action("updating HEAD", msg, sizeof(msg)); update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR); diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 2af9f29341..ea1ad6e35f 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -226,7 +226,7 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref) if (args.verbose) fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); if (ref->deletion) { - delete_ref(rs.dst, NULL); + delete_ref(rs.dst, NULL, 0); } else update_ref("update by push", rs.dst, ref->new_sha1, NULL, 0, 0); diff --git a/builtin-tag.c b/builtin-tag.c index f2853d08c7..5b141c5846 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -125,7 +125,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn) static int delete_tag(const char *name, const char *ref, const unsigned char *sha1) { - if (delete_ref(ref, sha1)) + if (delete_ref(ref, sha1, 0)) return 1; printf("Deleted tag '%s'\n", name); return 0; diff --git a/builtin-update-ref.c b/builtin-update-ref.c index 56a0b1b39c..d8f3142c06 100644 --- a/builtin-update-ref.c +++ b/builtin-update-ref.c @@ -48,7 +48,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) die("%s: not a valid old SHA1", oldval); if (delete) - return delete_ref(refname, oldval ? oldsha1 : NULL); + return delete_ref(refname, oldval ? oldsha1 : NULL, 0); else return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL, no_deref ? REF_NODEREF : 0, DIE_ON_ERR); diff --git a/cache.h b/cache.h index 884fae826c..715348a066 100644 --- a/cache.h +++ b/cache.h @@ -420,7 +420,7 @@ extern int commit_locked_index(struct lock_file *); extern void set_alternate_index_output(const char *); extern int close_lock_file(struct lock_file *); extern void rollback_lock_file(struct lock_file *); -extern int delete_ref(const char *, const unsigned char *sha1); +extern int delete_ref(const char *, const unsigned char *sha1, int delopt); /* Environment bits from configuration mechanism */ extern int trust_executable_bit; diff --git a/receive-pack.c b/receive-pack.c index d44c19e6b5..f0145bd901 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -222,7 +222,7 @@ static const char *update(struct command *cmd) warning ("Allowing deletion of corrupt ref."); old_sha1 = NULL; } - if (delete_ref(name, old_sha1)) { + if (delete_ref(name, old_sha1, 0)) { error("failed to delete %s", name); return "failed to delete"; } diff --git a/refs.c b/refs.c index 39a3b23804..b929301752 100644 --- a/refs.c +++ b/refs.c @@ -912,25 +912,33 @@ static int repack_without_ref(const char *refname) return commit_lock_file(&packlock); } -int delete_ref(const char *refname, const unsigned char *sha1) +int delete_ref(const char *refname, const unsigned char *sha1, int delopt) { struct ref_lock *lock; - int err, i, ret = 0, flag = 0; + int err, i = 0, ret = 0, flag = 0; lock = lock_ref_sha1_basic(refname, sha1, 0, &flag); if (!lock) return 1; if (!(flag & REF_ISPACKED)) { /* loose */ - i = strlen(lock->lk->filename) - 5; /* .lock */ - lock->lk->filename[i] = 0; - err = unlink(lock->lk->filename); + const char *path; + + if (!(delopt & REF_NODEREF)) { + i = strlen(lock->lk->filename) - 5; /* .lock */ + lock->lk->filename[i] = 0; + path = lock->lk->filename; + } else { + path = git_path(refname); + } + err = unlink(path); if (err && errno != ENOENT) { ret = 1; error("unlink(%s) failed: %s", - lock->lk->filename, strerror(errno)); + path, strerror(errno)); } - lock->lk->filename[i] = '.'; + if (!(delopt & REF_NODEREF)) + lock->lk->filename[i] = '.'; } /* removing the loose one could have resurrected an earlier * packed one. Also, if it was not loose we need to repack @@ -955,11 +963,16 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) struct ref_lock *lock; struct stat loginfo; int log = !lstat(git_path("logs/%s", oldref), &loginfo); + const char *symref = NULL; + int is_symref = 0; if (S_ISLNK(loginfo.st_mode)) return error("reflog for %s is a symlink", oldref); - if (!resolve_ref(oldref, orig_sha1, 1, &flag)) + symref = resolve_ref(oldref, orig_sha1, 1, &flag); + if (flag & REF_ISSYMREF) + is_symref = 1; + if (!symref) return error("refname %s not found", oldref); if (!is_refname_available(newref, oldref, get_packed_refs(), 0)) @@ -979,12 +992,12 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) return error("unable to move logfile logs/%s to tmp-renamed-log: %s", oldref, strerror(errno)); - if (delete_ref(oldref, orig_sha1)) { + if (delete_ref(oldref, orig_sha1, REF_NODEREF)) { error("unable to delete old %s", oldref); goto rollback; } - if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1)) { + if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1, REF_NODEREF)) { if (errno==EISDIR) { if (remove_empty_directories(git_path("%s", newref))) { error("Directory not empty: %s", newref); @@ -1022,18 +1035,20 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) } logmoved = log; - lock = lock_ref_sha1_basic(newref, NULL, 0, NULL); - if (!lock) { - error("unable to lock %s for update", newref); - goto rollback; - } - - lock->force_write = 1; - hashcpy(lock->old_sha1, orig_sha1); - if (write_ref_sha1(lock, orig_sha1, logmsg)) { - error("unable to write current sha1 into %s", newref); - goto rollback; - } + if (!is_symref) { + lock = lock_ref_sha1_basic(newref, NULL, 0, NULL); + if (!lock) { + error("unable to lock %s for update", newref); + goto rollback; + } + lock->force_write = 1; + hashcpy(lock->old_sha1, orig_sha1); + if (write_ref_sha1(lock, orig_sha1, logmsg)) { + error("unable to write current sha1 into %s", newref); + goto rollback; + } + } else + create_symref(newref, symref, logmsg); return 0; diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 2147eacc50..fdeb1f529c 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -112,6 +112,15 @@ test_expect_success 'config information was renamed, too' \ "test $(git config branch.s.dummy) = Hello && test_must_fail git config branch.s/s/dummy" +test_expect_success 'renaming a symref' \ +' + git symbolic-ref refs/heads/master2 refs/heads/master && + git branch -m master2 master3 && + git symbolic-ref refs/heads/master3 && + test -f .git/refs/heads/master && + ! test -f .git/refs/heads/master2 +' + test_expect_success \ 'git branch -m u v should fail when the reflog for u is a symlink' ' git branch -l u && From 450d4c0f5a966b3f5835107ec4d8c344c8c25908 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sun, 26 Oct 2008 03:33:57 +0100 Subject: [PATCH 051/100] rename_ref(): handle the case when the reflog of a ref does not exist We tried to check if a reflog of a ref is a symlink without first checking if it exists, which is a bug. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- refs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/refs.c b/refs.c index b929301752..b39e6f2c2d 100644 --- a/refs.c +++ b/refs.c @@ -966,7 +966,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) const char *symref = NULL; int is_symref = 0; - if (S_ISLNK(loginfo.st_mode)) + if (log && S_ISLNK(loginfo.st_mode)) return error("reflog for %s is a symlink", oldref); symref = resolve_ref(oldref, orig_sha1, 1, &flag); From 569740bdd0533ef5cf032edd6233710161a35725 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sun, 26 Oct 2008 03:33:58 +0100 Subject: [PATCH 052/100] Fix git update-ref --no-deref -d. Till now --no-deref was just ignored when deleting refs, fix this. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- builtin-update-ref.c | 8 +++++--- t/t1400-update-ref.sh | 7 +++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/builtin-update-ref.c b/builtin-update-ref.c index d8f3142c06..378dc1b7a6 100644 --- a/builtin-update-ref.c +++ b/builtin-update-ref.c @@ -13,7 +13,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) { const char *refname, *oldval, *msg=NULL; unsigned char sha1[20], oldsha1[20]; - int delete = 0, no_deref = 0; + int delete = 0, no_deref = 0, flags = 0; struct option options[] = { OPT_STRING( 'm', NULL, &msg, "reason", "reason of the update"), OPT_BOOLEAN('d', NULL, &delete, "deletes the reference"), @@ -47,9 +47,11 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) if (oldval && *oldval && get_sha1(oldval, oldsha1)) die("%s: not a valid old SHA1", oldval); + if (no_deref) + flags = REF_NODEREF; if (delete) - return delete_ref(refname, oldval ? oldsha1 : NULL, 0); + return delete_ref(refname, oldval ? oldsha1 : NULL, flags); else return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL, - no_deref ? REF_NODEREF : 0, DIE_ON_ERR); + flags, DIE_ON_ERR); } diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 04c2b164bc..8139cd6cc9 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -75,6 +75,13 @@ test_expect_success "delete $m (by HEAD)" ' ' rm -f .git/$m +cp -f .git/HEAD .git/HEAD.orig +test_expect_success "delete symref without dereference" ' + git update-ref --no-deref -d HEAD && + ! test -f .git/HEAD +' +cp -f .git/HEAD.orig .git/HEAD + test_expect_success '(not) create HEAD with old sha1' " test_must_fail git update-ref HEAD $A $B " From ddff8563510a2c5c675d488a02e2642306430fc1 Mon Sep 17 00:00:00 2001 From: Charles Bailey Date: Sat, 25 Oct 2008 11:38:14 -0400 Subject: [PATCH 053/100] git-archive: work in bare repos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This moves the call to git_config to a place where it doesn't break the logic for using git archive in a bare repository but retains the fix to make git archive respect core.autocrlf. Tests are by René Scharfe. Signed-off-by: Charles Bailey Tested-by: Deskin Miller Signed-off-by: Junio C Hamano --- archive.c | 2 ++ builtin-archive.c | 2 -- t/t5000-tar-tree.sh | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/archive.c b/archive.c index e2280df567..45d242b884 100644 --- a/archive.c +++ b/archive.c @@ -338,5 +338,7 @@ int write_archive(int argc, const char **argv, const char *prefix, parse_treeish_arg(argv, &args, prefix); parse_pathspec_arg(argv + 1, &args); + git_config(git_default_config, NULL); + return ar->write_archive(&args); } diff --git a/builtin-archive.c b/builtin-archive.c index 432ce2acc6..5ceec433fd 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -111,8 +111,6 @@ int cmd_archive(int argc, const char **argv, const char *prefix) { const char *remote = NULL; - git_config(git_default_config, NULL); - remote = extract_remote_arg(&argc, argv); if (remote) return run_remote_archiver(remote, argc, argv); diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index e395ff4e34..0f27d73049 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -57,6 +57,11 @@ test_expect_success \ git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \ git commit-tree $treeid b3.tar' + +test_expect_success \ + 'git archive vs. the same in a bare repo' \ + 'test_cmp b.tar b3.tar' + test_expect_success \ 'validate file modification time' \ 'mkdir extract && @@ -151,6 +164,14 @@ test_expect_success \ 'git archive --format=zip' \ 'git archive --format=zip HEAD >d.zip' +test_expect_success \ + 'git archive --format=zip in a bare repo' \ + '(cd bare.git && git archive --format=zip HEAD) >d1.zip' + +test_expect_success \ + 'git archive --format=zip vs. the same in a bare repo' \ + 'test_cmp d.zip d1.zip' + $UNZIP -v >/dev/null 2>&1 if [ $? -eq 127 ]; then echo "Skipping ZIP tests, because unzip was not found" From 9fe7a643fcc56ca1c17afec5d5f8adca10cabdd1 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sun, 26 Oct 2008 20:37:06 +0100 Subject: [PATCH 054/100] add -p: warn if only binary changes present Current 'git add -p' will say "No changes." if there are no changes to text files, which can be confusing if there _are_ changes to binary files. Add some code to distinguish the two cases, and give a different message in the latter one. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/git-add--interactive.perl b/git-add--interactive.perl index da768ee7ac..b0223c3419 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -811,11 +811,16 @@ EOF } sub patch_update_cmd { - my @mods = grep { !($_->{BINARY}) } list_modified('file-only'); + my @all_mods = list_modified('file-only'); + my @mods = grep { !($_->{BINARY}) } @all_mods; my @them; if (!@mods) { - print STDERR "No changes.\n"; + if (@all_mods) { + print STDERR "Only binary files changed.\n"; + } else { + print STDERR "No changes.\n"; + } return 0; } if ($patch_mode) { From 108bebeab31881654b7b0f1b5b393a6655d74d3f Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sun, 26 Oct 2008 22:59:13 +0100 Subject: [PATCH 055/100] Add mksnpath which allows you to specify the output buffer This is just vsnprintf's but additionally calls cleanup_path() on the result. To be used as alternatives to mkpath() where the buffer for the created path may not be reused by subsequent calls of the same formatting function. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- cache.h | 3 +++ path.c | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/cache.h b/cache.h index 884fae826c..aea13b0822 100644 --- a/cache.h +++ b/cache.h @@ -480,6 +480,9 @@ extern int check_repository_format(void); #define DATA_CHANGED 0x0020 #define TYPE_CHANGED 0x0040 +extern char *mksnpath(char *buf, size_t n, const char *fmt, ...) + __attribute__((format (printf, 3, 4))); + /* Return a statically allocated filename matching the sha1 signature */ extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); diff --git a/path.c b/path.c index 76e8872622..8b64878c21 100644 --- a/path.c +++ b/path.c @@ -32,6 +32,21 @@ static char *cleanup_path(char *path) return path; } +char *mksnpath(char *buf, size_t n, const char *fmt, ...) +{ + va_list args; + unsigned len; + + va_start(args, fmt); + len = vsnprintf(buf, n, fmt, args); + va_end(args); + if (len >= n) { + snprintf(buf, n, bad_path); + return buf; + } + return cleanup_path(buf); +} + char *mkpath(const char *fmt, ...) { va_list args; From 94cc355287a7efc3eda76af6ae31f503a1ac098b Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sun, 26 Oct 2008 23:07:24 +0100 Subject: [PATCH 056/100] Fix mkpath abuse in dwim_ref and dwim_log of sha1_name.c Otherwise the function sometimes fail to resolve obviously correct refnames, because the string data pointed to by "str" argument were reused. The change in dwim_log does not fix anything, just optimizes away strcpy code as the path can be created directly in the available buffer. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- sha1_name.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sha1_name.c b/sha1_name.c index 4fb77f8863..75a5a7e96f 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -245,11 +245,13 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) *ref = NULL; for (p = ref_rev_parse_rules; *p; p++) { + char fullref[PATH_MAX]; unsigned char sha1_from_ref[20]; unsigned char *this_result; this_result = refs_found ? sha1_from_ref : sha1; - r = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL); + mksnpath(fullref, sizeof(fullref), *p, len, str); + r = resolve_ref(fullref, this_result, 1, NULL); if (r) { if (!refs_found++) *ref = xstrdup(r); @@ -272,7 +274,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log) char path[PATH_MAX]; const char *ref, *it; - strcpy(path, mkpath(*p, len, str)); + mksnpath(path, sizeof(path), *p, len, str); ref = resolve_ref(path, hash, 1, NULL); if (!ref) continue; From 9fa03c177ff826b439537072338af958fe01c257 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sun, 26 Oct 2008 23:08:52 +0100 Subject: [PATCH 057/100] Fix potentially dangerous uses of mkpath and git_path Replace them with mksnpath/git_snpath and a local buffer for the resulting string. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- builtin-apply.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin-apply.c b/builtin-apply.c index e9d49f133a..50b623e54c 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2850,8 +2850,8 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned unsigned int nr = getpid(); for (;;) { - const char *newpath; - newpath = mkpath("%s~%u", path, nr); + char newpath[PATH_MAX]; + mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr); if (!try_create_file(newpath, mode, buf, size)) { if (!rename(newpath, path)) return; From 058412d09748c90be996a70729b961adad084175 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sun, 26 Oct 2008 23:08:52 +0100 Subject: [PATCH 058/100] Fix potentially dangerous uses of mkpath and git_path Replace them with mksnpath/git_snpath and a local buffer for the resulting string. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- builtin-for-each-ref.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index fa6c1ed752..e46b7adc97 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -620,14 +620,16 @@ static char *get_short_ref(struct refinfo *ref) for (j = 0; j < i; j++) { const char *rule = ref_rev_parse_rules[j]; unsigned char short_objectname[20]; + char refname[PATH_MAX]; /* * the short name is ambiguous, if it resolves * (with this previous rule) to a valid ref * read_ref() returns 0 on success */ - if (!read_ref(mkpath(rule, short_name_len, short_name), - short_objectname)) + mksnpath(refname, sizeof(refname), + rule, short_name_len, short_name); + if (!read_ref(refname, short_objectname)) break; } From 18378655be4e48360241d81d7bafd9e7aa622560 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Sun, 24 Aug 2008 23:23:25 +0300 Subject: [PATCH 059/100] Install git-shell in bindir, too /etc/passwd shell field must be something execable, you can't enter "/usr/bin/git shell" there. git-shell must be present as a separate executable, or it is useless. Signed-off-by: Tommi Virtanen Signed-off-by: Junio C Hamano --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0d40f0ecca..e7e6f9c718 100644 --- a/Makefile +++ b/Makefile @@ -1355,7 +1355,7 @@ install: all $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' - $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X '$(DESTDIR_SQ)$(bindir_SQ)' + $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X '$(DESTDIR_SQ)$(bindir_SQ)' $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install ifndef NO_TCLTK From 1df2a1ce806de11d382816644cc971d16aab06b7 Mon Sep 17 00:00:00 2001 From: Nanako Shiraishi Date: Wed, 10 Sep 2008 20:03:18 +0900 Subject: [PATCH 060/100] Install git-cvsserver in $(bindir) It is one of the server side programs and needs to be found on usual $PATH. Signed-off-by: Nanako Shiraishi Signed-off-by: Junio C Hamano --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e7e6f9c718..becd008e04 100644 --- a/Makefile +++ b/Makefile @@ -1355,7 +1355,7 @@ install: all $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' - $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X '$(DESTDIR_SQ)$(bindir_SQ)' + $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X git-cvsserver '$(DESTDIR_SQ)$(bindir_SQ)' $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install ifndef NO_TCLTK From 6e381d3aff89e09d13bd855ed6e18b0aa6f1e441 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 27 Oct 2008 13:06:16 -0700 Subject: [PATCH 061/100] Add file delete/create info when we overflow rename_limit When we refuse to do rename detection due to having too many files created or deleted, let the user know the numbers. That way there is a reasonable starting point for setting the diff.renamelimit option. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diffcore-rename.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffcore-rename.c b/diffcore-rename.c index 1b2ebb4001..168a95b541 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -493,7 +493,7 @@ void diffcore_rename(struct diff_options *options) if ((num_create > rename_limit && num_src > rename_limit) || (num_create * num_src > rename_limit * rename_limit)) { if (options->warn_on_too_large_rename) - warning("too many files, skipping inexact rename detection"); + warning("too many files (created: %d deleted: %d), skipping inexact rename detection", num_create, num_src); goto cleanup; } From 797484392a823c66bd846545a43d76aa2c64f5ff Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 23 Oct 2008 22:54:09 -0700 Subject: [PATCH 062/100] compat/cygwin.c: make runtime detection of lstat/stat lessor impact The original patch that lead to an earlier commit adbc0b6 (cygwin: Use native Win32 API for stat, 2008-09-30) did not call git_default_config() and it was a good thing. The lazy config reading when lstat/stat is called for the first time to find out if core.filemode is set can happen anytime in the calling program. If it happens after the calling program parsed the configuration file to prime its default parameter settings and processed its command line parameters to tweak them, this will overwrite the values set by the program with the values read from the config file. This essentially reverts the code to the version as submitted by Mark, with a bit more comments to clarify why we do not fall back on the default configuration parser from git_cygwin_config(). Signed-off-by: Junio C Hamano --- compat/cygwin.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/compat/cygwin.c b/compat/cygwin.c index f1967532ba..ebac148392 100644 --- a/compat/cygwin.c +++ b/compat/cygwin.c @@ -91,26 +91,32 @@ static int cygwin_stat(const char *path, struct stat *buf) * functions should be used. The choice is determined by core.ignorecygwinfstricks. * Reading this option is not always possible immediately as git_dir may be * not be set yet. So until it is set, use cygwin lstat/stat functions. - * However, if the trust_executable_bit is set, we must use the Cygwin posix + * However, if core.filemode is set, we must use the Cygwin posix * stat/lstat as the Windows stat fuctions do not determine posix filemode. + * + * Note that git_cygwin_config() does NOT call git_default_config() and this + * is deliberate. Many commands read from config to establish initial + * values in variables and later tweak them from elsewhere (e.g. command line). + * init_stat() is called lazily on demand, typically much late in the program, + * and calling git_default_config() from here would break such variables. */ static int native_stat = 1; -extern int trust_executable_bit; +static int core_filemode; static int git_cygwin_config(const char *var, const char *value, void *cb) { - if (!strcmp(var, "core.ignorecygwinfstricks")) { + if (!strcmp(var, "core.ignorecygwinfstricks")) native_stat = git_config_bool(var, value); - return 0; - } - return git_default_config(var, value, cb); + else if (!strcmp(var, "core.filemode")) + core_filemode = git_config_bool(var, value); + return 0; } static int init_stat(void) { if (have_git_dir()) { git_config(git_cygwin_config, NULL); - if (!trust_executable_bit && native_stat) { + if (!core_filemode && native_stat) { cygwin_stat_fn = cygwin_stat; cygwin_lstat_fn = cygwin_lstat; } else { From fa58186c9ba50514b36ac5ef192cd7e0bc4d7780 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Wed, 29 Oct 2008 01:05:27 +0100 Subject: [PATCH 063/100] git branch -m: forbid renaming of a symref There may be cases where one would really want to rename the symbolic ref without changing its value, but "git branch -m" is not such a use-case. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- refs.c | 29 +++++++++++++---------------- t/t3200-branch.sh | 8 ++++---- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/refs.c b/refs.c index b39e6f2c2d..8a38e0822f 100644 --- a/refs.c +++ b/refs.c @@ -964,14 +964,14 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) struct stat loginfo; int log = !lstat(git_path("logs/%s", oldref), &loginfo); const char *symref = NULL; - int is_symref = 0; if (log && S_ISLNK(loginfo.st_mode)) return error("reflog for %s is a symlink", oldref); symref = resolve_ref(oldref, orig_sha1, 1, &flag); if (flag & REF_ISSYMREF) - is_symref = 1; + return error("refname %s is a symbolic ref, renaming it is not supported", + oldref); if (!symref) return error("refname %s not found", oldref); @@ -1035,20 +1035,17 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) } logmoved = log; - if (!is_symref) { - lock = lock_ref_sha1_basic(newref, NULL, 0, NULL); - if (!lock) { - error("unable to lock %s for update", newref); - goto rollback; - } - lock->force_write = 1; - hashcpy(lock->old_sha1, orig_sha1); - if (write_ref_sha1(lock, orig_sha1, logmsg)) { - error("unable to write current sha1 into %s", newref); - goto rollback; - } - } else - create_symref(newref, symref, logmsg); + lock = lock_ref_sha1_basic(newref, NULL, 0, NULL); + if (!lock) { + error("unable to lock %s for update", newref); + goto rollback; + } + lock->force_write = 1; + hashcpy(lock->old_sha1, orig_sha1); + if (write_ref_sha1(lock, orig_sha1, logmsg)) { + error("unable to write current sha1 into %s", newref); + goto rollback; + } return 0; diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index fdeb1f529c..25e9971fd8 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -112,13 +112,13 @@ test_expect_success 'config information was renamed, too' \ "test $(git config branch.s.dummy) = Hello && test_must_fail git config branch.s/s/dummy" -test_expect_success 'renaming a symref' \ +test_expect_success 'renaming a symref is not allowed' \ ' git symbolic-ref refs/heads/master2 refs/heads/master && - git branch -m master2 master3 && - git symbolic-ref refs/heads/master3 && + test_must_fail git branch -m master2 master3 && + git symbolic-ref refs/heads/master2 && test -f .git/refs/heads/master && - ! test -f .git/refs/heads/master2 + ! test -f .git/refs/heads/master3 ' test_expect_success \ From d258b25887f763db96c20d4ec805ae685f55bdd1 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Tue, 28 Oct 2008 18:27:33 +0100 Subject: [PATCH 064/100] Plug a memleak in builtin-revert Probably happened when working around git_path's problem with returned buffer being reused. Signed-off-by: Junio C Hamano --- builtin-revert.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin-revert.c b/builtin-revert.c index 27881e9493..c41788685b 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -269,7 +269,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) int i; char *oneline, *reencoded_message = NULL; const char *message, *encoding; - const char *defmsg = xstrdup(git_path("MERGE_MSG")); + char *defmsg = xstrdup(git_path("MERGE_MSG")); git_config(git_default_config, NULL); me = action == REVERT ? "revert" : "cherry-pick"; @@ -426,6 +426,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) return execv_git_cmd(args); } free(reencoded_message); + free(defmsg); return 0; } From fe2d7776d5191896e361973f478ca078fa95b324 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 27 Oct 2008 10:22:21 +0100 Subject: [PATCH 065/100] Add git_snpath: a .git path formatting routine with output buffer The function's purpose is to replace git_path where the buffer of formatted path may not be reused by subsequent calls of the function or will be copied anyway. Signed-off-by: Junio C Hamano --- cache.h | 2 ++ path.c | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/cache.h b/cache.h index aea13b0822..3d5a08ea81 100644 --- a/cache.h +++ b/cache.h @@ -482,6 +482,8 @@ extern int check_repository_format(void); extern char *mksnpath(char *buf, size_t n, const char *fmt, ...) __attribute__((format (printf, 3, 4))); +extern char *git_snpath(char *buf, size_t n, const char *fmt, ...) + __attribute__((format (printf, 3, 4))); /* Return a statically allocated filename matching the sha1 signature */ extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); diff --git a/path.c b/path.c index 8b64878c21..85ab28a0f1 100644 --- a/path.c +++ b/path.c @@ -47,6 +47,29 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...) return cleanup_path(buf); } +char *git_snpath(char *buf, size_t n, const char *fmt, ...) +{ + const char *git_dir = get_git_dir(); + va_list args; + size_t len; + + len = strlen(git_dir); + if (n < len + 1) + goto bad; + memcpy(buf, git_dir, len); + if (len && !is_dir_sep(git_dir[len-1])) + buf[len++] = '/'; + va_start(args, fmt); + len += vsnprintf(buf + len, n - len, fmt, args); + va_end(args); + if (len >= n) + goto bad; + return cleanup_path(buf); +bad: + snprintf(buf, n, bad_path); + return buf; +} + char *mkpath(const char *fmt, ...) { va_list args; From 958a4789e0e74da245175e31bd3b9b354ee0e063 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 27 Oct 2008 11:11:40 +0100 Subject: [PATCH 066/100] Fix potentially dangerous use of git_path in ref.c Signed-off-by: Junio C Hamano --- refs.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/refs.c b/refs.c index 39a3b23804..71443cdf85 100644 --- a/refs.c +++ b/refs.c @@ -401,7 +401,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * *flag = 0; for (;;) { - const char *path = git_path("%s", ref); + char path[PATH_MAX]; struct stat st; char *buf; int fd; @@ -409,6 +409,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * if (--depth < 0) return NULL; + git_snpath(path, sizeof(path), "%s", ref); /* Special case: non-existing file. * Not having the refs/heads/new-branch is OK * if we are writing into it, so is .git/HEAD @@ -1121,13 +1122,14 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1, int logfd, written, oflags = O_APPEND | O_WRONLY; unsigned maxlen, len; int msglen; - char *log_file, *logrec; + char log_file[PATH_MAX]; + char *logrec; const char *committer; if (log_all_ref_updates < 0) log_all_ref_updates = !is_bare_repository(); - log_file = git_path("logs/%s", ref_name); + git_snpath(log_file, sizeof(log_file), "logs/%s", ref_name); if (log_all_ref_updates && (!prefixcmp(ref_name, "refs/heads/") || From aba13e7c0566f578f866504bfcb388a72f7e5079 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 27 Oct 2008 11:17:51 +0100 Subject: [PATCH 067/100] git_pathdup: returns xstrdup-ed copy of the formatted path Signed-off-by: Junio C Hamano --- cache.h | 2 ++ path.c | 24 ++++++++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/cache.h b/cache.h index 3d5a08ea81..eaacd6dd9f 100644 --- a/cache.h +++ b/cache.h @@ -484,6 +484,8 @@ extern char *mksnpath(char *buf, size_t n, const char *fmt, ...) __attribute__((format (printf, 3, 4))); extern char *git_snpath(char *buf, size_t n, const char *fmt, ...) __attribute__((format (printf, 3, 4))); +extern char *git_pathdup(const char *fmt, ...) + __attribute__((format (printf, 1, 2))); /* Return a statically allocated filename matching the sha1 signature */ extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); diff --git a/path.c b/path.c index 85ab28a0f1..092ce57190 100644 --- a/path.c +++ b/path.c @@ -47,10 +47,9 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...) return cleanup_path(buf); } -char *git_snpath(char *buf, size_t n, const char *fmt, ...) +static char *git_vsnpath(char *buf, size_t n, const char *fmt, va_list args) { const char *git_dir = get_git_dir(); - va_list args; size_t len; len = strlen(git_dir); @@ -59,9 +58,7 @@ char *git_snpath(char *buf, size_t n, const char *fmt, ...) memcpy(buf, git_dir, len); if (len && !is_dir_sep(git_dir[len-1])) buf[len++] = '/'; - va_start(args, fmt); len += vsnprintf(buf + len, n - len, fmt, args); - va_end(args); if (len >= n) goto bad; return cleanup_path(buf); @@ -70,6 +67,25 @@ bad: return buf; } +char *git_snpath(char *buf, size_t n, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + (void)git_vsnpath(buf, n, fmt, args); + va_end(args); + return buf; +} + +char *git_pathdup(const char *fmt, ...) +{ + char path[PATH_MAX]; + va_list args; + va_start(args, fmt); + (void)git_vsnpath(path, sizeof(path), fmt, args); + va_end(args); + return xstrdup(path); +} + char *mkpath(const char *fmt, ...) { va_list args; From a4f34cbb4cea1f0b0e625b528f269f4b517c64f8 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 27 Oct 2008 11:22:09 +0100 Subject: [PATCH 068/100] Use git_pathdup instead of xstrdup(git_path(...)) Signed-off-by: Junio C Hamano --- builtin-config.c | 2 +- builtin-reflog.c | 4 ++-- builtin-revert.c | 2 +- builtin-tag.c | 2 +- config.c | 6 +++--- environment.c | 2 +- refs.c | 2 +- rerere.c | 2 +- server-info.c | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/builtin-config.c b/builtin-config.c index 91fdc4985d..f71016204b 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -84,7 +84,7 @@ static int get_value(const char* key_, const char* regex_) local = config_exclusive_filename; if (!local) { const char *home = getenv("HOME"); - local = repo_config = xstrdup(git_path("config")); + local = repo_config = git_pathdup("config"); if (git_config_global() && home) global = xstrdup(mkpath("%s/.gitconfig", home)); if (git_config_system()) diff --git a/builtin-reflog.c b/builtin-reflog.c index 196fa03b7f..da96da317b 100644 --- a/builtin-reflog.c +++ b/builtin-reflog.c @@ -277,11 +277,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, lock = lock_any_ref_for_update(ref, sha1, 0); if (!lock) return error("cannot lock ref '%s'", ref); - log_file = xstrdup(git_path("logs/%s", ref)); + log_file = git_pathdup("logs/%s", ref); if (!file_exists(log_file)) goto finish; if (!cmd->dry_run) { - newlog_path = xstrdup(git_path("logs/%s.lock", ref)); + newlog_path = git_pathdup("logs/%s.lock", ref); cb.newlog = fopen(newlog_path, "w"); } diff --git a/builtin-revert.c b/builtin-revert.c index 27881e9493..5c4ab58f46 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -269,7 +269,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) int i; char *oneline, *reencoded_message = NULL; const char *message, *encoding; - const char *defmsg = xstrdup(git_path("MERGE_MSG")); + const char *defmsg = git_pathdup("MERGE_MSG"); git_config(git_default_config, NULL); me = action == REVERT ? "revert" : "cherry-pick"; diff --git a/builtin-tag.c b/builtin-tag.c index f2853d08c7..6c6c35176e 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -283,7 +283,7 @@ static void create_tag(const unsigned char *object, const char *tag, int fd; /* write the template message before editing: */ - path = xstrdup(git_path("TAG_EDITMSG")); + path = git_pathdup("TAG_EDITMSG"); fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); if (fd < 0) die("could not create file '%s': %s", diff --git a/config.c b/config.c index 53f04a076a..82807c83b2 100644 --- a/config.c +++ b/config.c @@ -630,7 +630,7 @@ int git_config(config_fn_t fn, void *data) free(user_config); } - repo_config = xstrdup(git_path("config")); + repo_config = git_pathdup("config"); ret += git_config_from_file(fn, repo_config, data); free(repo_config); return ret; @@ -872,7 +872,7 @@ int git_config_set_multivar(const char* key, const char* value, if (config_exclusive_filename) config_filename = xstrdup(config_exclusive_filename); else - config_filename = xstrdup(git_path("config")); + config_filename = git_pathdup("config"); /* * Since "key" actually contains the section name and the real @@ -1132,7 +1132,7 @@ int git_config_rename_section(const char *old_name, const char *new_name) if (config_exclusive_filename) config_filename = xstrdup(config_exclusive_filename); else - config_filename = xstrdup(git_path("config")); + config_filename = git_pathdup("config"); out_fd = hold_lock_file_for_update(lock, config_filename, 0); if (out_fd < 0) { ret = error("could not lock config file %s", config_filename); diff --git a/environment.c b/environment.c index 0c6d11f6a0..df4f03a95f 100644 --- a/environment.c +++ b/environment.c @@ -71,7 +71,7 @@ static void setup_git_env(void) } git_graft_file = getenv(GRAFT_ENVIRONMENT); if (!git_graft_file) - git_graft_file = xstrdup(git_path("info/grafts")); + git_graft_file = git_pathdup("info/grafts"); } int is_bare_repository(void) diff --git a/refs.c b/refs.c index 71443cdf85..d589b25562 100644 --- a/refs.c +++ b/refs.c @@ -1258,7 +1258,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master, const char *lockpath; char ref[1000]; int fd, len, written; - char *git_HEAD = xstrdup(git_path("%s", ref_target)); + char *git_HEAD = git_pathdup("%s", ref_target); unsigned char old_sha1[20], new_sha1[20]; if (logmsg && read_ref(ref_target, old_sha1)) diff --git a/rerere.c b/rerere.c index 323e493daf..3d6ee8fa2a 100644 --- a/rerere.c +++ b/rerere.c @@ -345,7 +345,7 @@ int setup_rerere(struct string_list *merge_rr) if (!is_rerere_enabled()) return -1; - merge_rr_path = xstrdup(git_path("MERGE_RR")); + merge_rr_path = git_pathdup("MERGE_RR"); fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1); read_rr(merge_rr); return fd; diff --git a/server-info.c b/server-info.c index c1c073b2f0..66b0d9d878 100644 --- a/server-info.c +++ b/server-info.c @@ -25,7 +25,7 @@ static int add_info_ref(const char *path, const unsigned char *sha1, int flag, v static int update_info_refs(int force) { - char *path0 = xstrdup(git_path("info/refs")); + char *path0 = git_pathdup("info/refs"); int len = strlen(path0); char *path1 = xmalloc(len + 2); From 9b6f84d2c2ff84ac3ef2215b6ea8ee45b6d943cd Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Thu, 30 Oct 2008 11:45:03 +0100 Subject: [PATCH 069/100] asciidoc: add minor workaround to add an empty line after code blocks Insert an empty in manpages after code blocks to force and empty line. The problem can be seen on the manpage for the git tutorial, where an example command and the following paragraph is printed with no empty line between them: First, note that you can get documentation for a command such as git log --graph with: $ man git-log It is a good idea to introduce yourself to git [...] Signed-off-by: Jonas Fonseca Signed-off-by: Junio C Hamano --- Documentation/asciidoc.conf | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf index 40d43b78ee..2da867d2f8 100644 --- a/Documentation/asciidoc.conf +++ b/Documentation/asciidoc.conf @@ -40,6 +40,26 @@ endif::doctype-manpage[] {title#} endif::docbook-xsl-172[] + +ifdef::docbook-xsl-172[] +ifdef::doctype-manpage[] +# The following two small workarounds insert a simple paragraph after screen +[listingblock] +{title} + +| + +{title#} + +[verseblock] +{title} +{title%} +{title#} +| + +{title#} +endif::doctype-manpage[] +endif::docbook-xsl-172[] endif::backend-docbook[] ifdef::doctype-manpage[] From 0ea9ca07aa76bef80c066b1a70366cf90caa703d Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Thu, 30 Oct 2008 10:00:29 +0100 Subject: [PATCH 070/100] Add --verbose|-v to test-chmtime This allows us replace perl when getting the mtime of a file because of time zone conversions, though at the moment only one platform which does this has been identified: Cygwin when used with ActiveState Perl (as usual). The output format is: TAB TAB ... which, if only mtime is needed can be parsed with cut(1): test-chmtime -v +0 filename1 | cut -f 1 Also, the change adds a description of programs features, with examples. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- test-chmtime.c | 97 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 24 deletions(-) diff --git a/test-chmtime.c b/test-chmtime.c index 90da448ebe..d5358cbaac 100644 --- a/test-chmtime.c +++ b/test-chmtime.c @@ -1,39 +1,83 @@ +/* + * This program can either change modification time of the given + * file(s) or just print it. The program does not change atime nor + * ctime (their values are explicitely preserved). + * + * The mtime can be changed to an absolute value: + * + * test-chmtime = file... + * + * Relative to the current time as returned by time(3): + * + * test-chmtime =+ (or =-) file... + * + * Or relative to the current mtime of the file: + * + * test-chmtime file... + * test-chmtime + (or -) file... + * + * Examples: + * + * To just print the mtime use --verbose and set the file mtime offset to 0: + * + * test-chmtime -v +0 file + * + * To set the mtime to current time: + * + * test-chmtime =+0 file + * + */ #include "git-compat-util.h" #include -static const char usage_str[] = "(+|=|=+|=-|-) ..."; +static const char usage_str[] = "-v|--verbose (+|=|=+|=-|-) ..."; + +static int timespec_arg(const char *arg, long int *set_time, int *set_eq) +{ + char *test; + const char *timespec = arg; + *set_eq = (*timespec == '=') ? 1 : 0; + if (*set_eq) { + timespec++; + if (*timespec == '+') { + *set_eq = 2; /* relative "in the future" */ + timespec++; + } + } + *set_time = strtol(timespec, &test, 10); + if (*test) { + fprintf(stderr, "Not a base-10 integer: %s\n", arg + 1); + return 0; + } + if ((*set_eq && *set_time < 0) || *set_eq == 2) { + time_t now = time(NULL); + *set_time += now; + } + return 1; +} int main(int argc, const char *argv[]) { - int i; - int set_eq; - long int set_time; - char *test; - const char *timespec; + static int verbose; + + int i = 1; + /* no mtime change by default */ + int set_eq = 0; + long int set_time = 0; if (argc < 3) goto usage; - timespec = argv[1]; - set_eq = (*timespec == '=') ? 1 : 0; - if (set_eq) { - timespec++; - if (*timespec == '+') { - set_eq = 2; /* relative "in the future" */ - timespec++; - } + if (strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) { + verbose = 1; + ++i; } - set_time = strtol(timespec, &test, 10); - if (*test) { - fprintf(stderr, "Not a base-10 integer: %s\n", argv[1] + 1); + if (timespec_arg(argv[i], &set_time, &set_eq)) + ++i; + else goto usage; - } - if ((set_eq && set_time < 0) || set_eq == 2) { - time_t now = time(NULL); - set_time += now; - } - for (i = 2; i < argc; i++) { + for (; i < argc; i++) { struct stat sb; struct utimbuf utb; @@ -46,7 +90,12 @@ int main(int argc, const char *argv[]) utb.actime = sb.st_atime; utb.modtime = set_eq ? set_time : sb.st_mtime + set_time; - if (utime(argv[i], &utb) < 0) { + if (verbose) { + uintmax_t mtime = utb.modtime < 0 ? 0: utb.modtime; + printf("%"PRIuMAX"\t%s\n", mtime, argv[i]); + } + + if (utb.modtime != sb.st_mtime && utime(argv[i], &utb) < 0) { fprintf(stderr, "Failed to modify time on %s: %s\n", argv[i], strerror(errno)); return -1; From 111539a3c75c22df7f1bbe242ce2f886b01d2491 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Thu, 30 Oct 2008 11:20:27 +0100 Subject: [PATCH 071/100] Use test-chmtime -v instead of perl in t5000 to get mtime of a file The test was broken on admittedly broken combination of Windows, Cygwin, and ActiveState Perl. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- t/t5000-tar-tree.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 0f27d73049..c942c8be85 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -90,7 +90,7 @@ test_expect_success \ 'validate file modification time' \ 'mkdir extract && "$TAR" xf b.tar -C extract a/a && - perl -e '\''print((stat("extract/a/a"))[9], "\n")'\'' >b.mtime && + test-chmtime -v +0 extract/a/a |cut -f 1 >b.mtime && echo "1117231200" >expected.mtime && diff expected.mtime b.mtime' From 41d8cf7d7fd79fe1fd00b04052c49bffaedfd309 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Fri, 31 Oct 2008 01:04:46 +0100 Subject: [PATCH 072/100] bash completion: add doubledash to "git show" Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 751e273e1a..39a1ce5a39 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1410,6 +1410,8 @@ _git_shortlog () _git_show () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --pretty=*) From 2c850f1221efd2441c3794e163ea3918ce31c15b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kr=C3=BCger?= Date: Thu, 30 Oct 2008 19:14:33 +0100 Subject: [PATCH 073/100] Documentation: clarify information about 'ident' attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation spoke of the attribute being set "to" a path; this can mistakenly be interpreted as "the attribute needs to have its value set to some kind of path". This clarifies things. Signed-off-by: Jan Krüger Signed-off-by: Junio C Hamano --- Documentation/gitattributes.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 53da9b4f6b..42776f089b 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -163,8 +163,8 @@ few exceptions. Even though... `ident` ^^^^^^^ -When the attribute `ident` is set to a path, git replaces -`$Id$` in the blob object with `$Id:`, followed by +When the attribute `ident` is set for a path, git replaces +`$Id$` in the blob object with `$Id:`, followed by the 40-character hexadecimal blob object name, followed by a dollar sign `$` upon checkout. Any byte sequence that begins with `$Id:` and ends with `$` in the worktree file is replaced From e855bfc0404d3344787541c1bdaa1e8d44398eb3 Mon Sep 17 00:00:00 2001 From: Deskin Miller Date: Fri, 31 Oct 2008 00:10:25 -0400 Subject: [PATCH 074/100] git-svn: change dashed git-commit-tree to git commit-tree Signed-off-by: Deskin Miller Signed-off-by: Junio C Hamano --- git-svn.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-svn.perl b/git-svn.perl index 2e68c68d49..56238dad08 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2202,7 +2202,7 @@ sub do_git_commit { } die "Tree is not a valid sha1: $tree\n" if $tree !~ /^$::sha1$/o; - my @exec = ('git-commit-tree', $tree); + my @exec = ('git', 'commit-tree', $tree); foreach ($self->get_commit_parents($log_entry)) { push @exec, '-p', $_; } From 045a476f91a9a308c823a2709977163238baa3fd Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sat, 1 Nov 2008 00:25:44 +0100 Subject: [PATCH 075/100] update-ref --no-deref -d: handle the case when the pointed ref is packed In this case we did nothing in the past, but we should delete the reference in fact. The problem was that when the symref is not packed but the referenced ref is packed, then we assumed that the symref is packed as well, but symrefs are never packed. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- refs.c | 2 +- t/t1400-update-ref.sh | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/refs.c b/refs.c index 8a38e0822f..0d239e15b0 100644 --- a/refs.c +++ b/refs.c @@ -920,7 +920,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt) lock = lock_ref_sha1_basic(refname, sha1, 0, &flag); if (!lock) return 1; - if (!(flag & REF_ISPACKED)) { + if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) { /* loose */ const char *path; diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 8139cd6cc9..bd589268fc 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -82,6 +82,17 @@ test_expect_success "delete symref without dereference" ' ' cp -f .git/HEAD.orig .git/HEAD +test_expect_success "delete symref without dereference when the referred ref is packed" ' + echo foo >foo.c && + git add foo.c && + git commit -m foo && + git pack-refs --all && + git update-ref --no-deref -d HEAD && + ! test -f .git/HEAD +' +cp -f .git/HEAD.orig .git/HEAD +git update-ref -d $m + test_expect_success '(not) create HEAD with old sha1' " test_must_fail git update-ref HEAD $A $B " From 98a6846bb8c5acb55d25d429954ec4ea2f4f320e Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 15 Oct 2008 13:28:20 +0400 Subject: [PATCH 076/100] git-gui: Add a dialog that shows the OpenSSH public key. Generating a new SSH key or finding an existing one may be a difficult task for non-technical users, especially on Windows. This commit adds a new dialog that shows the public key, or allows the user to generate a new one if none were found. Since this is a convenience/informational feature for new users, and the dialog is mostly read-only, it is located in the Help menu. The command line used to invoke ssh-keygen is designed to force it to use SSH_ASKPASS if available, or accept empty passphrases, but _never_ wait for user response on the tty. Signed-off-by: Alexander Gavrilov Acked-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- git-gui.sh | 4 ++ lib/sshkey.tcl | 126 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 lib/sshkey.tcl diff --git a/git-gui.sh b/git-gui.sh index 4f951399ff..e4d1f70f1c 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2536,6 +2536,10 @@ proc start_browser {url} { .mbar.help add command -label [mc "Online Documentation"] \ -command [list start_browser $doc_url] + +.mbar.help add command -label [mc "Show SSH Key"] \ + -command do_ssh_key + unset doc_path doc_url # -- Standard bindings diff --git a/lib/sshkey.tcl b/lib/sshkey.tcl new file mode 100644 index 0000000000..82a1a80ff4 --- /dev/null +++ b/lib/sshkey.tcl @@ -0,0 +1,126 @@ +# git-gui about git-gui dialog +# Copyright (C) 2006, 2007 Shawn Pearce + +proc find_ssh_key {} { + foreach name {~/.ssh/id_dsa.pub ~/.ssh/id_rsa.pub ~/.ssh/identity.pub} { + if {[file exists $name]} { + set fh [open $name r] + set cont [read $fh] + close $fh + return [list $name $cont] + } + } + + return {} +} + +proc do_ssh_key {} { + global sshkey_title have_tk85 sshkey_fd + + set w .sshkey_dialog + if {[winfo exists $w]} { + raise $w + return + } + + toplevel $w + wm transient $w . + + set finfo [find_ssh_key] + if {$finfo eq {}} { + set sshkey_title [mc "No keys found."] + set gen_state normal + } else { + set sshkey_title [mc "Found a public key in: %s" [lindex $finfo 0]] + set gen_state disabled + } + + frame $w.header -relief flat + label $w.header.lbl -textvariable sshkey_title -anchor w + button $w.header.gen -text [mc "Generate Key"] \ + -command [list make_ssh_key $w] -state $gen_state + pack $w.header.lbl -side left -expand 1 -fill x + pack $w.header.gen -side right + pack $w.header -fill x -pady 5 -padx 5 + + text $w.contents -width 60 -height 10 -wrap char -relief sunken + pack $w.contents -fill both -expand 1 + if {$have_tk85} { + $w.contents configure -inactiveselectbackground darkblue + } + + frame $w.buttons + button $w.buttons.close -text [mc Close] \ + -default active -command [list destroy $w] + pack $w.buttons.close -side right + button $w.buttons.copy -text [mc "Copy To Clipboard"] \ + -command [list tk_textCopy $w.contents] + pack $w.buttons.copy -side left + pack $w.buttons -side bottom -fill x -pady 5 -padx 5 + + if {$finfo ne {}} { + $w.contents insert end [lindex $finfo 1] sel + } + $w.contents configure -state disabled + + bind $w "grab $w; focus $w.buttons.close" + bind $w "destroy $w" + bind $w "destroy $w" + bind $w kill_sshkey + wm title $w [mc "Your OpenSSH Public Key"] + tk::PlaceWindow $w widget . + tkwait window $w +} + +proc make_ssh_key {w} { + global sshkey_title sshkey_output sshkey_fd + + set sshkey_title [mc "Generating..."] + $w.header.gen configure -state disabled + + set cmdline [list sh -c {echo | ssh-keygen -q -t rsa -f ~/.ssh/id_rsa 2>&1}] + + if {[catch { set sshkey_fd [_open_stdout_stderr $cmdline] } err]} { + error_popup [mc "Could not start ssh-keygen:\n\n%s" $err] + return + } + + set sshkey_output {} + fconfigure $sshkey_fd -blocking 0 + fileevent $sshkey_fd readable [list read_sshkey_output $sshkey_fd $w] +} + +proc kill_sshkey {} { + global sshkey_fd + if {![info exists sshkey_fd]} return + catch { kill_file_process $sshkey_fd } + catch { close $sshkey_fd } +} + +proc read_sshkey_output {fd w} { + global sshkey_fd sshkey_output sshkey_title + + set sshkey_output "$sshkey_output[read $fd]" + if {![eof $fd]} return + + fconfigure $fd -blocking 1 + unset sshkey_fd + + $w.contents configure -state normal + if {[catch {close $fd} err]} { + set sshkey_title [mc "Generation failed."] + $w.contents insert end $err + $w.contents insert end "\n" + $w.contents insert end $sshkey_output + } else { + set finfo [find_ssh_key] + if {$finfo eq {}} { + set sshkey_title [mc "Generation succeded, but no keys found."] + $w.contents insert end $sshkey_output + } else { + set sshkey_title [mc "Your key is in: %s" [lindex $finfo 0]] + $w.contents insert end [lindex $finfo 1] sel + } + } + $w.contents configure -state disable +} From 8c76212529570aed70c46d6f252cb1a4010f3f2e Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 15 Oct 2008 13:28:21 +0400 Subject: [PATCH 077/100] git-gui: Add a simple implementation of SSH_ASKPASS. OpenSSH allows specifying an external program to use for direct user interaction. While most Linux systems already have such programs, some environments, for instance, msysgit, lack it. This patch adds a simple fallback Tcl implementation of the tool. In msysgit it is also necessary to set a fake value of the DISPLAY variable, because otherwise ssh won't even try to use SSH_ASKPASS handlers. Signed-off-by: Alexander Gavrilov Acked-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- Makefile | 2 ++ git-gui--askpass | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ git-gui.sh | 12 ++++++++++ 3 files changed, 73 insertions(+) create mode 100755 git-gui--askpass diff --git a/Makefile b/Makefile index 55765c8a3a..3ad8a21b30 100644 --- a/Makefile +++ b/Makefile @@ -285,6 +285,7 @@ all:: $(GITGUI_MAIN) lib/tclIndex $(ALL_MSGFILES) install: all $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL_D1) $(QUIET)$(INSTALL_X0)git-gui $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' + $(QUIET)$(INSTALL_X0)git-gui--askpass $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(INSTALL_L0)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L1)'$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' $(INSTALL_L2)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L3) &&) true ifdef GITGUI_WINDOWS_WRAPPER $(QUIET)$(INSTALL_R0)git-gui.tcl $(INSTALL_R1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' @@ -302,6 +303,7 @@ endif uninstall: $(QUIET)$(CLEAN_DST) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui $(REMOVE_F1) + $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui--askpass $(REMOVE_F1) $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/$p $(REMOVE_F1) &&) true ifdef GITGUI_WINDOWS_WRAPPER $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui.tcl $(REMOVE_F1) diff --git a/git-gui--askpass b/git-gui--askpass new file mode 100755 index 0000000000..12e117ecb1 --- /dev/null +++ b/git-gui--askpass @@ -0,0 +1,59 @@ +#!/bin/sh +# Tcl ignores the next line -*- tcl -*- \ +exec wish "$0" -- "$@" + +# This is a trivial implementation of an SSH_ASKPASS handler. +# Git-gui uses this script if none are already configured. + +set answer {} +set yesno 0 +set rc 255 + +if {$argc < 1} { + set prompt "Enter your OpenSSH passphrase:" +} else { + set prompt [join $argv " "] + if {[regexp -nocase {\(yes\/no\)\?\s*$} $prompt]} { + set yesno 1 + } +} + +message .m -text $prompt -justify center -aspect 4000 +pack .m -side top -fill x -padx 20 -pady 20 -expand 1 + +entry .e -textvariable answer -width 50 +pack .e -side top -fill x -padx 10 -pady 10 + +if {!$yesno} { + .e configure -show "*" +} + +frame .b +button .b.ok -text OK -command finish +button .b.cancel -text Cancel -command {destroy .} + +pack .b.ok -side left -expand 1 +pack .b.cancel -side right -expand 1 +pack .b -side bottom -fill x -padx 10 -pady 10 + +bind . {focus -force .e} +bind . finish +bind . {destroy .} +bind . {exit $rc} + +proc finish {} { + if {$::yesno} { + if {$::answer ne "yes" && $::answer ne "no"} { + tk_messageBox -icon error -title "Error" -type ok \ + -message "Only 'yes' or 'no' input allowed." + return + } + } + + set ::rc 0 + puts $::answer + destroy . +} + +wm title . "OpenSSH" +tk::PlaceWindow . diff --git a/git-gui.sh b/git-gui.sh index e4d1f70f1c..12b496bec9 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -592,6 +592,11 @@ bind . { if {[is_Windows]} { wm iconbitmap . -default $oguilib/git-gui.ico set ::tk::AlwaysShowSelection 1 + + # Spoof an X11 display for SSH + if {![info exists env(DISPLAY)]} { + set env(DISPLAY) :9999 + } } ###################################################################### @@ -1070,6 +1075,13 @@ set nullid2 "0000000000000000000000000000000000000001" set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}] +###################################################################### + +# Suggest our implementation of askpass, if none is set +if {![info exists env(SSH_ASKPASS)]} { + set env(SSH_ASKPASS) [gitexec git-gui--askpass] +} + ###################################################################### ## ## task management From 63aa1d0cb78e8fcf36ea2b8b65750d9a45d59f63 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 14 Oct 2008 13:48:37 +0200 Subject: [PATCH 078/100] git-gui: Do not munge conflict marker lines in a normal diff Previously, conflict markers were highlighted in two ways: (1) They received a distinguishing color; and (2) they had the '+' removed at the beginning of the line. However, by doing (2), a hunk that contained conflict markers could not be staged or unstaged because the resulting patch was corrupted. With this change we no longer modify the diff text of a 2-way diff, so that "Stage Hunk" and friends work. Note that 3-way diff of a conflicted file is unaffected by this change, and '++' before conflict markers is still removed. But this has no negative impact because in this mode staging hunks or lines is disabled anyway. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- lib/diff.tcl | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/diff.tcl b/lib/diff.tcl index bdcbbf86e9..94ee38cccc 100644 --- a/lib/diff.tcl +++ b/lib/diff.tcl @@ -377,7 +377,6 @@ proc read_diff {fd cont_info} { {+} { if {[regexp {^\+([<>]{7} |={7})} $line _g op]} { set is_conflict_diff 1 - set line [string replace $line 0 0 { }] set tags d$op } else { set tags d_+ From 9d83c6aa44d2ff68111865b6f72e7321e54a8972 Mon Sep 17 00:00:00 2001 From: Christian Stimming Date: Sat, 25 Oct 2008 22:51:05 +0200 Subject: [PATCH 079/100] git-gui: Update German translation. Not yet completed, though. Signed-off-by: Christian Stimming Signed-off-by: Shawn O. Pearce --- po/de.po | 279 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 233 insertions(+), 46 deletions(-) diff --git a/po/de.po b/po/de.po index 793cca1e79..5c04812b1a 100644 --- a/po/de.po +++ b/po/de.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: git-gui\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-09-13 10:20+0200\n" -"PO-Revision-Date: 2008-09-13 10:24+0200\n" +"POT-Creation-Date: 2008-10-25 13:32+0200\n" +"PO-Revision-Date: 2008-10-25 22:47+0200\n" "Last-Translator: Christian Stimming \n" "Language-Team: German\n" "MIME-Version: 1.0\n" @@ -86,7 +86,17 @@ msgstr "Dateistatus aktualisieren..." msgid "Scanning for modified files ..." msgstr "Nach geänderten Dateien suchen..." -#: git-gui.sh:1324 lib/browser.tcl:246 +#: git-gui.sh:1325 +#, fuzzy +msgid "Calling prepare-commit-msg hook..." +msgstr "Aufrufen der Vor-Eintragen-Kontrolle..." + +#: git-gui.sh:1342 +#, fuzzy +msgid "Commit declined by prepare-commit-msg hook." +msgstr "Eintragen abgelehnt durch Vor-Eintragen-Kontrolle (»pre-commit hook«)." + +#: git-gui.sh:1502 lib/browser.tcl:246 msgid "Ready." msgstr "Bereit." @@ -170,7 +180,11 @@ msgstr "Zusammenführen" msgid "Remote" msgstr "Andere Archive" -#: git-gui.sh:1879 +#: git-gui.sh:2242 +msgid "Explore Working Copy" +msgstr "Arbeitskopie im Dateimanager" + +#: git-gui.sh:2247 msgid "Browse Current Branch's Files" msgstr "Aktuellen Zweig durchblättern" @@ -267,7 +281,15 @@ msgstr "Löschen..." msgid "Reset..." msgstr "Zurücksetzen..." -#: git-gui.sh:2002 git-gui.sh:2389 +#: git-gui.sh:2372 +msgid "Done" +msgstr "Fertig" + +#: git-gui.sh:2374 +msgid "Commit@@verb" +msgstr "Eintragen" + +#: git-gui.sh:2383 git-gui.sh:2786 msgid "New Commit" msgstr "Neue Version" @@ -307,11 +329,7 @@ msgstr "Mehr Zeilen anzeigen" msgid "Sign Off" msgstr "Abzeichnen" -#: git-gui.sh:2053 git-gui.sh:2372 -msgid "Commit@@verb" -msgstr "Eintragen" - -#: git-gui.sh:2064 +#: git-gui.sh:2458 msgid "Local Merge..." msgstr "Lokales Zusammenführen..." @@ -319,11 +337,19 @@ msgstr "Lokales Zusammenführen..." msgid "Abort Merge..." msgstr "Zusammenführen abbrechen..." -#: git-gui.sh:2081 +#: git-gui.sh:2475 +msgid "Add..." +msgstr "Hinzufügen..." + +#: git-gui.sh:2479 msgid "Push..." msgstr "Versenden..." -#: git-gui.sh:2197 git-gui.sh:2219 lib/about.tcl:14 +#: git-gui.sh:2483 +msgid "Delete Branch..." +msgstr "Zweig löschen..." + +#: git-gui.sh:2493 git-gui.sh:2515 lib/about.tcl:14 #: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50 #, tcl-format msgid "About %s" @@ -416,7 +442,11 @@ msgstr "Schriftgröße verkleinern" msgid "Increase Font Size" msgstr "Schriftgröße vergrößern" -#: git-gui.sh:2870 +#: git-gui.sh:3033 lib/blame.tcl:281 +msgid "Encoding" +msgstr "Zeichenkodierung" + +#: git-gui.sh:3044 msgid "Apply/Reverse Hunk" msgstr "Kontext anwenden/umkehren" @@ -440,11 +470,7 @@ msgstr "Lokale Version benutzen" msgid "Revert To Base" msgstr "Ursprüngliche Version benutzen" -#: git-gui.sh:2906 -msgid "Stage Working Copy" -msgstr "Arbeitskopie bereitstellen" - -#: git-gui.sh:2925 +#: git-gui.sh:3091 msgid "Unstage Hunk From Commit" msgstr "Kontext aus Bereitstellung herausnehmen" @@ -583,7 +609,12 @@ msgstr "Eintragender:" msgid "Original File:" msgstr "Ursprüngliche Datei:" -#: lib/blame.tcl:990 +#: lib/blame.tcl:1013 +#, fuzzy +msgid "Cannot find HEAD commit:" +msgstr "Elternversion kann nicht gefunden werden:" + +#: lib/blame.tcl:1068 msgid "Cannot find parent commit:" msgstr "Elternversion kann nicht gefunden werden:" @@ -1041,11 +1072,15 @@ msgstr "Datei »%s« existiert bereits." msgid "Clone" msgstr "Klonen" -#: lib/choose_repository.tcl:468 -msgid "URL:" -msgstr "URL:" +#: lib/choose_repository.tcl:467 +msgid "Source Location:" +msgstr "" -#: lib/choose_repository.tcl:489 +#: lib/choose_repository.tcl:478 +msgid "Target Directory:" +msgstr "Zielverzeichnis:" + +#: lib/choose_repository.tcl:490 msgid "Clone Type:" msgstr "Art des Klonens:" @@ -1525,7 +1560,27 @@ msgstr "" msgid "Loading diff of %s..." msgstr "Vergleich von »%s« laden..." -#: lib/diff.tcl:114 lib/diff.tcl:184 +#: lib/diff.tcl:120 +msgid "" +"LOCAL: deleted\n" +"REMOTE:\n" +msgstr "" + +#: lib/diff.tcl:125 +msgid "" +"REMOTE: deleted\n" +"LOCAL:\n" +msgstr "" + +#: lib/diff.tcl:132 +msgid "LOCAL:\n" +msgstr "" + +#: lib/diff.tcl:135 +msgid "REMOTE:\n" +msgstr "" + +#: lib/diff.tcl:197 lib/diff.tcl:296 #, tcl-format msgid "Unable to display %s" msgstr "Datei »%s« kann nicht angezeigt werden" @@ -1542,7 +1597,22 @@ msgstr "Git-Projektarchiv (Unterprojekt)" msgid "* Binary file (not showing content)." msgstr "* Binärdatei (Inhalt wird nicht angezeigt)" -#: lib/diff.tcl:313 +#: lib/diff.tcl:222 +#, tcl-format +msgid "" +"* Untracked file is %d bytes.\n" +"* Showing only first %d bytes.\n" +msgstr "" + +#: lib/diff.tcl:228 +#, tcl-format +msgid "" +"\n" +"* Untracked file clipped here by %s.\n" +"* To see the entire file, use an external editor.\n" +msgstr "" + +#: lib/diff.tcl:437 msgid "Failed to unstage selected hunk." msgstr "" "Fehler beim Herausnehmen des gewählten Kontexts aus der Bereitstellung." @@ -1559,6 +1629,19 @@ msgstr "Fehler beim Herausnehmen der gewählten Zeile aus der Bereitstellung." msgid "Failed to stage selected line." msgstr "Fehler beim Bereitstellen der gewählten Zeile." +#: lib/encoding.tcl:443 +msgid "Default" +msgstr "Voreinstellung" + +#: lib/encoding.tcl:448 +#, tcl-format +msgid "System (%s)" +msgstr "Systemweit (%s)" + +#: lib/encoding.tcl:459 lib/encoding.tcl:465 +msgid "Other" +msgstr "Andere" + #: lib/error.tcl:20 lib/error.tcl:114 msgid "error" msgstr "Fehler" @@ -1811,7 +1894,12 @@ msgstr "" "Diese Operation kann nur rückgängig gemacht werden, wenn die\n" "Zusammenführung erneut gestartet wird." -#: lib/mergetool.tcl:32 +#: lib/mergetool.tcl:45 +#, tcl-format +msgid "File %s seems to have unresolved conflicts, still stage?" +msgstr "Datei »%s« hat nicht aufgelöste Konflikte. Trotzdem bereitstellen?" + +#: lib/mergetool.tcl:60 #, tcl-format msgid "Adding resolution for %s" msgstr "Auflösung hinzugefügt für %s" @@ -1868,12 +1956,17 @@ msgstr "Zusammenführungswerkzeug starten..." msgid "Merge tool failed." msgstr "Zusammenführungswerkzeug fehlgeschlagen." -#: lib/mergetool.tcl:353 +#: lib/option.tcl:11 #, tcl-format -msgid "File %s unchanged, still accept as resolved?" -msgstr "Datei »%s« unverändert. Trotzdem Konflikt als gelöst akzeptieren?" +msgid "Invalid global encoding '%s'" +msgstr "Ungültige globale Zeichenkodierung »%s«" -#: lib/option.tcl:95 +#: lib/option.tcl:19 +#, tcl-format +msgid "Invalid repo encoding '%s'" +msgstr "Ungültige Archiv-Zeichenkodierung »%s«" + +#: lib/option.tcl:117 msgid "Restore Defaults" msgstr "Voreinstellungen wiederherstellen" @@ -1950,7 +2043,15 @@ msgstr "Textbreite der Versionsbeschreibung" msgid "New Branch Name Template" msgstr "Namensvorschlag für neue Zweige" -#: lib/option.tcl:192 +#: lib/option.tcl:155 +msgid "Default File Contents Encoding" +msgstr "Vorgestellte Zeichenkodierung" + +#: lib/option.tcl:203 +msgid "Change" +msgstr "Ändern" + +#: lib/option.tcl:230 msgid "Spelling Dictionary:" msgstr "Wörterbuch Rechtschreibprüfung:" @@ -1975,9 +2076,85 @@ msgstr "Einstellungen" msgid "Failed to completely save options:" msgstr "Optionen konnten nicht gespeichert werden:" +#: lib/remote_add.tcl:19 +msgid "Add Remote" +msgstr "Anderes Archiv hinzufügen" + +#: lib/remote_add.tcl:24 +msgid "Add New Remote" +msgstr "Neues anderes Archiv hinzufügen" + +#: lib/remote_add.tcl:28 +msgid "Add" +msgstr "Hinzufügen" + +#: lib/remote_add.tcl:37 +msgid "Remote Details" +msgstr "Einzelheiten des anderen Archivs" + +#: lib/remote_add.tcl:50 +msgid "Location:" +msgstr "Adresse:" + +#: lib/remote_add.tcl:62 +msgid "Further Action" +msgstr "Weitere Aktion jetzt" + +#: lib/remote_add.tcl:65 +msgid "Fetch Immediately" +msgstr "Gleich anfordern" + +#: lib/remote_add.tcl:71 +msgid "Initialize Remote Repository and Push" +msgstr "Anderes Archiv initialisieren und dahin versenden" + +#: lib/remote_add.tcl:77 +msgid "Do Nothing Else Now" +msgstr "Nichts tun" + +#: lib/remote_add.tcl:101 +#, fuzzy +msgid "Please supply a remote name." +msgstr "Bitte geben Sie einen Zweignamen an." + +#: lib/remote_add.tcl:114 +#, fuzzy, tcl-format +msgid "'%s' is not an acceptable remote name." +msgstr "»%s« ist kein zulässiger Zweigname." + +#: lib/remote_add.tcl:125 +#, fuzzy, tcl-format +msgid "Failed to add remote '%s' of location '%s'." +msgstr "Fehler beim Umbenennen von »%s«." + +#: lib/remote_add.tcl:133 lib/transport.tcl:6 +#, tcl-format +msgid "fetch %s" +msgstr "»%s« anfordern" + +#: lib/remote_add.tcl:134 +#, fuzzy, tcl-format +msgid "Fetching the %s" +msgstr "Änderungen »%s« von »%s« anfordern" + +#: lib/remote_add.tcl:157 +#, tcl-format +msgid "Do not know how to initialize repository at location '%s'." +msgstr "Initialisieren eines anderen Archivs an Adresse »%s« ist nicht möglich." + +#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:71 +#, tcl-format +msgid "push %s" +msgstr "»%s« versenden..." + +#: lib/remote_add.tcl:164 +#, tcl-format +msgid "Setting up the %s (at %s)" +msgstr "Einrichten von »%s« an »%s«" + #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34 -msgid "Delete Remote Branch" -msgstr "Zweig in anderem Projektarchiv löschen" +msgid "Delete Branch Remotely" +msgstr "Zweig in anderem Archiv löschen" #: lib/remote_branch_delete.tcl:47 msgid "From Repository" @@ -1988,8 +2165,8 @@ msgid "Remote:" msgstr "Anderes Archiv:" #: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138 -msgid "Arbitrary URL:" -msgstr "Archiv-URL:" +msgid "Arbitrary Location:" +msgstr "Adresse:" #: lib/remote_branch_delete.tcl:84 msgid "Branches" @@ -2061,7 +2238,11 @@ msgstr "Kein Projektarchiv ausgewählt." msgid "Scanning %s..." msgstr "»%s« laden..." -#: lib/remote.tcl:165 +#: lib/remote.tcl:163 +msgid "Remove Remote" +msgstr "Anderes Archiv entfernen" + +#: lib/remote.tcl:168 msgid "Prune from" msgstr "Aufräumen von" @@ -2073,6 +2254,22 @@ msgstr "Anfordern von" msgid "Push to" msgstr "Versenden nach" +#: lib/search.tcl:21 +msgid "Find:" +msgstr "Suchen:" + +#: lib/search.tcl:22 +msgid "Next" +msgstr "Nächster" + +#: lib/search.tcl:23 +msgid "Prev" +msgstr "Voriger" + +#: lib/search.tcl:24 +msgid "Case-Sensitive" +msgstr "" + #: lib/shortcut.tcl:20 lib/shortcut.tcl:61 msgid "Cannot write shortcut:" msgstr "Fehler beim Schreiben der Verknüpfung:" @@ -2123,11 +2320,6 @@ msgstr "Rechtschreibprüfung fehlgeschlagen" msgid "%s ... %*i of %*i %s (%3i%%)" msgstr "%s ... %*i von %*i %s (%3i%%)" -#: lib/transport.tcl:6 -#, tcl-format -msgid "fetch %s" -msgstr "»%s« anfordern" - #: lib/transport.tcl:7 #, tcl-format msgid "Fetching new changes from %s" @@ -2143,11 +2335,6 @@ msgstr "Aufräumen von »%s«" msgid "Pruning tracking branches deleted from %s" msgstr "Übernahmezweige aufräumen und entfernen, die in »%s« gelöscht wurden" -#: lib/transport.tcl:25 lib/transport.tcl:71 -#, tcl-format -msgid "push %s" -msgstr "»%s« versenden..." - #: lib/transport.tcl:26 #, tcl-format msgid "Pushing changes to %s" From 13494ed14c3539b3e36ff47d1d8b65f5a9a3043b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 23 Oct 2008 04:30:58 +0000 Subject: [PATCH 080/100] correct cache_entry allocation Most cache_entry structs are allocated by using the cache_entry_size macro, which rounds the size of the struct up to the nearest multiple of 8 bytes (presumably to avoid memory fragmentation). There is one exception: the special "conflict entry" is allocated with an empty name, and so is explicitly given just one extra byte to hold the NUL. However, later code doesn't realize that this particular struct has been allocated differently, and happily tries reading and copying it based on the ce_size macro, which assumes the 8-byte alignment. This can lead to reading uninitalized data, though since that data is simply padding, there shouldn't be any problem as a result. Still, it makes sense to hold the padding assumption so as not to surprise later maintainers. This fixes valgrind errors in t1005, t3030, t4002, and t4114. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- unpack-trees.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unpack-trees.c b/unpack-trees.c index e59d144d28..e5749ef638 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -382,7 +382,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options o->merge_size = len; if (!dfc) - dfc = xcalloc(1, sizeof(struct cache_entry) + 1); + dfc = xcalloc(1, cache_entry_size(0)); o->df_conflict_entry = dfc; if (len) { From 421b488a58fea89ceb55d5b358738e9251d44f5e Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 23 Oct 2008 04:31:03 +0000 Subject: [PATCH 081/100] pack-objects: avoid reading uninitalized data In the main loop of find_deltas, we do: struct object_entry *entry = *list++; ... if (!*list_size) ... break Because we look at and increment *list _before_ the check of list_size, in the very last iteration of the loop we will look at uninitialized data, and increment the pointer beyond one past the end of the allocated space. Since we don't actually do anything with the data until after the check, this is not a problem in practice. But since it technically violates the C standard, and because it provokes a spurious valgrind warning, let's just move the initialization of entry to a safe place. This fixes valgrind errors in t5300, t5301, t5302, t303, and t9400. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 4004e73e40..b0dddbee4f 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1377,7 +1377,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, memset(array, 0, array_size); for (;;) { - struct object_entry *entry = *list++; + struct object_entry *entry; struct unpacked *n = array + idx; int j, max_depth, best_base = -1; @@ -1386,6 +1386,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, progress_unlock(); break; } + entry = *list++; (*list_size)--; if (!entry->preferred_base) { (*processed)++; From 1442171bc913a9cddae5c6ad0d0a4be3a1ca86e8 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 23 Oct 2008 04:32:23 +0000 Subject: [PATCH 082/100] fix overlapping memcpy in normalize_absolute_path The comments for normalize_absolute_path explicitly claim that the source and destination buffers may be the same (though they may not otherwise overlap). Thus the call to memcpy may involve copying overlapping data, and memmove should be used instead. This fixes a valgrind error in t1504. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- path.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path.c b/path.c index 76e8872622..c1cb54b7b8 100644 --- a/path.c +++ b/path.c @@ -348,7 +348,7 @@ int normalize_absolute_path(char *buf, const char *path) goto next; } - memcpy(dst, comp_start, comp_len); + memmove(dst, comp_start, comp_len); dst += comp_len; next: comp_start = comp_end; From 30affa1e9a15e848f77c9c14544dd6dd593559d5 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 29 Oct 2008 05:17:55 +0000 Subject: [PATCH 083/100] send-pack: do not send out single-level refs such as refs/stash Since no version of receive-pack accepts these "funny refs", we should mirror the check when considering the list of refs to send. IOW, don't even make them eligible for matching or mirroring. Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 2af9f29341..301f230432 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -132,7 +132,13 @@ static struct ref *remote_refs, **remote_tail; static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { struct ref *ref; - int len = strlen(refname) + 1; + int len; + + /* we already know it starts with refs/ to get here */ + if (check_ref_format(refname + 5)) + return 0; + + len = strlen(refname) + 1; ref = xcalloc(1, sizeof(*ref) + len); hashcpy(ref->new_sha1, sha1); memcpy(ref->name, refname, len); From 8c178687959732edab8bf91da9ed9f4fbcf7c14e Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Fri, 31 Oct 2008 18:57:10 +0000 Subject: [PATCH 084/100] git send-email: avoid leaking directory file descriptors. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- git-send-email.perl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index d2fd899076..18529c76e6 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -407,10 +407,9 @@ for my $f (@ARGV) { push @files, grep { -f $_ } map { +$f . "/" . $_ } sort readdir(DH); - + closedir(DH); } elsif (-f $f or -p $f) { push @files, $f; - } else { print STDERR "Skipping $f - not found.\n"; } From 4f73e240f98c38e608afe68887639c6e63b1d18f Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 1 Nov 2008 06:24:55 +0000 Subject: [PATCH 085/100] Documentation/gitattributes: Add subsection header for each attribute This makes attributes easier to find; before this patch some attributes had individual subsections, and some didn't. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- Documentation/gitattributes.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 42776f089b..37fff208ff 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -213,6 +213,9 @@ with `crlf`, and then `ident` and fed to `filter`. Generating diff text ~~~~~~~~~~~~~~~~~~~~ +`diff` +^^^^^^ + The attribute `diff` affects if 'git-diff' generates textual patch for the path or just says `Binary files differ`. It also can affect what line is shown on the hunk header `@@ -k,l +n,m @@` @@ -323,6 +326,9 @@ patterns are available: Performing a three-way merge ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`merge` +^^^^^^^ + The attribute `merge` affects how three versions of a file is merged when a file-level merge is necessary during `git merge`, and other programs such as `git revert` and `git cherry-pick`. From c2163c6aa2343b21ebc73f7e62cbdecc8dba3a42 Mon Sep 17 00:00:00 2001 From: Tom Preston-Werner Date: Sat, 1 Nov 2008 15:28:18 +0000 Subject: [PATCH 086/100] add instructions on how to send patches to the mailing list with Gmail Gmail is one of the most popular email providers in the world. Now that Gmail supports IMAP, sending properly formatted patches via `git imap-send` is trivial. This section in SubmittingPatches explains how to do so. Signed-off-by: Tom Preston-Werner Signed-off-by: Junio C Hamano --- Documentation/SubmittingPatches | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 841bead9db..c59edfa6a9 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -456,3 +456,30 @@ This should help you to submit patches inline using KMail. 5) Back in the compose window: add whatever other text you wish to the message, complete the addressing and subject fields, and press send. + + +Gmail +----- + +Submitting properly formatted patches via Gmail is simple now that +IMAP support is available. First, edit your ~/.gitconfig to specify your +account settings: + +[imap] + folder = "[Gmail]/Drafts" + host = imaps://imap.gmail.com + user = user@gmail.com + pass = p4ssw0rd + port = 993 + sslverify = false + +Next, ensure that your Gmail settings are correct. In "Settings" the +"Use Unicode (UTF-8) encoding for outgoing messages" should be checked. + +Once your commits are ready to send to the mailing list, run the following +command to send the patch emails to your Gmail Drafts folder. + + $ git format-patch -M --stdout origin/master | git imap-send + +Go to your Gmail account, open the Drafts folder, find the patch email, fill +in the To: and CC: fields and send away! From 1a9016aae5aa8c468bb2d8d5dd16303d5c005869 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 2 Nov 2008 00:14:04 -0700 Subject: [PATCH 087/100] Start 1.6.0.4 cycle Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.0.4.txt | 29 +++++++++++++++++++++++++++++ RelNotes | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 Documentation/RelNotes-1.6.0.4.txt diff --git a/Documentation/RelNotes-1.6.0.4.txt b/Documentation/RelNotes-1.6.0.4.txt new file mode 100644 index 0000000000..4a4530b728 --- /dev/null +++ b/Documentation/RelNotes-1.6.0.4.txt @@ -0,0 +1,29 @@ +GIT v1.6.0.4 Release Notes +========================== + +Fixes since v1.6.0.3 +-------------------- + +* 'git-add -p' said "No changes" when only binary files were changed. + +* git-archive did not work correctly in bare repositories. + +* when we refuse to detect renames because there are too many new or + deleted files, we did not say how many there are. + +* 'git-push --mirror' tried and failed to push the stash; there is no + point in sending it to begin with. + +* 'git-send-email' had a small fd leak while scanning directory. + +* git-svn used deprecated 'git-foo' form of subcommand invocaition. + +* Plugged small memleaks here and there. + +* Also contains many documentation updates. + +-- +exec >/var/tmp/1 +O=v1.6.0.3-22-gc2163c6 +echo O=$(git describe maint) +git shortlog --no-merges $O..maint diff --git a/RelNotes b/RelNotes index a677737b90..f9eb552873 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.6.0.3.txt \ No newline at end of file +Documentation/RelNotes-1.6.0.4.txt \ No newline at end of file From a240de1137bd63a969c6bd56ab5186d7216e28ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kr=C3=BCger?= Date: Sat, 1 Nov 2008 15:42:16 +0100 Subject: [PATCH 088/100] Introduce receive.denyDeletes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Occasionally, it may be useful to prevent branches from getting deleted from a centralized repository, particularly when no administrative access to the server is available to undo it via reflog. It also makes receive.denyNonFastForwards more useful if it is used for access control since it prevents force-updating by deleting and re-creating a ref. Signed-off-by: Jan Krüger Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- Documentation/config.txt | 4 ++++ builtin-receive-pack.c | 12 ++++++++++++ t/t5400-send-pack.sh | 11 +++++++++++ 3 files changed, 27 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index 29369d051b..965ed746da 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1188,6 +1188,10 @@ receive.unpackLimit:: especially on slow filesystems. If not set, the value of `transfer.unpackLimit` is used instead. +receive.denyDeletes:: + If set to true, git-receive-pack will deny a ref update that deletes + the ref. Use this to prevent such a ref deletion via a push. + receive.denyNonFastForwards:: If set to true, git-receive-pack will deny a ref update which is not a fast forward. Use this to prevent such an update via a push, diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c index 9f60f31c2b..2c0225c89a 100644 --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@ -11,6 +11,7 @@ static const char receive_pack_usage[] = "git-receive-pack "; +static int deny_deletes = 0; static int deny_non_fast_forwards = 0; static int receive_fsck_objects; static int receive_unpack_limit = -1; @@ -23,6 +24,11 @@ static int capabilities_sent; static int receive_pack_config(const char *var, const char *value, void *cb) { + if (strcmp(var, "receive.denydeletes") == 0) { + deny_deletes = git_config_bool(var, value); + return 0; + } + if (strcmp(var, "receive.denynonfastforwards") == 0) { deny_non_fast_forwards = git_config_bool(var, value); return 0; @@ -185,6 +191,12 @@ static const char *update(struct command *cmd) "but I can't find it!", sha1_to_hex(new_sha1)); return "bad pack"; } + if (deny_deletes && is_null_sha1(new_sha1) && + !is_null_sha1(old_sha1) && + !prefixcmp(name, "refs/heads/")) { + error("denying ref deletion for %s", name); + return "deletion prohibited"; + } if (deny_non_fast_forwards && !is_null_sha1(new_sha1) && !is_null_sha1(old_sha1) && !prefixcmp(name, "refs/heads/")) { diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh index 544771d8fa..6fe2f87b85 100755 --- a/t/t5400-send-pack.sh +++ b/t/t5400-send-pack.sh @@ -103,6 +103,17 @@ unset GIT_CONFIG GIT_CONFIG_LOCAL HOME=`pwd`/no-such-directory export HOME ;# this way we force the victim/.git/config to be used. +test_expect_failure \ + 'pushing a delete should be denied with denyDeletes' ' + cd victim && + git config receive.denyDeletes true && + git branch extra master && + cd .. && + test -f victim/.git/refs/heads/extra && + test_must_fail git send-pack ./victim/.git/ :extra master +' +rm -f victim/.git/refs/heads/extra + test_expect_success \ 'pushing with --force should be denied with denyNonFastforwards' ' cd victim && From a80732897931c737c01d04289dd1512bd12a4b04 Mon Sep 17 00:00:00 2001 From: Tom Preston-Werner Date: Sat, 1 Nov 2008 11:44:45 -0700 Subject: [PATCH 089/100] connect.c: add a way for git-daemon to pass an error back to client The current behavior of git-daemon is to simply close the connection on any error condition. This leaves the client without any information as to the cause of the failed fetch/push/etc. This patch allows get_remote_heads to accept a line prefixed with "ERR" that it can display to the user in an informative fashion. Once clients can understand this ERR line, git-daemon can be made to properly report "repository not found", "permission denied", or other errors. Example S: ERR No matching repository. C: fatal: remote error: No matching repository. Signed-off-by: Tom Preston-Werner Signed-off-by: Junio C Hamano --- connect.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/connect.c b/connect.c index 0c50d0a26a..584e04c217 100644 --- a/connect.c +++ b/connect.c @@ -70,6 +70,9 @@ struct ref **get_remote_heads(int in, struct ref **list, if (buffer[len-1] == '\n') buffer[--len] = 0; + if (len > 4 && !prefixcmp(buffer, "ERR ")) + die("remote error: %s", buffer + 4); + if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ') die("protocol error: expected sha/ref, got '%s'", buffer); name = buffer + 41; From 310e0216c80484d429dbdec547ab52a93236eb60 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 23 Oct 2008 19:44:27 -0700 Subject: [PATCH 090/100] Stop using compat/regex.c on platforms with working regexp library We used to have non-POSIX comformant BRE in our code, and linked with GNU regexp library on a few platforms (Darwin, FreeBSD and AIX) to work it around. This was backwards. We've fixed the broken regexps to use ERE that native regexp libraries on these platforms can handle just fine. There is no need to link with GNU regexp library on these platforms anymore. Tested-on-AIX-by: Mike Ralphson Tested-on-FreeBSD-by: Jeff King Tested-on-Darwin-by: Arjen Laarhoven Tested-on-Darwin-by: Pieter de Bie Signed-off-by: Junio C Hamano --- Makefile | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Makefile b/Makefile index d6f3695c97..40309e1537 100644 --- a/Makefile +++ b/Makefile @@ -640,8 +640,6 @@ ifeq ($(uname_S),Darwin) endif NO_STRLCPY = YesPlease NO_MEMMEM = YesPlease - COMPAT_CFLAGS += -Icompat/regex - COMPAT_OBJS += compat/regex/regex.o endif ifeq ($(uname_S),SunOS) NEEDS_SOCKET = YesPlease @@ -692,8 +690,6 @@ ifeq ($(uname_S),FreeBSD) BASIC_LDFLAGS += -L/usr/local/lib DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease THREADED_DELTA_SEARCH = YesPlease - COMPAT_CFLAGS += -Icompat/regex - COMPAT_OBJS += compat/regex/regex.o endif ifeq ($(uname_S),OpenBSD) NO_STRCASESTR = YesPlease @@ -720,8 +716,6 @@ ifeq ($(uname_S),AIX) INTERNAL_QSORT = UnfortunatelyYes NEEDS_LIBICONV=YesPlease BASIC_CFLAGS += -D_LARGE_FILES - COMPAT_CFLAGS += -Icompat/regex - COMPAT_OBJS += compat/regex/regex.o endif ifeq ($(uname_S),GNU) # GNU/Hurd From 6a509a6f7f38906996ac791449d5bcc2f32eef23 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 2 Nov 2008 00:36:07 -0700 Subject: [PATCH 091/100] Update draft release notes to 1.6.1 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.1.txt | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/Documentation/RelNotes-1.6.1.txt b/Documentation/RelNotes-1.6.1.txt index afd1150eaa..f5a1311a19 100644 --- a/Documentation/RelNotes-1.6.1.txt +++ b/Documentation/RelNotes-1.6.1.txt @@ -62,6 +62,8 @@ on. * "git bisect" is careful about a user mistake and suggests testing of merge base first when good is not a strict ancestor of bad. +* "git check-attr --stdin" can check attributes for multiple paths. + * "git checkout --track origin/hack" used to be a syntax error. It now DWIMs to create a corresponding local branch "hack", i.e. acts as if you said "git checkout --track -b hack origin/hack". @@ -74,6 +76,8 @@ on. * "git cherry-pick" can also utilize rerere for conflict resolution. +* "git clone" learned to be verbose with -v + * "git commit --author=$name" can look up author name from existing commits. @@ -85,6 +89,12 @@ on. * "git daemon" learned --max-connections= option. +* "git daemon" exports REMOTE_ADDR to record client address, so that + spawned programs can act differently on it. + +* "git describe --tags" favours closer lightweight tags than farther + annotated tags now. + * "git diff" learned to mimic --suppress-blank-empty from GNU diff via a configuration option. @@ -99,9 +109,16 @@ on. * "git diff" hunk header pattern for ObjC has been added. +* a "textconv" filter that makes binary files textual form for human + consumption can be specified as an attribute for paths; "git diff" + learnt to make use of it. + * "git for-each-ref" learned "refname:short" token that gives an unambiguously abbreviated refname. +* Auto-numbering of the subject lines is the default for "git + format-patch" now. + * "git grep" learned to accept -z similar to GNU grep. * "git help" learned to use GIT_MAN_VIEWER environment variable before @@ -127,9 +144,18 @@ on. * "git merge -s $strategy" can use a custom built strategy if you have a command "git-merge-$strategy" on your $PATH. +* "git rebase" honours pre-rebase hook; use --no-verify to bypass it. + +* "git rebase -p" uses interactive rebase machinery now to preserve the merges. + * "git reflog expire branch" can be used in place of "git reflog expire refs/heads/branch". +* "git remote show $remote" lists remote branches one-per-line now. + +* when giving up resolving a conflicted merge, "git reset --hard" failed + to remove new paths from the working tree. [cherry-pick to 'maint'?] + * "git submodule foreach" subcommand allows you to iterate over checked out submodules. @@ -179,6 +205,6 @@ release, unless otherwise noted. -- exec >/var/tmp/1 -O=v1.6.0.2-553-g58e0fa5 +O=v1.6.0.3-574-gaebd173 echo O=$(git describe master) git shortlog --no-merges $O..master ^maint From 02893a8503158823e42ca1d4d59faae691684b54 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 2 Nov 2008 14:35:41 -0800 Subject: [PATCH 092/100] Update draft 1.6.0.4 release notes Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.0.4.txt | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/Documentation/RelNotes-1.6.0.4.txt b/Documentation/RelNotes-1.6.0.4.txt index 4a4530b728..db1002e8c7 100644 --- a/Documentation/RelNotes-1.6.0.4.txt +++ b/Documentation/RelNotes-1.6.0.4.txt @@ -4,19 +4,30 @@ GIT v1.6.0.4 Release Notes Fixes since v1.6.0.3 -------------------- -* 'git-add -p' said "No changes" when only binary files were changed. +* 'git add -p' said "No changes" when only binary files were changed. -* git-archive did not work correctly in bare repositories. +* 'git archive' did not work correctly in bare repositories. + +* 'git checkout -t -b newbranch' when you are on detached HEAD was broken. * when we refuse to detect renames because there are too many new or - deleted files, we did not say how many there are. + deleted files, 'git diff' did not say how many there are. -* 'git-push --mirror' tried and failed to push the stash; there is no +* 'git push --mirror' tried and failed to push the stash; there is no point in sending it to begin with. -* 'git-send-email' had a small fd leak while scanning directory. +* 'git pull $there $branch:$current_branch' did not work when you were on + a branch yet to be born. -* git-svn used deprecated 'git-foo' form of subcommand invocaition. +* when giving up resolving a conflicted merge, 'git reset --hard' failed + to remove new paths from the working tree. + +* 'git send-email' had a small fd leak while scanning directory. + +* 'git status' incorrectly reported a submodule directory as an untracked + directory. + +* 'git svn' used deprecated 'git-foo' form of subcommand invocaition. * Plugged small memleaks here and there. @@ -24,6 +35,6 @@ Fixes since v1.6.0.3 -- exec >/var/tmp/1 -O=v1.6.0.3-22-gc2163c6 +O=v1.6.0.3-34-gf6276b7 echo O=$(git describe maint) git shortlog --no-merges $O..maint From 7756ba74c04caf03d47718d581439a48bae7ed73 Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Sun, 2 Nov 2008 19:32:46 +0100 Subject: [PATCH 093/100] Document that git-log takes --all-match. Signed-off-by: Mikael Magnusson Signed-off-by: Junio C Hamano --- Documentation/rev-list-options.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 735cf07b20..5df35ce519 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -174,6 +174,10 @@ endif::git-rev-list[] Limit the commits output to ones with log message that matches the specified pattern (regular expression). +--all-match:: + Limit the commits output to ones that match all given --grep, + --author and --committer instead of ones that match at least one. + -i:: --regexp-ignore-case:: From a5a323f33cd25829e0dde3939b196cf743d7d9d8 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Sun, 2 Nov 2008 14:37:28 +0100 Subject: [PATCH 094/100] Add reference for status letters in documentation. Also fix error in diff_filepair::status documentation, and point to the in-code reference as well as the doc. Signed-off-by: Yann Dirson Signed-off-by: Junio C Hamano --- Documentation/diff-format.txt | 16 ++++++++++++++++ diffcore.h | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt index 400cbb3b1c..aafd3a3941 100644 --- a/Documentation/diff-format.txt +++ b/Documentation/diff-format.txt @@ -46,6 +46,22 @@ That is, from the left to the right: . path for "dst"; only exists for C or R. . an LF or a NUL when '-z' option is used, to terminate the record. +Possible status letters are: + +- A: addition of a file +- C: copy of a file into a new one +- D: deletion of a file +- M: modification of the contents or mode of a file +- R: renaming of a file +- T: change in the type of the file +- U: file is unmerged (you must complete the merge before it can +be committed) +- X: "unknown" change type (most probably a bug, please report it) + +Status letters C and M are always followed by a score (denoting the +percentage of similarity between the source and target of the move or +copy), and are the only ones to be so. + is shown as all 0's if a file is new on the filesystem and it is out of sync with the index. diff --git a/diffcore.h b/diffcore.h index 8ae35785fd..1ebfdae8b8 100644 --- a/diffcore.h +++ b/diffcore.h @@ -58,7 +58,7 @@ struct diff_filepair { struct diff_filespec *one; struct diff_filespec *two; unsigned short int score; - char status; /* M C R N D U (see Documentation/diff-format.txt) */ + char status; /* M C R A D U etc. (see Documentation/diff-format.txt or DIFF_STATUS_* in diff.h) */ unsigned broken_pair : 1; unsigned renamed_pair : 1; unsigned is_unmerged : 1; From 9f8f132621faedd250fded3e260402623da91a6f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 2 Nov 2008 15:21:02 -0800 Subject: [PATCH 095/100] Update draft release notes to 1.6.1 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.1.txt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Documentation/RelNotes-1.6.1.txt b/Documentation/RelNotes-1.6.1.txt index f5a1311a19..6f84c0c2f8 100644 --- a/Documentation/RelNotes-1.6.1.txt +++ b/Documentation/RelNotes-1.6.1.txt @@ -62,6 +62,9 @@ on. * "git bisect" is careful about a user mistake and suggests testing of merge base first when good is not a strict ancestor of bad. +* "git blame" re-encodes the commit metainfo to UTF-8 from i18n.commitEncoding + by default. + * "git check-attr --stdin" can check attributes for multiple paths. * "git checkout --track origin/hack" used to be a syntax error. It now @@ -144,6 +147,9 @@ on. * "git merge -s $strategy" can use a custom built strategy if you have a command "git-merge-$strategy" on your $PATH. +* "git push" can be told to reject deletion of refs with receive.denyDeletes + configuration. + * "git rebase" honours pre-rebase hook; use --no-verify to bypass it. * "git rebase -p" uses interactive rebase machinery now to preserve the merges. @@ -164,6 +170,8 @@ on. * "git svn branch" can create new branches on the other end. +* "gitweb" can use more saner PATH_INFO based URL. + (internal) * "git hash-object" learned to lie about the path being hashed, so that @@ -173,6 +181,10 @@ on. * various callers of git-merge-recursive avoid forking it as an external process. +* Git class defined in "Git.pm" can be subclasses a bit more easily. + +* We used to link GNU regex library as a compatibility layer for some platforms, + but it turns out it is not necessary on most of them. Fixes since v1.6.0 ------------------ @@ -205,6 +217,6 @@ release, unless otherwise noted. -- exec >/var/tmp/1 -O=v1.6.0.3-574-gaebd173 +O=v1.6.0.3-610-g94dc4b8 echo O=$(git describe master) git shortlog --no-merges $O..master ^maint From 16088d8870b7da6d4dd280be2d1728dd3be346b5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 2 Nov 2008 20:45:55 -0800 Subject: [PATCH 096/100] format-patch documentation: mention the special case of showing a single commit Even long timers seem to have missed that "format-patch -1 $commit" is a much simpler and more obvious way to say "format-patch $commit^..$commit" from the current documentation (and an example "format-patch -3 $commit" to get three patches). Add an explicit instruction in a much earlier part of the documentation to make it easier to find. Signed-off-by: Junio C Hamano --- Documentation/git-format-patch.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index adb4ea7b1b..7426109f62 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -46,7 +46,8 @@ applies to that command line and you do not get "everything since the beginning of the time". If you want to format everything since project inception to one commit, say "git format-patch \--root " to make it clear that it is the -latter case. +latter case. If you want to format a single commit, you can do +this with "git format-patch -1 ". By default, each output file is numbered sequentially from 1, and uses the first line of the commit message (massaged for pathname safety) as From b1a46b70b3dcf139c05160e1420e915358b9f947 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Sun, 2 Nov 2008 18:53:03 +0100 Subject: [PATCH 097/100] Makefile: add install-man rules (quick and normal) Signed-off-by: Junio C Hamano --- Documentation/Makefile | 8 ++++++-- INSTALL | 5 +++-- Makefile | 6 ++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Documentation/Makefile b/Documentation/Makefile index e33ddcb250..c34c1cae20 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -87,7 +87,9 @@ man7: $(DOC_MAN7) info: git.info gitman.info -install: man +install: install-man + +install-man: man $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir) $(INSTALL) -d -m 755 $(DESTDIR)$(man5dir) $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir) @@ -220,7 +222,9 @@ $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt install-webdoc : html sh ./install-webdoc.sh $(WEBDOC_DEST) -quick-install: +quick-install: quick-install-man + +quick-install-man: sh ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir) quick-install-html: diff --git a/INSTALL b/INSTALL index a4fd8624bc..d1deb0b3c7 100644 --- a/INSTALL +++ b/INSTALL @@ -126,8 +126,9 @@ Issues of note: http://www.kernel.org/pub/software/scm/git/docs/ - There are also "make quick-install-doc" and "make quick-install-html" - which install preformatted man pages and html documentation. + There are also "make quick-install-doc", "make quick-install-man" + and "make quick-install-html" which install preformatted man pages + and html documentation. This does not require asciidoc/xmlto, but it only works from within a cloned checkout of git.git with these two extra branches, and will not work for the maintainer for obvious chicken-and-egg reasons. diff --git a/Makefile b/Makefile index 40309e1537..220de39914 100644 --- a/Makefile +++ b/Makefile @@ -1405,6 +1405,9 @@ endif install-doc: $(MAKE) -C Documentation install +install-man: + $(MAKE) -C Documentation install-man + install-html: $(MAKE) -C Documentation install-html @@ -1414,6 +1417,9 @@ install-info: quick-install-doc: $(MAKE) -C Documentation quick-install +quick-install-man: + $(MAKE) -C Documentation quick-install-man + quick-install-html: $(MAKE) -C Documentation quick-install-html From 4471649f44d7a8e4b7b927e43b848bb71b75630d Mon Sep 17 00:00:00 2001 From: Pete Harlan Date: Mon, 3 Nov 2008 23:19:53 -0800 Subject: [PATCH 098/100] contrib/hooks/post-receive-email: Put rev display in separate function The display of a revision in an email-appropriate format is done in two places with similar code. In preparation for making that display more complex, move it into a separate function that handles both cases. Signed-off-by: Pete Harlan Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 41 +++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 41368950d6..2cd373d625 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -224,13 +224,7 @@ generate_create_branch_email() echo "" echo $LOGBEGIN - # This shows all log entries that are not already covered by - # another ref - i.e. commits that are now accessible from this - # ref that were previously not accessible - # (see generate_update_branch_email for the explanation of this - # command) - git rev-parse --not --branches | grep -v $(git rev-parse $refname) | - git rev-list --pretty --stdin $newrev + show_new_revisions echo $LOGEND } @@ -390,8 +384,7 @@ generate_update_branch_email() echo "" echo $LOGBEGIN - git rev-parse --not --branches | grep -v $(git rev-parse $refname) | - git rev-list --pretty --stdin $oldrev..$newrev + show_new_revisions # XXX: Need a way of detecting whether git rev-list actually # outputted anything, so that we can issue a "no new @@ -591,6 +584,36 @@ generate_delete_general_email() echo $LOGEND } + +# --------------- Miscellaneous utilities + +# +# Show new revisions as the user would like to see them in the email. +# +show_new_revisions() +{ + # This shows all log entries that are not already covered by + # another ref - i.e. commits that are now accessible from this + # ref that were previously not accessible + # (see generate_update_branch_email for the explanation of this + # command) + + # Revision range passed to rev-list differs for new vs. updated + # branches. + if [ "$change_type" = create ] + then + # Show all revisions exclusive to this (new) branch. + revspec=$newrev + else + # Branch update; show revisions not part of $oldrev. + revspec=$oldrev..$newrev + fi + + git rev-parse --not --branches | grep -v $(git rev-parse $refname) | + git rev-list --pretty --stdin $revspec +} + + send_mail() { if [ -n "$envelopesender" ]; then From b0a7d111732f644623253927c9ef2d4f3009e668 Mon Sep 17 00:00:00 2001 From: Pete Harlan Date: Mon, 3 Nov 2008 23:19:54 -0800 Subject: [PATCH 099/100] contrib/hooks/post-receive-email: Make revision display configurable Add configuration option hooks.showrev, letting the user override how revisions will be shown in the commit email. Signed-off-by: Pete Harlan Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 2cd373d625..28a3c0e46e 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -38,6 +38,12 @@ # hooks.emailprefix # All emails have their subjects prefixed with this prefix, or "[SCM]" # if emailprefix is unset, to aid filtering +# hooks.showrev +# The shell command used to format each revision in the email, with +# "%s" replaced with the commit id. Defaults to "git rev-list -1 +# --pretty %s", displaying the commit id, author, date and log +# message. To list full patches separated by a blank line, you +# could set this to "git show -C %s; echo". # # Notes # ----- @@ -610,7 +616,16 @@ show_new_revisions() fi git rev-parse --not --branches | grep -v $(git rev-parse $refname) | - git rev-list --pretty --stdin $revspec + if [ -z "$custom_showrev" ] + then + git rev-list --pretty --stdin $revspec + else + git rev-list --stdin $revspec | + while read onerev + do + eval $(printf "$custom_showrev" $onerev) + done + fi } @@ -650,6 +665,7 @@ recipients=$(git config hooks.mailinglist) announcerecipients=$(git config hooks.announcelist) envelopesender=$(git config hooks.envelopesender) emailprefix=$(git config hooks.emailprefix || echo '[SCM] ') +custom_showrev=$(git config hooks.showrev) # --- Main loop # Allow dual mode: run from the command line just like the update hook, or From 6331adb9c4ec36c70dc3ecc6eb46b7dddb36952d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 5 Nov 2008 12:20:55 -0800 Subject: [PATCH 100/100] Update draft release notes to 1.6.1 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.1.txt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Documentation/RelNotes-1.6.1.txt b/Documentation/RelNotes-1.6.1.txt index 6f84c0c2f8..d0e30e95d4 100644 --- a/Documentation/RelNotes-1.6.1.txt +++ b/Documentation/RelNotes-1.6.1.txt @@ -183,8 +183,13 @@ on. * Git class defined in "Git.pm" can be subclasses a bit more easily. -* We used to link GNU regex library as a compatibility layer for some platforms, - but it turns out it is not necessary on most of them. +* We used to link GNU regex library as a compatibility layer for some + platforms, but it turns out it is not necessary on most of them. + +* Some path handling routines used fixed number of buffers used alternately + but depending on the call depth, this arrangement led to hard to track + bugs. This issue is being addressed. + Fixes since v1.6.0 ------------------ @@ -217,6 +222,6 @@ release, unless otherwise noted. -- exec >/var/tmp/1 -O=v1.6.0.3-610-g94dc4b8 +O=v1.6.0.3-639-ga1a846a echo O=$(git describe master) git shortlog --no-merges $O..master ^maint