From fbc0e7ac1479b4262f6090098a4f68c3438aa94b Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 Jul 2008 00:12:28 +0400 Subject: Fix pre-commit hooks under MinGW/MSYS Apply the work-around for checking the executable permission of hook files not only on Cygwin, but on Windows in general. Signed-off-by: Alexander Gavrilov Signed-off-by: Shawn O. Pearce --- git-gui.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 940677cbd8..e3b6669880 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -473,10 +473,10 @@ proc githook_read {hook_name args} { set pchook [gitdir hooks $hook_name] lappend args 2>@1 - # On Cygwin [file executable] might lie so we need to ask + # On Windows [file executable] might lie so we need to ask # the shell if the hook is executable. Yes that's annoying. # - if {[is_Cygwin]} { + if {[is_Windows]} { upvar #0 _sh interp if {![info exists interp]} { set interp [_which sh] -- cgit v1.2.1 From 57cae87b77c93e8bdfd11293f11f140ff827269a Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 Jul 2008 00:43:48 +0400 Subject: Add options to control the search for copies in blame. On huge repositories, -C -C can be way too slow to be unconditionally enabled, and it can also be useful to control its precision. Signed-off-by: Alexander Gavrilov Signed-off-by: Shawn O. Pearce --- git-gui.sh | 2 ++ lib/blame.tcl | 20 ++++++++++++-------- lib/option.tcl | 2 ++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index e3b6669880..b1ed0ec568 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -642,6 +642,8 @@ set default_config(user.email) {} set default_config(gui.matchtrackingbranch) false set default_config(gui.pruneduringfetch) false set default_config(gui.trustmtime) false +set default_config(gui.fastcopyblame) false +set default_config(gui.copyblamethreshold) 40 set default_config(gui.diffcontext) 5 set default_config(gui.commitmsgwidth) 75 set default_config(gui.newbranchtemplate) {} diff --git a/lib/blame.tcl b/lib/blame.tcl index 92fac1bad4..192505d983 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -33,13 +33,6 @@ variable group_colors { #ececec } -# Switches for original location detection -# -variable original_options [list -C -C] -if {[git-version >= 1.5.3]} { - lappend original_options -w ; # ignore indentation changes -} - # Current blame data; cleared/reset on each load # field commit ; # input commit to blame @@ -511,7 +504,6 @@ method _exec_blame {cur_w cur_d options cur_s} { method _read_blame {fd cur_w cur_d} { upvar #0 $cur_d line_data variable group_colors - variable original_options if {$fd ne $current_fd} { catch {close $fd} @@ -684,6 +676,18 @@ method _read_blame {fd cur_w cur_d} { if {[eof $fd]} { close $fd if {$cur_w eq $w_asim} { + # Switches for original location detection + set threshold [get_config gui.copyblamethreshold] + set original_options [list "-C$threshold"] + + if {![is_config_true gui.fastcopyblame]} { + # thorough copy search; insert before the threshold + set original_options [linsert $original_options 0 -C] + } + if {[git-version >= 1.5.3]} { + lappend original_options -w ; # ignore indentation changes + } + _exec_blame $this $w_amov @amov_data \ $original_options \ [mc "Loading original location annotations..."] diff --git a/lib/option.tcl b/lib/option.tcl index 9270512582..ffb3f00ff0 100644 --- a/lib/option.tcl +++ b/lib/option.tcl @@ -123,6 +123,8 @@ proc do_options {} { {b gui.trustmtime {mc "Trust File Modification Timestamps"}} {b gui.pruneduringfetch {mc "Prune Tracking Branches During Fetch"}} {b gui.matchtrackingbranch {mc "Match Tracking Branches"}} + {b gui.fastcopyblame {mc "Blame Copy Only On Changed Files"}} + {i-20..200 gui.copyblamethreshold {mc "Minimum Letters To Blame Copy On"}} {i-0..99 gui.diffcontext {mc "Number of Diff Context Lines"}} {i-0..99 gui.commitmsgwidth {mc "Commit Message Text Width"}} {t gui.newbranchtemplate {mc "New Branch Name Template"}} -- cgit v1.2.1 From e6131d30c21d2c308571078729dc8d2e1b746285 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 Jul 2008 00:48:08 +0400 Subject: Kill the blame back-end on window close. Currently 'git-gui blame' does not kill its back-end process, hoping that it will die anyway when the pipe is closed. However, in some cases the process works for a long time without producing any output. This behavior results in a runaway CPU hog. Signed-off-by: Alexander Gavrilov Signed-off-by: Shawn O. Pearce --- git-gui.sh | 14 ++++++++++++++ lib/blame.tcl | 16 ++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index b1ed0ec568..83e2645714 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -497,6 +497,20 @@ proc githook_read {hook_name args} { return {} } +proc kill_file_process {fd} { + set process [pid $fd] + + catch { + if {[is_Windows]} { + # Use a Cygwin-specific flag to allow killing + # native Windows processes + exec kill -f $process + } else { + exec kill $process + } + } +} + proc sq {value} { regsub -all ' $value "'\\''" value return "'$value'" diff --git a/lib/blame.tcl b/lib/blame.tcl index 192505d983..2c190482df 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -326,19 +326,27 @@ constructor new {i_commit i_path} { bind $w.file_pane \ "if {{$w.file_pane} eq {%W}} {[cb _resize %h]}" + wm protocol $top WM_DELETE_WINDOW "destroy $top" + bind $top [cb _kill] + _load $this {} } +method _kill {} { + if {$current_fd ne {}} { + kill_file_process $current_fd + catch {close $current_fd} + set current_fd {} + } +} + method _load {jump} { variable group_colors _hide_tooltip $this if {$total_lines != 0 || $current_fd ne {}} { - if {$current_fd ne {}} { - catch {close $current_fd} - set current_fd {} - } + _kill $this foreach i $w_columns { $i conf -state normal -- cgit v1.2.1 From a01fe996a2f70b759b4d94bd3e9985a01d514ad7 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 Jul 2008 00:51:20 +0400 Subject: Add a menu item to invoke full copy detection in blame. Add a context menu item to invoke blame -C -C -C on a chunk of the file. The results are used to update the 'original location' column of the blame display. The chunk is computed as the smallest line range that covers both the 'last change' and 'original location' ranges of the line that was clicked to open the menu. Signed-off-by: Alexander Gavrilov Signed-off-by: Shawn O. Pearce --- lib/blame.tcl | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/lib/blame.tcl b/lib/blame.tcl index 2c190482df..b6e42cbc8f 100644 --- a/lib/blame.tcl +++ b/lib/blame.tcl @@ -256,6 +256,9 @@ constructor new {i_commit i_path} { $w.ctxm add command \ -label [mc "Copy Commit"] \ -command [cb _copycommit] + $w.ctxm add command \ + -label [mc "Do Full Copy Detection"] \ + -command [cb _fullcopyblame] foreach i $w_columns { for {set g 0} {$g < [llength $group_colors]} {incr g} { @@ -708,6 +711,72 @@ method _read_blame {fd cur_w cur_d} { } } ifdeleted { catch {close $fd} } +method _find_commit_bound {data_list start_idx delta} { + upvar #0 $data_list line_data + set pos $start_idx + set limit [expr {[llength $line_data] - 1}] + set base_commit [lindex $line_data $pos 0] + + while {$pos > 0 && $pos < $limit} { + set new_pos [expr {$pos + $delta}] + if {[lindex $line_data $new_pos 0] ne $base_commit} { + return $pos + } + + set pos $new_pos + } + + return $pos +} + +method _fullcopyblame {} { + if {$current_fd ne {}} { + tk_messageBox \ + -icon error \ + -type ok \ + -title [mc "Busy"] \ + -message [mc "Annotation process is already running."] + + return + } + + # Switches for original location detection + set threshold [get_config gui.copyblamethreshold] + set original_options [list -C -C "-C$threshold"] + + if {[git-version >= 1.5.3]} { + lappend original_options -w ; # ignore indentation changes + } + + # Find the line range + set pos @$::cursorX,$::cursorY + set lno [lindex [split [$::cursorW index $pos] .] 0] + set min_amov_lno [_find_commit_bound $this @amov_data $lno -1] + set max_amov_lno [_find_commit_bound $this @amov_data $lno 1] + set min_asim_lno [_find_commit_bound $this @asim_data $lno -1] + set max_asim_lno [_find_commit_bound $this @asim_data $lno 1] + + if {$min_asim_lno < $min_amov_lno} { + set min_amov_lno $min_asim_lno + } + + if {$max_asim_lno > $max_amov_lno} { + set max_amov_lno $max_asim_lno + } + + lappend original_options -L "$min_amov_lno,$max_amov_lno" + + # Clear lines + for {set i $min_amov_lno} {$i <= $max_amov_lno} {incr i} { + lset amov_data $i [list ] + } + + # Start the back-end process + _exec_blame $this $w_amov @amov_data \ + $original_options \ + [mc "Running thorough copy detection..."] +} + method _click {cur_w pos} { set lno [lindex [split [$cur_w index $pos] .] 0] _showcommit $this $cur_w $lno -- cgit v1.2.1 From 38bd64979a2a3ffa178af801c6a62e6fcd658274 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 21 Jul 2008 11:23:43 +0200 Subject: Enable threaded delta search on *BSD and Linux. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 551bde9ff0..82f89b7b64 100644 --- a/Makefile +++ b/Makefile @@ -565,9 +565,11 @@ EXTLIBS = ifeq ($(uname_S),Linux) NO_STRLCPY = YesPlease + THREADED_DELTA_SEARCH = YesPlease endif ifeq ($(uname_S),GNU/kFreeBSD) NO_STRLCPY = YesPlease + THREADED_DELTA_SEARCH = YesPlease endif ifeq ($(uname_S),UnixWare) CC = cc @@ -665,6 +667,7 @@ ifeq ($(uname_S),FreeBSD) BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease + THREADED_DELTA_SEARCH = YesPlease endif ifeq ($(uname_S),OpenBSD) NO_STRCASESTR = YesPlease @@ -672,6 +675,7 @@ ifeq ($(uname_S),OpenBSD) NEEDS_LIBICONV = YesPlease BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib + THREADED_DELTA_SEARCH = YesPlease endif ifeq ($(uname_S),NetBSD) ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2) @@ -680,6 +684,7 @@ ifeq ($(uname_S),NetBSD) BASIC_CFLAGS += -I/usr/pkg/include BASIC_LDFLAGS += -L/usr/pkg/lib ALL_LDFLAGS += -Wl,-rpath,/usr/pkg/lib + THREADED_DELTA_SEARCH = YesPlease endif ifeq ($(uname_S),AIX) NO_STRCASESTR=YesPlease -- cgit v1.2.1 From f5242ebf0dcd858ae9c72f39aed9773696d7283d Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 23 Jul 2008 12:15:32 +0200 Subject: git-checkout: fix command line parsing. This fixes an issue when you use: $ git checkout -- [...] and that can also be understood as a reference. git-checkout mistakenly understands this as the same as: $ git checkout -- [...] because parse-options was eating the '--' and the argument parser thought he was parsing: $ git checkout [...] Where there indeed is an ambiguity Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- builtin-checkout.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index 93ea69bfaa..aec2bc6f8d 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -520,7 +520,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.track = git_branch_track; - argc = parse_options(argc, argv, options, checkout_usage, 0); + argc = parse_options(argc, argv, options, checkout_usage, + PARSE_OPT_KEEP_DASHDASH); if (argc) { arg = argv[0]; if (get_sha1(arg, rev)) -- cgit v1.2.1 From f9bbefc701e319983bb8e13df8d615293c6f386a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 22 Jul 2008 23:03:01 +0100 Subject: git daemon: avoid waking up too often To avoid waking up unnecessarily, a pipe is set up that is only ever written to by child_handler(), when a child disconnects, as suggested per Junio. This avoids waking up the main process every second to see if a child was disconnected. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- daemon.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/daemon.c b/daemon.c index 7df41a6a49..4540e8df5a 100644 --- a/daemon.c +++ b/daemon.c @@ -16,6 +16,7 @@ static int log_syslog; static int verbose; static int reuseaddr; +static int child_handler_pipe[2]; static const char daemon_usage[] = "git daemon [--verbose] [--syslog] [--export-all]\n" @@ -788,6 +789,7 @@ static void child_handler(int signo) pid = -pid; dead_child[reaped % MAX_CHILDREN] = pid; children_reaped = reaped + 1; + write(child_handler_pipe[1], &status, 1); continue; } break; @@ -933,29 +935,24 @@ static int service_loop(int socknum, int *socklist) struct pollfd *pfd; int i; - pfd = xcalloc(socknum, sizeof(struct pollfd)); + if (pipe(child_handler_pipe) < 0) + die ("Could not set up pipe for child handler"); + + pfd = xcalloc(socknum + 1, sizeof(struct pollfd)); for (i = 0; i < socknum; i++) { pfd[i].fd = socklist[i]; pfd[i].events = POLLIN; } + pfd[socknum].fd = child_handler_pipe[0]; + pfd[socknum].events = POLLIN; signal(SIGCHLD, child_handler); for (;;) { int i; - int timeout; - /* - * This 1-sec timeout could lead to idly looping but it is - * here so that children culled in child_handler() are reported - * without too much delay. We could probably set up a pipe - * to ourselves that we poll, and write to the fd from child_handler() - * to wake us up (and consume it when the poll() returns... - */ - timeout = (children_spawned != children_deleted) ? 1000 : -1; - i = poll(pfd, socknum, timeout); - if (i < 0) { + if (poll(pfd, socknum + 1, -1) < 0) { if (errno != EINTR) { error("poll failed, resuming: %s", strerror(errno)); @@ -963,9 +960,9 @@ static int service_loop(int socknum, int *socklist) } continue; } - if (i == 0) { + if (pfd[socknum].revents & POLLIN) { + read(child_handler_pipe[0], &i, 1); check_dead_children(); - continue; } for (i = 0; i < socknum; i++) { -- cgit v1.2.1 From eb72a51407228c8c7947e762d4f1c320712c9052 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Wed, 23 Jul 2008 00:24:35 +0200 Subject: Documentation/git-filter-branch: teach "rm" instead of "update-index --remove" The example to remove paths using index-filter was done with "git update-index --remove"; "git rm --cached" would be more familiar to new people and is sufficient for this particular case. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- Documentation/git-filter-branch.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index a3edc00246..7ba9dab5e6 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -191,7 +191,7 @@ Thus you may instead want to use `rm -f filename` as the script. A significantly faster version: -------------------------------------------------------------------------- -git filter-branch --index-filter 'git update-index --remove filename' HEAD +git filter-branch --index-filter 'git rm --cached filename' HEAD -------------------------------------------------------------------------- Now, you will get the rewritten history saved in HEAD. -- cgit v1.2.1 From e8a43a132d97165f6e56fa03923a3933cfedde81 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Wed, 23 Jul 2008 23:08:27 +0300 Subject: svnimport: newer libsvn wants us to ask for the root with "", not "/" In r27729, libsvn introduced an assert which explicitly forbids searching the tree at "/". Luckily enough, it still accepts an empty string "" as the starting point. http://svn.collab.net/viewvc/svn/trunk/subversion/libsvn_ra/ra_loader.c?r1=27653&r2=27729 Tested against libsvn0-1.5.0-4mdv2009.0 (needs the fix), libsvn0-1.4.6-5mdv2008.1 (works anyway) Signed-off-by: P. Christeas Signed-off-by: Junio C Hamano --- contrib/examples/git-svnimport.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/examples/git-svnimport.perl b/contrib/examples/git-svnimport.perl index ea8c1b2f60..a13bb6afec 100755 --- a/contrib/examples/git-svnimport.perl +++ b/contrib/examples/git-svnimport.perl @@ -933,7 +933,7 @@ while ($to_rev < $opt_l) { $to_rev = $from_rev + $repack_after; $to_rev = $opt_l if $opt_l < $to_rev; print "Fetching from $from_rev to $to_rev ...\n" if $opt_v; - $svn->{'svn'}->get_log("/",$from_rev,$to_rev,0,1,1,\&commit_all); + $svn->{'svn'}->get_log("",$from_rev,$to_rev,0,1,1,\&commit_all); my $pid = fork(); die "Fork: $!\n" unless defined $pid; unless($pid) { -- cgit v1.2.1 From 08c701d4761abf58dce607e84bf41fb280e38a9e Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Wed, 23 Jul 2008 15:21:08 -0600 Subject: bash completion: Add long options for 'git rm' Options added: --cached --dry-run --ignore-unmatch --quiet Signed-off-by: Lee Marlow Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8fc9145282..e20d57a1ba 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1170,6 +1170,20 @@ _git_reset () __gitcomp "$(__git_refs)" } +_git_rm () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --dry-run --ignore-unmatch --quiet" + return + ;; + esac + COMPREPLY=() +} + _git_shortlog () { __git_has_doubledash && return @@ -1425,6 +1439,7 @@ _git () rebase) _git_rebase ;; remote) _git_remote ;; reset) _git_reset ;; + rm) _git_rm ;; send-email) _git_send_email ;; shortlog) _git_shortlog ;; show) _git_show ;; -- cgit v1.2.1 From 2ca880fe54660869bc93a2302efced9ab64511d9 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Wed, 23 Jul 2008 23:36:15 +0200 Subject: git-completion.bash: provide completion for 'show-branch' It previously used the same as 'log', but the options are quite different and the arguments must be single refs (or globs). Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e20d57a1ba..3b049348c3 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1224,6 +1224,22 @@ _git_show () __git_complete_file } +_git_show_branch () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --all --remotes --topo-order --current --more= + --list --independent --merge-base --no-name + --sha1-name --topics --reflog + " + return + ;; + esac + __git_complete_revlist +} + _git_stash () { local subcommands='save list show apply clear drop pop create' @@ -1443,7 +1459,7 @@ _git () send-email) _git_send_email ;; shortlog) _git_shortlog ;; show) _git_show ;; - show-branch) _git_log ;; + show-branch) _git_show_branch ;; stash) _git_stash ;; submodule) _git_submodule ;; svn) _git_svn ;; -- cgit v1.2.1 From 0c4cd7f4a7aae5527560090aad7ef8dae85f1264 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 23 Jul 2008 17:22:58 -0700 Subject: ignore non-existent refs in dwim_log() f2eba66 (Enable HEAD@{...} and make it independent from the current branch, 2007-02-03) introduced dwim_log() to handle @{...} syntax, and as part of its processing, it checks if the ref exists by calling refsolve_ref(). It should call it as a reader to make sure the call returns NULL for a nonexistent ref (not as a potential writer in which case it does not return NULL). Signed-off-by: Junio C Hamano --- sha1_name.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sha1_name.c b/sha1_name.c index b0b2167578..4fb77f8863 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -273,7 +273,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log) const char *ref, *it; strcpy(path, mkpath(*p, len, str)); - ref = resolve_ref(path, hash, 0, NULL); + ref = resolve_ref(path, hash, 1, NULL); if (!ref) continue; if (!stat(git_path("logs/%s", path), &st) && -- cgit v1.2.1 From 186458b11b090835fa793bcdbf6b5552b053276c Mon Sep 17 00:00:00 2001 From: Stephan Beyer Date: Thu, 24 Jul 2008 01:09:35 +0200 Subject: Make non-static functions, that may be static, static Signed-off-by: Stephan Beyer Signed-off-by: Junio C Hamano --- builtin-commit.c | 2 +- builtin-config.c | 2 +- builtin-for-each-ref.c | 2 +- builtin-merge.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin-commit.c b/builtin-commit.c index 7434797d1b..6a9dc0e30f 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -876,7 +876,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1) } } -int git_commit_config(const char *k, const char *v, void *cb) +static int git_commit_config(const char *k, const char *v, void *cb) { if (!strcmp(k, "commit.template")) return git_config_string(&template_file, k, v); diff --git a/builtin-config.c b/builtin-config.c index 0cf191a112..91fdc4985d 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -145,7 +145,7 @@ free_strings: return ret; } -char *normalize_value(const char *key, const char *value) +static char *normalize_value(const char *key, const char *value) { char *normalized; diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index 76282ad791..445039e19c 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -809,7 +809,7 @@ static struct ref_sort *default_sort(void) return sort; } -int opt_parse_sort(const struct option *opt, const char *arg, int unset) +static int opt_parse_sort(const struct option *opt, const char *arg, int unset) { struct ref_sort **sort_tail = opt->value; struct ref_sort *s; diff --git a/builtin-merge.c b/builtin-merge.c index 8825dcf8d9..e78fa18b3a 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -431,7 +431,7 @@ static void merge_name(const char *remote, struct strbuf *msg) sha1_to_hex(remote_head->sha1), remote); } -int git_merge_config(const char *k, const char *v, void *cb) +static int git_merge_config(const char *k, const char *v, void *cb) { if (branch && !prefixcmp(k, "branch.") && !prefixcmp(k + 7, branch) && -- cgit v1.2.1 From 0e8316cc16f1a938c36f12f5fcae4a081b6ec6ff Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Thu, 24 Jul 2008 17:41:12 -0500 Subject: Teach fsck and prune about the new location of temporary objects Since 5723fe7e, temporary objects are now created in their final destination directories, rather than in .git/objects/. Teach fsck to recognize and ignore the temporary objects it encounters, and teach prune to remove them. Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- builtin-fsck.c | 4 ++++ builtin-prune.c | 41 ++++++++++++++++++++++++----------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/builtin-fsck.c b/builtin-fsck.c index 7326dc33a5..7a4a4f144f 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -377,6 +377,10 @@ static void fsck_dir(int i, char *path) if (de->d_name[0] != '.') break; continue; + case 14: + if (prefixcmp(de->d_name, "tmp_obj_")) + break; + continue; case 38: sprintf(name, "%02x", i); memcpy(name+2, de->d_name, len+1); diff --git a/builtin-prune.c b/builtin-prune.c index 7de4cabe07..947de8cf25 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -13,6 +13,22 @@ static const char * const prune_usage[] = { static int show_only; static unsigned long expire; +static int prune_tmp_object(char *path, const char *filename) +{ + const char *fullpath = mkpath("%s/%s", path, filename); + if (expire) { + struct stat st; + if (lstat(fullpath, &st)) + return error("Could not stat '%s'", fullpath); + if (st.st_mtime > expire) + return 0; + } + printf("Removing stale temporary file %s\n", fullpath); + if (!show_only) + unlink(fullpath); + return 0; +} + static int prune_object(char *path, const char *filename, const unsigned char *sha1) { const char *fullpath = mkpath("%s/%s", path, filename); @@ -53,6 +69,11 @@ static int prune_dir(int i, char *path) if (de->d_name[0] != '.') break; continue; + case 14: + if (prefixcmp(de->d_name, "tmp_obj_")) + break; + prune_tmp_object(path, de->d_name); + continue; case 38: sprintf(name, "%02x", i); memcpy(name+2, de->d_name, len+1); @@ -105,23 +126,9 @@ static void remove_temporary_files(void) dirname); return; } - while ((de = readdir(dir)) != NULL) { - if (!prefixcmp(de->d_name, "tmp_")) { - char name[PATH_MAX]; - int c = snprintf(name, PATH_MAX, "%s/%s", - dirname, de->d_name); - if (c < 0 || c >= PATH_MAX) - continue; - if (expire) { - struct stat st; - if (stat(name, &st) != 0 || st.st_mtime >= expire) - continue; - } - printf("Removing stale temporary file %s\n", name); - if (!show_only) - unlink(name); - } - } + while ((de = readdir(dir)) != NULL) + if (!prefixcmp(de->d_name, "tmp_")) + prune_tmp_object(dirname, de->d_name); closedir(dir); } -- cgit v1.2.1 From 9944d1a0e4ce741b07248b95fff2f506b5f1729c Mon Sep 17 00:00:00 2001 From: Olivier Marin Date: Thu, 24 Jul 2008 14:44:40 +0200 Subject: update test case to protect am --skip behaviour Signed-off-by: Olivier Marin Signed-off-by: Junio C Hamano --- t/t4151-am-abort.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh index f45ab0a2e8..7d86cdff64 100755 --- a/t/t4151-am-abort.sh +++ b/t/t4151-am-abort.sh @@ -17,6 +17,8 @@ test_expect_success setup ' for i in 2 3 4 5 6 do echo $i >>file-1 && + echo $i >otherfile-$i && + git add otherfile-$i && test_tick && git commit -a -m $i || break done && -- cgit v1.2.1 From 859fdabaede6733c98b1ca8df2fabce000522bf9 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 23 Jul 2008 12:15:33 +0200 Subject: git-checkout: improve error messages, detect ambiguities. The patch is twofold: it moves the option consistency checks just under the parse_options call so that it doesn't get in the way of the tree reference vs. pathspecs desambiguation. The other part rewrites the way to understand arguments so that when git-checkout fails it does with an understandable message. Compared to the previous behavior we now have: - a better error message when doing: git checkout -- now complains about the reference not pointing to a tree, instead of things like: error: pathspec did not match any file(s) known to git. error: pathspec '--' did not match any file(s) known to git. - a better error message when doing: git checkout -- It now complains about not being a reference instead of the completely obscure: error: pathspec '--' did not match any file(s) known to git. - an error when -- wasn't used, and the first argument is ambiguous (i.e. can be interpreted as both ref and as path). Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- builtin-checkout.c | 82 ++++++++++++++++++++++++++++++++++--------- t/t2010-checkout-ambiguous.sh | 50 ++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 17 deletions(-) create mode 100755 t/t2010-checkout-ambiguous.sh diff --git a/builtin-checkout.c b/builtin-checkout.c index 9cadf9c299..411cc513c6 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -430,6 +430,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT_BOOLEAN('m', NULL, &opts.merge, "merge"), OPT_END(), }; + int has_dash_dash; memset(&opts, 0, sizeof(opts)); memset(&new, 0, sizeof(new)); @@ -440,11 +441,55 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, options, checkout_usage, PARSE_OPT_KEEP_DASHDASH); + + if (!opts.new_branch && (opts.track != git_branch_track)) + die("git checkout: --track and --no-track require -b"); + + if (opts.force && opts.merge) + die("git checkout: -f and -m are incompatible"); + + /* + * case 1: git checkout -- [] + * + * must be a valid tree, everything after the '--' must be + * a path. + * + * case 2: git checkout -- [] + * + * everything after the '--' must be paths. + * + * case 3: git checkout [] + * + * With no paths, if is a commit, that is to + * switch to the branch or detach HEAD at it. + * + * Otherwise shall not be ambiguous. + * - If it's *only* a reference, treat it like case (1). + * - If it's only a path, treat it like case (2). + * - else: fail. + * + */ if (argc) { + if (!strcmp(argv[0], "--")) { /* case (2) */ + argv++; + argc--; + goto no_reference; + } + arg = argv[0]; - if (get_sha1(arg, rev)) - ; - else if ((new.commit = lookup_commit_reference_gently(rev, 1))) { + has_dash_dash = (argc > 1) && !strcmp(argv[1], "--"); + + if (get_sha1(arg, rev)) { + if (has_dash_dash) /* case (1) */ + die("invalid reference: %s", arg); + goto no_reference; /* case (3 -> 2) */ + } + + /* we can't end up being in (2) anymore, eat the argument */ + argv++; + argc--; + + if ((new.commit = lookup_commit_reference_gently(rev, 1))) { new.name = arg; setup_branch_path(&new); if (resolve_ref(new.path, rev, 1, NULL)) @@ -453,25 +498,28 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) new.path = NULL; parse_commit(new.commit); source_tree = new.commit->tree; - argv++; - argc--; - } else if ((source_tree = parse_tree_indirect(rev))) { + } else + source_tree = parse_tree_indirect(rev); + + if (!source_tree) /* case (1): want a tree */ + die("reference is not a tree: %s", arg); + if (!has_dash_dash) {/* case (3 -> 1) */ + /* + * Do not complain the most common case + * git checkout branch + * even if there happen to be a file called 'branch'; + * it would be extremely annoying. + */ + if (argc) + verify_non_filename(NULL, arg); + } + else { argv++; argc--; } } - if (argc && !strcmp(argv[0], "--")) { - argv++; - argc--; - } - - if (!opts.new_branch && (opts.track != git_branch_track)) - die("git checkout: --track and --no-track require -b"); - - if (opts.force && opts.merge) - die("git checkout: -f and -m are incompatible"); - +no_reference: if (argc) { const char **pathspec = get_pathspec(prefix, argv); diff --git a/t/t2010-checkout-ambiguous.sh b/t/t2010-checkout-ambiguous.sh new file mode 100755 index 0000000000..7cc0a3582e --- /dev/null +++ b/t/t2010-checkout-ambiguous.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +test_description='checkout and pathspecs/refspecs ambiguities' + +. ./test-lib.sh + +test_expect_success 'setup' ' + echo hello >world && + echo hello >all && + git add all world && + git commit -m initial && + git branch world +' + +test_expect_success 'reference must be a tree' ' + test_must_fail git checkout $(git hash-object ./all) -- +' + +test_expect_success 'branch switching' ' + test "refs/heads/master" = "$(git symbolic-ref HEAD)" && + git checkout world -- && + test "refs/heads/world" = "$(git symbolic-ref HEAD)" +' + +test_expect_success 'checkout world from the index' ' + echo bye > world && + git checkout -- world && + git diff --exit-code --quiet +' + +test_expect_success 'non ambiguous call' ' + git checkout all +' + +test_expect_success 'allow the most common case' ' + git checkout world && + test "refs/heads/world" = "$(git symbolic-ref HEAD)" +' + +test_expect_success 'check ambiguity' ' + test_must_fail git checkout world all +' + +test_expect_success 'disambiguate checking out from a tree-ish' ' + echo bye > world && + git checkout world -- world && + git diff --exit-code --quiet +' + +test_done -- cgit v1.2.1 From b302ddd2f8de2944ed3425d48b1a640fab004d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Wed, 23 Jul 2008 13:49:21 +0200 Subject: checkout: mention '--' in the docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'git checkout' uses '--' to separate options from paths, but it was not mentioned in the documentation Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- Documentation/git-checkout.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 2abfbdaadb..5aa69c0e12 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git checkout' [-q] [-f] [[--track | --no-track] -b [-l]] [-m] [] -'git checkout' [] ... +'git checkout' [] [--] ... DESCRIPTION ----------- -- cgit v1.2.1 From c84bb14ce52b6559e0b8e10d554ff9b47149c042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Wed, 23 Jul 2008 13:49:22 +0200 Subject: bash: offer only paths after '--' for 'git checkout' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit d773c631 (bash: offer only paths after '--', 2008-07-08) did the same for several other git commands, but 'git checkout' went unnoticed. Signed-off-by: SZEDER Gábor 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 3b049348c3..40b3d99737 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -626,6 +626,8 @@ _git_bundle () _git_checkout () { + __git_has_doubledash && return + __gitcomp "$(__git_refs)" } -- cgit v1.2.1 From 058fb414804d545cc7dcca1313078d6a7e7bbd37 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Tue, 22 Jul 2008 16:15:41 -0500 Subject: perl/Makefile: update NO_PERL_MAKEMAKER section The perl modules must be copied to blib/lib so they are available for testing. Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- perl/Makefile | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/perl/Makefile b/perl/Makefile index 5e079ad011..b8547db2c6 100644 --- a/perl/Makefile +++ b/perl/Makefile @@ -22,13 +22,18 @@ clean: ifdef NO_PERL_MAKEMAKER instdir_SQ = $(subst ','\'',$(prefix)/lib) $(makfile): ../GIT-CFLAGS Makefile - echo all: > $@ - echo ' :' >> $@ + echo all: private-Error.pm Git.pm > $@ + echo ' mkdir -p blib/lib' >> $@ + echo ' $(RM) blib/lib/Git.pm; cp Git.pm blib/lib/' >> $@ + echo ' $(RM) blib/lib/Error.pm' >> $@ + '$(PERL_PATH_SQ)' -MError -e 'exit($$Error::VERSION < 0.15009)' || \ + echo ' cp private-Error.pm blib/lib/Error.pm' >> $@ echo install: >> $@ echo ' mkdir -p $(instdir_SQ)' >> $@ echo ' $(RM) $(instdir_SQ)/Git.pm; cp Git.pm $(instdir_SQ)' >> $@ - echo ' $(RM) $(instdir_SQ)/Error.pm; \ - cp private-Error.pm $(instdir_SQ)/Error.pm' >> $@ + echo ' $(RM) $(instdir_SQ)/Error.pm' >> $@ + '$(PERL_PATH_SQ)' -MError -e 'exit($$Error::VERSION < 0.15009)' || \ + echo ' cp private-Error.pm $(instdir_SQ)/Error.pm' >> $@ echo instlibdir: >> $@ echo ' echo $(instdir_SQ)' >> $@ else -- cgit v1.2.1 From a2d07d80ec9dfff0e51cac534be7c1642a47f8fb Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Thu, 24 Jul 2008 11:55:25 -0500 Subject: document that git-tag can tag more than heads After looking the git-tag manpage, someone on #git wondered how to tag a commit that is not a branch head. This patch changes the synopsis to say " | " instead of "" to address his question. Samuel Bronson had the idea of putting " | " for "" because most tags point to commits (and for the rest of the manpage, all tags point to commits). Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- Documentation/git-tag.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index b8dc88fa38..046ab3542b 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -9,7 +9,8 @@ git-tag - Create, list, delete or verify a tag object signed with GPG SYNOPSIS -------- [verse] -'git tag' [-a | -s | -u ] [-f] [-m | -F ] [] +'git tag' [-a | -s | -u ] [-f] [-m | -F ] + [ | ] 'git tag' -d ... 'git tag' [-n[]] -l [] 'git tag' -v ... -- cgit v1.2.1 From 8892048d516fffab2000c2b47e006d8d8ebaa8c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Valdemar=20M=C3=B8rch?= Date: Fri, 25 Jul 2008 15:06:48 +0200 Subject: send-email: find body-encoding correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 8291db6 (git-send-email: add charset header if we add encoded 'From', 2007-11-16), "$1" is used from a regexp without using () to capture anything in $1. Later, when that value was used, it causes a warning about a variable being undefined, instead of using the correct value for comparison (not that it makes difference in the current code that does not do actual re-encoding). Signed-off-by: Peter Valdemar Mørch Signed-off-by: Junio C Hamano --- git-send-email.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-send-email.perl b/git-send-email.perl index 0b04ba32f0..385ff7c219 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -850,7 +850,7 @@ foreach my $t (@files) { } elsif (/^Content-type:/i) { $has_content_type = 1; - if (/charset="?[^ "]+/) { + if (/charset="?([^ "]+)/) { $body_encoding = $1; } push @xh, $_; -- cgit v1.2.1 From 72de2883bd7d4ceda05f107826c7607c594de965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Thu, 24 Jul 2008 18:32:00 +0100 Subject: index-pack.c: correctly initialize appended objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When index-pack completes a thin pack it appends objects to the pack. Since the commit 92392b4(index-pack: Honor core.deltaBaseCacheLimit when resolving deltas) such an object can be pruned in case of memory pressure, and will be read back again by get_data_from_pack(). For this to work, the fields in object_entry structure need to be initialized properly. Noticed by Pierre Habouzit. Signed-off-by: Björn Steinbrink Acked-by: Nicolas Pitre Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- index-pack.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index-pack.c b/index-pack.c index c359f8c9df..7d5344abc0 100644 --- a/index-pack.c +++ b/index-pack.c @@ -698,6 +698,10 @@ static struct object_entry *append_obj_to_pack( write_or_die(output_fd, header, n); obj[0].idx.crc32 = crc32(0, Z_NULL, 0); obj[0].idx.crc32 = crc32(obj[0].idx.crc32, header, n); + obj[0].size = size; + obj[0].hdr_size = n; + obj[0].type = type; + obj[0].real_type = type; obj[1].idx.offset = obj[0].idx.offset + n; obj[1].idx.offset += write_compressed(output_fd, buf, size, &obj[0].idx.crc32); hashcpy(obj->idx.sha1, sha1); -- cgit v1.2.1 From c30e699fc509f43f459c17d3148e7d866fb9157a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Jul 2008 11:09:48 -0700 Subject: tests: propagate $(TAR) down from the toplevel Makefile Signed-off-by: Junio C Hamano --- Makefile | 1 + t/t5000-tar-tree.sh | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b003e3e60a..1d142095a7 100644 --- a/Makefile +++ b/Makefile @@ -1212,6 +1212,7 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS @echo SHELL_PATH=\''$(SHELL_PATH_SQ)'\' >$@ + @echo TAR=\''$(subst ','\'',$(TAR))'\' >>$@ ### Detect Tck/Tk interpreter path changes ifndef NO_TCLTK diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 9b0baac8db..5eb119ed0b 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -25,7 +25,6 @@ commit id embedding: ' . ./test-lib.sh -TAR=${TAR:-tar} UNZIP=${UNZIP:-unzip} SUBSTFORMAT=%H%n -- cgit v1.2.1 From bfce5087ee01fdead5cdc52180c8eef22adbbd71 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Jul 2008 12:35:10 -0700 Subject: Makefile: fix shell quoting Makefile records paths to a few programs in GIT-BUILD-OPTIONS file. These paths need to be quoted twice: once to protect specials from the shell that runs the generated GIT-BUILD-OPTIONS file, and again to protect them (and the first level of quoting itself) from the shell that runs the "echo" inside the Makefile. You can test this by trying: $ ln -s /bin/tar "$HOME/Tes' program/tar" $ make TAR="$HOME/Tes' program/tar" test Signed-off-by: Junio C Hamano --- Makefile | 7 +++++-- t/t5000-tar-tree.sh | 10 +++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 1d142095a7..f13184b2ba 100644 --- a/Makefile +++ b/Makefile @@ -1210,9 +1210,12 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS echo "$$FLAGS" >GIT-CFLAGS; \ fi +# We need to apply sq twice, once to protect from the shell +# that runs GIT-BUILD-OPTIONS, and then again to protect it +# and the first level quoting from the shell that runs "echo". GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS - @echo SHELL_PATH=\''$(SHELL_PATH_SQ)'\' >$@ - @echo TAR=\''$(subst ','\'',$(TAR))'\' >>$@ + @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@ + @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@ ### Detect Tck/Tk interpreter path changes ifndef NO_TCLTK diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 5eb119ed0b..87902f81ef 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -67,7 +67,7 @@ test_expect_success \ test_expect_success \ 'validate file modification time' \ 'mkdir extract && - $TAR xf b.tar -C extract a/a && + "$TAR" xf b.tar -C extract a/a && perl -e '\''print((stat("extract/a/a"))[9], "\n")'\'' >b.mtime && echo "1117231200" >expected.mtime && diff expected.mtime b.mtime' @@ -79,7 +79,7 @@ test_expect_success \ test_expect_success \ 'extract tar archive' \ - '(cd b && $TAR xf -) Date: Fri, 25 Jul 2008 00:34:47 -0700 Subject: Documentation: clarify how to disable elements in core.whitespace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Noticed by Peter Valdemar Mørch. Signed-off-by: Junio C Hamano --- Documentation/config.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index e7848055a9..798b551514 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -358,7 +358,8 @@ core.whitespace:: A comma separated list of common whitespace problems to notice. 'git-diff' will use `color.diff.whitespace` to highlight them, and 'git-apply --whitespace=error' will - consider them as errors: + consider them as errors. You can prefix `-` to disable + any of them (e.g. `-trailing-space`): + * `trailing-space` treats trailing whitespaces at the end of the line as an error (enabled by default). -- cgit v1.2.1 From 15430be5a1d17b888b45b608daab7573f24cf9f1 Mon Sep 17 00:00:00 2001 From: Abhijit Menon-Sen Date: Thu, 24 Jul 2008 18:58:53 +0530 Subject: git-gui: Look for gitk in $PATH, not $LIBEXEC/git-core Signed-off-by: Abhijit Menon-Sen Signed-off-by: Shawn O. Pearce --- git-gui.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 83e2645714..7c27a43a5d 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1686,10 +1686,10 @@ proc do_gitk {revs} { # -- Always start gitk through whatever we were loaded with. This # lets us bypass using shell process on Windows systems. # - set exe [file join [file dirname $::_git] gitk] + set exe [_which gitk] set cmd [list [info nameofexecutable] $exe] - if {! [file exists $exe]} { - error_popup [mc "Unable to start gitk:\n\n%s does not exist" $exe] + if {$exe eq {}} { + error_popup [mc "Couldn't find gitk in PATH"] } else { global env -- cgit v1.2.1 From 7838d3fb41ed7d738e2cbdf8e4f40f0367f4f46f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 25 Jul 2008 15:08:33 -0700 Subject: git-gui: Correct 'Visualize Branches' on Mac OS X to start gitk In Git 1.6 and later gitk is in $prefix/bin while git-gui and all of the other commands are in $gitexecdir, which is typically not the same as $prefix/bin. So we cannot launch $gitexecdir/gitk and expect it to actually start gitk properly. By allowing git-gui to locate the script via $PATH and then using exactly that path when we source it during the application start we can correctly run gitk on any Git 1.5 or later. Signed-off-by: Shawn O. Pearce --- macosx/AppMain.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macosx/AppMain.tcl b/macosx/AppMain.tcl index 41ca08e2b7..ddbe6334a2 100644 --- a/macosx/AppMain.tcl +++ b/macosx/AppMain.tcl @@ -7,7 +7,7 @@ if {[string first -psn [lindex $argv 0]] == 0} { } if {[file tail [lindex $argv 0]] eq {gitk}} { - set argv0 [file join $gitexecdir gitk] + set argv0 [lindex $argv 0] set AppMain_source $argv0 } else { set argv0 [file join $gitexecdir [file tail [lindex $argv 0]]] -- cgit v1.2.1 From d82f33e20dcf068e679eff2a16ecf8fe7b10fb31 Mon Sep 17 00:00:00 2001 From: Stephan Beyer Date: Fri, 25 Jul 2008 18:28:41 +0200 Subject: Move launch_editor() from builtin-tag.c to editor.c launch_editor() is declared in strbuf.h but defined in builtin-tag.c. This patch moves launch_editor() into a new source file editor.c, but keeps the declaration in strbuf.h. Signed-off-by: Stephan Beyer Signed-off-by: Junio C Hamano --- Makefile | 1 + builtin-tag.c | 53 ----------------------------------------------------- editor.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 53 deletions(-) create mode 100644 editor.c diff --git a/Makefile b/Makefile index f4b8e38e80..d0add9d8ec 100644 --- a/Makefile +++ b/Makefile @@ -410,6 +410,7 @@ LIB_OBJS += diff-no-index.o LIB_OBJS += diff-lib.o LIB_OBJS += diff.o LIB_OBJS += dir.o +LIB_OBJS += editor.o LIB_OBJS += entry.o LIB_OBJS += environment.o LIB_OBJS += exec_cmd.o diff --git a/builtin-tag.c b/builtin-tag.c index c2cca6cb6d..219f51d6fc 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -23,59 +23,6 @@ static const char * const git_tag_usage[] = { static char signingkey[1000]; -void launch_editor(const char *path, struct strbuf *buffer, const char *const *env) -{ - const char *editor, *terminal; - - editor = getenv("GIT_EDITOR"); - if (!editor && editor_program) - editor = editor_program; - if (!editor) - editor = getenv("VISUAL"); - if (!editor) - editor = getenv("EDITOR"); - - terminal = getenv("TERM"); - if (!editor && (!terminal || !strcmp(terminal, "dumb"))) { - fprintf(stderr, - "Terminal is dumb but no VISUAL nor EDITOR defined.\n" - "Please supply the message using either -m or -F option.\n"); - exit(1); - } - - if (!editor) - editor = "vi"; - - if (strcmp(editor, ":")) { - size_t len = strlen(editor); - int i = 0; - const char *args[6]; - struct strbuf arg0; - - strbuf_init(&arg0, 0); - if (strcspn(editor, "$ \t'") != len) { - /* there are specials */ - strbuf_addf(&arg0, "%s \"$@\"", editor); - args[i++] = "sh"; - args[i++] = "-c"; - args[i++] = arg0.buf; - } - args[i++] = editor; - args[i++] = path; - args[i] = NULL; - - if (run_command_v_opt_cd_env(args, 0, NULL, env)) - die("There was a problem with the editor %s.", editor); - strbuf_release(&arg0); - } - - if (!buffer) - return; - if (strbuf_read_file(buffer, path, 0) < 0) - die("could not read message file '%s': %s", - path, strerror(errno)); -} - struct tag_filter { const char *pattern; int lines; diff --git a/editor.c b/editor.c new file mode 100644 index 0000000000..483b62d2ce --- /dev/null +++ b/editor.c @@ -0,0 +1,56 @@ +#include "cache.h" +#include "strbuf.h" +#include "run-command.h" + +void launch_editor(const char *path, struct strbuf *buffer, const char *const *env) +{ + const char *editor, *terminal; + + editor = getenv("GIT_EDITOR"); + if (!editor && editor_program) + editor = editor_program; + if (!editor) + editor = getenv("VISUAL"); + if (!editor) + editor = getenv("EDITOR"); + + terminal = getenv("TERM"); + if (!editor && (!terminal || !strcmp(terminal, "dumb"))) { + fprintf(stderr, + "Terminal is dumb but no VISUAL nor EDITOR defined.\n" + "Please supply the message using either -m or -F option.\n"); + exit(1); + } + + if (!editor) + editor = "vi"; + + if (strcmp(editor, ":")) { + size_t len = strlen(editor); + int i = 0; + const char *args[6]; + struct strbuf arg0; + + strbuf_init(&arg0, 0); + if (strcspn(editor, "$ \t'") != len) { + /* there are specials */ + strbuf_addf(&arg0, "%s \"$@\"", editor); + args[i++] = "sh"; + args[i++] = "-c"; + args[i++] = arg0.buf; + } + args[i++] = editor; + args[i++] = path; + args[i] = NULL; + + if (run_command_v_opt_cd_env(args, 0, NULL, env)) + die("There was a problem with the editor %s.", editor); + strbuf_release(&arg0); + } + + if (!buffer) + return; + if (strbuf_read_file(buffer, path, 0) < 0) + die("could not read message file '%s': %s", + path, strerror(errno)); +} -- cgit v1.2.1 From 7198203ae37c11327c0d01f1e37f3e74381755a9 Mon Sep 17 00:00:00 2001 From: Stephan Beyer Date: Fri, 25 Jul 2008 18:28:42 +0200 Subject: editor.c: Libify launch_editor() This patch removes exit()/die() calls and builtin-specific messages from launch_editor(), so that it can be used as a general libgit.a function to launch an editor. Signed-off-by: Stephan Beyer Signed-off-by: Junio C Hamano --- builtin-commit.c | 6 +++++- builtin-tag.c | 6 +++++- editor.c | 24 ++++++++++++------------ strbuf.h | 2 +- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/builtin-commit.c b/builtin-commit.c index 6a9dc0e30f..9a11ca0bcd 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -646,7 +646,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix) char index[PATH_MAX]; const char *env[2] = { index, NULL }; snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); - launch_editor(git_path(commit_editmsg), NULL, env); + if (launch_editor(git_path(commit_editmsg), NULL, env)) { + fprintf(stderr, + "Please supply the message using either -m or -F option.\n"); + exit(1); + } } if (!no_verify && diff --git a/builtin-tag.c b/builtin-tag.c index 219f51d6fc..325b1b2632 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -295,7 +295,11 @@ static void create_tag(const unsigned char *object, const char *tag, write_or_die(fd, tag_template, strlen(tag_template)); close(fd); - launch_editor(path, buf, NULL); + if (launch_editor(path, buf, NULL)) { + fprintf(stderr, + "Please supply the message using either -m or -F option.\n"); + exit(1); + } unlink(path); free(path); diff --git a/editor.c b/editor.c index 483b62d2ce..eebc3e95fe 100644 --- a/editor.c +++ b/editor.c @@ -2,7 +2,7 @@ #include "strbuf.h" #include "run-command.h" -void launch_editor(const char *path, struct strbuf *buffer, const char *const *env) +int launch_editor(const char *path, struct strbuf *buffer, const char *const *env) { const char *editor, *terminal; @@ -15,12 +15,8 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e editor = getenv("EDITOR"); terminal = getenv("TERM"); - if (!editor && (!terminal || !strcmp(terminal, "dumb"))) { - fprintf(stderr, - "Terminal is dumb but no VISUAL nor EDITOR defined.\n" - "Please supply the message using either -m or -F option.\n"); - exit(1); - } + if (!editor && (!terminal || !strcmp(terminal, "dumb"))) + return error("Terminal is dumb but no VISUAL nor EDITOR defined."); if (!editor) editor = "vi"; @@ -28,6 +24,7 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e if (strcmp(editor, ":")) { size_t len = strlen(editor); int i = 0; + int failed; const char *args[6]; struct strbuf arg0; @@ -43,14 +40,17 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e args[i++] = path; args[i] = NULL; - if (run_command_v_opt_cd_env(args, 0, NULL, env)) - die("There was a problem with the editor %s.", editor); + failed = run_command_v_opt_cd_env(args, 0, NULL, env); strbuf_release(&arg0); + if (failed) + return error("There was a problem with the editor '%s'.", + editor); } if (!buffer) - return; + return 0; if (strbuf_read_file(buffer, path, 0) < 0) - die("could not read message file '%s': %s", - path, strerror(errno)); + return error("could not read file '%s': %s", + path, strerror(errno)); + return 0; } diff --git a/strbuf.h b/strbuf.h index 0c6ffad53a..eba7ba423a 100644 --- a/strbuf.h +++ b/strbuf.h @@ -123,6 +123,6 @@ extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint); extern int strbuf_getline(struct strbuf *, FILE *, int); extern void stripspace(struct strbuf *buf, int skip_comments); -extern void launch_editor(const char *path, struct strbuf *buffer, const char *const *env); +extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env); #endif /* STRBUF_H */ -- cgit v1.2.1 From 837c8767130ec71089e654f92cf24e8277a3bda5 Mon Sep 17 00:00:00 2001 From: Steve Haslam Date: Fri, 25 Jul 2008 18:51:51 +0100 Subject: Propagate -u/--upload-pack option of "git clone" to transport. The -u option to override the remote system's path to git-upload-pack was being ignored by "git clone"; caused by a missing call to transport_set_option to set TRANS_OPT_UPLOADPACK. Presumably this crept in when git-clone was converted from shell to C. Signed-off-by: Steve Haslam Signed-off-by: Junio C Hamano --- builtin-clone.c | 4 ++++ t/t5602-clone-remote-exec.sh | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100755 t/t5602-clone-remote-exec.sh diff --git a/builtin-clone.c b/builtin-clone.c index 352224591f..e086a40b41 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -480,6 +480,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_quiet) transport->verbose = -1; + if (option_upload_pack) + transport_set_option(transport, TRANS_OPT_UPLOADPACK, + option_upload_pack); + refs = transport_get_remote_refs(transport); transport_fetch_refs(transport, refs); } diff --git a/t/t5602-clone-remote-exec.sh b/t/t5602-clone-remote-exec.sh new file mode 100755 index 0000000000..8367a6845f --- /dev/null +++ b/t/t5602-clone-remote-exec.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +test_description=clone + +. ./test-lib.sh + +test_expect_success setup ' + echo "#!/bin/sh" > not_ssh + echo "echo \"\$*\" > not_ssh_output" >> not_ssh + echo "exit 1" >> not_ssh + chmod +x not_ssh +' + +test_expect_success 'clone calls git-upload-pack unqualified with no -u option' ' + GIT_SSH=./not_ssh git clone localhost:/path/to/repo junk + echo "localhost git-upload-pack '\''/path/to/repo'\''" >expected + test_cmp expected not_ssh_output +' + +test_expect_success 'clone calls specified git-upload-pack with -u option' ' + GIT_SSH=./not_ssh git clone -u /something/bin/git-upload-pack localhost:/path/to/repo junk + echo "localhost /something/bin/git-upload-pack '\''/path/to/repo'\''" >expected + test_cmp expected not_ssh_output +' + +test_done -- cgit v1.2.1 From 09f8d055f9c2af79a72f8d2a2f13698d9bbe6956 Mon Sep 17 00:00:00 2001 From: Stephan Beyer Date: Fri, 25 Jul 2008 20:22:23 +0200 Subject: git-am: Mention --abort in usage string part of OPTIONS_SPEC The three separate lines for --skip, --resolved and --abort are merged into one so that it is easy to see that they're alternative and related options. Signed-off-by: Stephan Beyer Signed-off-by: Junio C Hamano --- git-am.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/git-am.sh b/git-am.sh index f4abd9db15..6aa819280e 100755 --- a/git-am.sh +++ b/git-am.sh @@ -6,8 +6,7 @@ SUBDIRECTORY_OK=Yes OPTIONS_KEEPDASHDASH= OPTIONS_SPEC="\ git am [options] [|...] -git am [options] --resolved -git am [options] --skip +git am [options] (--resolved | --skip | --abort) -- d,dotest= (removed -- do not use) i,interactive run interactively -- cgit v1.2.1 From d3296e37b61fdd80a8b785270b1d11db34dab2f5 Mon Sep 17 00:00:00 2001 From: Steve Haslam Date: Fri, 25 Jul 2008 19:37:48 +0100 Subject: Remove references to git-fetch-pack from "git clone" documentation. "git clone" no longer calls "git-fetch-pack", so the documentation is a bit stale. Instead, state that the -u option is to be used when accessing a repository over ssh. Signed-off-by: Steve Haslam Signed-off-by: Junio C Hamano --- Documentation/git-clone.txt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 91efac920e..26fd1b1117 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -87,8 +87,8 @@ then the cloned repository will become corrupt. --quiet:: -q:: - Operate quietly. This flag is passed to "rsync" and - 'git-fetch-pack' commands when given. + Operate quietly. This flag is also passed to the `rsync' + command when given. --no-checkout:: -n:: @@ -113,9 +113,8 @@ then the cloned repository will become corrupt. --upload-pack :: -u :: - When given, and the repository to clone from is handled - by 'git-fetch-pack', `--exec=` is passed to - the command to specify non-default path for the command + When given, and the repository to clone from is accessed + via ssh, this specifies a non-default path for the command run on the other end. --template=:: -- cgit v1.2.1 From 6e94e6835f397cd2602ca1eb12002e51aa6b0500 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Fri, 25 Jul 2008 12:41:21 +0200 Subject: archive: add write_archive() Both archive and upload-archive have to parse command line arguments and then call the archiver specific write function. Move the duplicate code to a new function, write_archive(). Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive.c | 18 ++++++++++++++++++ archive.h | 1 + builtin-archive.c | 13 +------------ builtin-upload-archive.c | 10 +--------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/archive.c b/archive.c index b8b45bad77..75eb257760 100644 --- a/archive.c +++ b/archive.c @@ -155,3 +155,21 @@ int write_archive_entries(struct archiver_args *args, err = 0; return err; } + +int write_archive(int argc, const char **argv, const char *prefix, + int setup_prefix) +{ + const struct archiver *ar = NULL; + struct archiver_args args; + int tree_idx; + + tree_idx = parse_archive_args(argc, argv, &ar, &args); + if (setup_prefix && prefix == NULL) + prefix = setup_git_directory(); + + argv += tree_idx; + parse_treeish_arg(argv, &args, prefix); + parse_pathspec_arg(argv + 1, &args); + + return ar->write_archive(&args); +} diff --git a/archive.h b/archive.h index 4a02371f37..6b5fe5af45 100644 --- a/archive.h +++ b/archive.h @@ -41,5 +41,6 @@ extern int write_tar_archive(struct archiver_args *); extern int write_zip_archive(struct archiver_args *); extern int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry); +extern int write_archive(int argc, const char **argv, const char *prefix, int setup_prefix); #endif /* ARCHIVE_H */ diff --git a/builtin-archive.c b/builtin-archive.c index df97724696..502b339e6b 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -232,9 +232,6 @@ static const char *extract_remote_arg(int *ac, const char **av) int cmd_archive(int argc, const char **argv, const char *prefix) { - const struct archiver *ar = NULL; - struct archiver_args args; - int tree_idx; const char *remote = NULL; remote = extract_remote_arg(&argc, argv); @@ -243,13 +240,5 @@ int cmd_archive(int argc, const char **argv, const char *prefix) setvbuf(stderr, NULL, _IOLBF, BUFSIZ); - tree_idx = parse_archive_args(argc, argv, &ar, &args); - if (prefix == NULL) - prefix = setup_git_directory(); - - argv += tree_idx; - parse_treeish_arg(argv, &args, prefix); - parse_pathspec_arg(argv + 1, &args); - - return ar->write_archive(&args); + return write_archive(argc, argv, prefix, 1); } diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c index 13a6c6203e..cc37b36d99 100644 --- a/builtin-upload-archive.c +++ b/builtin-upload-archive.c @@ -19,12 +19,9 @@ static const char lostchild[] = static int run_upload_archive(int argc, const char **argv, const char *prefix) { - const struct archiver *ar; - struct archiver_args args; const char *sent_argv[MAX_ARGS]; const char *arg_cmd = "argument "; char *p, buf[4096]; - int treeish_idx; int sent_argc; int len; @@ -66,12 +63,7 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix) sent_argv[sent_argc] = NULL; /* parse all options sent by the client */ - treeish_idx = parse_archive_args(sent_argc, sent_argv, &ar, &args); - - parse_treeish_arg(sent_argv + treeish_idx, &args, prefix); - parse_pathspec_arg(sent_argv + treeish_idx + 1, &args); - - return ar->write_archive(&args); + return write_archive(sent_argc, sent_argv, prefix, 0); } static void error_clnt(const char *fmt, ...) -- cgit v1.2.1 From c0885435537e4b93709d2bf39ce36454186057a1 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Fri, 25 Jul 2008 12:41:22 +0200 Subject: archive: move parameter parsing code to archive.c write_archive() in archive.c is the only callsite for the command line parsing functions located in builtin-archive.c. Move them to the place where they are used, un-export them and make them static, as hinted at by Stephan. Cc: Stephan Beyer Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ archive.h | 8 ---- builtin-archive.c | 137 ------------------------------------------------------ 3 files changed, 137 insertions(+), 145 deletions(-) diff --git a/archive.c b/archive.c index 75eb257760..c4662a2830 100644 --- a/archive.c +++ b/archive.c @@ -1,8 +1,19 @@ #include "cache.h" #include "commit.h" +#include "tree-walk.h" #include "attr.h" #include "archive.h" +static const char archive_usage[] = \ +"git archive --format= [--prefix=/] [--verbose] [] [path...]"; + +#define USES_ZLIB_COMPRESSION 1 + +const struct archiver archivers[] = { + { "tar", write_tar_archive }, + { "zip", write_zip_archive, USES_ZLIB_COMPRESSION }, +}; + static void format_subst(const struct commit *commit, const char *src, size_t len, struct strbuf *buf) @@ -156,6 +167,132 @@ int write_archive_entries(struct archiver_args *args, return err; } +static const struct archiver *lookup_archiver(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(archivers); i++) { + if (!strcmp(name, archivers[i].name)) + return &archivers[i]; + } + return NULL; +} + +static void parse_pathspec_arg(const char **pathspec, + struct archiver_args *ar_args) +{ + ar_args->pathspec = get_pathspec(ar_args->base, pathspec); +} + +static void parse_treeish_arg(const char **argv, + struct archiver_args *ar_args, const char *prefix) +{ + const char *name = argv[0]; + const unsigned char *commit_sha1; + time_t archive_time; + struct tree *tree; + const struct commit *commit; + unsigned char sha1[20]; + + if (get_sha1(name, sha1)) + die("Not a valid object name"); + + commit = lookup_commit_reference_gently(sha1, 1); + if (commit) { + commit_sha1 = commit->object.sha1; + archive_time = commit->date; + } else { + commit_sha1 = NULL; + archive_time = time(NULL); + } + + tree = parse_tree_indirect(sha1); + if (tree == NULL) + die("not a tree object"); + + if (prefix) { + unsigned char tree_sha1[20]; + unsigned int mode; + int err; + + err = get_tree_entry(tree->object.sha1, prefix, + tree_sha1, &mode); + if (err || !S_ISDIR(mode)) + die("current working directory is untracked"); + + tree = parse_tree_indirect(tree_sha1); + } + ar_args->tree = tree; + ar_args->commit_sha1 = commit_sha1; + ar_args->commit = commit; + ar_args->time = archive_time; +} + +static int parse_archive_args(int argc, const char **argv, + const struct archiver **ar, struct archiver_args *args) +{ + const char *format = "tar"; + const char *base = ""; + int compression_level = -1; + int verbose = 0; + int i; + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { + for (i = 0; i < ARRAY_SIZE(archivers); i++) + printf("%s\n", archivers[i].name); + exit(0); + } + if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) { + verbose = 1; + continue; + } + if (!prefixcmp(arg, "--format=")) { + format = arg + 9; + continue; + } + if (!prefixcmp(arg, "--prefix=")) { + base = arg + 9; + continue; + } + if (!strcmp(arg, "--")) { + i++; + break; + } + if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0') { + compression_level = arg[1] - '0'; + continue; + } + if (arg[0] == '-') + die("Unknown argument: %s", arg); + break; + } + + /* We need at least one parameter -- tree-ish */ + if (argc - 1 < i) + usage(archive_usage); + *ar = lookup_archiver(format); + if (!*ar) + die("Unknown archive format '%s'", format); + + args->compression_level = Z_DEFAULT_COMPRESSION; + if (compression_level != -1) { + if ((*ar)->flags & USES_ZLIB_COMPRESSION) + args->compression_level = compression_level; + else { + die("Argument not supported for format '%s': -%d", + format, compression_level); + } + } + args->verbose = verbose; + args->base = base; + args->baselen = strlen(base); + + return i; +} + int write_archive(int argc, const char **argv, const char *prefix, int setup_prefix) { diff --git a/archive.h b/archive.h index 6b5fe5af45..f6ceaebc8f 100644 --- a/archive.h +++ b/archive.h @@ -26,14 +26,6 @@ struct archiver { unsigned int flags; }; -extern int parse_archive_args(int argc, const char **argv, const struct archiver **ar, struct archiver_args *args); - -extern void parse_treeish_arg(const char **treeish, - struct archiver_args *ar_args, - const char *prefix); - -extern void parse_pathspec_arg(const char **pathspec, - struct archiver_args *args); /* * Archive-format specific backends. */ diff --git a/builtin-archive.c b/builtin-archive.c index 502b339e6b..4dd2716c0f 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -5,21 +5,9 @@ #include "cache.h" #include "builtin.h" #include "archive.h" -#include "commit.h" -#include "tree-walk.h" #include "pkt-line.h" #include "sideband.h" -static const char archive_usage[] = \ -"git archive --format= [--prefix=/] [--verbose] [] [path...]"; - -#define USES_ZLIB_COMPRESSION 1 - -const struct archiver archivers[] = { - { "tar", write_tar_archive }, - { "zip", write_zip_archive, USES_ZLIB_COMPRESSION }, -}; - static int run_remote_archiver(const char *remote, int argc, const char **argv) { @@ -74,131 +62,6 @@ static int run_remote_archiver(const char *remote, int argc, return !!rv; } -static const struct archiver *lookup_archiver(const char *name) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(archivers); i++) { - if (!strcmp(name, archivers[i].name)) - return &archivers[i]; - } - return NULL; -} - -void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args) -{ - ar_args->pathspec = get_pathspec(ar_args->base, pathspec); -} - -void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, - const char *prefix) -{ - const char *name = argv[0]; - const unsigned char *commit_sha1; - time_t archive_time; - struct tree *tree; - const struct commit *commit; - unsigned char sha1[20]; - - if (get_sha1(name, sha1)) - die("Not a valid object name"); - - commit = lookup_commit_reference_gently(sha1, 1); - if (commit) { - commit_sha1 = commit->object.sha1; - archive_time = commit->date; - } else { - commit_sha1 = NULL; - archive_time = time(NULL); - } - - tree = parse_tree_indirect(sha1); - if (tree == NULL) - die("not a tree object"); - - if (prefix) { - unsigned char tree_sha1[20]; - unsigned int mode; - int err; - - err = get_tree_entry(tree->object.sha1, prefix, - tree_sha1, &mode); - if (err || !S_ISDIR(mode)) - die("current working directory is untracked"); - - tree = parse_tree_indirect(tree_sha1); - } - ar_args->tree = tree; - ar_args->commit_sha1 = commit_sha1; - ar_args->commit = commit; - ar_args->time = archive_time; -} - -int parse_archive_args(int argc, const char **argv, const struct archiver **ar, - struct archiver_args *args) -{ - const char *format = "tar"; - const char *base = ""; - int compression_level = -1; - int verbose = 0; - int i; - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { - for (i = 0; i < ARRAY_SIZE(archivers); i++) - printf("%s\n", archivers[i].name); - exit(0); - } - if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) { - verbose = 1; - continue; - } - if (!prefixcmp(arg, "--format=")) { - format = arg + 9; - continue; - } - if (!prefixcmp(arg, "--prefix=")) { - base = arg + 9; - continue; - } - if (!strcmp(arg, "--")) { - i++; - break; - } - if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0') { - compression_level = arg[1] - '0'; - continue; - } - if (arg[0] == '-') - die("Unknown argument: %s", arg); - break; - } - - /* We need at least one parameter -- tree-ish */ - if (argc - 1 < i) - usage(archive_usage); - *ar = lookup_archiver(format); - if (!*ar) - die("Unknown archive format '%s'", format); - - args->compression_level = Z_DEFAULT_COMPRESSION; - if (compression_level != -1) { - if ((*ar)->flags & USES_ZLIB_COMPRESSION) - args->compression_level = compression_level; - else { - die("Argument not supported for format '%s': -%d", - format, compression_level); - } - } - args->verbose = verbose; - args->base = base; - args->baselen = strlen(base); - - return i; -} - static const char *extract_remote_arg(int *ac, const char **av) { int ix, iy, cnt = *ac; -- cgit v1.2.1 From 7f4d0511af9d6c93656dda5a683632f5ae5b5278 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Fri, 25 Jul 2008 12:41:23 +0200 Subject: archive: define MAX_ARGS where it's needed MAX_EXTRA_ARGS is not used anymore, so remove it. MAX_ARGS is used only in builtin-upload-archive.c, so define it there. Also report the actual value we're comparing against when the number of args is too big. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive.h | 3 --- builtin-upload-archive.c | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/archive.h b/archive.h index f6ceaebc8f..929368d80b 100644 --- a/archive.h +++ b/archive.h @@ -1,9 +1,6 @@ #ifndef ARCHIVE_H #define ARCHIVE_H -#define MAX_EXTRA_ARGS 32 -#define MAX_ARGS (MAX_EXTRA_ARGS + 32) - struct archiver_args { const char *base; size_t baselen; diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c index cc37b36d99..a9b02fa32f 100644 --- a/builtin-upload-archive.c +++ b/builtin-upload-archive.c @@ -16,6 +16,7 @@ static const char deadchild[] = static const char lostchild[] = "git upload-archive: archiver process was lost"; +#define MAX_ARGS (64) static int run_upload_archive(int argc, const char **argv, const char *prefix) { @@ -45,7 +46,7 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix) if (len == 0) break; /* got a flush */ if (sent_argc > MAX_ARGS - 2) - die("Too many options (>29)"); + die("Too many options (>%d)", MAX_ARGS - 2); if (p[len-1] == '\n') { p[--len] = 0; -- cgit v1.2.1 From f15f736d38d10f5c4f2ca367565019bdfe8e71dd Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Fri, 25 Jul 2008 12:41:24 +0200 Subject: archive: declare struct archiver where it's needed Move the declaration of struct archiver to archive.c, as this is the only file left where it is used. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive.c | 6 +++++- archive.h | 6 ------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/archive.c b/archive.c index c4662a2830..f834b5f51f 100644 --- a/archive.c +++ b/archive.c @@ -9,7 +9,11 @@ static const char archive_usage[] = \ #define USES_ZLIB_COMPRESSION 1 -const struct archiver archivers[] = { +const struct archiver { + const char *name; + write_archive_fn_t write_archive; + unsigned int flags; +} archivers[] = { { "tar", write_tar_archive }, { "zip", write_zip_archive, USES_ZLIB_COMPRESSION }, }; diff --git a/archive.h b/archive.h index 929368d80b..0b15b35143 100644 --- a/archive.h +++ b/archive.h @@ -17,12 +17,6 @@ typedef int (*write_archive_fn_t)(struct archiver_args *); typedef int (*write_archive_entry_fn_t)(struct archiver_args *args, const unsigned char *sha1, const char *path, size_t pathlen, unsigned int mode, void *buffer, unsigned long size); -struct archiver { - const char *name; - write_archive_fn_t write_archive; - unsigned int flags; -}; - /* * Archive-format specific backends. */ -- cgit v1.2.1 From 819b2b58246a7927376930e266b4ef8b43096115 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Fri, 25 Jul 2008 12:41:25 +0200 Subject: archive: allow --exec and --remote without equal sign Allow "--remote repo" and "--exec cmd" in addition to "--remote=repo" and "--exec=cmd" to make their usage consistent with parameters handled by parse_options(). Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- builtin-archive.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/builtin-archive.c b/builtin-archive.c index 4dd2716c0f..22445acbfc 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -15,7 +15,7 @@ static int run_remote_archiver(const char *remote, int argc, int fd[2], i, len, rv; struct child_process *conn; const char *exec = "git-upload-archive"; - int exec_at = 0; + int exec_at = 0, exec_value_at = 0; for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -24,7 +24,14 @@ static int run_remote_archiver(const char *remote, int argc, die("multiple --exec specified"); exec = arg + 7; exec_at = i; - break; + } else if (!strcmp(arg, "--exec")) { + if (exec_at) + die("multiple --exec specified"); + if (i + 1 >= argc) + die("option --exec requires a value"); + exec = argv[i + 1]; + exec_at = i; + exec_value_at = ++i; } } @@ -32,7 +39,7 @@ static int run_remote_archiver(const char *remote, int argc, conn = git_connect(fd, url, exec, 0); for (i = 1; i < argc; i++) { - if (i == exec_at) + if (i == exec_at || i == exec_value_at) continue; packet_write(fd[1], "argument %s\n", argv[i]); } @@ -78,6 +85,13 @@ static const char *extract_remote_arg(int *ac, const char **av) die("Multiple --remote specified"); remote = arg + 9; continue; + } else if (!strcmp(arg, "--remote")) { + if (remote) + die("Multiple --remote specified"); + if (++ix >= cnt) + die("option --remote requires a value"); + remote = av[ix]; + continue; } if (arg[0] != '-') no_more_options = 1; -- cgit v1.2.1 From c70a8d98a528f29bc6d6de7744e1eedd24f2a63d Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 21 Jul 2008 21:19:50 +0200 Subject: Makefile: Do not install a copy of 'git' in $(gitexecdir) There is already a copy in $(bindir). A subsequent patch will enable git to derive the exec-path from its invocation path. If git is invoked recursively, the first invocation puts the exec-path into PATH, so that the recursive invocation would find the instance in the exec-path. This second instance would again try to derive an exec-path from its invocation path, but would base its result on the wrong "bindir". We do install the copy of git first, but remove it later, so that we can use it as the source of the hardlinks for the builtins. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index d0add9d8ec..e6add0314d 100644 --- a/Makefile +++ b/Makefile @@ -1340,6 +1340,7 @@ endif '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X'; \ fi $(foreach p,$(BUILT_INS), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;) + $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' ifneq (,$X) $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p';) endif -- cgit v1.2.1 From 46beb55988e68da04c2e9d319c71b9b940f0854c Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 21 Jul 2008 21:19:51 +0200 Subject: Makefile: Normalize $(bindir) and $(gitexecdir) before comparing The install target needs to check whether the user has opted to make $(gitexecdir) equal to $(bindir). It did so by a straight string comparison. Since we are going to allow a relative $(gitexecdir), we have to normalize paths before comparison, which we do with $(cd there && pwd). The normalized paths are stored in shell variables. These we can now reuse in the subsequent install statements, which conveniently shortens the lines a bit. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- Makefile | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index e6add0314d..e096763060 100644 --- a/Makefile +++ b/Makefile @@ -1332,19 +1332,19 @@ ifndef NO_TCLTK $(MAKE) -C gitk-git install $(MAKE) -C git-gui install endif - if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \ - then \ - ln -f '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \ - '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' || \ - cp '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \ - '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X'; \ - fi - $(foreach p,$(BUILT_INS), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;) - $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' ifneq (,$X) $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p';) endif - ./check_bindir 'z$(bindir_SQ)' 'z$(gitexecdir_SQ)' '$(DESTDIR_SQ)$(bindir_SQ)/git-shell$X' + bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \ + execdir=$$(cd '$(DESTDIR_SQ)$(gitexecdir_SQ)' && pwd) && \ + if test "z$$bindir" != "z$$execdir"; \ + then \ + ln -f "$$bindir/git$X" "$$execdir/git$X" || \ + cp "$$bindir/git$X" "$$execdir/git$X"; \ + fi && \ + { $(foreach p,$(BUILT_INS), $(RM) "$$execdir/$p" && ln "$$execdir/git$X" "$$execdir/$p" ;) } && \ + $(RM) "$$execdir/git$X" && \ + ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-shell$X" install-doc: $(MAKE) -C Documentation install -- cgit v1.2.1 From e1464ca7bb0d705210ba7198f004b2fb2b807e12 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 21 Jul 2008 21:19:52 +0200 Subject: Record the command invocation path early We will need the command invocation path in system_path(). This path was passed to setup_path(), but system_path() can be called earlier, for example via: main commit_pager_choice setup_pager git_config git_etc_gitconfig system_path Therefore, we introduce git_set_argv0_path() and call it as soon as possible. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- exec_cmd.c | 10 ++++++++-- exec_cmd.h | 3 ++- git.c | 5 ++--- receive-pack.c | 2 +- shell.c | 4 ++-- upload-pack.c | 2 +- 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/exec_cmd.c b/exec_cmd.c index 8899e31b3b..dedb01da6f 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -5,6 +5,7 @@ extern char **environ; static const char *argv_exec_path; +static const char *argv0_path; static const char *builtin_exec_path(void) { @@ -50,6 +51,11 @@ const char *system_path(const char *path) return path; } +void git_set_argv0_path(const char *path) +{ + argv0_path = path; +} + void git_set_argv_exec_path(const char *exec_path) { argv_exec_path = exec_path; @@ -84,7 +90,7 @@ static void add_path(struct strbuf *out, const char *path) } } -void setup_path(const char *cmd_path) +void setup_path(void) { const char *old_path = getenv("PATH"); struct strbuf new_path; @@ -94,7 +100,7 @@ void setup_path(const char *cmd_path) add_path(&new_path, argv_exec_path); add_path(&new_path, getenv(EXEC_PATH_ENVIRONMENT)); add_path(&new_path, builtin_exec_path()); - add_path(&new_path, cmd_path); + add_path(&new_path, argv0_path); if (old_path) strbuf_addstr(&new_path, old_path); diff --git a/exec_cmd.h b/exec_cmd.h index 7eb94e5e11..0c46cd5636 100644 --- a/exec_cmd.h +++ b/exec_cmd.h @@ -2,8 +2,9 @@ #define GIT_EXEC_CMD_H extern void git_set_argv_exec_path(const char *exec_path); +extern void git_set_argv0_path(const char *path); extern const char* git_exec_path(void); -extern void setup_path(const char *); +extern void setup_path(void); extern int execv_git_cmd(const char **argv); /* NULL terminated */ extern int execl_git_cmd(const char *cmd, ...); extern const char *system_path(const char *path); diff --git a/git.c b/git.c index 1bfd271a71..37b1d76a08 100644 --- a/git.c +++ b/git.c @@ -418,7 +418,6 @@ int main(int argc, const char **argv) { const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help"; char *slash = (char *)cmd + strlen(cmd); - const char *cmd_path = NULL; int done_alias = 0; /* @@ -431,7 +430,7 @@ int main(int argc, const char **argv) while (cmd <= slash && !is_dir_sep(*slash)); if (cmd <= slash) { *slash++ = 0; - cmd_path = cmd; + git_set_argv0_path(cmd); cmd = slash; } @@ -475,7 +474,7 @@ int main(int argc, const char **argv) * environment, and the $(gitexecdir) from the Makefile at build * time. */ - setup_path(cmd_path); + setup_path(); while (1) { /* See if it's an internal command */ diff --git a/receive-pack.c b/receive-pack.c index fa653b49fe..d44c19e6b5 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -482,7 +482,7 @@ int main(int argc, char **argv) if (!dir) usage(receive_pack_usage); - setup_path(NULL); + setup_path(); if (!enter_repo(dir, 0)) die("'%s': unable to chdir or not a git archive", dir); diff --git a/shell.c b/shell.c index 91ca7de082..6a48de05ff 100644 --- a/shell.c +++ b/shell.c @@ -15,7 +15,7 @@ static int do_generic_cmd(const char *me, char *arg) { const char *my_argv[4]; - setup_path(NULL); + setup_path(); if (!arg || !(arg = sq_dequote(arg))) die("bad argument"); if (prefixcmp(me, "git-")) @@ -37,7 +37,7 @@ static int do_cvs_cmd(const char *me, char *arg) if (!arg || strcmp(arg, "server")) die("git-cvsserver only handles server: %s", arg); - setup_path(NULL); + setup_path(); return execv_git_cmd(cvsserver_argv); } diff --git a/upload-pack.c b/upload-pack.c index 9f82941f8b..c911e70c9a 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -638,7 +638,7 @@ int main(int argc, char **argv) if (i != argc-1) usage(upload_pack_usage); - setup_path(NULL); + setup_path(); dir = argv[i]; -- cgit v1.2.1 From 966c6edd318f2e44dd150103ec2b6b7a53be58f0 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 21 Jul 2008 21:19:53 +0200 Subject: Fix relative built-in paths to be relative to the command invocation $(gitexecdir) (as defined in the Makefile) has gained another path component, but the relative paths in the MINGW section of the Makefile, which are interpreted relative to it, do not account for it. Instead of adding another ../ in front of the path, we change the code that constructs the absolute paths to do it relative to the command's directory, which is essentially $(bindir). We do it this way because we will also allow a relative $(gitexecdir) later. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- Makefile | 2 +- exec_cmd.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index e096763060..fa8cf8adb6 100644 --- a/Makefile +++ b/Makefile @@ -1315,7 +1315,7 @@ remove-dashes: ### Installation rules ifeq ($(firstword $(subst /, ,$(template_dir))),..) -template_instdir = $(gitexecdir)/$(template_dir) +template_instdir = $(bindir)/$(template_dir) else template_instdir = $(template_dir) endif diff --git a/exec_cmd.c b/exec_cmd.c index dedb01da6f..45f92eb164 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -43,9 +43,9 @@ static const char *builtin_exec_path(void) const char *system_path(const char *path) { - if (!is_absolute_path(path)) { + if (!is_absolute_path(path) && argv0_path) { struct strbuf d = STRBUF_INIT; - strbuf_addf(&d, "%s/%s", git_exec_path(), path); + strbuf_addf(&d, "%s/%s", argv0_path, path); path = strbuf_detach(&d, NULL); } return path; -- cgit v1.2.1 From 49fa65a7a8185e81c1098815df607042103b0493 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Wed, 23 Jul 2008 21:12:18 +0200 Subject: Allow the built-in exec path to be relative to the command invocation path If GIT_EXEC_PATH (the macro that is defined in the Makefile) is relative, it is interpreted relative to the command's invocation path, which usually is $(bindir). The Makefile rules were written with the assumption that $(gitexecdir) is an absolute path. We introduce a separate variable that names the (absolute) installation directory. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- Makefile | 30 ++++++++++++++++++++++++------ exec_cmd.c | 38 ++------------------------------------ 2 files changed, 26 insertions(+), 42 deletions(-) diff --git a/Makefile b/Makefile index fa8cf8adb6..fceca92184 100644 --- a/Makefile +++ b/Makefile @@ -170,6 +170,16 @@ ALL_CFLAGS = $(CFLAGS) ALL_LDFLAGS = $(LDFLAGS) STRIP ?= strip +# Among the variables below, these: +# gitexecdir +# template_dir +# htmldir +# ETC_GITCONFIG (but not sysconfdir) +# can be specified as a relative path ../some/where/else (which must begin +# with ../); this is interpreted as relative to $(bindir) and "git" at +# runtime figures out where they are based on the path to the executable. +# This can help installing the suite in a relocatable way. + prefix = $(HOME) bindir = $(prefix)/bin mandir = $(prefix)/share/man @@ -205,7 +215,7 @@ GITWEB_FAVICON = git-favicon.png GITWEB_SITE_HEADER = GITWEB_SITE_FOOTER = -export prefix bindir gitexecdir sharedir htmldir sysconfdir +export prefix bindir sharedir htmldir sysconfdir CC = gcc AR = ar @@ -1321,22 +1331,30 @@ template_instdir = $(template_dir) endif export template_instdir +ifeq ($(firstword $(subst /, ,$(gitexecdir))),..) +gitexec_instdir = $(bindir)/$(gitexecdir) +else +gitexec_instdir = $(gitexecdir) +endif +gitexec_instdir_SQ = $(subst ','\'',$(gitexec_instdir)) +export gitexec_instdir + install: all $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexecdir_SQ)' - $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_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)' $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install ifndef NO_TCLTK $(MAKE) -C gitk-git install - $(MAKE) -C git-gui install + $(MAKE) -C git-gui gitexecdir='$(gitexec_instdir_SQ)' install endif ifneq (,$X) - $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p';) + $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p';) endif bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \ - execdir=$$(cd '$(DESTDIR_SQ)$(gitexecdir_SQ)' && pwd) && \ + execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \ if test "z$$bindir" != "z$$execdir"; \ then \ ln -f "$$bindir/git$X" "$$execdir/git$X" || \ diff --git a/exec_cmd.c b/exec_cmd.c index 45f92eb164..c23603452e 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -7,40 +7,6 @@ extern char **environ; static const char *argv_exec_path; static const char *argv0_path; -static const char *builtin_exec_path(void) -{ -#ifndef __MINGW32__ - return GIT_EXEC_PATH; -#else - int len; - char *p, *q, *sl; - static char *ep; - if (ep) - return ep; - - len = strlen(_pgmptr); - if (len < 2) - return ep = "."; - - p = ep = xmalloc(len+1); - q = _pgmptr; - sl = NULL; - /* copy program name, turn '\\' into '/', skip last part */ - while ((*p = *q)) { - if (*q == '\\' || *q == '/') { - *p = '/'; - sl = p; - } - p++, q++; - } - if (sl) - *sl = '\0'; - else - ep[0] = '.', ep[1] = '\0'; - return ep; -#endif -} - const char *system_path(const char *path) { if (!is_absolute_path(path) && argv0_path) { @@ -75,7 +41,7 @@ const char *git_exec_path(void) return env; } - return builtin_exec_path(); + return system_path(GIT_EXEC_PATH); } static void add_path(struct strbuf *out, const char *path) @@ -99,7 +65,7 @@ void setup_path(void) add_path(&new_path, argv_exec_path); add_path(&new_path, getenv(EXEC_PATH_ENVIRONMENT)); - add_path(&new_path, builtin_exec_path()); + add_path(&new_path, system_path(GIT_EXEC_PATH)); add_path(&new_path, argv0_path); if (old_path) -- cgit v1.2.1 From 10c4c881c4d2cb0ece0508e7142e189e68445257 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 21 Jul 2008 21:19:55 +0200 Subject: Allow add_path() to add non-existent directories to the path This function had used make_absolute_path(); but this function dies if the directory that contains the entry whose relative path was supplied in the argument does not exist. This is a problem if the argument is, for example, "../libexec/git-core", and that "../libexec" does not exist. Since the resolution of symbolic links is not required for elements in PATH, we can fall back to using make_nonrelative_path(), which simply prepends $PWD to the path. We have to move make_nonrelative_path() alongside make_absolute_path() in abspath.c so that git-shell can be linked. See 5b8e6f85f. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- abspath.c | 36 ++++++++++++++++++++++++++++++++++++ exec_cmd.c | 2 +- path.c | 36 ------------------------------------ 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/abspath.c b/abspath.c index 4f95a954d5..0d561246e0 100644 --- a/abspath.c +++ b/abspath.c @@ -66,3 +66,39 @@ const char *make_absolute_path(const char *path) return buf; } + +static const char *get_pwd_cwd(void) +{ + static char cwd[PATH_MAX + 1]; + char *pwd; + struct stat cwd_stat, pwd_stat; + if (getcwd(cwd, PATH_MAX) == NULL) + return NULL; + pwd = getenv("PWD"); + if (pwd && strcmp(pwd, cwd)) { + stat(cwd, &cwd_stat); + if (!stat(pwd, &pwd_stat) && + pwd_stat.st_dev == cwd_stat.st_dev && + pwd_stat.st_ino == cwd_stat.st_ino) { + strlcpy(cwd, pwd, PATH_MAX); + } + } + return cwd; +} + +const char *make_nonrelative_path(const char *path) +{ + static char buf[PATH_MAX + 1]; + + if (is_absolute_path(path)) { + if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) + die("Too long path: %.*s", 60, path); + } else { + const char *cwd = get_pwd_cwd(); + if (!cwd) + die("Cannot determine the current working directory"); + if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX) + die("Too long path: %.*s", 60, path); + } + return buf; +} diff --git a/exec_cmd.c b/exec_cmd.c index c23603452e..0ed768ddc0 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -50,7 +50,7 @@ static void add_path(struct strbuf *out, const char *path) if (is_absolute_path(path)) strbuf_addstr(out, path); else - strbuf_addstr(out, make_absolute_path(path)); + strbuf_addstr(out, make_nonrelative_path(path)); strbuf_addch(out, PATH_SEP); } diff --git a/path.c b/path.c index 504eae061f..9df447bd6d 100644 --- a/path.c +++ b/path.c @@ -291,42 +291,6 @@ int adjust_shared_perm(const char *path) return 0; } -static const char *get_pwd_cwd(void) -{ - static char cwd[PATH_MAX + 1]; - char *pwd; - struct stat cwd_stat, pwd_stat; - if (getcwd(cwd, PATH_MAX) == NULL) - return NULL; - pwd = getenv("PWD"); - if (pwd && strcmp(pwd, cwd)) { - stat(cwd, &cwd_stat); - if (!stat(pwd, &pwd_stat) && - pwd_stat.st_dev == cwd_stat.st_dev && - pwd_stat.st_ino == cwd_stat.st_ino) { - strlcpy(cwd, pwd, PATH_MAX); - } - } - return cwd; -} - -const char *make_nonrelative_path(const char *path) -{ - static char buf[PATH_MAX + 1]; - - if (is_absolute_path(path)) { - if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) - die ("Too long path: %.*s", 60, path); - } else { - const char *cwd = get_pwd_cwd(); - if (!cwd) - die("Cannot determine the current working directory"); - if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX) - die ("Too long path: %.*s", 60, path); - } - return buf; -} - const char *make_relative_path(const char *abs, const char *base) { static char buf[PATH_MAX + 1]; -- cgit v1.2.1 From bf74a88244c4fe631535b50ef090541dddf9029d Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 21 Jul 2008 21:19:56 +0200 Subject: Windows: Make $(gitexecdir) relative Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index fceca92184..3a0dc88c85 100644 --- a/Makefile +++ b/Makefile @@ -756,6 +756,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o compat/winansi.o EXTLIBS += -lws2_32 X = .exe + gitexecdir = ../libexec/git-core template_dir = ../share/git-core/templates/ ETC_GITCONFIG = ../etc/gitconfig endif -- cgit v1.2.1 From 35eeef47220c68c92d0f377f5678c42da2232c20 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 21 Jul 2008 21:19:57 +0200 Subject: Windows: Make sure argv[0] has a path Since the exec-path on Windows is derived from the program invocation path, we must ensure that argv[0] always has a path. Unfortunately, if a program is invoked from CMD, argv[0] has no path. But on the other hand, the C runtime offers a global variable, _pgmptr, that always has the full path to the program. We hook into main() with a preprocessor macro, where we replace argv[0]. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- compat/mingw.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compat/mingw.h b/compat/mingw.h index 8ffec51e73..290a9e6f82 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -223,3 +223,15 @@ void mingw_open_html(const char *path); char **copy_environ(void); void free_environ(char **env); char **env_setenv(char **env, const char *name); + +/* + * A replacement of main() that ensures that argv[0] has a path + */ + +#define main(c,v) main(int argc, const char **argv) \ +{ \ + static int mingw_main(); \ + argv[0] = xstrdup(_pgmptr); \ + return mingw_main(argc, argv); \ +} \ +static int mingw_main(c,v) -- cgit v1.2.1 From 7b4b59a963c726d30b319a79c1f8243a82d6e12f Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 21 Jul 2008 21:19:58 +0200 Subject: Windows: Do not compile git-shell Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 3a0dc88c85..798a2f2f77 100644 --- a/Makefile +++ b/Makefile @@ -293,7 +293,6 @@ PROGRAMS += git-pack-redundant$X PROGRAMS += git-patch-id$X PROGRAMS += git-receive-pack$X PROGRAMS += git-send-pack$X -PROGRAMS += git-shell$X PROGRAMS += git-show-index$X PROGRAMS += git-unpack-file$X PROGRAMS += git-update-server-info$X @@ -823,6 +822,7 @@ EXTLIBS += -lz ifndef NO_POSIX_ONLY_PROGRAMS PROGRAMS += git-daemon$X PROGRAMS += git-imap-send$X + PROGRAMS += git-shell$X endif ifndef NO_OPENSSL OPENSSL_LIBSSL = -lssl @@ -1363,7 +1363,7 @@ endif fi && \ { $(foreach p,$(BUILT_INS), $(RM) "$$execdir/$p" && ln "$$execdir/git$X" "$$execdir/$p" ;) } && \ $(RM) "$$execdir/git$X" && \ - ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-shell$X" + ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X" install-doc: $(MAKE) -C Documentation install -- cgit v1.2.1 From 128de6576743901b11f243509753320038e74ba7 Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 25 Jul 2008 11:32:37 -0400 Subject: git-svn: teach dcommit about svn auto-props Subversion repositories often require files to have properties such as svn:mime-type and svn:eol-style set when they are added. Users typically set these properties automatically using the SVN auto-props feature with 'svn add'. This commit teaches dcommit to look at the user SVN configuration and apply matching auto-props entries for files added by a diff as it is applied to the SVN remote. Signed-off-by: Brad King Acked-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 53 +++++++++++++++++++++ t/t9124-git-svn-dcommit-auto-props.sh | 86 +++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100755 t/t9124-git-svn-dcommit-auto-props.sh diff --git a/git-svn.perl b/git-svn.perl index 2e0e55242f..cf6dbbc427 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3340,6 +3340,7 @@ sub new { $self->{rm} = { }; $self->{path_prefix} = length $self->{svn_path} ? "$self->{svn_path}/" : ''; + $self->{config} = $opts->{config}; return $self; } @@ -3528,6 +3529,57 @@ sub ensure_path { return $bat->{$c}; } +# Subroutine to convert a globbing pattern to a regular expression. +# From perl cookbook. +sub glob2pat { + my $globstr = shift; + my %patmap = ('*' => '.*', '?' => '.', '[' => '[', ']' => ']'); + $globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge; + return '^' . $globstr . '$'; +} + +sub check_autoprop { + my ($self, $pattern, $properties, $file, $fbat) = @_; + # Convert the globbing pattern to a regular expression. + my $regex = glob2pat($pattern); + # Check if the pattern matches the file name. + if($file =~ m/($regex)/) { + # Parse the list of properties to set. + my @props = split(/;/, $properties); + foreach my $prop (@props) { + # Parse 'name=value' syntax and set the property. + if ($prop =~ /([^=]+)=(.*)/) { + my ($n,$v) = ($1,$2); + for ($n, $v) { + s/^\s+//; s/\s+$//; + } + $self->change_file_prop($fbat, $n, $v); + } + } + } +} + +sub apply_autoprops { + my ($self, $file, $fbat) = @_; + my $conf_t = ${$self->{config}}{'config'}; + no warnings 'once'; + # Check [miscellany]/enable-auto-props in svn configuration. + if (SVN::_Core::svn_config_get_bool( + $conf_t, + $SVN::_Core::SVN_CONFIG_SECTION_MISCELLANY, + $SVN::_Core::SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, + 0)) { + # Auto-props are enabled. Enumerate them to look for matches. + my $callback = sub { + $self->check_autoprop($_[0], $_[1], $file, $fbat); + }; + SVN::_Core::svn_config_enumerate( + $conf_t, + $SVN::_Core::SVN_CONFIG_SECTION_AUTO_PROPS, + $callback); + } +} + sub A { my ($self, $m) = @_; my ($dir, $file) = split_path($m->{file_b}); @@ -3535,6 +3587,7 @@ sub A { my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, undef, -1); print "\tA\t$m->{file_b}\n" unless $::_q; + $self->apply_autoprops($file, $fbat); $self->chg_file($fbat, $m); $self->close_file($fbat,undef,$self->{pool}); } diff --git a/t/t9124-git-svn-dcommit-auto-props.sh b/t/t9124-git-svn-dcommit-auto-props.sh new file mode 100755 index 0000000000..8223c5909e --- /dev/null +++ b/t/t9124-git-svn-dcommit-auto-props.sh @@ -0,0 +1,86 @@ +#!/bin/sh +# +# Copyright (c) 2008 Brad King + +test_description='git-svn dcommit honors auto-props' + +. ./lib-git-svn.sh + +generate_auto_props() { +cat << EOF +[miscellany] +enable-auto-props=$1 +[auto-props] +*.sh = svn:mime-type=application/x-shellscript; svn:eol-style=LF +*.txt = svn:mime-type=text/plain; svn:eol-style = native +EOF +} + +test_expect_success 'initialize git-svn' ' + mkdir import && + ( + cd import && + echo foo >foo && + svn import -m "import for git-svn" . "$svnrepo" + ) && + rm -rf import && + git-svn init "$svnrepo" + git-svn fetch +' + +test_expect_success 'enable auto-props config' ' + cd "$gittestrepo" && + mkdir user && + generate_auto_props yes >user/config +' + +test_expect_success 'add files matching auto-props' ' + cd "$gittestrepo" && + echo "#!$SHELL_PATH" >exec1.sh && + chmod +x exec1.sh && + echo "hello" >hello.txt && + echo bar >bar && + git add exec1.sh hello.txt bar && + git commit -m "files for enabled auto-props" && + git svn dcommit --config-dir=user +' + +test_expect_success 'disable auto-props config' ' + cd "$gittestrepo" && + generate_auto_props no >user/config +' + +test_expect_success 'add files matching disabled auto-props' ' + cd "$gittestrepo" && + echo "#$SHELL_PATH" >exec2.sh && + chmod +x exec2.sh && + echo "world" >world.txt && + echo zot >zot && + git add exec2.sh world.txt zot && + git commit -m "files for disabled auto-props" && + git svn dcommit --config-dir=user +' + +test_expect_success 'check resulting svn repository' ' + mkdir work && + cd work && + svn co "$svnrepo" && + cd svnrepo && + + # Check properties from first commit. + test "x$(svn propget svn:executable exec1.sh)" = "x*" && + test "x$(svn propget svn:mime-type exec1.sh)" = \ + "xapplication/x-shellscript" && + test "x$(svn propget svn:mime-type hello.txt)" = "xtext/plain" && + test "x$(svn propget svn:eol-style hello.txt)" = "xnative" && + test "x$(svn propget svn:mime-type bar)" = "x" && + + # Check properties from second commit. + test "x$(svn propget svn:executable exec2.sh)" = "x*" && + test "x$(svn propget svn:mime-type exec2.sh)" = "x" && + test "x$(svn propget svn:mime-type world.txt)" = "x" && + test "x$(svn propget svn:eol-style world.txt)" = "x" && + test "x$(svn propget svn:mime-type zot)" = "x" +' + +test_done -- cgit v1.2.1 From b0320eaf6a25fbc4adf35d611c27006e6d853aa8 Mon Sep 17 00:00:00 2001 From: Stephan Beyer Date: Fri, 25 Jul 2008 22:49:08 +0200 Subject: git-reset: Let -q hush "locally modified" messages "git reset -q" is advertised to "only report errors", but "locally modified" messages are still shown. They are not errors but diagnostics. Signed-off-by: Stephan Beyer Signed-off-by: Junio C Hamano --- builtin-reset.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/builtin-reset.c b/builtin-reset.c index 4d246c31b1..c24c219091 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -85,7 +85,7 @@ static void print_new_head_line(struct commit *commit) printf("\n"); } -static int update_index_refresh(int fd, struct lock_file *index_lock) +static int update_index_refresh(int fd, struct lock_file *index_lock, int flags) { int result; @@ -96,7 +96,8 @@ static int update_index_refresh(int fd, struct lock_file *index_lock) if (read_cache() < 0) return error("Could not read index"); - result = refresh_cache(REFRESH_SAY_CHANGED) ? 1 : 0; + + result = refresh_cache(flags) ? 1 : 0; if (write_cache(fd, active_cache, active_nr) || commit_locked_index(index_lock)) return error ("Could not refresh index"); @@ -128,7 +129,7 @@ static void update_index_from_diff(struct diff_queue_struct *q, } static int read_from_tree(const char *prefix, const char **argv, - unsigned char *tree_sha1) + unsigned char *tree_sha1, int refresh_flags) { struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); int index_fd, index_was_discarded = 0; @@ -152,7 +153,7 @@ static int read_from_tree(const char *prefix, const char **argv, if (!index_was_discarded) /* The index is still clobbered from do_diff_cache() */ discard_cache(); - return update_index_refresh(index_fd, lock); + return update_index_refresh(index_fd, lock, refresh_flags); } static void prepend_reflog_action(const char *action, char *buf, size_t size) @@ -246,7 +247,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) else if (reset_type != NONE) die("Cannot do %s reset with paths.", reset_type_names[reset_type]); - return read_from_tree(prefix, argv + i, sha1); + return read_from_tree(prefix, argv + i, sha1, + quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED); } if (reset_type == NONE) reset_type = MIXED; /* by default */ @@ -286,7 +288,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) case SOFT: /* Nothing else to do. */ break; case MIXED: /* Report what has not been updated. */ - update_index_refresh(0, NULL); + update_index_refresh(0, NULL, + quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED); break; } -- cgit v1.2.1 From 041aee31be378b3b38e3a0913b29970a7f78873b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 21 Jul 2008 01:24:17 -0700 Subject: builtin-add.c: restructure the code for maintainability A private function add_files_to_cache() in builtin-add.c was borrowed by checkout and commit re-implementors without getting properly refactored to more library-ish place. This does the refactoring. Signed-off-by: Junio C Hamano --- builtin-add.c | 57 ------------------------------------------------------- cache.h | 1 + read-cache.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 57 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index fc3f96eaef..0de516ad95 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -8,10 +8,6 @@ #include "dir.h" #include "exec_cmd.h" #include "cache-tree.h" -#include "diff.h" -#include "diffcore.h" -#include "commit.h" -#include "revision.h" #include "run-command.h" #include "parse-options.h" @@ -79,59 +75,6 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec, prune_directory(dir, pathspec, baselen); } -struct update_callback_data -{ - int flags; - int add_errors; -}; - -static void update_callback(struct diff_queue_struct *q, - struct diff_options *opt, void *cbdata) -{ - int i; - struct update_callback_data *data = cbdata; - - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - const char *path = p->one->path; - switch (p->status) { - default: - die("unexpected diff status %c", p->status); - case DIFF_STATUS_UNMERGED: - case DIFF_STATUS_MODIFIED: - case DIFF_STATUS_TYPE_CHANGED: - if (add_file_to_cache(path, data->flags)) { - if (!(data->flags & ADD_CACHE_IGNORE_ERRORS)) - die("updating files failed"); - data->add_errors++; - } - break; - case DIFF_STATUS_DELETED: - if (!(data->flags & ADD_CACHE_PRETEND)) - remove_file_from_cache(path); - if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE)) - printf("remove '%s'\n", path); - break; - } - } -} - -int add_files_to_cache(const char *prefix, const char **pathspec, int flags) -{ - struct update_callback_data data; - struct rev_info rev; - init_revisions(&rev, prefix); - setup_revisions(0, NULL, &rev, NULL); - rev.prune_data = pathspec; - rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; - rev.diffopt.format_callback = update_callback; - data.flags = flags; - data.add_errors = 0; - rev.diffopt.format_callback_data = &data; - run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); - return !!data.add_errors; -} - static void refresh(int verbose, const char **pathspec) { char *seen; diff --git a/cache.h b/cache.h index 38985aa63e..6f374add88 100644 --- a/cache.h +++ b/cache.h @@ -375,6 +375,7 @@ extern int remove_file_from_index(struct index_state *, const char *path); #define ADD_CACHE_VERBOSE 1 #define ADD_CACHE_PRETEND 2 #define ADD_CACHE_IGNORE_ERRORS 4 +#define ADD_CACHE_IGNORE_REMOVAL 8 extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags); extern int add_file_to_index(struct index_state *, const char *path, int flags); extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh); diff --git a/read-cache.c b/read-cache.c index a50a851125..6833af6cf1 100644 --- a/read-cache.c +++ b/read-cache.c @@ -8,6 +8,11 @@ #include "cache-tree.h" #include "refs.h" #include "dir.h" +#include "tree.h" +#include "commit.h" +#include "diff.h" +#include "diffcore.h" +#include "revision.h" /* Index extensions. * @@ -1444,3 +1449,59 @@ int read_index_unmerged(struct index_state *istate) istate->cache_nr = dst - istate->cache; return !!last; } + +struct update_callback_data +{ + int flags; + int add_errors; +}; + +static void update_callback(struct diff_queue_struct *q, + struct diff_options *opt, void *cbdata) +{ + int i; + struct update_callback_data *data = cbdata; + + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + const char *path = p->one->path; + switch (p->status) { + default: + die("unexpected diff status %c", p->status); + case DIFF_STATUS_UNMERGED: + case DIFF_STATUS_MODIFIED: + case DIFF_STATUS_TYPE_CHANGED: + if (add_file_to_index(&the_index, path, data->flags)) { + if (!(data->flags & ADD_CACHE_IGNORE_ERRORS)) + die("updating files failed"); + data->add_errors++; + } + break; + case DIFF_STATUS_DELETED: + if (data->flags & ADD_CACHE_IGNORE_REMOVAL) + break; + if (!(data->flags & ADD_CACHE_PRETEND)) + remove_file_from_index(&the_index, path); + if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE)) + printf("remove '%s'\n", path); + break; + } + } +} + +int add_files_to_cache(const char *prefix, const char **pathspec, int flags) +{ + struct update_callback_data data; + struct rev_info rev; + init_revisions(&rev, prefix); + setup_revisions(0, NULL, &rev, NULL); + rev.prune_data = pathspec; + rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; + rev.diffopt.format_callback = update_callback; + data.flags = flags; + data.add_errors = 0; + rev.diffopt.format_callback_data = &data; + run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); + return !!data.add_errors; +} + -- cgit v1.2.1 From 1e5f764c93edfd8f6575b6ede769b079a1fc5a21 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 22 Jul 2008 22:30:40 -0700 Subject: builtin-add.c: optimize -A option and "git add ." The earlier "git add -A" change was done in a quite inefficient way (i.e. it is as unefficient as "git add -u && git add ." modulo one fork/exec and read/write index). When the user asks "git add .", we do not have to examine all paths we encounter and perform the excluded() and dir_add_name() processing, both of which are slower code and use slower data structure by git standards, especially when the index is already populated. Instead, we implement "git add $pathspec..." as: - read the index; - read_directory() to process untracked, unignored files the current way, that is, recursively doing readdir(), filtering them by pathspec and excluded(), queueing them via dir_add_name() and finally do add_files(); and - iterate over the index, filtering them by pathspec, and update only the modified/type changed paths but not deleted ones. And "git add -A" becomes exactly the same as above, modulo: - missing $pathspec means "." instead of being an error; and - "iterate over the index" part handles deleted ones as well, i.e. exactly what the current update_callback() in builtin-add.c does. In either case, because fill_directory() does not use read_directory() to read everything in, we need to add an extra logic to iterate over the index to catch mistyped pathspec. Signed-off-by: Junio C Hamano --- builtin-add.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index 0de516ad95..1834e2d7cd 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -18,6 +18,27 @@ static const char * const builtin_add_usage[] = { static int patch_interactive = 0, add_interactive = 0; static int take_worktree_changes; +static void fill_pathspec_matches(const char **pathspec, char *seen, int specs) +{ + int num_unmatched = 0, i; + + /* + * Since we are walking the index as if we are warlking the directory, + * we have to mark the matched pathspec as seen; otherwise we will + * mistakenly think that the user gave a pathspec that did not match + * anything. + */ + for (i = 0; i < specs; i++) + if (!seen[i]) + num_unmatched++; + if (!num_unmatched) + return; + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen); + } +} + static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) { char *seen; @@ -37,6 +58,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p *dst++ = entry; } dir->nr = dst - dir->entries; + fill_pathspec_matches(pathspec, seen, specs); for (i = 0; i < specs; i++) { if (!seen[i] && !file_exists(pathspec[i])) @@ -201,7 +223,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (addremove && take_worktree_changes) die("-A and -u are mutually incompatible"); - if (addremove && !argc) { + if ((addremove || take_worktree_changes) && !argc) { static const char *here[2] = { ".", NULL }; argc = 1; argv = here; @@ -214,7 +236,9 @@ int cmd_add(int argc, const char **argv, const char *prefix) flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | (show_only ? ADD_CACHE_PRETEND : 0) | - (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0)); + (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) | + (!(addremove || take_worktree_changes) + ? ADD_CACHE_IGNORE_REMOVAL : 0)); if (require_pathspec && argc == 0) { fprintf(stderr, "Nothing specified, nothing added.\n"); @@ -223,24 +247,19 @@ int cmd_add(int argc, const char **argv, const char *prefix) } pathspec = get_pathspec(prefix, argv); - /* - * If we are adding new files, we need to scan the working - * tree to find the ones that match pathspecs; this needs - * to be done before we read the index. - */ - if (add_new_files) - fill_directory(&dir, pathspec, ignored_too); - if (read_cache() < 0) die("index file corrupt"); + if (add_new_files) + /* This picks up the paths that are not tracked */ + fill_directory(&dir, pathspec, ignored_too); + if (refresh_only) { refresh(verbose, pathspec); goto finish; } - if (take_worktree_changes || addremove) - exit_status |= add_files_to_cache(prefix, pathspec, flags); + exit_status |= add_files_to_cache(prefix, pathspec, flags); if (add_new_files) exit_status |= add_files(&dir, flags); -- cgit v1.2.1 From ccf08bc3d06050fbe9b76846f6e2ab6d1cd6bd09 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 22 Jul 2008 03:12:46 -0400 Subject: run-command: add pre-exec callback This is a function provided by the caller which is called _after_ the process is forked, but before the spawned program is executed. On platforms (like mingw) where subprocesses are forked and executed in a single call, the preexec callback is simply ignored. This will be used in the following patch to do some setup for 'less' that must happen in the forked child. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- run-command.c | 2 ++ run-command.h | 1 + 2 files changed, 3 insertions(+) diff --git a/run-command.c b/run-command.c index 6e29fdf9e2..73d0c31276 100644 --- a/run-command.c +++ b/run-command.c @@ -110,6 +110,8 @@ int start_command(struct child_process *cmd) unsetenv(*cmd->env); } } + if (cmd->preexec_cb) + cmd->preexec_cb(); if (cmd->git_cmd) { execv_git_cmd(cmd->argv); } else { diff --git a/run-command.h b/run-command.h index 5203a9ebb1..4f2b7d7d40 100644 --- a/run-command.h +++ b/run-command.h @@ -42,6 +42,7 @@ struct child_process { unsigned no_stderr:1; unsigned git_cmd:1; /* if this is to be git sub-command */ unsigned stdout_to_stderr:1; + void (*preexec_cb)(void); }; int start_command(struct child_process *); -- cgit v1.2.1 From ea27a18ce2bf5860974745c04c24864231029e1d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 22 Jul 2008 03:14:12 -0400 Subject: spawn pager via run_command interface This has two important effects: 1. The pager is now the _child_ process, instead of the parent. This means that whatever spawned git (e.g., the shell) will see the exit code of the git process, and not the pager. 2. The mingw and regular code are now unified, which makes the setup_pager function much simpler. There are two caveats: 1. We used to call execlp directly on the pager, followed by trying to exec it via the shall. We now just use the shell (which is what mingw has always done). This may have different results for pager names which contain shell metacharacters. It is also slightly less efficient because we unnecessarily run the shell; however, pager spawning is by definition an interactive task, so it shouldn't be a huge problem. 2. The git process will remain in memory while the user looks through the pager. This is potentially wasteful. We could get around this by turning the parent into a meta-process which spawns _both_ git and the pager, collects the exit status from git, waits for both to end, and then exits with git's exit code. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- pager.c | 55 +++++++++++-------------------------------------------- 1 file changed, 11 insertions(+), 44 deletions(-) diff --git a/pager.c b/pager.c index 6b5c9e44b4..aa0966c9c5 100644 --- a/pager.c +++ b/pager.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "run-command.h" /* * This is split up from the rest of git so that we can do @@ -8,7 +9,7 @@ static int spawned_pager; #ifndef __MINGW32__ -static void run_pager(const char *pager) +static void pager_preexec(void) { /* * Work around bug in "less" by not starting it until we @@ -20,17 +21,13 @@ static void run_pager(const char *pager) FD_SET(0, &in); select(1, &in, NULL, &in, NULL); - execlp(pager, pager, NULL); - execl("/bin/sh", "sh", "-c", pager, NULL); + setenv("LESS", "FRSX", 0); } -#else -#include "run-command.h" +#endif static const char *pager_argv[] = { "sh", "-c", NULL, NULL }; -static struct child_process pager_process = { - .argv = pager_argv, - .in = -1 -}; +static struct child_process pager_process; + static void wait_for_pager(void) { fflush(stdout); @@ -40,14 +37,9 @@ static void wait_for_pager(void) close(2); finish_command(&pager_process); } -#endif void setup_pager(void) { -#ifndef __MINGW32__ - pid_t pid; - int fd[2]; -#endif const char *pager = getenv("GIT_PAGER"); if (!isatty(1)) @@ -66,37 +58,13 @@ void setup_pager(void) spawned_pager = 1; /* means we are emitting to terminal */ -#ifndef __MINGW32__ - if (pipe(fd) < 0) - return; - pid = fork(); - if (pid < 0) { - close(fd[0]); - close(fd[1]); - return; - } - - /* return in the child */ - if (!pid) { - dup2(fd[1], 1); - dup2(fd[1], 2); - close(fd[0]); - close(fd[1]); - return; - } - - /* The original process turns into the PAGER */ - dup2(fd[0], 0); - close(fd[0]); - close(fd[1]); - - setenv("LESS", "FRSX", 0); - run_pager(pager); - die("unable to execute pager '%s'", pager); - exit(255); -#else /* spawn the pager */ pager_argv[2] = pager; + pager_process.argv = pager_argv; + pager_process.in = -1; +#ifndef __MINGW32__ + pager_process.preexec_cb = pager_preexec; +#endif if (start_command(&pager_process)) return; @@ -107,7 +75,6 @@ void setup_pager(void) /* this makes sure that the parent terminates after the pager */ atexit(wait_for_pager); -#endif } int pager_in_use(void) -- cgit v1.2.1 From a0406b94d57e8443a7b31b5412763cac4ddb5d21 Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Wed, 9 Jul 2008 22:28:59 +0100 Subject: git-imap-send: Allow the program to be run from subdirectories of a git tree Call setup_git_directory_gently to allow git-imap-send to be used from subdirectories of a git tree. Signed-off-by: Robert Shearman Signed-off-by: Junio C Hamano --- imap-send.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imap-send.c b/imap-send.c index 1ec1310921..24d76a7031 100644 --- a/imap-send.c +++ b/imap-send.c @@ -1292,10 +1292,12 @@ main(int argc, char **argv) int ofs = 0; int r; int total, n = 0; + int nongit_ok; /* init the random number generator */ arc4_init(); + setup_git_directory_gently(&nongit_ok); git_config(git_imap_config, NULL); if (!imap_folder) { -- cgit v1.2.1 From 684ec6c63cd92a3755500b3b63f3d5ad9f6828b2 Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Wed, 9 Jul 2008 22:29:00 +0100 Subject: git-imap-send: Support SSL Allow SSL to be used when a imaps:// URL is used for the host name. Also, automatically use TLS when not using imaps:// by using the IMAP STARTTLS command, if the server supports it. Tested with Courier and Gimap IMAP servers. Signed-off-by: Robert Shearman Signed-off-by: Junio C Hamano --- Documentation/git-imap-send.txt | 3 +- Makefile | 4 +- git-compat-util.h | 5 ++ imap-send.c | 160 ++++++++++++++++++++++++++++++++++++---- 4 files changed, 156 insertions(+), 16 deletions(-) diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt index b3d8da33ee..136c82bfdd 100644 --- a/Documentation/git-imap-send.txt +++ b/Documentation/git-imap-send.txt @@ -37,10 +37,11 @@ configuration file (shown with examples): Tunnel = "ssh -q user@server.com /usr/bin/imapd ./Maildir 2> /dev/null" [imap] - Host = imap.server.com + Host = imap://imap.example.com User = bob Pass = pwd Port = 143 + sslverify = false .......................... diff --git a/Makefile b/Makefile index 798a2f2f77..bb2a988288 100644 --- a/Makefile +++ b/Makefile @@ -1208,7 +1208,9 @@ endif git-%$X: %.o $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) -git-imap-send$X: imap-send.o $(LIB_FILE) +git-imap-send$X: imap-send.o $(GITLIBS) + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ + $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL) http.o http-walker.o http-push.o transport.o: http.h diff --git a/git-compat-util.h b/git-compat-util.h index cf89cdf459..fbf791a63b 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -99,6 +99,11 @@ #include #endif +#ifndef NO_OPENSSL +#include +#include +#endif + /* On most systems would have given us this, but * not on some systems (e.g. GNU/Hurd). */ diff --git a/imap-send.c b/imap-send.c index 24d76a7031..802633494d 100644 --- a/imap-send.c +++ b/imap-send.c @@ -23,6 +23,9 @@ */ #include "cache.h" +#ifdef NO_OPENSSL +typedef void *SSL; +#endif typedef struct store_conf { char *name; @@ -129,6 +132,8 @@ typedef struct imap_server_conf { int port; char *user; char *pass; + int use_ssl; + int ssl_verify; } imap_server_conf_t; typedef struct imap_store_conf { @@ -148,6 +153,7 @@ typedef struct _list { typedef struct { int fd; + SSL *ssl; } Socket_t; typedef struct { @@ -201,6 +207,7 @@ enum CAPABILITY { UIDPLUS, LITERALPLUS, NAMESPACE, + STARTTLS, }; static const char *cap_list[] = { @@ -208,6 +215,7 @@ static const char *cap_list[] = { "UIDPLUS", "LITERAL+", "NAMESPACE", + "STARTTLS", }; #define RESP_OK 0 @@ -225,19 +233,101 @@ static const char *Flags[] = { "Deleted", }; +#ifndef NO_OPENSSL +static void ssl_socket_perror(const char *func) +{ + fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), 0)); +} +#endif + static void socket_perror( const char *func, Socket_t *sock, int ret ) { - if (ret < 0) - perror( func ); +#ifndef NO_OPENSSL + if (sock->ssl) { + int sslerr = SSL_get_error(sock->ssl, ret); + switch (sslerr) { + case SSL_ERROR_NONE: + break; + case SSL_ERROR_SYSCALL: + perror("SSL_connect"); + break; + default: + ssl_socket_perror("SSL_connect"); + break; + } + } else +#endif + { + if (ret < 0) + perror(func); + else + fprintf(stderr, "%s: unexpected EOF\n", func); + } +} + +static int ssl_socket_connect(Socket_t *sock, int use_tls_only, int verify) +{ +#ifdef NO_OPENSSL + fprintf(stderr, "SSL requested but SSL support not compiled in\n"); + return -1; +#else + SSL_METHOD *meth; + SSL_CTX *ctx; + int ret; + + SSL_library_init(); + SSL_load_error_strings(); + + if (use_tls_only) + meth = TLSv1_method(); else - fprintf( stderr, "%s: unexpected EOF\n", func ); + meth = SSLv23_method(); + + if (!meth) { + ssl_socket_perror("SSLv23_method"); + return -1; + } + + ctx = SSL_CTX_new(meth); + + if (verify) + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); + + if (!SSL_CTX_set_default_verify_paths(ctx)) { + ssl_socket_perror("SSL_CTX_set_default_verify_paths"); + return -1; + } + sock->ssl = SSL_new(ctx); + if (!sock->ssl) { + ssl_socket_perror("SSL_new"); + return -1; + } + if (!SSL_set_fd(sock->ssl, sock->fd)) { + ssl_socket_perror("SSL_set_fd"); + return -1; + } + + ret = SSL_connect(sock->ssl); + if (ret <= 0) { + socket_perror("SSL_connect", sock, ret); + return -1; + } + + return 0; +#endif } static int socket_read( Socket_t *sock, char *buf, int len ) { - ssize_t n = xread( sock->fd, buf, len ); + ssize_t n; +#ifndef NO_OPENSSL + if (sock->ssl) + n = SSL_read(sock->ssl, buf, len); + else +#endif + n = xread( sock->fd, buf, len ); if (n <= 0) { socket_perror( "read", sock, n ); close( sock->fd ); @@ -249,7 +339,13 @@ socket_read( Socket_t *sock, char *buf, int len ) static int socket_write( Socket_t *sock, const char *buf, int len ) { - int n = write_in_full( sock->fd, buf, len ); + int n; +#ifndef NO_OPENSSL + if (sock->ssl) + n = SSL_write(sock->ssl, buf, len); + else +#endif + n = write_in_full( sock->fd, buf, len ); if (n != len) { socket_perror( "write", sock, n ); close( sock->fd ); @@ -258,6 +354,17 @@ socket_write( Socket_t *sock, const char *buf, int len ) return n; } +static void socket_shutdown(Socket_t *sock) +{ +#ifndef NO_OPENSSL + if (sock->ssl) { + SSL_shutdown(sock->ssl); + SSL_free(sock->ssl); + } +#endif + close(sock->fd); +} + /* simple line buffering */ static int buffer_gets( buffer_t * b, char **s ) @@ -875,7 +982,7 @@ imap_close_server( imap_store_t *ictx ) if (imap->buf.sock.fd != -1) { imap_exec( ictx, NULL, "LOGOUT" ); - close( imap->buf.sock.fd ); + socket_shutdown( &imap->buf.sock ); } free_list( imap->ns_personal ); free_list( imap->ns_other ); @@ -958,10 +1065,15 @@ imap_open_store( imap_server_conf_t *srvc ) perror( "connect" ); goto bail; } - imap_info( "ok\n" ); imap->buf.sock.fd = s; + if (srvc->use_ssl && + ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) { + close(s); + goto bail; + } + imap_info( "ok\n" ); } /* read the greeting string */ @@ -986,7 +1098,18 @@ imap_open_store( imap_server_conf_t *srvc ) goto bail; if (!preauth) { - +#ifndef NO_OPENSSL + if (!srvc->use_ssl && CAP(STARTTLS)) { + if (imap_exec(ctx, 0, "STARTTLS") != RESP_OK) + goto bail; + if (ssl_socket_connect(&imap->buf.sock, 1, + srvc->ssl_verify)) + goto bail; + /* capabilities may have changed, so get the new capabilities */ + if (imap_exec(ctx, 0, "CAPABILITY") != RESP_OK) + goto bail; + } +#endif imap_info ("Logging in...\n"); if (!srvc->user) { fprintf( stderr, "Skipping server %s, no user\n", srvc->host ); @@ -1014,7 +1137,9 @@ imap_open_store( imap_server_conf_t *srvc ) fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host ); goto bail; } - imap_warn( "*** IMAP Warning *** Password is being sent in the clear\n" ); + if (!imap->buf.sock.ssl) + imap_warn( "*** IMAP Warning *** Password is being " + "sent in the clear\n" ); if (imap_exec( ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass ) != RESP_OK) { fprintf( stderr, "IMAP error: LOGIN failed\n" ); goto bail; @@ -1242,6 +1367,8 @@ static imap_server_conf_t server = 0, /* port */ NULL, /* user */ NULL, /* pass */ + 0, /* use_ssl */ + 1, /* ssl_verify */ }; static char *imap_folder; @@ -1262,11 +1389,11 @@ git_imap_config(const char *key, const char *val, void *cb) if (!strcmp( "folder", key )) { imap_folder = xstrdup( val ); } else if (!strcmp( "host", key )) { - { - if (!prefixcmp(val, "imap:")) - val += 5; - if (!server.port) - server.port = 143; + if (!prefixcmp(val, "imap:")) + val += 5; + else if (!prefixcmp(val, "imaps:")) { + val += 6; + server.use_ssl = 1; } if (!prefixcmp(val, "//")) val += 2; @@ -1280,6 +1407,8 @@ git_imap_config(const char *key, const char *val, void *cb) server.port = git_config_int( key, val ); else if (!strcmp( "tunnel", key )) server.tunnel = xstrdup( val ); + else if (!strcmp( "sslverify", key )) + server.ssl_verify = git_config_bool( key, val ); return 0; } @@ -1300,6 +1429,9 @@ main(int argc, char **argv) setup_git_directory_gently(&nongit_ok); git_config(git_imap_config, NULL); + if (!server.port) + server.port = server.use_ssl ? 993 : 143; + if (!imap_folder) { fprintf( stderr, "no imap store specified\n" ); return 1; -- cgit v1.2.1 From 95c539081e8519e00155c284ac7519727bbacc67 Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Wed, 9 Jul 2008 22:29:01 +0100 Subject: imap-send.c: style fixes Signed-off-by: Robert Shearman Signed-off-by: Junio C Hamano --- imap-send.c | 629 +++++++++++++++++++++++++++++------------------------------- 1 file changed, 300 insertions(+), 329 deletions(-) diff --git a/imap-send.c b/imap-send.c index 802633494d..a7f7dfd22e 100644 --- a/imap-send.c +++ b/imap-send.c @@ -99,14 +99,14 @@ typedef struct { static int Verbose, Quiet; -static void imap_info( const char *, ... ); -static void imap_warn( const char *, ... ); +static void imap_info(const char *, ...); +static void imap_warn(const char *, ...); -static char *next_arg( char ** ); +static char *next_arg(char **); -static void free_generic_messages( message_t * ); +static void free_generic_messages(message_t *); -static int nfsnprintf( char *buf, int blen, const char *fmt, ... ); +static int nfsnprintf(char *buf, int blen, const char *fmt, ...); static int nfvasprintf(char **strp, const char *fmt, va_list ap) { @@ -122,8 +122,8 @@ static int nfvasprintf(char **strp, const char *fmt, va_list ap) return len; } -static void arc4_init( void ); -static unsigned char arc4_getbyte( void ); +static void arc4_init(void); +static unsigned char arc4_getbyte(void); typedef struct imap_server_conf { char *name; @@ -184,8 +184,8 @@ typedef struct imap_store { } imap_store_t; struct imap_cmd_cb { - int (*cont)( imap_store_t *ctx, struct imap_cmd *cmd, const char *prompt ); - void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response); + int (*cont)(imap_store_t *ctx, struct imap_cmd *cmd, const char *prompt); + void (*done)(imap_store_t *ctx, struct imap_cmd *cmd, int response); void *ctx; char *data; int dlen; @@ -222,7 +222,7 @@ static const char *cap_list[] = { #define RESP_NO 1 #define RESP_BAD 2 -static int get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ); +static int get_cmd_result(imap_store_t *ctx, struct imap_cmd *tcmd); static const char *Flags[] = { @@ -240,8 +240,7 @@ static void ssl_socket_perror(const char *func) } #endif -static void -socket_perror( const char *func, Socket_t *sock, int ret ) +static void socket_perror(const char *func, Socket_t *sock, int ret) { #ifndef NO_OPENSSL if (sock->ssl) { @@ -318,8 +317,7 @@ static int ssl_socket_connect(Socket_t *sock, int use_tls_only, int verify) #endif } -static int -socket_read( Socket_t *sock, char *buf, int len ) +static int socket_read(Socket_t *sock, char *buf, int len) { ssize_t n; #ifndef NO_OPENSSL @@ -327,17 +325,16 @@ socket_read( Socket_t *sock, char *buf, int len ) n = SSL_read(sock->ssl, buf, len); else #endif - n = xread( sock->fd, buf, len ); + n = xread(sock->fd, buf, len); if (n <= 0) { - socket_perror( "read", sock, n ); - close( sock->fd ); + socket_perror("read", sock, n); + close(sock->fd); sock->fd = -1; } return n; } -static int -socket_write( Socket_t *sock, const char *buf, int len ) +static int socket_write(Socket_t *sock, const char *buf, int len) { int n; #ifndef NO_OPENSSL @@ -345,10 +342,10 @@ socket_write( Socket_t *sock, const char *buf, int len ) n = SSL_write(sock->ssl, buf, len); else #endif - n = write_in_full( sock->fd, buf, len ); + n = write_in_full(sock->fd, buf, len); if (n != len) { - socket_perror( "write", sock, n ); - close( sock->fd ); + socket_perror("write", sock, n); + close(sock->fd); sock->fd = -1; } return n; @@ -366,8 +363,7 @@ static void socket_shutdown(Socket_t *sock) } /* simple line buffering */ -static int -buffer_gets( buffer_t * b, char **s ) +static int buffer_gets(buffer_t * b, char **s) { int n; int start = b->offset; @@ -381,7 +377,7 @@ buffer_gets( buffer_t * b, char **s ) /* shift down used bytes */ *s = b->buf; - assert( start <= b->bytes ); + assert(start <= b->bytes); n = b->bytes - start; if (n) @@ -391,8 +387,8 @@ buffer_gets( buffer_t * b, char **s ) start = 0; } - n = socket_read( &b->sock, b->buf + b->bytes, - sizeof(b->buf) - b->bytes ); + n = socket_read(&b->sock, b->buf + b->bytes, + sizeof(b->buf) - b->bytes); if (n <= 0) return -1; @@ -401,12 +397,12 @@ buffer_gets( buffer_t * b, char **s ) } if (b->buf[b->offset] == '\r') { - assert( b->offset + 1 < b->bytes ); + assert(b->offset + 1 < b->bytes); if (b->buf[b->offset + 1] == '\n') { b->buf[b->offset] = 0; /* terminate the string */ b->offset += 2; /* next line */ if (Verbose) - puts( *s ); + puts(*s); return 0; } } @@ -416,39 +412,36 @@ buffer_gets( buffer_t * b, char **s ) /* not reached */ } -static void -imap_info( const char *msg, ... ) +static void imap_info(const char *msg, ...) { va_list va; if (!Quiet) { - va_start( va, msg ); - vprintf( msg, va ); - va_end( va ); - fflush( stdout ); + va_start(va, msg); + vprintf(msg, va); + va_end(va); + fflush(stdout); } } -static void -imap_warn( const char *msg, ... ) +static void imap_warn(const char *msg, ...) { va_list va; if (Quiet < 2) { - va_start( va, msg ); - vfprintf( stderr, msg, va ); - va_end( va ); + va_start(va, msg); + vfprintf(stderr, msg, va); + va_end(va); } } -static char * -next_arg( char **s ) +static char *next_arg(char **s) { char *ret; if (!s || !*s) return NULL; - while (isspace( (unsigned char) **s )) + while (isspace((unsigned char) **s)) (*s)++; if (!**s) { *s = NULL; @@ -457,10 +450,10 @@ next_arg( char **s ) if (**s == '"') { ++*s; ret = *s; - *s = strchr( *s, '"' ); + *s = strchr(*s, '"'); } else { ret = *s; - while (**s && !isspace( (unsigned char) **s )) + while (**s && !isspace((unsigned char) **s)) (*s)++; } if (*s) { @@ -472,27 +465,25 @@ next_arg( char **s ) return ret; } -static void -free_generic_messages( message_t *msgs ) +static void free_generic_messages(message_t *msgs) { message_t *tmsg; for (; msgs; msgs = tmsg) { tmsg = msgs->next; - free( msgs ); + free(msgs); } } -static int -nfsnprintf( char *buf, int blen, const char *fmt, ... ) +static int nfsnprintf(char *buf, int blen, const char *fmt, ...) { int ret; va_list va; - va_start( va, fmt ); - if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen) - die( "Fatal: buffer too small. Please report a bug.\n"); - va_end( va ); + va_start(va, fmt); + if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen) + die("Fatal: buffer too small. Please report a bug.\n"); + va_end(va); return ret; } @@ -500,21 +491,20 @@ static struct { unsigned char i, j, s[256]; } rs; -static void -arc4_init( void ) +static void arc4_init(void) { int i, fd; unsigned char j, si, dat[128]; - if ((fd = open( "/dev/urandom", O_RDONLY )) < 0 && (fd = open( "/dev/random", O_RDONLY )) < 0) { - fprintf( stderr, "Fatal: no random number source available.\n" ); - exit( 3 ); + if ((fd = open("/dev/urandom", O_RDONLY)) < 0 && (fd = open("/dev/random", O_RDONLY)) < 0) { + fprintf(stderr, "Fatal: no random number source available.\n"); + exit(3); } - if (read_in_full( fd, dat, 128 ) != 128) { - fprintf( stderr, "Fatal: cannot read random number source.\n" ); - exit( 3 ); + if (read_in_full(fd, dat, 128) != 128) { + fprintf(stderr, "Fatal: cannot read random number source.\n"); + exit(3); } - close( fd ); + close(fd); for (i = 0; i < 256; i++) rs.s[i] = i; @@ -530,8 +520,7 @@ arc4_init( void ) arc4_getbyte(); } -static unsigned char -arc4_getbyte( void ) +static unsigned char arc4_getbyte(void) { unsigned char si, sj; @@ -544,54 +533,54 @@ arc4_getbyte( void ) return rs.s[(si + sj) & 0xff]; } -static struct imap_cmd * -v_issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb, - const char *fmt, va_list ap ) +static struct imap_cmd *v_issue_imap_cmd(imap_store_t *ctx, + struct imap_cmd_cb *cb, + const char *fmt, va_list ap) { imap_t *imap = ctx->imap; struct imap_cmd *cmd; int n, bufl; char buf[1024]; - cmd = xmalloc( sizeof(struct imap_cmd) ); - nfvasprintf( &cmd->cmd, fmt, ap ); + cmd = xmalloc(sizeof(struct imap_cmd)); + nfvasprintf(&cmd->cmd, fmt, ap); cmd->tag = ++imap->nexttag; if (cb) cmd->cb = *cb; else - memset( &cmd->cb, 0, sizeof(cmd->cb) ); + memset(&cmd->cb, 0, sizeof(cmd->cb)); while (imap->literal_pending) - get_cmd_result( ctx, NULL ); + get_cmd_result(ctx, NULL); - bufl = nfsnprintf( buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ? + bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ? "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n", - cmd->tag, cmd->cmd, cmd->cb.dlen ); + cmd->tag, cmd->cmd, cmd->cb.dlen); if (Verbose) { if (imap->num_in_progress) - printf( "(%d in progress) ", imap->num_in_progress ); - if (memcmp( cmd->cmd, "LOGIN", 5 )) - printf( ">>> %s", buf ); + printf("(%d in progress) ", imap->num_in_progress); + if (memcmp(cmd->cmd, "LOGIN", 5)) + printf(">>> %s", buf); else - printf( ">>> %d LOGIN \n", cmd->tag ); + printf(">>> %d LOGIN \n", cmd->tag); } - if (socket_write( &imap->buf.sock, buf, bufl ) != bufl) { - free( cmd->cmd ); - free( cmd ); + if (socket_write(&imap->buf.sock, buf, bufl) != bufl) { + free(cmd->cmd); + free(cmd); if (cb) - free( cb->data ); + free(cb->data); return NULL; } if (cmd->cb.data) { if (CAP(LITERALPLUS)) { - n = socket_write( &imap->buf.sock, cmd->cb.data, cmd->cb.dlen ); - free( cmd->cb.data ); + n = socket_write(&imap->buf.sock, cmd->cb.data, cmd->cb.dlen); + free(cmd->cb.data); if (n != cmd->cb.dlen || - (n = socket_write( &imap->buf.sock, "\r\n", 2 )) != 2) + (n = socket_write(&imap->buf.sock, "\r\n", 2)) != 2) { - free( cmd->cmd ); - free( cmd ); + free(cmd->cmd); + free(cmd); return NULL; } cmd->cb.data = NULL; @@ -606,109 +595,106 @@ v_issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb, return cmd; } -static struct imap_cmd * -issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... ) +static struct imap_cmd *issue_imap_cmd(imap_store_t *ctx, + struct imap_cmd_cb *cb, + const char *fmt, ...) { struct imap_cmd *ret; va_list ap; - va_start( ap, fmt ); - ret = v_issue_imap_cmd( ctx, cb, fmt, ap ); - va_end( ap ); + va_start(ap, fmt); + ret = v_issue_imap_cmd(ctx, cb, fmt, ap); + va_end(ap); return ret; } -static int -imap_exec( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... ) +static int imap_exec(imap_store_t *ctx, struct imap_cmd_cb *cb, + const char *fmt, ...) { va_list ap; struct imap_cmd *cmdp; - va_start( ap, fmt ); - cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap ); - va_end( ap ); + va_start(ap, fmt); + cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap); + va_end(ap); if (!cmdp) return RESP_BAD; - return get_cmd_result( ctx, cmdp ); + return get_cmd_result(ctx, cmdp); } -static int -imap_exec_m( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... ) +static int imap_exec_m(imap_store_t *ctx, struct imap_cmd_cb *cb, + const char *fmt, ...) { va_list ap; struct imap_cmd *cmdp; - va_start( ap, fmt ); - cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap ); - va_end( ap ); + va_start(ap, fmt); + cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap); + va_end(ap); if (!cmdp) return DRV_STORE_BAD; - switch (get_cmd_result( ctx, cmdp )) { + switch (get_cmd_result(ctx, cmdp)) { case RESP_BAD: return DRV_STORE_BAD; case RESP_NO: return DRV_MSG_BAD; default: return DRV_OK; } } -static int -is_atom( list_t *list ) +static int is_atom(list_t *list) { return list && list->val && list->val != NIL && list->val != LIST; } -static int -is_list( list_t *list ) +static int is_list(list_t *list) { return list && list->val == LIST; } -static void -free_list( list_t *list ) +static void free_list(list_t *list) { list_t *tmp; for (; list; list = tmp) { tmp = list->next; - if (is_list( list )) - free_list( list->child ); - else if (is_atom( list )) - free( list->val ); - free( list ); + if (is_list(list)) + free_list(list->child); + else if (is_atom(list)) + free(list->val); + free(list); } } -static int -parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level ) +static int parse_imap_list_l(imap_t *imap, char **sp, list_t **curp, int level) { list_t *cur; char *s = *sp, *p; int n, bytes; for (;;) { - while (isspace( (unsigned char)*s )) + while (isspace((unsigned char)*s)) s++; if (level && *s == ')') { s++; break; } - *curp = cur = xmalloc( sizeof(*cur) ); + *curp = cur = xmalloc(sizeof(*cur)); curp = &cur->next; cur->val = NULL; /* for clean bail */ if (*s == '(') { /* sublist */ s++; cur->val = LIST; - if (parse_imap_list_l( imap, &s, &cur->child, level + 1 )) + if (parse_imap_list_l(imap, &s, &cur->child, level + 1)) goto bail; } else if (imap && *s == '{') { /* literal */ - bytes = cur->len = strtol( s + 1, &s, 10 ); + bytes = cur->len = strtol(s + 1, &s, 10); if (*s != '}') goto bail; - s = cur->val = xmalloc( cur->len ); + s = cur->val = xmalloc(cur->len); /* dump whats left over in the input buffer */ n = imap->buf.bytes - imap->buf.offset; @@ -717,7 +703,7 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level ) /* the entire message fit in the buffer */ n = bytes; - memcpy( s, imap->buf.buf + imap->buf.offset, n ); + memcpy(s, imap->buf.buf + imap->buf.offset, n); s += n; bytes -= n; @@ -726,13 +712,13 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level ) /* now read the rest of the message */ while (bytes > 0) { - if ((n = socket_read (&imap->buf.sock, s, bytes)) <= 0) + if ((n = socket_read(&imap->buf.sock, s, bytes)) <= 0) goto bail; s += n; bytes -= n; } - if (buffer_gets( &imap->buf, &s )) + if (buffer_gets(&imap->buf, &s)) goto bail; } else if (*s == '"') { /* quoted string */ @@ -747,11 +733,11 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level ) } else { /* atom */ p = s; - for (; *s && !isspace( (unsigned char)*s ); s++) + for (; *s && !isspace((unsigned char)*s); s++) if (level && *s == ')') break; cur->len = s - p; - if (cur->len == 3 && !memcmp ("NIL", p, 3)) { + if (cur->len == 3 && !memcmp("NIL", p, 3)) { cur->val = NIL; } else { cur->val = xmemdupz(p, cur->len); @@ -772,39 +758,36 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level ) return -1; } -static list_t * -parse_imap_list( imap_t *imap, char **sp ) +static list_t *parse_imap_list(imap_t *imap, char **sp) { list_t *head; - if (!parse_imap_list_l( imap, sp, &head, 0 )) + if (!parse_imap_list_l(imap, sp, &head, 0)) return head; - free_list( head ); + free_list(head); return NULL; } -static list_t * -parse_list( char **sp ) +static list_t *parse_list(char **sp) { - return parse_imap_list( NULL, sp ); + return parse_imap_list(NULL, sp); } -static void -parse_capability( imap_t *imap, char *cmd ) +static void parse_capability(imap_t *imap, char *cmd) { char *arg; unsigned i; imap->caps = 0x80000000; - while ((arg = next_arg( &cmd ))) + while ((arg = next_arg(&cmd))) for (i = 0; i < ARRAY_SIZE(cap_list); i++) - if (!strcmp( cap_list[i], arg )) + if (!strcmp(cap_list[i], arg)) imap->caps |= 1 << i; imap->rcaps = imap->caps; } -static int -parse_response_code( imap_store_t *ctx, struct imap_cmd_cb *cb, char *s ) +static int parse_response_code(imap_store_t *ctx, struct imap_cmd_cb *cb, + char *s) { imap_t *imap = ctx->imap; char *arg, *p; @@ -812,43 +795,42 @@ parse_response_code( imap_store_t *ctx, struct imap_cmd_cb *cb, char *s ) if (*s != '[') return RESP_OK; /* no response code */ s++; - if (!(p = strchr( s, ']' ))) { - fprintf( stderr, "IMAP error: malformed response code\n" ); + if (!(p = strchr(s, ']'))) { + fprintf(stderr, "IMAP error: malformed response code\n"); return RESP_BAD; } *p++ = 0; - arg = next_arg( &s ); - if (!strcmp( "UIDVALIDITY", arg )) { - if (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg ))) { - fprintf( stderr, "IMAP error: malformed UIDVALIDITY status\n" ); + arg = next_arg(&s); + if (!strcmp("UIDVALIDITY", arg)) { + if (!(arg = next_arg(&s)) || !(ctx->gen.uidvalidity = atoi(arg))) { + fprintf(stderr, "IMAP error: malformed UIDVALIDITY status\n"); return RESP_BAD; } - } else if (!strcmp( "UIDNEXT", arg )) { - if (!(arg = next_arg( &s )) || !(imap->uidnext = atoi( arg ))) { - fprintf( stderr, "IMAP error: malformed NEXTUID status\n" ); + } else if (!strcmp("UIDNEXT", arg)) { + if (!(arg = next_arg(&s)) || !(imap->uidnext = atoi(arg))) { + fprintf(stderr, "IMAP error: malformed NEXTUID status\n"); return RESP_BAD; } - } else if (!strcmp( "CAPABILITY", arg )) { - parse_capability( imap, s ); - } else if (!strcmp( "ALERT", arg )) { + } else if (!strcmp("CAPABILITY", arg)) { + parse_capability(imap, s); + } else if (!strcmp("ALERT", arg)) { /* RFC2060 says that these messages MUST be displayed * to the user */ - for (; isspace( (unsigned char)*p ); p++); - fprintf( stderr, "*** IMAP ALERT *** %s\n", p ); - } else if (cb && cb->ctx && !strcmp( "APPENDUID", arg )) { - if (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg )) || - !(arg = next_arg( &s )) || !(*(int *)cb->ctx = atoi( arg ))) + for (; isspace((unsigned char)*p); p++); + fprintf(stderr, "*** IMAP ALERT *** %s\n", p); + } else if (cb && cb->ctx && !strcmp("APPENDUID", arg)) { + if (!(arg = next_arg(&s)) || !(ctx->gen.uidvalidity = atoi(arg)) || + !(arg = next_arg(&s)) || !(*(int *)cb->ctx = atoi(arg))) { - fprintf( stderr, "IMAP error: malformed APPENDUID status\n" ); + fprintf(stderr, "IMAP error: malformed APPENDUID status\n"); return RESP_BAD; } } return RESP_OK; } -static int -get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ) +static int get_cmd_result(imap_store_t *ctx, struct imap_cmd *tcmd) { imap_t *imap = ctx->imap; struct imap_cmd *cmdp, **pcmdp, *ncmdp; @@ -856,38 +838,38 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ) int n, resp, resp2, tag; for (;;) { - if (buffer_gets( &imap->buf, &cmd )) + if (buffer_gets(&imap->buf, &cmd)) return RESP_BAD; - arg = next_arg( &cmd ); + arg = next_arg(&cmd); if (*arg == '*') { - arg = next_arg( &cmd ); + arg = next_arg(&cmd); if (!arg) { - fprintf( stderr, "IMAP error: unable to parse untagged response\n" ); + fprintf(stderr, "IMAP error: unable to parse untagged response\n"); return RESP_BAD; } - if (!strcmp( "NAMESPACE", arg )) { - imap->ns_personal = parse_list( &cmd ); - imap->ns_other = parse_list( &cmd ); - imap->ns_shared = parse_list( &cmd ); - } else if (!strcmp( "OK", arg ) || !strcmp( "BAD", arg ) || - !strcmp( "NO", arg ) || !strcmp( "BYE", arg )) { - if ((resp = parse_response_code( ctx, NULL, cmd )) != RESP_OK) + if (!strcmp("NAMESPACE", arg)) { + imap->ns_personal = parse_list(&cmd); + imap->ns_other = parse_list(&cmd); + imap->ns_shared = parse_list(&cmd); + } else if (!strcmp("OK", arg) || !strcmp("BAD", arg) || + !strcmp("NO", arg) || !strcmp("BYE", arg)) { + if ((resp = parse_response_code(ctx, NULL, cmd)) != RESP_OK) return resp; - } else if (!strcmp( "CAPABILITY", arg )) - parse_capability( imap, cmd ); - else if ((arg1 = next_arg( &cmd ))) { - if (!strcmp( "EXISTS", arg1 )) - ctx->gen.count = atoi( arg ); - else if (!strcmp( "RECENT", arg1 )) - ctx->gen.recent = atoi( arg ); + } else if (!strcmp("CAPABILITY", arg)) + parse_capability(imap, cmd); + else if ((arg1 = next_arg(&cmd))) { + if (!strcmp("EXISTS", arg1)) + ctx->gen.count = atoi(arg); + else if (!strcmp("RECENT", arg1)) + ctx->gen.recent = atoi(arg); } else { - fprintf( stderr, "IMAP error: unable to parse untagged response\n" ); + fprintf(stderr, "IMAP error: unable to parse untagged response\n"); return RESP_BAD; } } else if (!imap->in_progress) { - fprintf( stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "" ); + fprintf(stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : ""); return RESP_BAD; } else if (*arg == '+') { /* This can happen only with the last command underway, as @@ -895,30 +877,30 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ) cmdp = (struct imap_cmd *)((char *)imap->in_progress_append - offsetof(struct imap_cmd, next)); if (cmdp->cb.data) { - n = socket_write( &imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen ); - free( cmdp->cb.data ); + n = socket_write(&imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen); + free(cmdp->cb.data); cmdp->cb.data = NULL; if (n != (int)cmdp->cb.dlen) return RESP_BAD; } else if (cmdp->cb.cont) { - if (cmdp->cb.cont( ctx, cmdp, cmd )) + if (cmdp->cb.cont(ctx, cmdp, cmd)) return RESP_BAD; } else { - fprintf( stderr, "IMAP error: unexpected command continuation request\n" ); + fprintf(stderr, "IMAP error: unexpected command continuation request\n"); return RESP_BAD; } - if (socket_write( &imap->buf.sock, "\r\n", 2 ) != 2) + if (socket_write(&imap->buf.sock, "\r\n", 2) != 2) return RESP_BAD; if (!cmdp->cb.cont) imap->literal_pending = 0; if (!tcmd) return DRV_OK; } else { - tag = atoi( arg ); + tag = atoi(arg); for (pcmdp = &imap->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next) if (cmdp->tag == tag) goto gottag; - fprintf( stderr, "IMAP error: unexpected tag %s\n", arg ); + fprintf(stderr, "IMAP error: unexpected tag %s\n", arg); return RESP_BAD; gottag: if (!(*pcmdp = cmdp->next)) @@ -926,26 +908,26 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ) imap->num_in_progress--; if (cmdp->cb.cont || cmdp->cb.data) imap->literal_pending = 0; - arg = next_arg( &cmd ); - if (!strcmp( "OK", arg )) + arg = next_arg(&cmd); + if (!strcmp("OK", arg)) resp = DRV_OK; else { - if (!strcmp( "NO", arg )) { - if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp( cmd, "[TRYCREATE]", 11 ))) { /* SELECT, APPEND or UID COPY */ - p = strchr( cmdp->cmd, '"' ); - if (!issue_imap_cmd( ctx, NULL, "CREATE \"%.*s\"", strchr( p + 1, '"' ) - p + 1, p )) { + if (!strcmp("NO", arg)) { + if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp(cmd, "[TRYCREATE]", 11))) { /* SELECT, APPEND or UID COPY */ + p = strchr(cmdp->cmd, '"'); + if (!issue_imap_cmd(ctx, NULL, "CREATE \"%.*s\"", strchr(p + 1, '"') - p + 1, p)) { resp = RESP_BAD; goto normal; } /* not waiting here violates the spec, but a server that does not grok this nonetheless violates it too. */ cmdp->cb.create = 0; - if (!(ncmdp = issue_imap_cmd( ctx, &cmdp->cb, "%s", cmdp->cmd ))) { + if (!(ncmdp = issue_imap_cmd(ctx, &cmdp->cb, "%s", cmdp->cmd))) { resp = RESP_BAD; goto normal; } - free( cmdp->cmd ); - free( cmdp ); + free(cmdp->cmd); + free(cmdp); if (!tcmd) return 0; /* ignored */ if (cmdp == tcmd) @@ -953,21 +935,21 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ) continue; } resp = RESP_NO; - } else /*if (!strcmp( "BAD", arg ))*/ + } else /*if (!strcmp("BAD", arg))*/ resp = RESP_BAD; - fprintf( stderr, "IMAP command '%s' returned response (%s) - %s\n", - memcmp (cmdp->cmd, "LOGIN", 5) ? + fprintf(stderr, "IMAP command '%s' returned response (%s) - %s\n", + memcmp(cmdp->cmd, "LOGIN", 5) ? cmdp->cmd : "LOGIN ", arg, cmd ? cmd : ""); } - if ((resp2 = parse_response_code( ctx, &cmdp->cb, cmd )) > resp) + if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp) resp = resp2; normal: if (cmdp->cb.done) - cmdp->cb.done( ctx, cmdp, resp ); - free( cmdp->cb.data ); - free( cmdp->cmd ); - free( cmdp ); + cmdp->cb.done(ctx, cmdp, resp); + free(cmdp->cb.data); + free(cmdp->cmd); + free(cmdp); if (!tcmd || tcmd == cmdp) return resp; } @@ -975,31 +957,28 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ) /* not reached */ } -static void -imap_close_server( imap_store_t *ictx ) +static void imap_close_server(imap_store_t *ictx) { imap_t *imap = ictx->imap; if (imap->buf.sock.fd != -1) { - imap_exec( ictx, NULL, "LOGOUT" ); - socket_shutdown( &imap->buf.sock ); + imap_exec(ictx, NULL, "LOGOUT"); + socket_shutdown(&imap->buf.sock); } - free_list( imap->ns_personal ); - free_list( imap->ns_other ); - free_list( imap->ns_shared ); - free( imap ); + free_list(imap->ns_personal); + free_list(imap->ns_other); + free_list(imap->ns_shared); + free(imap); } -static void -imap_close_store( store_t *ctx ) +static void imap_close_store(store_t *ctx) { - imap_close_server( (imap_store_t *)ctx ); - free_generic_messages( ctx->msgs ); - free( ctx ); + imap_close_server((imap_store_t *)ctx); + free_generic_messages(ctx->msgs); + free(ctx); } -static store_t * -imap_open_store( imap_server_conf_t *srvc ) +static store_t *imap_open_store(imap_server_conf_t *srvc) { imap_store_t *ctx; imap_t *imap; @@ -1009,60 +988,60 @@ imap_open_store( imap_server_conf_t *srvc ) int s, a[2], preauth; pid_t pid; - ctx = xcalloc( sizeof(*ctx), 1 ); + ctx = xcalloc(sizeof(*ctx), 1); - ctx->imap = imap = xcalloc( sizeof(*imap), 1 ); + ctx->imap = imap = xcalloc(sizeof(*imap), 1); imap->buf.sock.fd = -1; imap->in_progress_append = &imap->in_progress; /* open connection to IMAP server */ if (srvc->tunnel) { - imap_info( "Starting tunnel '%s'... ", srvc->tunnel ); + imap_info("Starting tunnel '%s'... ", srvc->tunnel); - if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) { - perror( "socketpair" ); - exit( 1 ); + if (socketpair(PF_UNIX, SOCK_STREAM, 0, a)) { + perror("socketpair"); + exit(1); } pid = fork(); if (pid < 0) - _exit( 127 ); + _exit(127); if (!pid) { - if (dup2( a[0], 0 ) == -1 || dup2( a[0], 1 ) == -1) - _exit( 127 ); - close( a[0] ); - close( a[1] ); - execl( "/bin/sh", "sh", "-c", srvc->tunnel, NULL ); - _exit( 127 ); + if (dup2(a[0], 0) == -1 || dup2(a[0], 1) == -1) + _exit(127); + close(a[0]); + close(a[1]); + execl("/bin/sh", "sh", "-c", srvc->tunnel, NULL); + _exit(127); } - close (a[0]); + close(a[0]); imap->buf.sock.fd = a[1]; - imap_info( "ok\n" ); + imap_info("ok\n"); } else { - memset( &addr, 0, sizeof(addr) ); - addr.sin_port = htons( srvc->port ); + memset(&addr, 0, sizeof(addr)); + addr.sin_port = htons(srvc->port); addr.sin_family = AF_INET; - imap_info( "Resolving %s... ", srvc->host ); - he = gethostbyname( srvc->host ); + imap_info("Resolving %s... ", srvc->host); + he = gethostbyname(srvc->host); if (!he) { - perror( "gethostbyname" ); + perror("gethostbyname"); goto bail; } - imap_info( "ok\n" ); + imap_info("ok\n"); addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]); - s = socket( PF_INET, SOCK_STREAM, 0 ); + s = socket(PF_INET, SOCK_STREAM, 0); - imap_info( "Connecting to %s:%hu... ", inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) ); - if (connect( s, (struct sockaddr *)&addr, sizeof(addr) )) { - close( s ); - perror( "connect" ); + imap_info("Connecting to %s:%hu... ", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) { + close(s); + perror("connect"); goto bail; } @@ -1073,28 +1052,28 @@ imap_open_store( imap_server_conf_t *srvc ) close(s); goto bail; } - imap_info( "ok\n" ); + imap_info("ok\n"); } /* read the greeting string */ - if (buffer_gets( &imap->buf, &rsp )) { - fprintf( stderr, "IMAP error: no greeting response\n" ); + if (buffer_gets(&imap->buf, &rsp)) { + fprintf(stderr, "IMAP error: no greeting response\n"); goto bail; } - arg = next_arg( &rsp ); - if (!arg || *arg != '*' || (arg = next_arg( &rsp )) == NULL) { - fprintf( stderr, "IMAP error: invalid greeting response\n" ); + arg = next_arg(&rsp); + if (!arg || *arg != '*' || (arg = next_arg(&rsp)) == NULL) { + fprintf(stderr, "IMAP error: invalid greeting response\n"); goto bail; } preauth = 0; - if (!strcmp( "PREAUTH", arg )) + if (!strcmp("PREAUTH", arg)) preauth = 1; - else if (strcmp( "OK", arg ) != 0) { - fprintf( stderr, "IMAP error: unknown greeting response\n" ); + else if (strcmp("OK", arg) != 0) { + fprintf(stderr, "IMAP error: unknown greeting response\n"); goto bail; } - parse_response_code( ctx, NULL, rsp ); - if (!imap->caps && imap_exec( ctx, NULL, "CAPABILITY" ) != RESP_OK) + parse_response_code(ctx, NULL, rsp); + if (!imap->caps && imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK) goto bail; if (!preauth) { @@ -1110,38 +1089,38 @@ imap_open_store( imap_server_conf_t *srvc ) goto bail; } #endif - imap_info ("Logging in...\n"); + imap_info("Logging in...\n"); if (!srvc->user) { - fprintf( stderr, "Skipping server %s, no user\n", srvc->host ); + fprintf(stderr, "Skipping server %s, no user\n", srvc->host); goto bail; } if (!srvc->pass) { char prompt[80]; - sprintf( prompt, "Password (%s@%s): ", srvc->user, srvc->host ); - arg = getpass( prompt ); + sprintf(prompt, "Password (%s@%s): ", srvc->user, srvc->host); + arg = getpass(prompt); if (!arg) { - perror( "getpass" ); - exit( 1 ); + perror("getpass"); + exit(1); } if (!*arg) { - fprintf( stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host ); + fprintf(stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host); goto bail; } /* * getpass() returns a pointer to a static buffer. make a copy * for long term storage. */ - srvc->pass = xstrdup( arg ); + srvc->pass = xstrdup(arg); } if (CAP(NOLOGIN)) { - fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host ); + fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host); goto bail; } if (!imap->buf.sock.ssl) imap_warn( "*** IMAP Warning *** Password is being " "sent in the clear\n" ); - if (imap_exec( ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass ) != RESP_OK) { - fprintf( stderr, "IMAP error: LOGIN failed\n" ); + if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) { + fprintf(stderr, "IMAP error: LOGIN failed\n"); goto bail; } } /* !preauth */ @@ -1151,12 +1130,11 @@ imap_open_store( imap_server_conf_t *srvc ) return (store_t *)ctx; bail: - imap_close_store( &ctx->gen ); + imap_close_store(&ctx->gen); return NULL; } -static int -imap_make_flags( int flags, char *buf ) +static int imap_make_flags(int flags, char *buf) { const char *s; unsigned i, d; @@ -1175,8 +1153,7 @@ imap_make_flags( int flags, char *buf ) #define TUIDL 8 -static int -imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) +static int imap_store_msg(store_t *gctx, msg_data_t *data, int *uid) { imap_store_t *ctx = (imap_store_t *)gctx; imap_t *imap = ctx->imap; @@ -1187,7 +1164,7 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) int start, sbreak = 0, ebreak = 0; char flagstr[128], tuid[TUIDL * 2 + 1]; - memset( &cb, 0, sizeof(cb) ); + memset(&cb, 0, sizeof(cb)); fmap = data->data; len = data->len; @@ -1203,18 +1180,18 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) sbreak = ebreak = i - 2 + nocr; goto mktid; } - if (!memcmp( fmap + start, "X-TUID: ", 8 )) { + if (!memcmp(fmap + start, "X-TUID: ", 8)) { extra -= (ebreak = i) - (sbreak = start) + nocr; goto mktid; } goto nloop; } /* invalid message */ - free( fmap ); + free(fmap); return DRV_MSG_BAD; mktid: for (j = 0; j < TUIDL; j++) - sprintf( tuid + j * 2, "%02x", arc4_getbyte() ); + sprintf(tuid + j * 2, "%02x", arc4_getbyte()); extra += 8 + TUIDL * 2 + 2; } if (nocr) @@ -1223,7 +1200,7 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) extra++; cb.dlen = len + extra; - buf = cb.data = xmalloc( cb.dlen ); + buf = cb.data = xmalloc(cb.dlen); i = 0; if (!CAP(UIDPLUS) && uid) { if (nocr) { @@ -1234,12 +1211,12 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) } else *buf++ = fmap[i]; } else { - memcpy( buf, fmap, sbreak ); + memcpy(buf, fmap, sbreak); buf += sbreak; } - memcpy( buf, "X-TUID: ", 8 ); + memcpy(buf, "X-TUID: ", 8); buf += 8; - memcpy( buf, tuid, TUIDL * 2 ); + memcpy(buf, tuid, TUIDL * 2); buf += TUIDL * 2; *buf++ = '\r'; *buf++ = '\n'; @@ -1253,13 +1230,13 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) } else *buf++ = fmap[i]; } else - memcpy( buf, fmap + i, len - i ); + memcpy(buf, fmap + i, len - i); - free( fmap ); + free(fmap); d = 0; if (data->flags) { - d = imap_make_flags( data->flags, flagstr ); + d = imap_make_flags(data->flags, flagstr); flagstr[d++] = ' '; } flagstr[d] = 0; @@ -1272,11 +1249,11 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) imap->caps = imap->rcaps & ~(1 << LITERALPLUS); } else { box = gctx->name; - prefix = !strcmp( box, "INBOX" ) ? "" : ctx->prefix; + prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix; cb.create = 0; } cb.ctx = uid; - ret = imap_exec_m( ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr ); + ret = imap_exec_m(ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr); imap->caps = imap->rcaps; if (ret != DRV_OK) return ret; @@ -1290,8 +1267,7 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) #define CHUNKSIZE 0x1000 -static int -read_message( FILE *f, msg_data_t *msg ) +static int read_message(FILE *f, msg_data_t *msg) { struct strbuf buf; @@ -1308,8 +1284,7 @@ read_message( FILE *f, msg_data_t *msg ) return msg->len; } -static int -count_messages( msg_data_t *msg ) +static int count_messages(msg_data_t *msg) { int count = 0; char *p = msg->data; @@ -1319,7 +1294,7 @@ count_messages( msg_data_t *msg ) count++; p += 5; } - p = strstr( p+5, "\nFrom "); + p = strstr(p+5, "\nFrom "); if (!p) break; p++; @@ -1327,22 +1302,21 @@ count_messages( msg_data_t *msg ) return count; } -static int -split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs ) +static int split_msg(msg_data_t *all_msgs, msg_data_t *msg, int *ofs) { char *p, *data; - memset( msg, 0, sizeof *msg ); + memset(msg, 0, sizeof *msg); if (*ofs >= all_msgs->len) return 0; - data = &all_msgs->data[ *ofs ]; + data = &all_msgs->data[*ofs]; msg->len = all_msgs->len - *ofs; if (msg->len < 5 || prefixcmp(data, "From ")) return 0; - p = strchr( data, '\n' ); + p = strchr(data, '\n'); if (p) { p = &p[1]; msg->len -= p-data; @@ -1350,7 +1324,7 @@ split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs ) data = p; } - p = strstr( data, "\nFrom " ); + p = strstr(data, "\nFrom "); if (p) msg->len = &p[1] - data; @@ -1359,8 +1333,7 @@ split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs ) return 1; } -static imap_server_conf_t server = -{ +static imap_server_conf_t server = { NULL, /* name */ NULL, /* tunnel */ NULL, /* host */ @@ -1373,12 +1346,11 @@ static imap_server_conf_t server = static char *imap_folder; -static int -git_imap_config(const char *key, const char *val, void *cb) +static int git_imap_config(const char *key, const char *val, void *cb) { char imap_key[] = "imap."; - if (strncmp( key, imap_key, sizeof imap_key - 1 )) + if (strncmp(key, imap_key, sizeof imap_key - 1)) return 0; if (!val) @@ -1386,9 +1358,9 @@ git_imap_config(const char *key, const char *val, void *cb) key += sizeof imap_key - 1; - if (!strcmp( "folder", key )) { - imap_folder = xstrdup( val ); - } else if (!strcmp( "host", key )) { + if (!strcmp("folder", key)) { + imap_folder = xstrdup(val); + } else if (!strcmp("host", key)) { if (!prefixcmp(val, "imap:")) val += 5; else if (!prefixcmp(val, "imaps:")) { @@ -1397,23 +1369,22 @@ git_imap_config(const char *key, const char *val, void *cb) } if (!prefixcmp(val, "//")) val += 2; - server.host = xstrdup( val ); + server.host = xstrdup(val); } - else if (!strcmp( "user", key )) - server.user = xstrdup( val ); - else if (!strcmp( "pass", key )) - server.pass = xstrdup( val ); - else if (!strcmp( "port", key )) - server.port = git_config_int( key, val ); - else if (!strcmp( "tunnel", key )) - server.tunnel = xstrdup( val ); - else if (!strcmp( "sslverify", key )) - server.ssl_verify = git_config_bool( key, val ); + else if (!strcmp("user", key)) + server.user = xstrdup(val); + else if (!strcmp("pass", key)) + server.pass = xstrdup(val); + else if (!strcmp("port", key)) + server.port = git_config_int(key, val); + else if (!strcmp("tunnel", key)) + server.tunnel = xstrdup(val); + else if (!strcmp("sslverify", key)) + server.ssl_verify = git_config_bool(key, val); return 0; } -int -main(int argc, char **argv) +int main(int argc, char **argv) { msg_data_t all_msgs, msg; store_t *ctx = NULL; @@ -1433,50 +1404,50 @@ main(int argc, char **argv) server.port = server.use_ssl ? 993 : 143; if (!imap_folder) { - fprintf( stderr, "no imap store specified\n" ); + fprintf(stderr, "no imap store specified\n"); return 1; } if (!server.host) { if (!server.tunnel) { - fprintf( stderr, "no imap host specified\n" ); + fprintf(stderr, "no imap host specified\n"); return 1; } server.host = "tunnel"; } /* read the messages */ - if (!read_message( stdin, &all_msgs )) { + if (!read_message(stdin, &all_msgs)) { fprintf(stderr,"nothing to send\n"); return 1; } - total = count_messages( &all_msgs ); + total = count_messages(&all_msgs); if (!total) { fprintf(stderr,"no messages to send\n"); return 1; } /* write it to the imap server */ - ctx = imap_open_store( &server ); + ctx = imap_open_store(&server); if (!ctx) { - fprintf( stderr,"failed to open store\n"); + fprintf(stderr,"failed to open store\n"); return 1; } - fprintf( stderr, "sending %d message%s\n", total, (total!=1)?"s":"" ); + fprintf(stderr, "sending %d message%s\n", total, (total!=1)?"s":""); ctx->name = imap_folder; while (1) { unsigned percent = n * 100 / total; - fprintf( stderr, "%4u%% (%d/%d) done\r", percent, n, total ); - if (!split_msg( &all_msgs, &msg, &ofs )) + fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total); + if (!split_msg(&all_msgs, &msg, &ofs)) break; - r = imap_store_msg( ctx, &msg, &uid ); + r = imap_store_msg(ctx, &msg, &uid); if (r != DRV_OK) break; n++; } - fprintf( stderr,"\n" ); + fprintf(stderr, "\n"); - imap_close_store( ctx ); + imap_close_store(ctx); return 0; } -- cgit v1.2.1 From 9f1ad541f95cf6b9024683c8883bfb0ca86f9a3d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 9 Jul 2008 16:37:38 -0700 Subject: imap-send.c: more style fixes The previous one sqeezed unnecessary whitespaces and joined function type and name in the definition split across lines. This patch further fixes many remaining style issues: - We are not particularly fond of typedef to hide the underlying struct definitions. - Asterisk comes next variable, i.e. "type *var", not "type * var" nor "type* var". - Casting to pointer to a type is "(type *)", not "(type*)". - An open brace comes on the same line as closing parenthesis of "if (...)" condition; "else" comes on the same line as closing brace of its corresponding "if (...) {". - Avoid single liner "if (...) ;"; they should be on two separate lines. Signed-off-by: Junio C Hamano --- imap-send.c | 225 ++++++++++++++++++++++++++++++------------------------------ 1 file changed, 111 insertions(+), 114 deletions(-) diff --git a/imap-send.c b/imap-send.c index a7f7dfd22e..af7e08c094 100644 --- a/imap-send.c +++ b/imap-send.c @@ -27,70 +27,70 @@ typedef void *SSL; #endif -typedef struct store_conf { +struct store_conf { char *name; const char *path; /* should this be here? its interpretation is driver-specific */ char *map_inbox; char *trash; unsigned max_size; /* off_t is overkill */ unsigned trash_remote_new:1, trash_only_new:1; -} store_conf_t; +}; -typedef struct string_list { +struct string_list { struct string_list *next; char string[1]; -} string_list_t; +}; -typedef struct channel_conf { +struct channel_conf { struct channel_conf *next; char *name; - store_conf_t *master, *slave; + struct store_conf *master, *slave; char *master_name, *slave_name; char *sync_state; - string_list_t *patterns; + struct string_list *patterns; int mops, sops; unsigned max_messages; /* for slave only */ -} channel_conf_t; +}; -typedef struct group_conf { +struct group_conf { struct group_conf *next; char *name; - string_list_t *channels; -} group_conf_t; + struct string_list *channels; +}; /* For message->status */ #define M_RECENT (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */ #define M_DEAD (1<<1) /* expunged */ #define M_FLAGS (1<<2) /* flags fetched */ -typedef struct message { +struct message { struct message *next; - /* string_list_t *keywords; */ + /* struct string_list *keywords; */ size_t size; /* zero implies "not fetched" */ int uid; unsigned char flags, status; -} message_t; +}; -typedef struct store { - store_conf_t *conf; /* foreign */ +struct store { + struct store_conf *conf; /* foreign */ /* currently open mailbox */ const char *name; /* foreign! maybe preset? */ char *path; /* own */ - message_t *msgs; /* own */ + struct message *msgs; /* own */ int uidvalidity; unsigned char opts; /* maybe preset? */ /* note that the following do _not_ reflect stats from msgs, but mailbox totals */ int count; /* # of messages */ int recent; /* # of recent messages - don't trust this beyond the initial read */ -} store_t; +}; -typedef struct { +struct msg_data { char *data; int len; unsigned char flags; unsigned int crlf:1; -} msg_data_t; +}; #define DRV_OK 0 #define DRV_MSG_BAD -1 @@ -104,7 +104,7 @@ static void imap_warn(const char *, ...); static char *next_arg(char **); -static void free_generic_messages(message_t *); +static void free_generic_messages(struct message *); static int nfsnprintf(char *buf, int blen, const char *fmt, ...); @@ -125,7 +125,7 @@ static int nfvasprintf(char **strp, const char *fmt, va_list ap) static void arc4_init(void); static unsigned char arc4_getbyte(void); -typedef struct imap_server_conf { +struct imap_server_conf { char *name; char *tunnel; char *host; @@ -134,58 +134,58 @@ typedef struct imap_server_conf { char *pass; int use_ssl; int ssl_verify; -} imap_server_conf_t; +}; -typedef struct imap_store_conf { - store_conf_t gen; - imap_server_conf_t *server; +struct imap_store_conf { + struct store_conf gen; + struct imap_server_conf *server; unsigned use_namespace:1; -} imap_store_conf_t; +}; -#define NIL (void*)0x1 -#define LIST (void*)0x2 +#define NIL (void *)0x1 +#define LIST (void *)0x2 -typedef struct _list { - struct _list *next, *child; +struct imap_list { + struct imap_list *next, *child; char *val; int len; -} list_t; +}; -typedef struct { +struct imap_socket { int fd; SSL *ssl; -} Socket_t; +}; -typedef struct { - Socket_t sock; +struct imap_buffer { + struct imap_socket sock; int bytes; int offset; char buf[1024]; -} buffer_t; +}; struct imap_cmd; -typedef struct imap { +struct imap { int uidnext; /* from SELECT responses */ - list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */ + struct imap_list *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */ unsigned caps, rcaps; /* CAPABILITY results */ /* command queue */ int nexttag, num_in_progress, literal_pending; struct imap_cmd *in_progress, **in_progress_append; - buffer_t buf; /* this is BIG, so put it last */ -} imap_t; + struct imap_buffer buf; /* this is BIG, so put it last */ +}; -typedef struct imap_store { - store_t gen; +struct imap_store { + struct store gen; int uidvalidity; - imap_t *imap; + struct imap *imap; const char *prefix; unsigned /*currentnc:1,*/ trashnc:1; -} imap_store_t; +}; struct imap_cmd_cb { - int (*cont)(imap_store_t *ctx, struct imap_cmd *cmd, const char *prompt); - void (*done)(imap_store_t *ctx, struct imap_cmd *cmd, int response); + int (*cont)(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt); + void (*done)(struct imap_store *ctx, struct imap_cmd *cmd, int response); void *ctx; char *data; int dlen; @@ -222,7 +222,7 @@ static const char *cap_list[] = { #define RESP_NO 1 #define RESP_BAD 2 -static int get_cmd_result(imap_store_t *ctx, struct imap_cmd *tcmd); +static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd); static const char *Flags[] = { @@ -240,7 +240,7 @@ static void ssl_socket_perror(const char *func) } #endif -static void socket_perror(const char *func, Socket_t *sock, int ret) +static void socket_perror(const char *func, struct imap_socket *sock, int ret) { #ifndef NO_OPENSSL if (sock->ssl) { @@ -265,7 +265,7 @@ static void socket_perror(const char *func, Socket_t *sock, int ret) } } -static int ssl_socket_connect(Socket_t *sock, int use_tls_only, int verify) +static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify) { #ifdef NO_OPENSSL fprintf(stderr, "SSL requested but SSL support not compiled in\n"); @@ -317,7 +317,7 @@ static int ssl_socket_connect(Socket_t *sock, int use_tls_only, int verify) #endif } -static int socket_read(Socket_t *sock, char *buf, int len) +static int socket_read(struct imap_socket *sock, char *buf, int len) { ssize_t n; #ifndef NO_OPENSSL @@ -334,7 +334,7 @@ static int socket_read(Socket_t *sock, char *buf, int len) return n; } -static int socket_write(Socket_t *sock, const char *buf, int len) +static int socket_write(struct imap_socket *sock, const char *buf, int len) { int n; #ifndef NO_OPENSSL @@ -351,7 +351,7 @@ static int socket_write(Socket_t *sock, const char *buf, int len) return n; } -static void socket_shutdown(Socket_t *sock) +static void socket_shutdown(struct imap_socket *sock) { #ifndef NO_OPENSSL if (sock->ssl) { @@ -363,7 +363,7 @@ static void socket_shutdown(Socket_t *sock) } /* simple line buffering */ -static int buffer_gets(buffer_t * b, char **s) +static int buffer_gets(struct imap_buffer *b, char **s) { int n; int start = b->offset; @@ -465,9 +465,9 @@ static char *next_arg(char **s) return ret; } -static void free_generic_messages(message_t *msgs) +static void free_generic_messages(struct message *msgs) { - message_t *tmsg; + struct message *tmsg; for (; msgs; msgs = tmsg) { tmsg = msgs->next; @@ -533,11 +533,11 @@ static unsigned char arc4_getbyte(void) return rs.s[(si + sj) & 0xff]; } -static struct imap_cmd *v_issue_imap_cmd(imap_store_t *ctx, +static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx, struct imap_cmd_cb *cb, const char *fmt, va_list ap) { - imap_t *imap = ctx->imap; + struct imap *imap = ctx->imap; struct imap_cmd *cmd; int n, bufl; char buf[1024]; @@ -577,8 +577,7 @@ static struct imap_cmd *v_issue_imap_cmd(imap_store_t *ctx, n = socket_write(&imap->buf.sock, cmd->cb.data, cmd->cb.dlen); free(cmd->cb.data); if (n != cmd->cb.dlen || - (n = socket_write(&imap->buf.sock, "\r\n", 2)) != 2) - { + (n = socket_write(&imap->buf.sock, "\r\n", 2)) != 2) { free(cmd->cmd); free(cmd); return NULL; @@ -595,7 +594,7 @@ static struct imap_cmd *v_issue_imap_cmd(imap_store_t *ctx, return cmd; } -static struct imap_cmd *issue_imap_cmd(imap_store_t *ctx, +static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx, struct imap_cmd_cb *cb, const char *fmt, ...) { @@ -608,7 +607,7 @@ static struct imap_cmd *issue_imap_cmd(imap_store_t *ctx, return ret; } -static int imap_exec(imap_store_t *ctx, struct imap_cmd_cb *cb, +static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb, const char *fmt, ...) { va_list ap; @@ -623,7 +622,7 @@ static int imap_exec(imap_store_t *ctx, struct imap_cmd_cb *cb, return get_cmd_result(ctx, cmdp); } -static int imap_exec_m(imap_store_t *ctx, struct imap_cmd_cb *cb, +static int imap_exec_m(struct imap_store *ctx, struct imap_cmd_cb *cb, const char *fmt, ...) { va_list ap; @@ -642,19 +641,19 @@ static int imap_exec_m(imap_store_t *ctx, struct imap_cmd_cb *cb, } } -static int is_atom(list_t *list) +static int is_atom(struct imap_list *list) { return list && list->val && list->val != NIL && list->val != LIST; } -static int is_list(list_t *list) +static int is_list(struct imap_list *list) { return list && list->val == LIST; } -static void free_list(list_t *list) +static void free_list(struct imap_list *list) { - list_t *tmp; + struct imap_list *tmp; for (; list; list = tmp) { tmp = list->next; @@ -666,9 +665,9 @@ static void free_list(list_t *list) } } -static int parse_imap_list_l(imap_t *imap, char **sp, list_t **curp, int level) +static int parse_imap_list_l(struct imap *imap, char **sp, struct imap_list **curp, int level) { - list_t *cur; + struct imap_list *cur; char *s = *sp, *p; int n, bytes; @@ -737,11 +736,10 @@ static int parse_imap_list_l(imap_t *imap, char **sp, list_t **curp, int level) if (level && *s == ')') break; cur->len = s - p; - if (cur->len == 3 && !memcmp("NIL", p, 3)) { + if (cur->len == 3 && !memcmp("NIL", p, 3)) cur->val = NIL; - } else { + else cur->val = xmemdupz(p, cur->len); - } } if (!level) @@ -753,14 +751,14 @@ static int parse_imap_list_l(imap_t *imap, char **sp, list_t **curp, int level) *curp = NULL; return 0; - bail: +bail: *curp = NULL; return -1; } -static list_t *parse_imap_list(imap_t *imap, char **sp) +static struct imap_list *parse_imap_list(struct imap *imap, char **sp) { - list_t *head; + struct imap_list *head; if (!parse_imap_list_l(imap, sp, &head, 0)) return head; @@ -768,12 +766,12 @@ static list_t *parse_imap_list(imap_t *imap, char **sp) return NULL; } -static list_t *parse_list(char **sp) +static struct imap_list *parse_list(char **sp) { return parse_imap_list(NULL, sp); } -static void parse_capability(imap_t *imap, char *cmd) +static void parse_capability(struct imap *imap, char *cmd) { char *arg; unsigned i; @@ -786,10 +784,10 @@ static void parse_capability(imap_t *imap, char *cmd) imap->rcaps = imap->caps; } -static int parse_response_code(imap_store_t *ctx, struct imap_cmd_cb *cb, +static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb, char *s) { - imap_t *imap = ctx->imap; + struct imap *imap = ctx->imap; char *arg, *p; if (*s != '[') @@ -821,8 +819,7 @@ static int parse_response_code(imap_store_t *ctx, struct imap_cmd_cb *cb, fprintf(stderr, "*** IMAP ALERT *** %s\n", p); } else if (cb && cb->ctx && !strcmp("APPENDUID", arg)) { if (!(arg = next_arg(&s)) || !(ctx->gen.uidvalidity = atoi(arg)) || - !(arg = next_arg(&s)) || !(*(int *)cb->ctx = atoi(arg))) - { + !(arg = next_arg(&s)) || !(*(int *)cb->ctx = atoi(arg))) { fprintf(stderr, "IMAP error: malformed APPENDUID status\n"); return RESP_BAD; } @@ -830,9 +827,9 @@ static int parse_response_code(imap_store_t *ctx, struct imap_cmd_cb *cb, return RESP_OK; } -static int get_cmd_result(imap_store_t *ctx, struct imap_cmd *tcmd) +static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd) { - imap_t *imap = ctx->imap; + struct imap *imap = ctx->imap; struct imap_cmd *cmdp, **pcmdp, *ncmdp; char *cmd, *arg, *arg1, *p; int n, resp, resp2, tag; @@ -902,7 +899,7 @@ static int get_cmd_result(imap_store_t *ctx, struct imap_cmd *tcmd) goto gottag; fprintf(stderr, "IMAP error: unexpected tag %s\n", arg); return RESP_BAD; - gottag: + gottag: if (!(*pcmdp = cmdp->next)) imap->in_progress_append = pcmdp; imap->num_in_progress--; @@ -944,7 +941,7 @@ static int get_cmd_result(imap_store_t *ctx, struct imap_cmd *tcmd) } if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp) resp = resp2; - normal: + normal: if (cmdp->cb.done) cmdp->cb.done(ctx, cmdp, resp); free(cmdp->cb.data); @@ -957,9 +954,9 @@ static int get_cmd_result(imap_store_t *ctx, struct imap_cmd *tcmd) /* not reached */ } -static void imap_close_server(imap_store_t *ictx) +static void imap_close_server(struct imap_store *ictx) { - imap_t *imap = ictx->imap; + struct imap *imap = ictx->imap; if (imap->buf.sock.fd != -1) { imap_exec(ictx, NULL, "LOGOUT"); @@ -971,17 +968,17 @@ static void imap_close_server(imap_store_t *ictx) free(imap); } -static void imap_close_store(store_t *ctx) +static void imap_close_store(struct store *ctx) { - imap_close_server((imap_store_t *)ctx); + imap_close_server((struct imap_store *)ctx); free_generic_messages(ctx->msgs); free(ctx); } -static store_t *imap_open_store(imap_server_conf_t *srvc) +static struct store *imap_open_store(struct imap_server_conf *srvc) { - imap_store_t *ctx; - imap_t *imap; + struct imap_store *ctx; + struct imap *imap; char *arg, *rsp; struct hostent *he; struct sockaddr_in addr; @@ -1117,8 +1114,8 @@ static store_t *imap_open_store(imap_server_conf_t *srvc) goto bail; } if (!imap->buf.sock.ssl) - imap_warn( "*** IMAP Warning *** Password is being " - "sent in the clear\n" ); + imap_warn("*** IMAP Warning *** Password is being " + "sent in the clear\n"); if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) { fprintf(stderr, "IMAP error: LOGIN failed\n"); goto bail; @@ -1127,9 +1124,9 @@ static store_t *imap_open_store(imap_server_conf_t *srvc) ctx->prefix = ""; ctx->trashnc = 1; - return (store_t *)ctx; + return (struct store *)ctx; - bail: +bail: imap_close_store(&ctx->gen); return NULL; } @@ -1153,10 +1150,10 @@ static int imap_make_flags(int flags, char *buf) #define TUIDL 8 -static int imap_store_msg(store_t *gctx, msg_data_t *data, int *uid) +static int imap_store_msg(struct store *gctx, struct msg_data *data, int *uid) { - imap_store_t *ctx = (imap_store_t *)gctx; - imap_t *imap = ctx->imap; + struct imap_store *ctx = (struct imap_store *)gctx; + struct imap *imap = ctx->imap; struct imap_cmd_cb cb; char *fmap, *buf; const char *prefix, *box; @@ -1171,7 +1168,7 @@ static int imap_store_msg(store_t *gctx, msg_data_t *data, int *uid) nocr = !data->crlf; extra = 0, i = 0; if (!CAP(UIDPLUS) && uid) { - nloop: + nloop: start = i; while (i < len) if (fmap[i++] == '\n') { @@ -1189,7 +1186,7 @@ static int imap_store_msg(store_t *gctx, msg_data_t *data, int *uid) /* invalid message */ free(fmap); return DRV_MSG_BAD; - mktid: + mktid: for (j = 0; j < TUIDL; j++) sprintf(tuid + j * 2, "%02x", arc4_getbyte()); extra += 8 + TUIDL * 2 + 2; @@ -1267,7 +1264,7 @@ static int imap_store_msg(store_t *gctx, msg_data_t *data, int *uid) #define CHUNKSIZE 0x1000 -static int read_message(FILE *f, msg_data_t *msg) +static int read_message(FILE *f, struct msg_data *msg) { struct strbuf buf; @@ -1284,7 +1281,7 @@ static int read_message(FILE *f, msg_data_t *msg) return msg->len; } -static int count_messages(msg_data_t *msg) +static int count_messages(struct msg_data *msg) { int count = 0; char *p = msg->data; @@ -1302,7 +1299,7 @@ static int count_messages(msg_data_t *msg) return count; } -static int split_msg(msg_data_t *all_msgs, msg_data_t *msg, int *ofs) +static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs) { char *p, *data; @@ -1333,7 +1330,7 @@ static int split_msg(msg_data_t *all_msgs, msg_data_t *msg, int *ofs) return 1; } -static imap_server_conf_t server = { +static struct imap_server_conf server = { NULL, /* name */ NULL, /* tunnel */ NULL, /* host */ @@ -1370,8 +1367,7 @@ static int git_imap_config(const char *key, const char *val, void *cb) if (!prefixcmp(val, "//")) val += 2; server.host = xstrdup(val); - } - else if (!strcmp("user", key)) + } else if (!strcmp("user", key)) server.user = xstrdup(val); else if (!strcmp("pass", key)) server.pass = xstrdup(val); @@ -1386,8 +1382,8 @@ static int git_imap_config(const char *key, const char *val, void *cb) int main(int argc, char **argv) { - msg_data_t all_msgs, msg; - store_t *ctx = NULL; + struct msg_data all_msgs, msg; + struct store *ctx = NULL; int uid = 0; int ofs = 0; int r; @@ -1417,24 +1413,24 @@ int main(int argc, char **argv) /* read the messages */ if (!read_message(stdin, &all_msgs)) { - fprintf(stderr,"nothing to send\n"); + fprintf(stderr, "nothing to send\n"); return 1; } total = count_messages(&all_msgs); if (!total) { - fprintf(stderr,"no messages to send\n"); + fprintf(stderr, "no messages to send\n"); return 1; } /* write it to the imap server */ ctx = imap_open_store(&server); if (!ctx) { - fprintf(stderr,"failed to open store\n"); + fprintf(stderr, "failed to open store\n"); return 1; } - fprintf(stderr, "sending %d message%s\n", total, (total!=1)?"s":""); + fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : ""); ctx->name = imap_folder; while (1) { unsigned percent = n * 100 / total; @@ -1442,7 +1438,8 @@ int main(int argc, char **argv) if (!split_msg(&all_msgs, &msg, &ofs)) break; r = imap_store_msg(ctx, &msg, &uid); - if (r != DRV_OK) break; + if (r != DRV_OK) + break; n++; } fprintf(stderr, "\n"); -- cgit v1.2.1 From c82b0748e53d51cc999c16f97bc76c25f2b46b12 Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Wed, 9 Jul 2008 22:29:02 +0100 Subject: Documentation: Improve documentation for git-imap-send(1) Change the description to be similar to that used for git-send-email(1) to give a better description of what the tool can be used for and sound more user-friendly. Document the configuration variables used by git-imap-send, split the example into tunnel and direct examples. Rephrase other parts of the git-imap-send documentation to use better grammar and to be clearer. Signed-off-by: Robert Shearman Signed-off-by: Junio C Hamano --- Documentation/git-imap-send.txt | 77 ++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt index 136c82bfdd..bd49a0aee8 100644 --- a/Documentation/git-imap-send.txt +++ b/Documentation/git-imap-send.txt @@ -3,7 +3,7 @@ git-imap-send(1) NAME ---- -git-imap-send - Dump a mailbox from stdin into an imap folder +git-imap-send - Send a collection of patches from stdin to an IMAP folder SYNOPSIS @@ -13,9 +13,9 @@ SYNOPSIS DESCRIPTION ----------- -This command uploads a mailbox generated with git-format-patch -into an imap drafts folder. This allows patches to be sent as -other email is sent with mail clients that cannot read mailbox +This command uploads a mailbox generated with 'git-format-patch' +into an IMAP drafts folder. This allows patches to be sent as +other email is when using mail clients that cannot read mailbox files directly. Typical usage is something like: @@ -26,21 +26,74 @@ git format-patch --signoff --stdout --attach origin | git imap-send CONFIGURATION ------------- -'git-imap-send' requires the following values in the repository -configuration file (shown with examples): +To use the tool, imap.folder and either imap.tunnel or imap.host must be set +to appropriate values. + +Variables +~~~~~~~~~ + +imap.folder:: + The folder to drop the mails into, which is typically the Drafts + folder. For example: "INBOX.Drafts", "INBOX/Drafts" or + "[Gmail]/Drafts". Required to use imap-send. + +imap.tunnel:: + Command used to setup a tunnel to the IMAP server through which + commands will be piped instead of using a direct network connection + to the server. Required when imap.host is not set to use imap-send. + +imap.host:: + A URL identifying the server. Use a `imap://` prefix for non-secure + connections and a `imaps://` prefix for secure connections. + Ignored when imap.tunnel is set, but required to use imap-send + otherwise. + +imap.user:: + The username to use when logging in to the server. + +imap.password:: + The password to use when logging in to the server. + +imap.port:: + An integer port number to connect to on the server. + Defaults to 143 for imap:// hosts and 993 for imaps:// hosts. + Ignored when imap.tunnel is set. + +imap.sslverify:: + A boolean to enable/disable verification of the server certificate + used by the SSL/TLS connection. Default is `true`. Ignored when + imap.tunnel is set. + +Examples +~~~~~~~~ + +Using tunnel mode: .......................... [imap] - Folder = "INBOX.Drafts" + folder = "INBOX.Drafts" + tunnel = "ssh -q -C user@example.com /usr/bin/imapd ./Maildir 2> /dev/null" +.......................... +Using direct mode: + +......................... [imap] - Tunnel = "ssh -q user@server.com /usr/bin/imapd ./Maildir 2> /dev/null" + folder = "INBOX.Drafts" + host = imap://imap.example.com + user = bob + pass = p4ssw0rd +.......................... + +Using direct mode with SSL: +......................... [imap] - Host = imap://imap.example.com - User = bob - Pass = pwd - Port = 143 + folder = "INBOX.Drafts" + host = imaps://imap.example.com + user = bob + pass = p4ssw0rd + port = 123 sslverify = false .......................... -- cgit v1.2.1 From fa6b5b3944b74f2ad2430f3dfec517f8f70a6663 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 15 Jul 2008 23:11:00 +0200 Subject: git-gui: Fix "Stage/Unstage Line" with one line of context. To "Stage/Unstage Line" we construct a patch that contains exactly one change (either addition or removal); the hunk header was forged by counting the old side and adjusting the count by +/-1 for the new side. But when we counted the context we never counted the changed line itself. If the hunk had only one removal line and one line of context, like this: @@ -1,3 +1,2 @@ context 1 -removal context 2 We had constructed this patch: @@ -1,2 +1,1 @@ context 1 -removal context 2 which does not apply because git apply deduces that it must apply at the end of the file. ("context 2" is considered garbage and ignored.) The fix is that removal lines must be counted towards the context of the old side. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- lib/diff.tcl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/diff.tcl b/lib/diff.tcl index 96ba94906c..ee7f391e2f 100644 --- a/lib/diff.tcl +++ b/lib/diff.tcl @@ -423,6 +423,9 @@ proc apply_line {x y} { # the line to stage/unstage set ln [$ui_diff get $i_l $next_l] set patch "$patch$ln" + if {$c1 eq {-}} { + set n [expr $n+1] + } } elseif {$c1 ne {-} && $c1 ne {+}} { # context line set ln [$ui_diff get $i_l $next_l] -- cgit v1.2.1 From c7f7457026dc2f6979842f81cc17098579fec8d8 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Thu, 17 Jul 2008 15:21:51 +0200 Subject: git-gui: "Stage Line": Treat independent changes in adjacent lines better Assume that we want to commit these states: Old state == HEAD Intermediate state New state -------------------------------------------------------- context before context before context before old 1 new 1 new 1 old 2 old 2 new 2 context after context after context after that is, want to commit two changes in this order: 1. transform "old 1" into "new 1" 2. transform "old 2" into "new 2" [This discussion and this patch is about this very case and one other case as outlined below; any other intermediate states that one could imagine are not affected by this patch.] Now assume further, that we have not staged and commited anything, but we have already changed the working file to the new state. Then we will see this hunk in the "Unstaged Changes": @@ -1,4 +1,4 @@ context before -old 1 -old 2 +new 1 +new 2 context after The obvious way to stage the intermediate state is to apply "Stage This Line" to "-old 1" and "+new 1". Unfortunately, this resulted in this intermediate state: context before old 2 new 1 context after which is not what we wanted. In fact, it was impossible to stage the intermediate state using "Stage Line". The crux was that if a "+" line was staged, then the "-" lines were converted to context lines and arranged *before* the "+" line in the forged hunk that we fed to 'git apply'. With this patch we now treat "+" lines that are staged differently. In particular, the "-" lines before the "+" block are moved *after* the staged "+" line. Now it is possible to get the correct intermediate state by staging "-old 1" and "+new 1". Problem solved. But there is a catch. Noticing that we didn't get the right intermediate state by staging "-old 1" and "+new 1", we could have had the idea to stage the complete hunk and to *unstage* "-old 2" and "+new 2". But... the result is the same. The reason is that there is the exact symmetric problem with unstaging the last "-" and "+" line that are in adjacent blocks of "-" and "+" lines. This patch does *not* change the way in which "-" lines are *unstaged*. Why? Because if we did (i.e. move "+" lines before the "-" line after converting them to context lines), then it would be impossible to stage this intermediate state: context before old 1 new 2 context after that is, it would be impossible to stage the two independet changes in the opposite order. Let's look at this case a bit further: The obvious way to get this intermediate state would be to apply "Stage This Line" to "-old 2" and "+new 2". Before this patch, this worked as expected. With this patch, it does not work as expected, but it can still be achieved by first staging the entire hunk, then *unstaging* "-old 1" and "+new 1". In summary, this patch makes a common case possible, at the expense that a less common case is made more complicated for the user. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- lib/diff.tcl | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/lib/diff.tcl b/lib/diff.tcl index ee7f391e2f..77990c537c 100644 --- a/lib/diff.tcl +++ b/lib/diff.tcl @@ -411,6 +411,53 @@ proc apply_line {x y} { set hh [lindex [split $hh ,] 0] set hln [lindex [split $hh -] 1] + # There is a special situation to take care of. Consider this hunk: + # + # @@ -10,4 +10,4 @@ + # context before + # -old 1 + # -old 2 + # +new 1 + # +new 2 + # context after + # + # We used to keep the context lines in the order they appear in the + # hunk. But then it is not possible to correctly stage only + # "-old 1" and "+new 1" - it would result in this staged text: + # + # context before + # old 2 + # new 1 + # context after + # + # (By symmetry it is not possible to *un*stage "old 2" and "new 2".) + # + # We resolve the problem by introducing an asymmetry, namely, when + # a "+" line is *staged*, it is moved in front of the context lines + # that are generated from the "-" lines that are immediately before + # the "+" block. That is, we construct this patch: + # + # @@ -10,4 +10,5 @@ + # context before + # +new 1 + # old 1 + # old 2 + # context after + # + # But we do *not* treat "-" lines that are *un*staged in a special + # way. + # + # With this asymmetry it is possible to stage the change + # "old 1" -> "new 1" directly, and to stage the change + # "old 2" -> "new 2" by first staging the entire hunk and + # then unstaging the change "old 1" -> "new 1". + + # This is non-empty if and only if we are _staging_ changes; + # then it accumulates the consecutive "-" lines (after converting + # them to context lines) in order to be moved after the "+" change + # line. + set pre_context {} + set n 0 set i_l [$ui_diff index "$i_l + 1 lines"] set patch {} @@ -422,19 +469,27 @@ proc apply_line {x y} { [$ui_diff compare $the_l < $next_l]} { # the line to stage/unstage set ln [$ui_diff get $i_l $next_l] - set patch "$patch$ln" if {$c1 eq {-}} { set n [expr $n+1] + set patch "$patch$pre_context$ln" + } else { + set patch "$patch$ln$pre_context" } + set pre_context {} } elseif {$c1 ne {-} && $c1 ne {+}} { # context line set ln [$ui_diff get $i_l $next_l] - set patch "$patch$ln" + set patch "$patch$pre_context$ln" set n [expr $n+1] + set pre_context {} } elseif {$c1 eq $to_context} { # turn change line into context line set ln [$ui_diff get "$i_l + 1 chars" $next_l] - set patch "$patch $ln" + if {$c1 eq {-}} { + set pre_context "$pre_context $ln" + } else { + set patch "$patch $ln" + } set n [expr $n+1] } set i_l $next_l -- cgit v1.2.1 From 7e09b1531faa260226e0d4f7c850660b7ccb2f04 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 27 Jul 2008 10:34:21 +0400 Subject: git-gui: Fix the Remote menu separator. It was positioned incorrectly (offset by one position) if the menu had a tear-off handle. Signed-off-by: Alexander Gavrilov Signed-off-by: Shawn O. Pearce --- git-gui.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/git-gui.sh b/git-gui.sh index 7c27a43a5d..ce941adf8d 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2925,6 +2925,7 @@ if {[is_enabled transport]} { populate_fetch_menu set n [expr {[.mbar.remote index end] - $n}] if {$n > 0} { + if {[.mbar.remote type 0] eq "tearoff"} { incr n } .mbar.remote insert $n separator } unset n -- cgit v1.2.1 From 25b8fb1e499d0e198e491d10c7023a5f5589e837 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 27 Jul 2008 10:35:38 +0400 Subject: git-gui: Preserve scroll position on reshow_diff. It is especially useful for Stage/Unstage Line, because they invoke full state scan and diff reload, which originally would reset the scroll position to the top of the file. Signed-off-by: Alexander Gavrilov Signed-off-by: Shawn O. Pearce --- lib/diff.tcl | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/diff.tcl b/lib/diff.tcl index 77990c537c..52b79e4a1f 100644 --- a/lib/diff.tcl +++ b/lib/diff.tcl @@ -19,6 +19,7 @@ proc clear_diff {} { proc reshow_diff {} { global file_states file_lists global current_diff_path current_diff_side + global ui_diff set p $current_diff_path if {$p eq {}} { @@ -28,7 +29,8 @@ proc reshow_diff {} { || [lsearch -sorted -exact $file_lists($current_diff_side) $p] == -1} { clear_diff } else { - show_diff $p $current_diff_side + set save_pos [lindex [$ui_diff yview] 0] + show_diff $p $current_diff_side {} $save_pos } } @@ -52,7 +54,7 @@ A rescan will be automatically started to find other files which may have the sa rescan ui_ready 0 } -proc show_diff {path w {lno {}}} { +proc show_diff {path w {lno {}} {scroll_pos {}}} { global file_states file_lists global is_3way_diff diff_active repo_config global ui_diff ui_index ui_workdir @@ -151,6 +153,10 @@ proc show_diff {path w {lno {}}} { $ui_diff conf -state disabled set diff_active 0 unlock_index + if {$scroll_pos ne {}} { + update + $ui_diff yview moveto $scroll_pos + } ui_ready return } @@ -190,10 +196,10 @@ proc show_diff {path w {lno {}}} { -blocking 0 \ -encoding binary \ -translation binary - fileevent $fd readable [list read_diff $fd] + fileevent $fd readable [list read_diff $fd $scroll_pos] } -proc read_diff {fd} { +proc read_diff {fd scroll_pos} { global ui_diff diff_active global is_3way_diff current_diff_header @@ -282,6 +288,10 @@ proc read_diff {fd} { close $fd set diff_active 0 unlock_index + if {$scroll_pos ne {}} { + update + $ui_diff yview moveto $scroll_pos + } ui_ready if {[$ui_diff index end] eq {2.0}} { -- cgit v1.2.1 From 35faca1f99b5bb8a729a4e9718f5577432572332 Mon Sep 17 00:00:00 2001 From: Abhijit Menon-Sen Date: Sat, 26 Jul 2008 22:20:35 +0530 Subject: Clarify that "git log x.c y.h" lists commits that touch either file Signed-off-by: Abhijit Menon-Sen Signed-off-by: Junio C Hamano --- Documentation/git-log.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 5a58d5b03d..05cbac56ac 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -58,7 +58,7 @@ include::diff-options.txt[] its size is not included. ...:: - Show only commits that affect the specified paths. + Show only commits that affect any of the specified paths. include::rev-list-options.txt[] -- cgit v1.2.1 From cbb504c97437653540dc55430a6f64da9ddd24fd Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sat, 26 Jul 2008 12:26:56 +0200 Subject: bash completion: Add long options for 'git describe' Signed-off-by: Thomas Rast Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 40b3d99737..2fb88a8fef 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -667,6 +667,15 @@ _git_commit () _git_describe () { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --all --tags --contains --abbrev= --candidates= + --exact-match --debug --long --match --always + " + return + esac __gitcomp "$(__git_refs)" } -- cgit v1.2.1 From 6bb9e51b8ef2f397f9f44cfaeaa120ca0cc741e6 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Sun, 27 Jul 2008 13:12:15 +0200 Subject: Documentation: fix diff.external example The diff.external examples pass a flag to gnu-diff, but GNU diff does not follow the GIT_EXTERNAL_DIFF interface. Signed-off-by: Anders Melchiorsen Signed-off-by: Junio C Hamano --- Documentation/config.txt | 10 ++++++---- Documentation/git-config.txt | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 798b551514..1a13abc922 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -92,7 +92,7 @@ Example # Our diff algorithm [diff] - external = "/usr/local/bin/gnu-diff -u" + external = /usr/local/bin/diff-wrapper renames = true [branch "devel"] @@ -563,9 +563,11 @@ diff.autorefreshindex:: diff.external:: If this config variable is set, diff generation is not performed using the internal diff machinery, but using the - given command. Note: if you want to use an external diff - program only on a subset of your files, you might want to - use linkgit:gitattributes[5] instead. + given command. Can be overridden with the `GIT_EXTERNAL_DIFF' + environment variable. The command is called with parameters + as described under "git Diffs" in linkgit:git[1]. Note: if + you want to use an external diff program only on a subset of + your files, you might want to use linkgit:gitattributes[5] instead. diff.renameLimit:: The number of files to consider when performing the copy/rename diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 697824cbab..28e1861094 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -222,7 +222,7 @@ Given a .git/config like this: ; Our diff algorithm [diff] - external = "/usr/local/bin/gnu-diff -u" + external = /usr/local/bin/diff-wrapper renames = true ; Proxy settings -- cgit v1.2.1 From 1eb7e2f83472f49fda62cefe1d2d9b4c668c6771 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Wed, 23 Jul 2008 18:07:23 -0600 Subject: bash completion: Add completion for 'git help' Rename cached __git_commandlist to __git_porcelain_commandlist and add __git_all_commandlist that only filters out *--* helpers. Completions for 'git help' will use the __git_all_commandlist, while the __git_porcelain_commandlist is used for git command completion. Users who actually read man pages may want to see help for plumbing commands. Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 46 ++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2fb88a8fef..30d870187e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -349,14 +349,32 @@ __git_complete_revlist () esac } -__git_commands () +__git_all_commands () { - if [ -n "$__git_commandlist" ]; then - echo "$__git_commandlist" + if [ -n "$__git_all_commandlist" ]; then + echo "$__git_all_commandlist" return fi local i IFS=" "$'\n' for i in $(git help -a|egrep '^ ') + do + case $i in + *--*) : helper pattern;; + *) echo $i;; + esac + done +} +__git_all_commandlist= +__git_all_commandlist="$(__git_all_commands 2>/dev/null)" + +__git_porcelain_commands () +{ + if [ -n "$__git_porcelain_commandlist" ]; then + echo "$__git_porcelain_commandlist" + return + fi + local i IFS=" "$'\n' + for i in "help" $(__git_all_commands) do case $i in *--*) : helper pattern;; @@ -427,8 +445,8 @@ __git_commands () esac done } -__git_commandlist= -__git_commandlist="$(__git_commands 2>/dev/null)" +__git_porcelain_commandlist= +__git_porcelain_commandlist="$(__git_porcelain_commands 2>/dev/null)" __git_aliases () { @@ -778,6 +796,18 @@ _git_gc () COMPREPLY=() } +_git_help () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--all --info --man --web" + return + ;; + esac + __gitcomp "$(__git_all_commands)" +} + _git_ls_remote () { __gitcomp "$(__git_remotes)" @@ -1410,7 +1440,8 @@ _git () case "$i" in --git-dir=*) __git_dir="${i#--git-dir=}" ;; --bare) __git_dir="." ;; - --version|--help|-p|--paginate) ;; + --version|-p|--paginate) ;; + --help) command="help"; break ;; *) command="$i"; break ;; esac c=$((++c)) @@ -1430,7 +1461,7 @@ _git () --help " ;; - *) __gitcomp "$(__git_commands) $(__git_aliases)" ;; + *) __gitcomp "$(__git_porcelain_commands) $(__git_aliases)" ;; esac return fi @@ -1455,6 +1486,7 @@ _git () fetch) _git_fetch ;; format-patch) _git_format_patch ;; gc) _git_gc ;; + help) _git_help ;; log) _git_log ;; ls-remote) _git_ls_remote ;; ls-tree) _git_ls_tree ;; -- cgit v1.2.1 From 77ef80a83e652d65a26213bc276b33051524998f Mon Sep 17 00:00:00 2001 From: Cesar Eduardo Barros Date: Sat, 26 Jul 2008 01:17:42 -0300 Subject: Documentation/git-submodule.txt: fix doubled word Signed-off-by: Cesar Eduardo Barros Signed-off-by: Junio C Hamano --- Documentation/git-submodule.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 829b03201d..35efeefb30 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -63,7 +63,7 @@ COMMANDS add:: Add the given repository as a submodule at the given path to the changeset to be committed next to the current - project: the current project is termed termed the "superproject". + project: the current project is termed the "superproject". + This requires two arguments: and . + -- cgit v1.2.1 From b890fa33a41c0976171d80590286d2bc8bda3df6 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Sat, 26 Jul 2008 23:53:30 -0500 Subject: t6030 (bisect): work around Mac OS X "ls" t6030-bisect-porcelain.sh relies on "ls" exiting with nonzero status when asked to list nonexistent files. Unfortunately, /bin/ls on Mac OS X 10.3 exits with exit code 0. So look at its output instead. Signed-off-by: Jonathan Nieder Acked-by: Christian Couder Signed-off-by: Junio C Hamano --- t/t6030-bisect-porcelain.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 0626544823..244fda62a5 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -76,7 +76,7 @@ test_expect_success 'bisect fails if given any junk instead of revs' ' test_must_fail git bisect start foo $HASH1 -- && test_must_fail git bisect start $HASH4 $HASH1 bar -- && test -z "$(git for-each-ref "refs/bisect/*")" && - test_must_fail ls .git/BISECT_* && + test -z "$(ls .git/BISECT_* 2>/dev/null)" && git bisect start && test_must_fail git bisect good foo $HASH1 && test_must_fail git bisect good $HASH1 bar && -- cgit v1.2.1 From ef115e26f7d6859f679d122d6c2300db09015396 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 27 Jul 2008 01:23:46 +0200 Subject: gitweb: More about how gitweb gets 'owner' of repository Signed-off-by: Jakub Narebski Acked-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/README | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gitweb/README b/gitweb/README index 6908036402..825162a0b6 100644 --- a/gitweb/README +++ b/gitweb/README @@ -277,7 +277,8 @@ You can use the following files in repository: * gitweb.owner You can use the gitweb.owner repository configuration variable to set repository's owner. It is displayed in the project list and summary - page. If it's not set, filesystem directory's owner is used. + page. If it's not set, filesystem directory's owner is used + (via GECOS field / real name field from getpwiud(3)). * various gitweb.* config variables (in config) Read description of %feature hash for detailed list, and some descriptions. -- cgit v1.2.1 From 47c6ef1c8def9a20b4ff40825456b45f5e63b51f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 26 Jul 2008 23:15:51 -0700 Subject: make sure parsed wildcard refspec ends with slash A wildcard refspec is internally parsed into a refspec structure with src and dst strings. Many parts of the code assumed that these do not include the trailing "/*" when matching the wildcard pattern with an actual ref we see at the remote. What this meant was that we needed to make sure not just that the prefix matched, and also that a slash followed the part that matched. But a codepath that scans the result from ls-remote and finds matching refs forgot to check the "matching part must be followed by a slash" rule. This resulted in "refs/heads/b1" from the remote side to mistakenly match the source side of "refs/heads/b/*:refs/remotes/b/*" refspec. Worse, the refspec crafted internally by "git-clone", and a hardcoded preparsed refspec that is used to implement "git-fetch --tags", violated this "parsed widcard refspec does not end with slash" rule; simply adding the "matching part must be followed by a slash" rule then would have broken codepaths that use these refspecs. This commit changes the rule to require a trailing slash to parsed wildcard refspecs. IOW, "refs/heads/b/*:refs/remotes/b/*" is parsed as src = "refs/heads/b/" and dst = "refs/remotes/b/". This allows us to simplify the matching logic because we only need to do a prefixcmp() to notice "refs/heads/b/one" matches and "refs/heads/b1" does not. Acked-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- remote.c | 52 +++++++++++++++++++++++++++++++++----------------- t/t5513-fetch-track.sh | 30 +++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 18 deletions(-) create mode 100755 t/t5513-fetch-track.sh diff --git a/remote.c b/remote.c index 0d6020beb8..f61a3ab399 100644 --- a/remote.c +++ b/remote.c @@ -427,6 +427,28 @@ static void read_config(void) alias_all_urls(); } +/* + * We need to make sure the tracking branches are well formed, but a + * wildcard refspec in "struct refspec" must have a trailing slash. We + * temporarily drop the trailing '/' while calling check_ref_format(), + * and put it back. The caller knows that a CHECK_REF_FORMAT_ONELEVEL + * error return is Ok for a wildcard refspec. + */ +static int verify_refname(char *name, int is_glob) +{ + int result, len = -1; + + if (is_glob) { + len = strlen(name); + assert(name[len - 1] == '/'); + name[len - 1] = '\0'; + } + result = check_ref_format(name); + if (is_glob) + name[len - 1] = '/'; + return result; +} + static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify) { int i; @@ -434,11 +456,11 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec); for (i = 0; i < nr_refspec; i++) { - size_t llen, rlen; + size_t llen; int is_glob; const char *lhs, *rhs; - llen = rlen = is_glob = 0; + llen = is_glob = 0; lhs = refspec[i]; if (*lhs == '+') { @@ -458,12 +480,9 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp } if (rhs) { - rhs++; - rlen = strlen(rhs); + size_t rlen = strlen(++rhs); is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*")); - if (is_glob) - rlen -= 2; - rs[i].dst = xstrndup(rhs, rlen); + rs[i].dst = xstrndup(rhs, rlen - is_glob); } llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); @@ -471,7 +490,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if ((rhs && !is_glob) || (!rhs && fetch)) goto invalid; is_glob = 1; - llen -= 2; + llen--; } else if (rhs && is_glob) { goto invalid; } @@ -488,7 +507,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if (!*rs[i].src) ; /* empty is ok */ else { - st = check_ref_format(rs[i].src); + st = verify_refname(rs[i].src, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } @@ -503,7 +522,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp } else if (!*rs[i].dst) { ; /* ok */ } else { - st = check_ref_format(rs[i].dst); + st = verify_refname(rs[i].dst, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } @@ -518,7 +537,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if (!*rs[i].src) ; /* empty is ok */ else if (is_glob) { - st = check_ref_format(rs[i].src); + st = verify_refname(rs[i].src, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } @@ -532,13 +551,13 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp * - otherwise it must be a valid looking ref. */ if (!rs[i].dst) { - st = check_ref_format(rs[i].src); + st = verify_refname(rs[i].src, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } else if (!*rs[i].dst) { goto invalid; } else { - st = check_ref_format(rs[i].dst); + st = verify_refname(rs[i].dst, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } @@ -687,8 +706,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec) if (!fetch->dst) continue; if (fetch->pattern) { - if (!prefixcmp(needle, key) && - needle[strlen(key)] == '/') { + if (!prefixcmp(needle, key)) { *result = xmalloc(strlen(value) + strlen(needle) - strlen(key) + 1); @@ -966,9 +984,7 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, continue; } - if (rs[i].pattern && - !prefixcmp(src->name, rs[i].src) && - src->name[strlen(rs[i].src)] == '/') + if (rs[i].pattern && !prefixcmp(src->name, rs[i].src)) return rs + i; } if (matching_refs != -1) diff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh new file mode 100755 index 0000000000..9e7486274b --- /dev/null +++ b/t/t5513-fetch-track.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +test_description='fetch follows remote tracking branches correctly' + +. ./test-lib.sh + +test_expect_success setup ' + >file && + git add . && + test_tick && + git commit -m Initial && + git branch b-0 && + git branch b1 && + git branch b/one && + test_create_repo other && + ( + cd other && + git config remote.origin.url .. && + git config remote.origin.fetch "+refs/heads/b/*:refs/remotes/b/*" + ) +' + +test_expect_success fetch ' + ( + cd other && git fetch origin && + test "$(git for-each-ref --format="%(refname)")" = refs/remotes/b/one + ) +' + +test_done -- cgit v1.2.1 From 2122f8b963d49a59762e121c2da571c2348dcce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Sat, 26 Jul 2008 18:37:56 +0200 Subject: rev-parse: Add support for the ^! and ^@ syntax MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those shorthands are explained in the rev-parse documentation but were not actually supported by rev-parse itself. gitk internally uses rev-parse to interpret its command line arguments, and being able to use these "limit with parents" syntax is handy there. Signed-off-by: Björn Steinbrink Signed-off-by: Junio C Hamano --- builtin-rev-parse.c | 32 ++++++++++++++++++++++++++++++++ t/t6101-rev-parse-parents.sh | 2 ++ 2 files changed, 34 insertions(+) diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index aa71f4a4fa..9aa049ec17 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -241,6 +241,36 @@ static int try_difference(const char *arg) return 0; } +static int try_parent_shorthands(const char *arg) +{ + char *dotdot; + unsigned char sha1[20]; + struct commit *commit; + struct commit_list *parents; + int parents_only; + + if ((dotdot = strstr(arg, "^!"))) + parents_only = 0; + else if ((dotdot = strstr(arg, "^@"))) + parents_only = 1; + + if (!dotdot || dotdot[2]) + return 0; + + *dotdot = 0; + if (get_sha1(arg, sha1)) + return 0; + + if (!parents_only) + show_rev(NORMAL, sha1, arg); + commit = lookup_commit_reference(sha1); + for (parents = commit->parents; parents; parents = parents->next) + show_rev(parents_only ? NORMAL : REVERSED, + parents->item->object.sha1, arg); + + return 1; +} + static int parseopt_dump(const struct option *o, const char *arg, int unset) { struct strbuf *parsed = o->value; @@ -573,6 +603,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) /* Not a flag argument */ if (try_difference(arg)) continue; + if (try_parent_shorthands(arg)) + continue; name = arg; type = NORMAL; if (*arg == '^') { diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh index efc831363e..919552a2fc 100755 --- a/t/t6101-rev-parse-parents.sh +++ b/t/t6101-rev-parse-parents.sh @@ -28,6 +28,8 @@ test_expect_success 'final^1^2 != final^1^1' "test $(git rev-parse final^1^2) != test_expect_success 'final^1^3 not valid' "if git rev-parse --verify final^1^3; then false; else :; fi" test_expect_success '--verify start2^1' 'test_must_fail git rev-parse --verify start2^1' test_expect_success '--verify start2^0' 'git rev-parse --verify start2^0' +test_expect_success 'final^1^@ = final^1^1 final^1^2' "test \"$(git rev-parse final^1^@)\" = \"$(git rev-parse final^1^1 final^1^2)\"" +test_expect_success 'final^1^! = final^1 ^final^1^1 ^final^1^2' "test \"$(git rev-parse final^1^\!)\" = \"$(git rev-parse final^1 ^final^1^1 ^final^1^2)\"" test_expect_success 'repack for next test' 'git repack -a -d' test_expect_success 'short SHA-1 works' ' -- cgit v1.2.1 From 346d437aab2496872f477bb759db10a43b5dfb5e Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Sat, 26 Jul 2008 12:27:23 +0200 Subject: builtin-branch: remove duplicated code The previous optimization to --[no-]merged ended up with some duplicated code which this patch removes. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- builtin-branch.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index 5db8ad836a..675a9b1637 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -214,7 +214,6 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, struct commit *commit; int kind; int len; - static struct commit_list branch; /* Detect kind */ if (!prefixcmp(refname, "refs/heads/")) { @@ -238,13 +237,9 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, if ((kind & ref_list->kinds) == 0) return 0; - if (merge_filter != NO_FILTER) { - branch.item = lookup_commit_reference_gently(sha1, 1); - if (!branch.item) - die("Unable to lookup tip of branch %s", refname); + if (merge_filter != NO_FILTER) add_pending_object(&ref_list->revs, - (struct object *)branch.item, refname); - } + (struct object *)commit, refname); /* Resize buffer */ if (ref_list->index >= ref_list->alloc) { -- cgit v1.2.1 From 7950cda782fc313569a8a09be0bfab234689627a Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Sat, 26 Jul 2008 12:27:24 +0200 Subject: builtin-branch: factor out merge_filter matching The logic for checking commits against merge_filter will be reused when we recalculate the maxwidth of refnames. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- builtin-branch.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index 675a9b1637..c0f1c2ede1 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -294,6 +294,17 @@ static void fill_tracking_info(char *stat, const char *branch_name) sprintf(stat, "[ahead %d, behind %d] ", ours, theirs); } +static int matches_merge_filter(struct commit *commit) +{ + int is_merged; + + if (merge_filter == NO_FILTER) + return 1; + + is_merged = !!(commit->object.flags & UNINTERESTING); + return (is_merged == (merge_filter == SHOW_MERGED)); +} + static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, int abbrev, int current) { @@ -301,11 +312,8 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, int color; struct commit *commit = item->commit; - if (merge_filter != NO_FILTER) { - int is_merged = !!(item->commit->object.flags & UNINTERESTING); - if (is_merged != (merge_filter == SHOW_MERGED)) - return; - } + if (!matches_merge_filter(commit)) + return; switch (item->kind) { case REF_LOCAL_BRANCH: -- cgit v1.2.1 From b6f637d199fed238179865a38f180fccc37ee8cc Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Sat, 26 Jul 2008 12:27:25 +0200 Subject: builtin-branch: fix -v for --[no-]merged After the optimization to --[no-]merged logic, the calculation of the width of the longest refname to be shown might become inaccurate (since the matching against merge_filter is performed after adding refs to ref_list). This patch forces a recalculation of maxwidth when it might be needed. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- builtin-branch.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/builtin-branch.c b/builtin-branch.c index c0f1c2ede1..b1a2ad7a6b 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -363,6 +363,19 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, } } +static int calc_maxwidth(struct ref_list *refs) +{ + int i, l, w = 0; + for (i = 0; i < refs->index; i++) { + if (!matches_merge_filter(refs->list[i].commit)) + continue; + l = strlen(refs->list[i].name); + if (l > w) + w = l; + } + return w; +} + static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit) { int i; @@ -383,6 +396,8 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str (struct object *) filter, ""); ref_list.revs.limited = 1; prepare_revision_walk(&ref_list.revs); + if (verbose) + ref_list.maxwidth = calc_maxwidth(&ref_list); } qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); -- cgit v1.2.1 From f5d600e2dd8721d7b9177f1c82fe54822d9f1d62 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 27 Jul 2008 22:34:14 +0200 Subject: Avoid chdir() in list_commands_in_dir() The function list_commands_in_dir() tried to be lazy and just chdir() to the directory which entries it listed, so that the check if the file is executable could be done on dir->d_name. However, there is no good reason to jump around wildly just to find all Git commands. Instead, have a strbuf and construct the full path dynamically. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- help.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/help.c b/help.c index bfc84aed10..3cb1962896 100644 --- a/help.c +++ b/help.c @@ -425,17 +425,24 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds, int prefix_len = strlen(prefix); DIR *dir = opendir(path); struct dirent *de; + struct strbuf buf = STRBUF_INIT; + int len; - if (!dir || chdir(path)) + if (!dir) return 0; + strbuf_addf(&buf, "%s/", path); + len = buf.len; + while ((de = readdir(dir)) != NULL) { int entlen; if (prefixcmp(de->d_name, prefix)) continue; - if (!is_executable(de->d_name)) + strbuf_setlen(&buf, len); + strbuf_addstr(&buf, de->d_name); + if (!is_executable(buf.buf)) continue; entlen = strlen(de->d_name) - prefix_len; @@ -448,6 +455,7 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds, add_cmdname(cmds, de->d_name + prefix_len, entlen); } closedir(dir); + strbuf_release(&buf); return longest; } -- cgit v1.2.1 From a08c53a119999df5b76e405195fa6c5e7d4b689f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 26 Jul 2008 21:33:00 -0500 Subject: fsck: Don't require tmp_obj_ file names are 14 bytes in length Not all temporary file creation routines will ensure 14 bytes are used to generate the temporary file name. In C Git this may be true, but alternate implementations such as jgit are not always able to generate a temporary file name with a specific prefix and also ensure the file name length is 14 bytes long. Since temporary files in a directory we are fsck'ing should be uncommon (as they are short lived only long enough for an active writer to finish writing the file and rename it) we shouldn't see these show up very often. Always using a prefixcmp() call and ignoring the length opens up room for other implementations to use different name generation schemes. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-fsck.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/builtin-fsck.c b/builtin-fsck.c index 7a4a4f144f..6eb7da88d3 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -377,10 +377,6 @@ static void fsck_dir(int i, char *path) if (de->d_name[0] != '.') break; continue; - case 14: - if (prefixcmp(de->d_name, "tmp_obj_")) - break; - continue; case 38: sprintf(name, "%02x", i); memcpy(name+2, de->d_name, len+1); @@ -389,6 +385,8 @@ static void fsck_dir(int i, char *path) add_sha1_list(sha1, DIRENT_SORT_HINT(de)); continue; } + if (prefixcmp(de->d_name, "tmp_obj_")) + continue; fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name); } closedir(dir); -- cgit v1.2.1 From f6c52fe4e871d1d07cb7d86692ccf8cc4de54579 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Wed, 16 Jul 2008 21:11:08 +0200 Subject: git-mv: Remove dead code branch The path list builder had a branch for the case the source is not in index, but this can happen only if the source was a directory. However, in that case we have already expanded the list to the directory contents and set mode to WORKING_DIRECTORY, which is tested earlier. The patch removes the superfluous branch and adds an assert() instead. git-mv testsuite still passes. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- builtin-mv.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/builtin-mv.c b/builtin-mv.c index 736a0b8bb1..e66fa54324 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -228,15 +228,13 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (mode == WORKING_DIRECTORY) continue; - if (cache_name_pos(src, strlen(src)) >= 0) { - string_list_insert(src, &deleted); - - /* destination can be a directory with 1 file inside */ - if (string_list_has_string(&overwritten, dst)) - string_list_insert(dst, &changed); - else - string_list_insert(dst, &added); - } else + assert(cache_name_pos(src, strlen(src)) >= 0); + + string_list_insert(src, &deleted); + /* destination can be a directory with 1 file inside */ + if (string_list_has_string(&overwritten, dst)) + string_list_insert(dst, &changed); + else string_list_insert(dst, &added); } -- cgit v1.2.1 From 81dc2307d0ad87a4da2e753a9d1b5586d6456eed Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Mon, 21 Jul 2008 02:25:56 +0200 Subject: git-mv: Keep moved index entries inact The rewrite of git-mv from a shell script to a builtin was perhaps a little too straightforward: the git add and git rm queues were emulated directly, which resulted in a rather complicated code and caused an inconsistent behaviour when moving dirty index entries; git mv would update the entry based on working tree state, except in case of overwrites, where the new entry would still have sha1 of the old file. This patch introduces rename_index_entry_at() into the index toolkit, which will rename an entry while removing any entries the new entry might render duplicate. This is then used in git mv instead of all the file queues, resulting in a major simplification of the code and an inevitable change in git mv -n output format. Also the code used to refuse renaming overwriting symlink with a regular file and vice versa; there is no need for that. A few new tests have been added to the testsuite to reflect this change. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- builtin-mv.c | 65 +++++++++-------------------------------------------------- cache.h | 2 ++ read-cache.c | 16 +++++++++++++++ t/t7001-mv.sh | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 55 deletions(-) diff --git a/builtin-mv.c b/builtin-mv.c index e66fa54324..4f65b5ae9b 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -36,18 +36,6 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec, return get_pathspec(prefix, result); } -static void show_list(const char *label, struct string_list *list) -{ - if (list->nr > 0) { - int i; - printf("%s", label); - for (i = 0; i < list->nr; i++) - printf("%s%s", i > 0 ? ", " : "", - list->items[i].string); - putchar('\n'); - } -} - static const char *add_slash(const char *path) { int len = strlen(path); @@ -76,11 +64,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) const char **source, **destination, **dest_path; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; - struct string_list overwritten = {NULL, 0, 0, 0}; struct string_list src_for_dst = {NULL, 0, 0, 0}; - struct string_list added = {NULL, 0, 0, 0}; - struct string_list deleted = {NULL, 0, 0, 0}; - struct string_list changed = {NULL, 0, 0, 0}; git_config(git_default_config, NULL); @@ -185,12 +169,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix) * only files can overwrite each other: * check both source and destination */ - if (S_ISREG(st.st_mode)) { + if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { fprintf(stderr, "Warning: %s;" " will overwrite!\n", bad); bad = NULL; - string_list_insert(dst, &overwritten); } else bad = "Cannot overwrite"; } @@ -219,6 +202,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; + int pos; if (show_only || verbose) printf("Renaming %s to %s\n", src, dst); if (!show_only && mode != INDEX && @@ -228,45 +212,16 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (mode == WORKING_DIRECTORY) continue; - assert(cache_name_pos(src, strlen(src)) >= 0); - - string_list_insert(src, &deleted); - /* destination can be a directory with 1 file inside */ - if (string_list_has_string(&overwritten, dst)) - string_list_insert(dst, &changed); - else - string_list_insert(dst, &added); + pos = cache_name_pos(src, strlen(src)); + assert(pos >= 0); + if (!show_only) + rename_cache_entry_at(pos, dst); } - if (show_only) { - show_list("Changed : ", &changed); - show_list("Adding : ", &added); - show_list("Deleting : ", &deleted); - } else { - for (i = 0; i < changed.nr; i++) { - const char *path = changed.items[i].string; - int j = cache_name_pos(path, strlen(path)); - struct cache_entry *ce = active_cache[j]; - - if (j < 0) - die ("Huh? Cache entry for %s unknown?", path); - refresh_cache_entry(ce, 0); - } - - for (i = 0; i < added.nr; i++) { - const char *path = added.items[i].string; - if (add_file_to_cache(path, verbose ? ADD_CACHE_VERBOSE : 0)) - die("updating index entries failed"); - } - - for (i = 0; i < deleted.nr; i++) - remove_file_from_cache(deleted.items[i].string); - - if (active_cache_changed) { - if (write_cache(newfd, active_cache, active_nr) || - commit_locked_index(&lock_file)) - die("Unable to write new index file"); - } + if (active_cache_changed) { + if (write_cache(newfd, active_cache, active_nr) || + commit_locked_index(&lock_file)) + die("Unable to write new index file"); } return 0; diff --git a/cache.h b/cache.h index 38985aa63e..4b6c0a6c59 100644 --- a/cache.h +++ b/cache.h @@ -260,6 +260,7 @@ static inline void remove_name_hash(struct cache_entry *ce) #define unmerged_cache() unmerged_index(&the_index) #define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen)) #define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option)) +#define rename_cache_entry_at(pos, new_name) rename_index_entry_at(&the_index, (pos), (new_name)) #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos)) #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path)) #define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags)) @@ -370,6 +371,7 @@ extern int index_name_pos(const struct index_state *, const char *name, int name #define ADD_CACHE_JUST_APPEND 8 /* Append only; tree.c::read_tree() */ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option); extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); +extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name); extern int remove_index_entry_at(struct index_state *, int pos); extern int remove_file_from_index(struct index_state *, const char *path); #define ADD_CACHE_VERBOSE 1 diff --git a/read-cache.c b/read-cache.c index a50a851125..6c0880337b 100644 --- a/read-cache.c +++ b/read-cache.c @@ -38,6 +38,22 @@ static void replace_index_entry(struct index_state *istate, int nr, struct cache istate->cache_changed = 1; } +void rename_index_entry_at(struct index_state *istate, int nr, const char *new_name) +{ + struct cache_entry *old = istate->cache[nr], *new; + int namelen = strlen(new_name); + + new = xmalloc(cache_entry_size(namelen)); + copy_cache_entry(new, old); + new->ce_flags &= ~(CE_STATE_MASK | CE_NAMEMASK); + new->ce_flags |= (namelen >= CE_NAMEMASK ? CE_NAMEMASK : namelen); + memcpy(new->name, new_name, namelen + 1); + + cache_tree_invalidate_path(istate->cache_tree, old->name); + remove_index_entry_at(istate, nr); + add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); +} + /* * This only updates the "non-critical" parts of the directory * cache, ie the parts that aren't tracked by GIT, and only used diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index 336cfaa1c5..b0fa407825 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -156,4 +156,56 @@ test_expect_success 'absolute pathname outside should fail' '( )' +test_expect_success 'git mv should not change sha1 of moved cache entry' ' + + rm -fr .git && + git init && + echo 1 >dirty && + git add dirty && + entry="$(git ls-files --stage dirty | cut -f 1)" + git mv dirty dirty2 && + [ "$entry" = "$(git ls-files --stage dirty2 | cut -f 1)" ] && + echo 2 >dirty2 && + git mv dirty2 dirty && + [ "$entry" = "$(git ls-files --stage dirty | cut -f 1)" ] + +' + +rm -f dirty dirty2 + +test_expect_success 'git mv should overwrite symlink to a file' ' + + rm -fr .git && + git init && + echo 1 >moved && + ln -s moved symlink && + git add moved symlink && + test_must_fail git mv moved symlink && + git mv -f moved symlink && + ! test -e moved && + test -f symlink && + test "$(cat symlink)" = 1 && + git diff-files --quiet + +' + +rm -f moved symlink + +test_expect_success 'git mv should overwrite file with a symlink' ' + + rm -fr .git && + git init && + echo 1 >moved && + ln -s moved symlink && + git add moved symlink && + test_must_fail git mv symlink moved && + git mv -f symlink moved && + ! test -e symlink && + test -h moved && + git diff-files --quiet + +' + +rm -f moved symlink + test_done -- cgit v1.2.1 From 8e1db3871c767cb17b5e0eeb7bea8d967821a055 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Jul 2008 12:37:51 -0700 Subject: GIT 1.6.0-rc1 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.0.txt | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Documentation/RelNotes-1.6.0.txt b/Documentation/RelNotes-1.6.0.txt index 7da62d08d6..2542cf53d2 100644 --- a/Documentation/RelNotes-1.6.0.txt +++ b/Documentation/RelNotes-1.6.0.txt @@ -15,6 +15,9 @@ release, but users are again strongly encouraged to adjust their scripts to use "git xyzzy" form, as we will stop installing "git-xyzzy" hardlinks for built-in commands in later releases. +An earlier change to page "git status" output was overwhelmingly unpopular +and has been reverted. + Source changes needed for porting to MinGW environment are now all in the main git.git codebase. @@ -179,6 +182,10 @@ Updates since v1.5.6 * "git rerere" can be told to update the index with auto-reused resolution with rerere.autoupdate configuration variable. +* git-rev-parse learned $commit^! and $commit^@ notations used in "log" + family. These notations are available in gitk as well, because the gitk + command internally uses rev-parse to interpret its arguments. + * git-rev-list learned --children option to show child commits it encountered during the traversal, instead of shoing parent commits. @@ -196,6 +203,9 @@ Updates since v1.5.6 * git-status gives the remote tracking statistics similar to the way git-checkout reports by how many commits your branch is ahead/behind. +* "git-svn dcommit" is now aware of auto-props setting the subversion user + has. + * You can tell "git status -u" to even more aggressively omit checking untracked files with --untracked-files=no. @@ -215,8 +225,15 @@ Fixes since v1.5.6 All of the fixes in v1.5.6 maintenance series are included in this release, unless otherwise noted. +* git-clone ignored its -u option; the fix needs to be backported to + 'maint'; + +* git-mv used to lose the distinction between changes that are staged + and that are only in the working tree, by staging both in the index + after moving such a path. + --- exec >/var/tmp/1 -O=v1.5.6.4-432-g6796399 +O=v1.6.0-rc0-104-g81dc230 echo O=$(git describe refs/heads/master) git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint -- cgit v1.2.1 From 172035f044ebae283f23e95e3ec4d119dc8057cb Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 28 Jul 2008 02:02:04 -0400 Subject: init: handle empty "template" parameter If a user passes "--template=", then our template parameter is blank. Unfortunately, copy_templates() assumes it has at least one character, and does all sorts of bad things like reading from template[-1] and then proceeding to link all of '/' into the .git directory. This patch just checks for that condition in copy_templates and aborts. As a side effect, this means that --template= now has the meaning "don't copy any templates." Signed-off-by: Junio C Hamano --- builtin-init-db.c | 2 ++ t/t0001-init.sh | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/builtin-init-db.c b/builtin-init-db.c index 38b4fcb6db..baf0d09ac4 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -117,6 +117,8 @@ static void copy_templates(const char *template_dir) template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT); if (!template_dir) template_dir = system_path(DEFAULT_GIT_TEMPLATE_DIR); + if (!template_dir[0]) + return; strcpy(template_path, template_dir); template_len = strlen(template_path); if (template_path[template_len-1] != '/') { diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 2a38d98cb4..620da5b320 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -141,4 +141,30 @@ test_expect_success 'reinit' ' test_cmp again/empty again/err2 ' +test_expect_success 'init with --template' ' + mkdir template-source && + echo content >template-source/file && + ( + mkdir template-custom && + cd template-custom && + git init --template=../template-source + ) && + test_cmp template-source/file template-custom/.git/file +' + +test_expect_success 'init with --template (blank)' ' + ( + mkdir template-plain && + cd template-plain && + git init + ) && + test -f template-plain/.git/info/exclude && + ( + mkdir template-blank && + cd template-blank && + git init --template= + ) && + ! test -f template-blank/.git/info/exclude +' + test_done -- cgit v1.2.1 From e84a063da86cbf4382fbfb6f85e75c2ca2a8b9c7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Jul 2008 00:02:48 -0700 Subject: Allow building without any git installed This is a follow-up patch to 49fa65a (Allow the built-in exec path to be relative to the command invocation path, 2008-07-23). Without specific gitexecdir passed from the command line, git-gui's build procedure would try to figure out the value for it by running an installed git. Signed-off-by: Junio C Hamano --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 798a2f2f77..7e30b30775 100644 --- a/Makefile +++ b/Makefile @@ -1067,7 +1067,7 @@ endif all:: ifndef NO_TCLTK - $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) all + $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) gitexecdir='$(gitexec_instdir_SQ)' all $(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all endif $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all -- cgit v1.2.1 From 1b65f38c06e277a13fd7bf77d3d70df604912c08 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Jul 2008 00:05:10 -0700 Subject: Allow installing in the traditional way In an earlier commit c70a8d9 (Makefile: Do not install a copy of 'git' in $(gitexecdir), 2008-07-21), we tried to avoid installing two git, one in /usr/bin/git and the other in /usr/libexec/git-core/git. It mistakenly removed the only copy of git when gitexecdir and bindir are set to the same directory, i.e. the traditional layout. Signed-off-by: Junio C Hamano --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7e30b30775..52c67c1a47 100644 --- a/Makefile +++ b/Makefile @@ -1362,7 +1362,10 @@ endif cp "$$bindir/git$X" "$$execdir/git$X"; \ fi && \ { $(foreach p,$(BUILT_INS), $(RM) "$$execdir/$p" && ln "$$execdir/git$X" "$$execdir/$p" ;) } && \ - $(RM) "$$execdir/git$X" && \ + if test "z$$bindir" != "z$$execdir"; \ + then \ + $(RM) "$$execdir/git$X"; \ + fi && \ ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X" install-doc: -- cgit v1.2.1 From 7ddea13af24d72deca06a779b1827a8ea2d6cfe6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Jul 2008 02:33:33 -0700 Subject: ls-tree documentation: enhance notes on subdirectory and pathspec behaviour When run in a working copy subdirectory, git-ls-tree will automagically add the prefix to the pathspec, which can result in an unexpected behavior when the tree object accessed is not the root tree object. Signed-off-by: Junio C Hamano --- Documentation/git-ls-tree.txt | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt index d7e73f568d..4c7262f1cd 100644 --- a/Documentation/git-ls-tree.txt +++ b/Documentation/git-ls-tree.txt @@ -16,10 +16,20 @@ SYNOPSIS DESCRIPTION ----------- Lists the contents of a given tree object, like what "/bin/ls -a" does -in the current working directory. Note that the usage is subtly different, -though - 'paths' denote just a list of patterns to match, e.g. so specifying -directory name (without '-r') will behave differently, and order of the -arguments does not matter. +in the current working directory. Note that: + + - the behaviour is slightly different from that of "/bin/ls" in that the + 'paths' denote just a list of patterns to match, e.g. so specifying + directory name (without '-r') will behave differently, and order of the + arguments does not matter. + + - the behaviour is similar to that of "/bin/ls" in that the 'paths' is + taken as relative to the current working directory. E.g. when you are + in a directory 'sub' that has a directory 'dir', you can run 'git + ls-tree -r HEAD dir' to list the contents of the tree (that is + 'sub/dir' in 'HEAD'). You don't want to give a tree that is not at the + root level (e.g. 'git ls-tree -r HEAD:sub dir') in this case, as that + would result in asking for 'sub/sub/dir' in the 'HEAD' commit. OPTIONS ------- -- cgit v1.2.1 From d2761895144b6fabcbe1f3bd40d6c6798914b518 Mon Sep 17 00:00:00 2001 From: Olivier Marin Date: Mon, 28 Jul 2008 12:48:44 +0200 Subject: builtin-verify-tag: fix -v option parsing Since the C rewrite, "git verify-tag -v" just does nothing instead of printing the usage message with an error. This patch fix the regression. Signed-off-by: Olivier Marin Signed-off-by: Junio C Hamano --- builtin-verify-tag.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c index 7d837f0f98..729a1593e6 100644 --- a/builtin-verify-tag.c +++ b/builtin-verify-tag.c @@ -92,14 +92,15 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); - if (argc == 1) - usage(builtin_verify_tag_usage); - - if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) { + if (argc > 1 && + (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))) { verbose = 1; i++; } + if (argc <= i) + usage(builtin_verify_tag_usage); + /* sometimes the program was terminated because this signal * was received in the process of writing the gpg input: */ signal(SIGPIPE, SIG_IGN); -- cgit v1.2.1 From 4933e5ebdefdb88841275132ef358e6649963751 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Mon, 28 Jul 2008 07:50:27 +0200 Subject: Refactor, adding prepare_git_cmd(const char **argv) prepare_git_cmd(const char **argv) adds a first entry "git" to the array argv. The new array is allocated on the heap. It's the caller's responsibility to release it with free(). The code was already present in execv_git_cmd() but could not be used from outside. Now it can also be called for preparing the command list in the MinGW codepath in run-command.c. Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano --- exec_cmd.c | 7 ++++++- exec_cmd.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/exec_cmd.c b/exec_cmd.c index 0ed768ddc0..ce6741eb68 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -78,7 +78,7 @@ void setup_path(void) strbuf_release(&new_path); } -int execv_git_cmd(const char **argv) +const char **prepare_git_cmd(const char **argv) { int argc; const char **nargv; @@ -91,6 +91,11 @@ int execv_git_cmd(const char **argv) for (argc = 0; argv[argc]; argc++) nargv[argc + 1] = argv[argc]; nargv[argc + 1] = NULL; + return nargv; +} + +int execv_git_cmd(const char **argv) { + const char **nargv = prepare_git_cmd(argv); trace_argv_printf(nargv, "trace: exec:"); /* execvp() can only ever return if it fails */ diff --git a/exec_cmd.h b/exec_cmd.h index 0c46cd5636..594f961387 100644 --- a/exec_cmd.h +++ b/exec_cmd.h @@ -5,6 +5,7 @@ extern void git_set_argv_exec_path(const char *exec_path); extern void git_set_argv0_path(const char *path); extern const char* git_exec_path(void); extern void setup_path(void); +extern const char **prepare_git_cmd(const char **argv); extern int execv_git_cmd(const char **argv); /* NULL terminated */ extern int execl_git_cmd(const char *cmd, ...); extern const char *system_path(const char *path); -- cgit v1.2.1 From 108ac313f73499572c69b1b80514661b22a2d62e Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Mon, 28 Jul 2008 07:50:28 +0200 Subject: run-command (Windows): Run dashless "git " We prefer running the dashless form, and POSIX side already does so; we should use it in MinGW's start_command(), too. Signed-off-by: Steffen Prohaska Acked-by: Johannes Sixt Signed-off-by: Junio C Hamano --- run-command.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/run-command.c b/run-command.c index 6e29fdf9e2..a3b28a64dc 100644 --- a/run-command.c +++ b/run-command.c @@ -119,9 +119,8 @@ int start_command(struct child_process *cmd) } #else int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */ - const char *sargv0 = cmd->argv[0]; + const char **sargv = cmd->argv; char **env = environ; - struct strbuf git_cmd; if (cmd->no_stdin) { s0 = dup(0); @@ -165,9 +164,7 @@ int start_command(struct child_process *cmd) } if (cmd->git_cmd) { - strbuf_init(&git_cmd, 0); - strbuf_addf(&git_cmd, "git-%s", cmd->argv[0]); - cmd->argv[0] = git_cmd.buf; + cmd->argv = prepare_git_cmd(cmd->argv); } cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env); @@ -175,9 +172,9 @@ int start_command(struct child_process *cmd) if (cmd->env) free_environ(env); if (cmd->git_cmd) - strbuf_release(&git_cmd); + free(cmd->argv); - cmd->argv[0] = sargv0; + cmd->argv = sargv; if (s0 >= 0) dup2(s0, 0), close(s0); if (s1 >= 0) -- cgit v1.2.1 From c297432d0d245af5505e0f5f99a5267119dd421e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Jul 2008 22:26:47 -0700 Subject: Documentation: clarify what is shown in "git-ls-files -s" output Signed-off-by: Junio C Hamano --- Documentation/git-ls-files.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index f43af41740..9f85d60b5f 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -53,7 +53,7 @@ OPTIONS -s:: --stage:: - Show stage files in the output + Show staged contents' object name, mode bits and stage number in the output. --directory:: If a whole directory is classified as "other", show just its -- cgit v1.2.1 From 65c35b2256ecbfaebcf04559fe6070807646afff Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Jul 2008 22:27:39 -0700 Subject: t7001: fix "git mv" test The test assumed that we can keep the cached stat information fresh across rename(2); many filesystems however update st_ctime (and POSIX allows them to do so), and that assumption does not hold. We can explicitly refresh the index for the purpose of these tests. The only thing we are interested in is the staged contents and the mode bits are preserved across "git mv". Signed-off-by: Junio C Hamano --- t/t7001-mv.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index b0fa407825..910a28c7e2 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -185,6 +185,7 @@ test_expect_success 'git mv should overwrite symlink to a file' ' ! test -e moved && test -f symlink && test "$(cat symlink)" = 1 && + git update-index --refresh && git diff-files --quiet ' @@ -202,6 +203,7 @@ test_expect_success 'git mv should overwrite file with a symlink' ' git mv -f symlink moved && ! test -e symlink && test -h moved && + git update-index --refresh && git diff-files --quiet ' -- cgit v1.2.1 From df57accb46695b50e2db94424e9d4ebfed0694c8 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 29 Jul 2008 07:42:53 +0200 Subject: merge-base: die with an error message if not passed a commit ref Before this patch "git merge-base" just exited with error code 1 and without an error message in case it was passed a ref to an object that is not a commit (for example a tree). This patch makes it "die" in this case with an error message. While at it, this patch also refactors the code to get the commit reference from an argument into a new "get_commit_reference" function. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin-merge-base.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/builtin-merge-base.c b/builtin-merge-base.c index 1cb2925d2f..3382b1382a 100644 --- a/builtin-merge-base.c +++ b/builtin-merge-base.c @@ -22,10 +22,23 @@ static int show_merge_base(struct commit *rev1, struct commit *rev2, int show_al static const char merge_base_usage[] = "git merge-base [--all] "; +static struct commit *get_commit_reference(const char *arg) +{ + unsigned char revkey[20]; + struct commit *r; + + if (get_sha1(arg, revkey)) + die("Not a valid object name %s", arg); + r = lookup_commit_reference(revkey); + if (!r) + die("Not a valid commit name %s", arg); + + return r; +} + int cmd_merge_base(int argc, const char **argv, const char *prefix) { struct commit *rev1, *rev2; - unsigned char rev1key[20], rev2key[20]; int show_all = 0; git_config(git_default_config, NULL); @@ -40,13 +53,8 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix) } if (argc != 3) usage(merge_base_usage); - if (get_sha1(argv[1], rev1key)) - die("Not a valid object name %s", argv[1]); - if (get_sha1(argv[2], rev2key)) - die("Not a valid object name %s", argv[2]); - rev1 = lookup_commit_reference(rev1key); - rev2 = lookup_commit_reference(rev2key); - if (!rev1 || !rev2) - return 1; + rev1 = get_commit_reference(argv[1]); + rev2 = get_commit_reference(argv[2]); + return show_merge_base(rev1, rev2, show_all); } -- cgit v1.2.1 From 1ce4790bf5eeceb212f003fffcb36069c0ebb45e Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 28 Jul 2008 08:31:28 +0200 Subject: Make use of stat.ctime configurable A new configuration variable 'core.trustctime' is introduced to allow ignoring st_ctime information when checking if paths in the working tree has changed, because there are situations where it produces too much false positives. Like when file system crawlers keep changing it when scanning and using the ctime for marking scanned files. The default is to notice ctime changes. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- Documentation/config.txt | 7 +++++++ Documentation/git-update-index.txt | 5 +++++ cache.h | 1 + config.c | 4 ++++ environment.c | 1 + read-cache.c | 2 +- 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 1a13abc922..61c376057c 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -117,6 +117,13 @@ core.fileMode:: the working copy are ignored; useful on broken filesystems like FAT. See linkgit:git-update-index[1]. True by default. +core.trustctime:: + If false, the ctime differences between the index and the + working copy are ignored; useful when the inode change time + is regularly modified by something outside Git (file system + crawlers and some backup systems). + See linkgit:git-update-index[1]. True by default. + core.quotepath:: The commands that output paths (e.g. 'ls-files', 'diff'), when not given the `-z` option, will quote diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index 6b930bc163..1d9d81a702 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -323,6 +323,11 @@ from symbolic link to regular file. The command looks at `core.ignorestat` configuration variable. See 'Using "assume unchanged" bit' section above. +The command also looks at `core.trustctime` configuration variable. +It can be useful when the inode change time is regularly modified by +something outside Git (file system crawlers and backup systems use +ctime for marking files processed) (see linkgit:git-config[1]). + SEE ALSO -------- diff --git a/cache.h b/cache.h index 4b6c0a6c59..2475de9fa8 100644 --- a/cache.h +++ b/cache.h @@ -423,6 +423,7 @@ extern int delete_ref(const char *, const unsigned char *sha1); /* Environment bits from configuration mechanism */ extern int trust_executable_bit; +extern int trust_ctime; extern int quote_path_fully; extern int has_symlinks; extern int ignore_case; diff --git a/config.c b/config.c index 1e066c71e0..53f04a076a 100644 --- a/config.c +++ b/config.c @@ -341,6 +341,10 @@ static int git_default_core_config(const char *var, const char *value) trust_executable_bit = git_config_bool(var, value); return 0; } + if (!strcmp(var, "core.trustctime")) { + trust_ctime = git_config_bool(var, value); + return 0; + } if (!strcmp(var, "core.quotepath")) { quote_path_fully = git_config_bool(var, value); diff --git a/environment.c b/environment.c index 4a88a17d54..0c6d11f6a0 100644 --- a/environment.c +++ b/environment.c @@ -13,6 +13,7 @@ char git_default_email[MAX_GITNAME]; char git_default_name[MAX_GITNAME]; int user_ident_explicitly_given; int trust_executable_bit = 1; +int trust_ctime = 1; int has_symlinks = 1; int ignore_case; int assume_unchanged; diff --git a/read-cache.c b/read-cache.c index 6c0880337b..1cae361c6c 100644 --- a/read-cache.c +++ b/read-cache.c @@ -197,7 +197,7 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) } if (ce->ce_mtime != (unsigned int) st->st_mtime) changed |= MTIME_CHANGED; - if (ce->ce_ctime != (unsigned int) st->st_ctime) + if (trust_ctime && ce->ce_ctime != (unsigned int) st->st_ctime) changed |= CTIME_CHANGED; if (ce->ce_uid != (unsigned int) st->st_uid || -- cgit v1.2.1 From 79317e5df184773f3211503be49b8836b712facc Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 29 Jul 2008 22:36:58 -0700 Subject: git-gui: Fix gitk search in $PATH to work on Windows Back in 15430be5a1 ("Look for gitk in $PATH, not $LIBEXEC/git-core") git-gui learned to use [_which gitk] to locate where gitk's script is as Git 1.6 will install gitk to $prefix/bin (in $PATH) and all of the other tools are in $gitexecdir. This failed on Windows because _which adds the ".exe" suffix as it searches for the program on $PATH, under the assumption that we can only execute something from Tcl if it is a proper Windows executable. When scanning for gitk on Windows we need to omit the ".exe" suffix. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index ce941adf8d..14b2d9aacd 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -317,7 +317,7 @@ proc _git_cmd {name} { return $v } -proc _which {what} { +proc _which {what args} { global env _search_exe _search_path if {$_search_path eq {}} { @@ -340,8 +340,14 @@ proc _which {what} { } } + if {[is_Windows] && [lsearch -exact $args -script] >= 0} { + set suffix {} + } else { + set suffix $_search_exe + } + foreach p $_search_path { - set p [file join $p $what$_search_exe] + set p [file join $p $what$suffix] if {[file exists $p]} { return [file normalize $p] } @@ -1686,7 +1692,7 @@ proc do_gitk {revs} { # -- Always start gitk through whatever we were loaded with. This # lets us bypass using shell process on Windows systems. # - set exe [_which gitk] + set exe [_which gitk -script] set cmd [list [info nameofexecutable] $exe] if {$exe eq {}} { error_popup [mc "Couldn't find gitk in PATH"] -- cgit v1.2.1 From f8f1acf339da302ea912c8dd114042a1ef6f994f Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Sun, 27 Jul 2008 14:23:30 -0700 Subject: git-gui: Correct installation of library to be $prefix/share We always wanted the library for git-gui to install into the $prefix/share directory, not $prefix/libexec/share. All of the files in our library are platform independent and may be reused across systems, like any other content stored in the share directory. Our computation of where our library should install to was broken when git itself started installing to $prefix/libexec/git-core, which was one level down from where we expected it to be. Signed-off-by: Steffen Prohaska Signed-off-by: Shawn O. Pearce --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index b19fb2d64e..c9d67fe98e 100644 --- a/Makefile +++ b/Makefile @@ -34,8 +34,12 @@ ifndef gitexecdir endif ifndef sharedir +ifeq (git-core,$(notdir $(gitexecdir))) + sharedir := $(dir $(patsubst %/,%,$(dir $(gitexecdir))))share +else sharedir := $(dir $(gitexecdir))share endif +endif ifndef INSTALL INSTALL = install -- cgit v1.2.1 From f57ddcc5ecf16d1fff4b6e222352c3220fe8120f Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Sun, 27 Jul 2008 18:49:42 +0200 Subject: git-gui (Windows): Switch to relative discovery of oguilib Instead of using an absolute path, git-gui can discover its gui library using a relative path from execdir. We want to use the relative path discovery on MinGW to avoid issues with translation of absolute paths. Signed-off-by: Steffen Prohaska Signed-off-by: Shawn O. Pearce --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index c9d67fe98e..55765c8a3a 100644 --- a/Makefile +++ b/Makefile @@ -160,6 +160,7 @@ endif ifneq (,$(findstring MINGW,$(uname_S))) NO_MSGFMT=1 GITGUI_WINDOWS_WRAPPER := YesPlease + GITGUI_RELATIVE := 1 endif ifdef GITGUI_MACOSXAPP -- cgit v1.2.1 From 5fc6edab76ea16c5fad7138389c2fcc2076534e7 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Sun, 27 Jul 2008 18:49:43 +0200 Subject: git-gui (Windows): Change wrapper to execdir 'libexec/git-core' git-gui needs bindir in PATH to be able to run 'git'. bindir however is not necessarily in PATH if started directly through a Windows shortcut. Therefore, we used to add the directory git-gui is located in. But with the new 'libexec/git-core' layout this directory is no longer identical to bindir. This commit modifies the wrapper script to discover the bindir and add it to PATH. Signed-off-by: Steffen Prohaska Signed-off-by: Shawn O. Pearce --- windows/git-gui.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/windows/git-gui.sh b/windows/git-gui.sh index 98f32c0a07..53c3a94686 100644 --- a/windows/git-gui.sh +++ b/windows/git-gui.sh @@ -8,9 +8,12 @@ if { $argc >=2 && [lindex $argv 0] == "--working-dir" } { incr argc -2 } -set gitguidir [file dirname [info script]] -regsub -all ";" $gitguidir "\\;" gitguidir -set env(PATH) "$gitguidir;$env(PATH)" -unset gitguidir +set bindir [file dirname \ + [file dirname \ + [file dirname [info script]]]] +set bindir [file join $bindir bin] +regsub -all ";" $bindir "\\;" bindir +set env(PATH) "$bindir;$env(PATH)" +unset bindir source [file join [file dirname [info script]] git-gui.tcl] -- cgit v1.2.1 From 53eda89b2fe13e974ca85367255f9a5e9ab9171f Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 30 Jul 2008 07:04:14 +0200 Subject: merge-base: teach "git merge-base" to drive underlying merge_bases_many() Even though the underlying function for get_merge_bases() can compute a merge base between one existing commit and another (possibly nonexistent) commit that would be created by merging many commits, the facility was not available to git-merge-base. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin-merge-base.c | 24 ++++++++++++++++-------- commit.h | 1 + 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/builtin-merge-base.c b/builtin-merge-base.c index 3382b1382a..b08da516e4 100644 --- a/builtin-merge-base.c +++ b/builtin-merge-base.c @@ -2,9 +2,11 @@ #include "cache.h" #include "commit.h" -static int show_merge_base(struct commit *rev1, struct commit *rev2, int show_all) +static int show_merge_base(struct commit **rev, int rev_nr, int show_all) { - struct commit_list *result = get_merge_bases(rev1, rev2, 0); + struct commit_list *result; + + result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0); if (!result) return 1; @@ -20,7 +22,7 @@ static int show_merge_base(struct commit *rev1, struct commit *rev2, int show_al } static const char merge_base_usage[] = -"git merge-base [--all] "; +"git merge-base [--all] ..."; static struct commit *get_commit_reference(const char *arg) { @@ -38,7 +40,8 @@ static struct commit *get_commit_reference(const char *arg) int cmd_merge_base(int argc, const char **argv, const char *prefix) { - struct commit *rev1, *rev2; + struct commit **rev; + int rev_nr = 0; int show_all = 0; git_config(git_default_config, NULL); @@ -51,10 +54,15 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix) usage(merge_base_usage); argc--; argv++; } - if (argc != 3) + if (argc < 3) usage(merge_base_usage); - rev1 = get_commit_reference(argv[1]); - rev2 = get_commit_reference(argv[2]); - return show_merge_base(rev1, rev2, show_all); + rev = xmalloc((argc - 1) * sizeof(*rev)); + + do { + rev[rev_nr++] = get_commit_reference(argv[1]); + argc--; argv++; + } while (argc > 1); + + return show_merge_base(rev, rev_nr, show_all); } diff --git a/commit.h b/commit.h index 77de9621d9..d163c7487b 100644 --- a/commit.h +++ b/commit.h @@ -121,6 +121,7 @@ int read_graft_file(const char *graft_file); struct commit_graft *lookup_commit_graft(const unsigned char *sha1); extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup); +extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup); extern struct commit_list *get_octopus_merge_bases(struct commit_list *in); extern int register_shallow(const unsigned char *sha1); -- cgit v1.2.1 From 99f1c04be09d73a40ef0a37c6868969d40391196 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 30 Jul 2008 07:04:43 +0200 Subject: documentation: merge-base: explain "git merge-base" with more than 2 args Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Documentation/git-merge-base.txt | 77 ++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 11 deletions(-) diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt index 1a7ecbf8f3..2f0c5259e0 100644 --- a/Documentation/git-merge-base.txt +++ b/Documentation/git-merge-base.txt @@ -8,26 +8,81 @@ git-merge-base - Find as good common ancestors as possible for a merge SYNOPSIS -------- -'git merge-base' [--all] +'git merge-base' [--all] ... DESCRIPTION ----------- -'git-merge-base' finds as good a common ancestor as possible between -the two commits. That is, given two commits A and B, `git merge-base A -B` will output a commit which is reachable from both A and B through -the parent relationship. +'git-merge-base' finds best common ancestor(s) between two commits to use +in a three-way merge. One common ancestor is 'better' than another common +ancestor if the latter is an ancestor of the former. A common ancestor +that does not have any better common ancestor than it is a 'best common +ancestor', i.e. a 'merge base'. Note that there can be more than one +merge bases between two commits. -Given a selection of equally good common ancestors it should not be -relied on to decide in any particular way. - -The 'git-merge-base' algorithm is still in flux - use the source... +Among the two commits to compute their merge bases, one is specified by +the first commit argument on the command line; the other commit is a +(possibly hypothetical) commit that is a merge across all the remaining +commits on the command line. As the most common special case, giving only +two commits from the command line means computing the merge base between +the given two commits. OPTIONS ------- --all:: - Output all common ancestors for the two commits instead of - just one. + Output all merge bases for the commits, instead of just one. + +DISCUSSION +---------- + +Given two commits 'A' and 'B', `git merge-base A B` will output a commit +which is reachable from both 'A' and 'B' through the parent relationship. + +For example, with this topology: + + o---o---o---B + / + ---o---1---o---o---o---A + +the merge base between 'A' and 'B' is '1'. + +Given three commits 'A', 'B' and 'C', `git merge-base A B C` will compute the +merge base between 'A' and an hypothetical commit 'M', which is a merge +between 'B' and 'C'. For example, with this topology: + + o---o---o---o---C + / + / o---o---o---B + / / + ---2---1---o---o---o---A + +the result of `git merge-base A B C` is '1'. This is because the +equivalent topology with a merge commit 'M' between 'B' and 'C' is: + + + o---o---o---o---o + / \ + / o---o---o---o---M + / / + ---2---1---o---o---o---A + +and the result of `git merge-base A M` is '1'. Commit '2' is also a +common ancestor between 'A' and 'M', but '1' is a better common ancestor, +because '2' is an ancestor of '1'. Hence, '2' is not a merge base. + +When the history involves criss-cross merges, there can be more than one +'best' common ancestors between two commits. For example, with this +topology: + + ---1---o---A + \ / + X + / \ + ---2---o---o---B + +both '1' and '2' are merge-base of A and B. Neither one is better than +the other (both are 'best' merge base). When `--all` option is not given, +it is unspecified which best one is output. Author ------ -- cgit v1.2.1 From a56bf5850a07e9119cd7e53de1465af432642ef8 Mon Sep 17 00:00:00 2001 From: Abhijit Menon-Sen Date: Wed, 30 Jul 2008 01:23:16 +0530 Subject: `git submodule add` now requires a Signed-off-by: Abhijit Menon-Sen Signed-off-by: Junio C Hamano --- Documentation/user-manual.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index c5641af190..00256ca57c 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -3458,7 +3458,7 @@ $ cd super $ git init $ for i in a b c d do - git submodule add ~/git/$i + git submodule add ~/git/$i $i done ------------------------------------------------- @@ -3471,10 +3471,10 @@ $ ls -a . .. .git .gitmodules a b c d ------------------------------------------------- -The `git-submodule add` command does a couple of things: +The `git-submodule add ` command does a couple of things: -- It clones the submodule under the current directory and by default checks out - the master branch. +- It clones the submodule from to the given under the + current directory and by default checks out the master branch. - It adds the submodule's clone path to the linkgit:gitmodules[5] file and adds this file to the index, ready to be committed. - It adds the submodule's current commit ID to the index, ready to be -- cgit v1.2.1 From 2c9693bda5f5c726847b4763dd6f82cfdca3e8d7 Mon Sep 17 00:00:00 2001 From: Abhijit Menon-Sen Date: Wed, 30 Jul 2008 04:43:38 +0530 Subject: Make it clear that push can take multiple refspecs Signed-off-by: Abhijit Menon-Sen Signed-off-by: Junio C Hamano --- Documentation/git-push.txt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 94d07ab812..050c3ddae2 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -10,7 +10,8 @@ SYNOPSIS -------- [verse] 'git push' [--all] [--dry-run] [--tags] [--receive-pack=] - [--repo=all] [-f | --force] [-v | --verbose] [ ...] + [--repo=all] [-f | --force] [-v | --verbose] + [ ...] DESCRIPTION ----------- @@ -29,8 +30,8 @@ OPTIONS The "remote" repository that is destination of a push operation. See the section <> below. -:: - The canonical format of a parameter is +...:: + The canonical format of each parameter is `+?:`; that is, an optional plus `+`, followed by the source ref, followed by a colon `:`, followed by the destination ref. @@ -180,11 +181,11 @@ git push origin :experimental:: Find a ref that matches `experimental` in the `origin` repository (e.g. `refs/heads/experimental`), and delete it. -git push origin master:satellite/master:: - Find a ref that matches `master` in the source repository - (most likely, it would find `refs/heads/master`), and update - the ref that matches `satellite/master` (most likely, it would - be `refs/remotes/satellite/master`) in `origin` repository with it. +git push origin master:satellite/master dev:satellite/dev:: + Use the source ref that matches `master` (e.g. `refs/heads/master`) + to update the ref that matches `satellite/master` (most probably + `refs/remotes/satellite/master`) in the `origin` repository, then + do the same for `dev` and `satellite/dev`. git push origin master:refs/heads/experimental:: Create the branch `experimental` in the `origin` repository -- cgit v1.2.1 From 8cb070a4cfea48f88ada02a87320c3ca63b934cb Mon Sep 17 00:00:00 2001 From: Brian Gernhardt Date: Tue, 29 Jul 2008 21:16:01 -0400 Subject: Documentation: Remove mentions of git-svnimport. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svnimport is no longer supported, so don't mention it in the documentation. This also updates the description, removing the historical discussion, since it mostly dealt with how it differed from svnimport. The new description gives some starting points into the rest of the documentation. Noticed by Jurko Gospodnetić Signed-off-by: Brian Gernhardt Signed-off-by: Junio C Hamano --- Documentation/git-svn.txt | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index e7c0f1c959..f230125a81 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -12,18 +12,18 @@ SYNOPSIS DESCRIPTION ----------- 'git-svn' is a simple conduit for changesets between Subversion and git. -It is not to be confused with linkgit:git-svnimport[1], which is -read-only. +It provides a bidirectional flow of changes between a Subversion and a git +respository. -'git-svn' was originally designed for an individual developer who wants a -bidirectional flow of changesets between a single branch in Subversion -and an arbitrary number of branches in git. Since its inception, -'git-svn' has gained the ability to track multiple branches in a manner -similar to 'git-svnimport'. +'git-svn' can track a single Subversion branch simply by using a +URL to the branch, follow branches laid out in the Subversion recommended +method (trunk, branches, tags directories) with the --stdlayout option, or +follow branches in any layout with the -T/-t/-b options (see options to +'init' below, and also the 'clone' command). -'git-svn' is especially useful when it comes to tracking repositories -not organized in the way Subversion developers recommend (trunk, -branches, tags directories). +Once tracking a Subversion branch (with any of the above methods), the git +repository can be updated from Subversion by the 'fetch' command and +Subversion updated from git by the 'dcommit' command. COMMANDS -------- @@ -218,8 +218,7 @@ Any other arguments are passed directly to 'git-log' 'commit-diff':: Commits the diff of two tree-ish arguments from the - command-line. This command is intended for interoperability with - 'git-svnimport' and does not rely on being inside an `git-svn + command-line. This command does not rely on being inside an `git-svn init`-ed repository. This command takes three arguments, (a) the original tree to diff against, (b) the new tree result, (c) the URL of the target Subversion repository. The final argument @@ -317,8 +316,7 @@ config key: svn.findcopiesharder -A:: --authors-file=:: -Syntax is compatible with the files used by 'git-svnimport' and -'git-cvsimport': +Syntax is compatible with the file used by 'git-cvsimport': ------------------------------------------------------------------------ loginname = Joe User -- cgit v1.2.1 From 6b2fbaaffc5ec762eae5d23b58b1dc0a88e2275e Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Tue, 29 Jul 2008 22:49:33 -0700 Subject: format-patch: Produce better output with --inline or --attach This patch makes two small changes to improve the output of --inline and --attach. The first is to write a newline preceding the boundary. This is needed because MIME defines the encapsulation boundary as including the preceding CRLF (or in this case, just LF), so we should be writing one. Without this, the last newline in the pre-diff content is consumed instead. The second change is to always write the line termination character (default: newline) even when using --inline or --attach. This is simply to improve the aesthetics of the resulting message. When using --inline an email client should render the resulting message identically to the non-inline version. And when using --attach this adds a blank line preceding the attachment in the email, which is visually attractive. Signed-off-by: Kevin Ballard Signed-off-by: Junio C Hamano --- diff.c | 3 +-- log-tree.c | 2 +- t/t4013/diff.format-patch_--attach_--stdout_initial..master | 6 ++++++ t/t4013/diff.format-patch_--attach_--stdout_initial..master^ | 4 ++++ t/t4013/diff.format-patch_--attach_--stdout_initial..side | 2 ++ ...atch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master | 6 ++++++ t/t4013/diff.format-patch_--inline_--stdout_initial..master | 6 ++++++ t/t4013/diff.format-patch_--inline_--stdout_initial..master^ | 4 ++++ t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ | 2 ++ t/t4013/diff.format-patch_--inline_--stdout_initial..side | 2 ++ 10 files changed, 34 insertions(+), 3 deletions(-) diff --git a/diff.c b/diff.c index a07812c5c7..cbf25473c5 100644 --- a/diff.c +++ b/diff.c @@ -3223,11 +3223,10 @@ void diff_flush(struct diff_options *options) if (output_format & DIFF_FORMAT_PATCH) { if (separator) { + putc(options->line_termination, options->file); if (options->stat_sep) { /* attach patch instead of inline */ fputs(options->stat_sep, options->file); - } else { - putc(options->line_termination, options->file); } } diff --git a/log-tree.c b/log-tree.c index 5505606ed6..bd8b9e45ab 100644 --- a/log-tree.c +++ b/log-tree.c @@ -198,7 +198,7 @@ void log_write_email_headers(struct rev_info *opt, const char *name, extra_headers = subject_buffer; snprintf(buffer, sizeof(buffer) - 1, - "--%s%s\n" + "\n--%s%s\n" "Content-Type: text/x-patch;" " name=\"%s.diff\"\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master b/t/t4013/diff.format-patch_--attach_--stdout_initial..master index cf6891f748..43346b9ba4 100644 --- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master +++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master @@ -19,6 +19,8 @@ This is the second commit. file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" Content-Transfer-Encoding: 8bit @@ -75,6 +77,8 @@ Content-Transfer-Encoding: 8bit file1 | 3 +++ 2 files changed, 5 insertions(+), 0 deletions(-) create mode 100644 file1 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" Content-Transfer-Encoding: 8bit @@ -122,6 +126,8 @@ Content-Transfer-Encoding: 8bit file3 | 4 ++++ 3 files changed, 9 insertions(+), 0 deletions(-) create mode 100644 file3 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" Content-Transfer-Encoding: 8bit diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ index fe0258720c..d7490a9fd7 100644 --- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ +++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ @@ -19,6 +19,8 @@ This is the second commit. file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" Content-Transfer-Encoding: 8bit @@ -75,6 +77,8 @@ Content-Transfer-Encoding: 8bit file1 | 3 +++ 2 files changed, 5 insertions(+), 0 deletions(-) create mode 100644 file1 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" Content-Transfer-Encoding: 8bit diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_initial..side index 9ff828ee9d..38f790290a 100644 --- a/t/t4013/diff.format-patch_--attach_--stdout_initial..side +++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..side @@ -17,6 +17,8 @@ Content-Transfer-Encoding: 8bit file3 | 4 ++++ 3 files changed, 9 insertions(+), 0 deletions(-) create mode 100644 file3 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" Content-Transfer-Encoding: 8bit diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master index a8093be7ca..fca5cce373 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master +++ b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master @@ -19,6 +19,8 @@ This is the second commit. file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" Content-Transfer-Encoding: 8bit @@ -75,6 +77,8 @@ Content-Transfer-Encoding: 8bit file1 | 3 +++ 2 files changed, 5 insertions(+), 0 deletions(-) create mode 100644 file1 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" Content-Transfer-Encoding: 8bit @@ -122,6 +126,8 @@ Content-Transfer-Encoding: 8bit file3 | 4 ++++ 3 files changed, 9 insertions(+), 0 deletions(-) create mode 100644 file3 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" Content-Transfer-Encoding: 8bit diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_initial..master index aa110c0e7f..6d6fac3908 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master @@ -19,6 +19,8 @@ This is the second commit. file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" Content-Transfer-Encoding: 8bit @@ -75,6 +77,8 @@ Content-Transfer-Encoding: 8bit file1 | 3 +++ 2 files changed, 5 insertions(+), 0 deletions(-) create mode 100644 file1 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" Content-Transfer-Encoding: 8bit @@ -122,6 +126,8 @@ Content-Transfer-Encoding: 8bit file3 | 4 ++++ 3 files changed, 9 insertions(+), 0 deletions(-) create mode 100644 file3 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" Content-Transfer-Encoding: 8bit diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ index 95e9ea4c59..18a1110def 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ @@ -19,6 +19,8 @@ This is the second commit. file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" Content-Transfer-Encoding: 8bit @@ -75,6 +77,8 @@ Content-Transfer-Encoding: 8bit file1 | 3 +++ 2 files changed, 5 insertions(+), 0 deletions(-) create mode 100644 file1 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" Content-Transfer-Encoding: 8bit diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ index b8e81e1552..4f258b8858 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ @@ -19,6 +19,8 @@ This is the second commit. file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" Content-Transfer-Encoding: 8bit diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..side b/t/t4013/diff.format-patch_--inline_--stdout_initial..side index 86ae923d71..e86dce69a3 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..side +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..side @@ -17,6 +17,8 @@ Content-Transfer-Encoding: 8bit file3 | 4 ++++ 3 files changed, 9 insertions(+), 0 deletions(-) create mode 100644 file3 + + --------------g-i-t--v-e-r-s-i-o-n Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" Content-Transfer-Encoding: 8bit -- cgit v1.2.1 From 940208a771066229bc6a486f6a058e332b71cfe4 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Wed, 30 Jul 2008 01:16:58 +0200 Subject: builtin-help: make some internal functions available to other builtins Make load_command_list() capable of filtering for a given prefix and loading into a pair of "struct cmdnames" supplied by the caller. Make the static add_cmdname(), exclude_cmds() and is_in_cmdlist() functions non-static. Make list_commands() accept a custom title, and work from a pair of "struct cmdnames" supplied by the caller. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- Makefile | 1 + help.c | 77 ++++++++++++++++++++++++++++++++-------------------------------- help.h | 23 +++++++++++++++++++ 3 files changed, 63 insertions(+), 38 deletions(-) create mode 100644 help.h diff --git a/Makefile b/Makefile index 52c67c1a47..83d79afd9c 100644 --- a/Makefile +++ b/Makefile @@ -355,6 +355,7 @@ LIB_H += git-compat-util.h LIB_H += graph.h LIB_H += grep.h LIB_H += hash.h +LIB_H += help.h LIB_H += list-objects.h LIB_H += ll-merge.h LIB_H += log-tree.h diff --git a/help.c b/help.c index 3cb1962896..88c0d5b340 100644 --- a/help.c +++ b/help.c @@ -9,6 +9,7 @@ #include "common-cmds.h" #include "parse-options.h" #include "run-command.h" +#include "help.h" static struct man_viewer_list { struct man_viewer_list *next; @@ -300,18 +301,11 @@ static inline void mput_char(char c, unsigned int num) putchar(c); } -static struct cmdnames { - int alloc; - int cnt; - struct cmdname { - size_t len; - char name[1]; - } **names; -} main_cmds, other_cmds; +struct cmdnames main_cmds, other_cmds; -static void add_cmdname(struct cmdnames *cmds, const char *name, int len) +void add_cmdname(struct cmdnames *cmds, const char *name, int len) { - struct cmdname *ent = xmalloc(sizeof(*ent) + len); + struct cmdname *ent = xmalloc(sizeof(*ent) + len + 1); ent->len = len; memcpy(ent->name, name, len); @@ -342,7 +336,7 @@ static void uniq(struct cmdnames *cmds) cmds->cnt = j; } -static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) +void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) { int ci, cj, ei; int cmp; @@ -418,11 +412,11 @@ static int is_executable(const char *name) } static unsigned int list_commands_in_dir(struct cmdnames *cmds, - const char *path) + const char *path, + const char *prefix) { unsigned int longest = 0; - const char *prefix = "git-"; - int prefix_len = strlen(prefix); + int prefix_len; DIR *dir = opendir(path); struct dirent *de; struct strbuf buf = STRBUF_INIT; @@ -430,6 +424,9 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds, if (!dir) return 0; + if (!prefix) + prefix = "git-"; + prefix_len = strlen(prefix); strbuf_addf(&buf, "%s/", path); len = buf.len; @@ -460,7 +457,9 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds, return longest; } -static unsigned int load_command_list(void) +unsigned int load_command_list(const char *prefix, + struct cmdnames *main_cmds, + struct cmdnames *other_cmds) { unsigned int longest = 0; unsigned int len; @@ -469,7 +468,7 @@ static unsigned int load_command_list(void) const char *exec_path = git_exec_path(); if (exec_path) - longest = list_commands_in_dir(&main_cmds, exec_path); + longest = list_commands_in_dir(main_cmds, exec_path, prefix); if (!env_path) { fprintf(stderr, "PATH not set\n"); @@ -481,7 +480,7 @@ static unsigned int load_command_list(void) if ((colon = strchr(path, PATH_SEP))) *colon = 0; - len = list_commands_in_dir(&other_cmds, path); + len = list_commands_in_dir(other_cmds, path, prefix); if (len > longest) longest = len; @@ -491,36 +490,38 @@ static unsigned int load_command_list(void) } free(paths); - qsort(main_cmds.names, main_cmds.cnt, - sizeof(*main_cmds.names), cmdname_compare); - uniq(&main_cmds); + qsort(main_cmds->names, main_cmds->cnt, + sizeof(*main_cmds->names), cmdname_compare); + uniq(main_cmds); - qsort(other_cmds.names, other_cmds.cnt, - sizeof(*other_cmds.names), cmdname_compare); - uniq(&other_cmds); - exclude_cmds(&other_cmds, &main_cmds); + qsort(other_cmds->names, other_cmds->cnt, + sizeof(*other_cmds->names), cmdname_compare); + uniq(other_cmds); + exclude_cmds(other_cmds, main_cmds); return longest; } -static void list_commands(void) +void list_commands(const char *title, unsigned int longest, + struct cmdnames *main_cmds, struct cmdnames *other_cmds) { - unsigned int longest = load_command_list(); const char *exec_path = git_exec_path(); - if (main_cmds.cnt) { - printf("available git commands in '%s'\n", exec_path); - printf("----------------------------"); - mput_char('-', strlen(exec_path)); + if (main_cmds->cnt) { + printf("available %s in '%s'\n", title, exec_path); + printf("----------------"); + mput_char('-', strlen(title) + strlen(exec_path)); putchar('\n'); - pretty_print_string_list(&main_cmds, longest); + pretty_print_string_list(main_cmds, longest); putchar('\n'); } - if (other_cmds.cnt) { - printf("git commands available from elsewhere on your $PATH\n"); - printf("---------------------------------------------------\n"); - pretty_print_string_list(&other_cmds, longest); + if (other_cmds->cnt) { + printf("%s available from elsewhere on your $PATH\n", title); + printf("---------------------------------------"); + mput_char('-', strlen(title)); + putchar('\n'); + pretty_print_string_list(other_cmds, longest); putchar('\n'); } } @@ -542,7 +543,7 @@ void list_common_cmds_help(void) } } -static int is_in_cmdlist(struct cmdnames *c, const char *s) +int is_in_cmdlist(struct cmdnames *c, const char *s) { int i; for (i = 0; i < c->cnt; i++) @@ -553,7 +554,6 @@ static int is_in_cmdlist(struct cmdnames *c, const char *s) static int is_git_command(const char *s) { - load_command_list(); return is_in_cmdlist(&main_cmds, s) || is_in_cmdlist(&other_cmds, s); } @@ -698,8 +698,9 @@ int cmd_help(int argc, const char **argv, const char *prefix) builtin_help_usage, 0); if (show_all) { + unsigned int longest = load_command_list("git-", &main_cmds, &other_cmds); printf("usage: %s\n\n", git_usage_string); - list_commands(); + list_commands("git commands", longest, &main_cmds, &other_cmds); printf("%s\n", git_more_info_string); return 0; } diff --git a/help.h b/help.h new file mode 100644 index 0000000000..d614e5491b --- /dev/null +++ b/help.h @@ -0,0 +1,23 @@ +#ifndef HELP_H +#define HELP_H + +struct cmdnames { + int alloc; + int cnt; + struct cmdname { + size_t len; + char name[FLEX_ARRAY]; + } **names; +}; + +unsigned int load_command_list(const char *prefix, + struct cmdnames *main_cmds, + struct cmdnames *other_cmds); +void add_cmdname(struct cmdnames *cmds, const char *name, int len); +/* Here we require that excludes is a sorted list. */ +void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes); +int is_in_cmdlist(struct cmdnames *c, const char *s); +void list_commands(const char *title, unsigned int longest, + struct cmdnames *main_cmds, struct cmdnames *other_cmds); + +#endif /* HELP_H */ -- cgit v1.2.1 From 87091b495e8af0daf317c0d0e08ac3ead74a0bb9 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Wed, 30 Jul 2008 01:16:59 +0200 Subject: builtin-merge: allow using a custom strategy Allow using a custom strategy, as long as it's named git-merge-foo. The error handling is now done using is_git_command(). The list of available strategies is now shown by list_commands(). If an invalid strategy is supplied, like -s foobar, then git-merge would list all git-merge-* commands. This is not perfect, since for example git-merge-index is not a valid strategy. These are removed from the output by scanning the list of main commands; if the git-merge-foo command is listed in the all_strategy list, then it's shown, otherwise excluded. This does not exclude commands somewhere else in the PATH, where custom strategies are expected. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- builtin-merge.c | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/builtin-merge.c b/builtin-merge.c index e78fa18b3a..1f9389bfd7 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -22,6 +22,7 @@ #include "log-tree.h" #include "color.h" #include "rerere.h" +#include "help.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@ -77,7 +78,9 @@ static int option_parse_message(const struct option *opt, static struct strategy *get_strategy(const char *name) { int i; - struct strbuf err; + struct strategy *ret; + static struct cmdnames main_cmds, other_cmds; + static int longest; if (!name) return NULL; @@ -86,12 +89,37 @@ static struct strategy *get_strategy(const char *name) if (!strcmp(name, all_strategy[i].name)) return &all_strategy[i]; - strbuf_init(&err, 0); - for (i = 0; i < ARRAY_SIZE(all_strategy); i++) - strbuf_addf(&err, " %s", all_strategy[i].name); - fprintf(stderr, "Could not find merge strategy '%s'.\n", name); - fprintf(stderr, "Available strategies are:%s.\n", err.buf); - exit(1); + if (!longest) { + struct cmdnames not_strategies; + + memset(&main_cmds, 0, sizeof(struct cmdnames)); + memset(&other_cmds, 0, sizeof(struct cmdnames)); + memset(¬_strategies, 0, sizeof(struct cmdnames)); + longest = load_command_list("git-merge-", &main_cmds, + &other_cmds); + for (i = 0; i < main_cmds.cnt; i++) { + int j, found = 0; + struct cmdname *ent = main_cmds.names[i]; + for (j = 0; j < ARRAY_SIZE(all_strategy); j++) + if (!strncmp(ent->name, all_strategy[j].name, ent->len) + && !all_strategy[j].name[ent->len]) + found = 1; + if (!found) + add_cmdname(¬_strategies, ent->name, ent->len); + exclude_cmds(&main_cmds, ¬_strategies); + } + } + if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) { + + fprintf(stderr, "Could not find merge strategy '%s'.\n\n", name); + list_commands("strategies", longest, &main_cmds, &other_cmds); + exit(1); + } + + ret = xmalloc(sizeof(struct strategy)); + memset(ret, 0, sizeof(struct strategy)); + ret->name = xstrdup(name); + return ret; } static void append_strategy(struct strategy *s) -- cgit v1.2.1 From 1b1d78fe77d676bb79a9272ff6f3517697811990 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Wed, 30 Jul 2008 01:17:00 +0200 Subject: Add a new test for using a custom merge strategy Testing is done by creating a simple git-merge-theirs strategy which is the opposite of ours. Using this in real merges is not recommended but it's perfect for our testing needs. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- t/t7606-merge-custom.sh | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100755 t/t7606-merge-custom.sh diff --git a/t/t7606-merge-custom.sh b/t/t7606-merge-custom.sh new file mode 100755 index 0000000000..07e0fc7974 --- /dev/null +++ b/t/t7606-merge-custom.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +test_description='git-merge + +Testing a custom strategy.' + +. ./test-lib.sh + +cat >git-merge-theirs <c0.c && + git add c0.c && + git commit -m c0 && + git tag c0 && + echo c1 >c1.c && + git add c1.c && + git commit -m c1 && + git tag c1 && + git reset --hard c0 && + echo c2 >c2.c && + git add c2.c && + git commit -m c2 && + git tag c2 +' + +test_expect_success 'merge c2 with a custom strategy' ' + git reset --hard c1 && + git merge -s theirs c2 && + test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" && + test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" && + test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" && + test "$(git rev-parse c2^{tree})" = "$(git rev-parse HEAD^{tree})" && + git diff --exit-code && + test -f c0.c && + test ! -f c1.c && + test -f c2.c +' + +test_done -- cgit v1.2.1 From dce61e728b89611109e76cbc9fa897fe49d7869d Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Wed, 30 Jul 2008 01:17:01 +0200 Subject: Add a second testcase for handling invalid strategies in git-merge This one tests '-s index' which is interesting because git-merge-index is an existing git command but it is not a valid strategy. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- t/t7600-merge.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 5eeb6c2b27..0329aee2cd 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -230,6 +230,10 @@ test_expect_success 'test option parsing' ' test_must_fail git merge ' +test_expect_success 'reject non-strategy with a git-merge-foo name' ' + test_must_fail git merge -s index c1 +' + test_expect_success 'merge c0 with c1' ' git reset --hard c0 && git merge c1 && -- cgit v1.2.1 From a7a66921772ca5e609d283b0b73b8dd446a2a506 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Jul 2008 23:51:41 -0700 Subject: merge-base-many: add trivial tests based on the documentation Signed-off-by: Junio C Hamano --- t/t6010-merge-base.sh | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh index b6e57b2426..04e4b7c5c2 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -108,4 +108,52 @@ test_expect_success 'compute merge-base (all)' \ 'MB=$(git merge-base --all PL PR) && expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"' +# Another set to demonstrate base between one commit and a merge +# in the documentation. + +test_expect_success 'merge-base for octopus-step (setup)' ' + test_tick && git commit --allow-empty -m root && git tag MMR && + test_tick && git commit --allow-empty -m 1 && git tag MM1 && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m A && git tag MMA && + git checkout MM1 && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m B && git tag MMB && + git checkout MMR && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m C && git tag MMC +' + +test_expect_success 'merge-base A B C' ' + MB=$(git merge-base --all MMA MMB MMC) && + MM1=$(git rev-parse --verify MM1) && + test "$MM1" = "$MB" +' + +test_expect_success 'criss-cross merge-base for octopus-step (setup)' ' + git reset --hard MMR && + test_tick && git commit --allow-empty -m 1 && git tag CC1 && + git reset --hard E && + test_tick && git commit --allow-empty -m 2 && git tag CC2 && + test_tick && git merge -s ours CC1 && + test_tick && git commit --allow-empty -m o && + test_tick && git commit --allow-empty -m B && git tag CCB && + git reset --hard CC1 && + test_tick && git merge -s ours CC2 && + test_tick && git commit --allow-empty -m A && git tag CCA +' + +test_expect_success 'merge-base B A^^ A^^2' ' + MB0=$(git merge-base --all CCB CCA^^ CCA^^2 | sort) && + MB1=$(git rev-parse CC1 CC2 | sort) && + test "$MB0" = "$MB1" +' + test_done -- cgit v1.2.1 From ae7c5dcef92d46cfc8987fde2c264614fe475bd1 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 27 Jul 2008 00:52:54 +0400 Subject: Support copy and rename detection in fast-export. Although it does not matter for Git itself, tools that export to systems that explicitly track copies and renames can benefit from such information. This patch makes fast-export output correct action logs when -M or -C are enabled. Signed-off-by: Alexander Gavrilov Signed-off-by: Junio C Hamano --- Documentation/git-fast-export.txt | 9 ++++++++ builtin-fast-export.c | 28 ++++++++++++++++++++++-- t/t9301-fast-export.sh | 46 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt index 4956964d85..b974e2115b 100644 --- a/Documentation/git-fast-export.txt +++ b/Documentation/git-fast-export.txt @@ -36,6 +36,15 @@ when encountering a signed tag. With 'strip', the tags will be made unsigned, with 'verbatim', they will be silently exported and with 'warn', they will be exported, but you will see a warning. +-M:: +-C:: + Perform move and/or copy detection, as described in the + linkgit:git-diff[1] manual page, and use it to generate + rename and copy commands in the output dump. ++ +Note that earlier versions of this command did not complain and +produced incorrect results if you gave these options. + --export-marks=:: Dumps the internal marks table to when complete. Marks are written one per line as `:markid SHA-1`. Only marks diff --git a/builtin-fast-export.c b/builtin-fast-export.c index e508ced6ba..070971616d 100644 --- a/builtin-fast-export.c +++ b/builtin-fast-export.c @@ -132,10 +132,27 @@ static void show_filemodify(struct diff_queue_struct *q, { int i; for (i = 0; i < q->nr; i++) { + struct diff_filespec *ospec = q->queue[i]->one; struct diff_filespec *spec = q->queue[i]->two; - if (is_null_sha1(spec->sha1)) + + switch (q->queue[i]->status) { + case DIFF_STATUS_DELETED: printf("D %s\n", spec->path); - else { + break; + + case DIFF_STATUS_COPIED: + case DIFF_STATUS_RENAMED: + printf("%c \"%s\" \"%s\"\n", q->queue[i]->status, + ospec->path, spec->path); + + if (!hashcmp(ospec->sha1, spec->sha1) && + ospec->mode == spec->mode) + break; + /* fallthrough */ + + case DIFF_STATUS_TYPE_CHANGED: + case DIFF_STATUS_MODIFIED: + case DIFF_STATUS_ADDED: /* * Links refer to objects in another repositories; * output the SHA-1 verbatim. @@ -148,6 +165,13 @@ static void show_filemodify(struct diff_queue_struct *q, printf("M %06o :%d %s\n", spec->mode, get_object_mark(object), spec->path); } + break; + + default: + die("Unexpected comparison status '%c' for %s, %s", + q->queue[i]->status, + ospec->path ? ospec->path : "none", + spec->path ? spec->path : "none"); } } } diff --git a/t/t9301-fast-export.sh b/t/t9301-fast-export.sh index 8324f6e3bd..c19b4a2bab 100755 --- a/t/t9301-fast-export.sh +++ b/t/t9301-fast-export.sh @@ -185,4 +185,50 @@ test_expect_success 'submodule fast-export | fast-import' ' ' +export GIT_AUTHOR_NAME='A U Thor' +export GIT_COMMITTER_NAME='C O Mitter' + +test_expect_success 'setup copies' ' + + git config --unset i18n.commitencoding && + git checkout -b copy rein && + git mv file file3 && + git commit -m move1 && + test_tick && + cp file2 file4 && + git add file4 && + git mv file2 file5 && + git commit -m copy1 && + test_tick && + cp file3 file6 && + git add file6 && + git commit -m copy2 && + test_tick && + echo more text >> file6 && + echo even more text >> file6 && + git add file6 && + git commit -m modify && + test_tick && + cp file6 file7 && + echo test >> file7 && + git add file7 && + git commit -m copy_modify + +' + +test_expect_success 'fast-export -C -C | fast-import' ' + + ENTRY=$(git rev-parse --verify copy) && + rm -rf new && + mkdir new && + git --git-dir=new/.git init && + git fast-export -C -C --signed-tags=strip --all > output && + grep "^C \"file6\" \"file7\"\$" output && + cat output | + (cd new && + git fast-import && + test $ENTRY = $(git rev-parse --verify refs/heads/copy)) + +' + test_done -- cgit v1.2.1 From bd2574ca69331d33429f8806ce189b2941e490e7 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Tue, 29 Jul 2008 22:12:22 +0200 Subject: Advertise the ability to abort a commit This treats aborting a commit more like a feature. Signed-off-by: Anders Melchiorsen Signed-off-by: Junio C Hamano --- builtin-commit.c | 1 + t/t7502-commit.sh | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/builtin-commit.c b/builtin-commit.c index 9a11ca0bcd..f7c053a010 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -555,6 +555,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix) fprintf(fp, "\n" "# Please enter the commit message for your changes.\n" + "# To abort the commit, use an empty commit message.\n" "# (Comment lines starting with '#' will "); if (cleanup_mode == CLEANUP_ALL) fprintf(fp, "not be included)\n"); diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index 4f2682ea80..f1112639b6 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -142,6 +142,7 @@ test_expect_success 'cleanup commit messages (strip,-F)' ' echo "sample # Please enter the commit message for your changes. +# To abort the commit, use an empty commit message. # (Comment lines starting with '#' will not be included)" >expect test_expect_success 'cleanup commit messages (strip,-F,-e)' ' @@ -149,7 +150,7 @@ test_expect_success 'cleanup commit messages (strip,-F,-e)' ' echo >>negative && { echo;echo sample;echo; } >text && git commit -e -F text -a && - head -n 4 .git/COMMIT_EDITMSG >actual && + head -n 5 .git/COMMIT_EDITMSG >actual && test_cmp expect actual ' @@ -162,7 +163,7 @@ test_expect_success 'author different from committer' ' echo >>negative && git commit -e -m "sample" - head -n 7 .git/COMMIT_EDITMSG >actual && + head -n 8 .git/COMMIT_EDITMSG >actual && test_cmp expect actual ' @@ -181,7 +182,7 @@ test_expect_success 'committer is automatic' ' # must fail because there is no change test_must_fail git commit -e -m "sample" ) && - head -n 8 .git/COMMIT_EDITMSG | \ + head -n 9 .git/COMMIT_EDITMSG | \ sed "s/^# Committer: .*/# Committer:/" >actual && test_cmp expect actual ' -- cgit v1.2.1 From c70115b4b1a8c197b9b829b1fd15d578a3ffcdc3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Jul 2008 01:13:44 -0700 Subject: Teach gitlinks to ie_modified() and ce_modified_check_fs() The ie_modified() function is the workhorse for refresh_cache_entry(), i.e. checking if an index entry that is stat-dirty actually has changes. After running quicker check to compare cached stat information with results from the latest lstat(2) to answer "has modification" early, the code goes on to check if there really is a change by comparing the staged data with what is on the filesystem by asking ce_modified_check_fs(). However, this function always said "no change" for any gitlinks that has a directory at the corresponding path. This made ie_modified() to miss actual changes in the subproject. The patch fixes this first by modifying an existing short-circuit logic before calling the ce_modified_check_fs() function. It knows that for any filesystem entity to which ie_match_stat() says its data has changed, if its cached size is nonzero then the contents cannot match, which is a correct optimization only for blob objects. We teach gitlink objects to this special case, as we already know that any gitlink that ie_match_stat() says is modified is indeed modified at this point in the codepath. With the above change, we could leave ce_modified_check_fs() broken, but it also futureproofs the code by teaching it to use ce_compare_gitlink(), instead of assuming (incorrectly) that any directory is unchanged. Originally noticed by Alex Riesen on Cygwin. Signed-off-by: Junio C Hamano --- read-cache.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/read-cache.c b/read-cache.c index 1cae361c6c..2c03ec3069 100644 --- a/read-cache.c +++ b/read-cache.c @@ -147,7 +147,7 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st) break; case S_IFDIR: if (S_ISGITLINK(ce->ce_mode)) - return 0; + return ce_compare_gitlink(ce) ? DATA_CHANGED : 0; default: return TYPE_CHANGED; } @@ -187,6 +187,7 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) changed |= TYPE_CHANGED; break; case S_IFGITLINK: + /* We ignore most of the st_xxx fields for gitlinks */ if (!S_ISDIR(st->st_mode)) changed |= TYPE_CHANGED; else if (ce_compare_gitlink(ce)) @@ -293,11 +294,22 @@ int ie_modified(const struct index_state *istate, if (changed & (MODE_CHANGED | TYPE_CHANGED)) return changed; - /* Immediately after read-tree or update-index --cacheinfo, - * the length field is zero. For other cases the ce_size - * should match the SHA1 recorded in the index entry. + /* + * Immediately after read-tree or update-index --cacheinfo, + * the length field is zero, as we have never even read the + * lstat(2) information once, and we cannot trust DATA_CHANGED + * returned by ie_match_stat() which in turn was returned by + * ce_match_stat_basic() to signal that the filesize of the + * blob changed. We have to actually go to the filesystem to + * see if the contents match, and if so, should answer "unchanged". + * + * The logic does not apply to gitlinks, as ce_match_stat_basic() + * already has checked the actual HEAD from the filesystem in the + * subproject. If ie_match_stat() already said it is different, + * then we know it is. */ - if ((changed & DATA_CHANGED) && ce->ce_size != 0) + if ((changed & DATA_CHANGED) && + (S_ISGITLINK(ce->ce_mode) || ce->ce_size != 0)) return changed; changed_fs = ce_modified_check_fs(ce, st); @@ -1326,6 +1338,11 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce) * falsely clean entry due to touch-update-touch race, so we leave * everything else as they are. We are called for entries whose * ce_mtime match the index file mtime. + * + * Note that this actually does not do much for gitlinks, for + * which ce_match_stat_basic() always goes to the actual + * contents. The caller checks with is_racy_timestamp() which + * always says "no" for gitlinks, so we are not called for them ;-) */ struct stat st; -- cgit v1.2.1 From 4fac1d3a98bb86d855b706d7ce6a5069b9f687e8 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Fri, 25 Jul 2008 12:41:26 +0200 Subject: archive: allow --exec and --remote without equal sign Convert git archive to parse_options(). The parameters --remote and --exec are still handled by their special parser. Define them anyway in order for them to show up in the usage notice. Note: in a command like "git archive --prefix --remote=a/ HEAD", the string "--remote=a/" will be interpreted as a remote option, not a prefix, because that special parser sees it first. If one needs such a strange prefix, it needs to be specified like this: "git archive --prefix=--remote=a/ HEAD" (with an equal sign). Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive.c | 110 ++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 68 insertions(+), 42 deletions(-) diff --git a/archive.c b/archive.c index f834b5f51f..5b40e261f1 100644 --- a/archive.c +++ b/archive.c @@ -3,9 +3,15 @@ #include "tree-walk.h" #include "attr.h" #include "archive.h" - -static const char archive_usage[] = \ -"git archive --format= [--prefix=/] [--verbose] [] [path...]"; +#include "parse-options.h" + +static char const * const archive_usage[] = { + "git archive [options] [path...]", + "git archive --list", + "git archive --remote [--exec ] [options] [path...]", + "git archive --remote [--exec ] --list", + NULL +}; #define USES_ZLIB_COMPRESSION 1 @@ -175,6 +181,9 @@ static const struct archiver *lookup_archiver(const char *name) { int i; + if (!name) + return NULL; + for (i = 0; i < ARRAY_SIZE(archivers); i++) { if (!strcmp(name, archivers[i].name)) return &archivers[i]; @@ -232,51 +241,70 @@ static void parse_treeish_arg(const char **argv, ar_args->time = archive_time; } +#define OPT__COMPR(s, v, h, p) \ + { OPTION_SET_INT, (s), NULL, (v), NULL, (h), \ + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, (p) } +#define OPT__COMPR_HIDDEN(s, v, p) \ + { OPTION_SET_INT, (s), NULL, (v), NULL, "", \ + PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN, NULL, (p) } + static int parse_archive_args(int argc, const char **argv, const struct archiver **ar, struct archiver_args *args) { const char *format = "tar"; - const char *base = ""; + const char *base = NULL; + const char *remote = NULL; + const char *exec = NULL; int compression_level = -1; int verbose = 0; int i; - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { - for (i = 0; i < ARRAY_SIZE(archivers); i++) - printf("%s\n", archivers[i].name); - exit(0); - } - if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) { - verbose = 1; - continue; - } - if (!prefixcmp(arg, "--format=")) { - format = arg + 9; - continue; - } - if (!prefixcmp(arg, "--prefix=")) { - base = arg + 9; - continue; - } - if (!strcmp(arg, "--")) { - i++; - break; - } - if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0') { - compression_level = arg[1] - '0'; - continue; - } - if (arg[0] == '-') - die("Unknown argument: %s", arg); - break; + int list = 0; + struct option opts[] = { + OPT_GROUP(""), + OPT_STRING(0, "format", &format, "fmt", "archive format"), + OPT_STRING(0, "prefix", &base, "prefix", + "prepend prefix to each pathname in the archive"), + OPT__VERBOSE(&verbose), + OPT__COMPR('0', &compression_level, "store only", 0), + OPT__COMPR('1', &compression_level, "compress faster", 1), + OPT__COMPR_HIDDEN('2', &compression_level, 2), + OPT__COMPR_HIDDEN('3', &compression_level, 3), + OPT__COMPR_HIDDEN('4', &compression_level, 4), + OPT__COMPR_HIDDEN('5', &compression_level, 5), + OPT__COMPR_HIDDEN('6', &compression_level, 6), + OPT__COMPR_HIDDEN('7', &compression_level, 7), + OPT__COMPR_HIDDEN('8', &compression_level, 8), + OPT__COMPR('9', &compression_level, "compress better", 9), + OPT_GROUP(""), + OPT_BOOLEAN('l', "list", &list, + "list supported archive formats"), + OPT_GROUP(""), + OPT_STRING(0, "remote", &remote, "repo", + "retrieve the archive from remote repository "), + OPT_STRING(0, "exec", &exec, "cmd", + "path to the remote git-upload-archive command"), + OPT_END() + }; + + argc = parse_options(argc, argv, opts, archive_usage, 0); + + if (remote) + die("Unexpected option --remote"); + if (exec) + die("Option --exec can only be used together with --remote"); + + if (!base) + base = ""; + + if (list) { + for (i = 0; i < ARRAY_SIZE(archivers); i++) + printf("%s\n", archivers[i].name); + exit(0); } /* We need at least one parameter -- tree-ish */ - if (argc - 1 < i) - usage(archive_usage); + if (argc < 1) + usage_with_options(archive_usage, opts); *ar = lookup_archiver(format); if (!*ar) die("Unknown archive format '%s'", format); @@ -294,7 +322,7 @@ static int parse_archive_args(int argc, const char **argv, args->base = base; args->baselen = strlen(base); - return i; + return argc; } int write_archive(int argc, const char **argv, const char *prefix, @@ -302,13 +330,11 @@ int write_archive(int argc, const char **argv, const char *prefix, { const struct archiver *ar = NULL; struct archiver_args args; - int tree_idx; - tree_idx = parse_archive_args(argc, argv, &ar, &args); + argc = parse_archive_args(argc, argv, &ar, &args); if (setup_prefix && prefix == NULL) prefix = setup_git_directory(); - argv += tree_idx; parse_treeish_arg(argv, &args, prefix); parse_pathspec_arg(argv + 1, &args); -- cgit v1.2.1 From 81b237d5db946b4196225712a37639952787c959 Mon Sep 17 00:00:00 2001 From: Cesar Eduardo Barros Date: Sat, 26 Jul 2008 21:46:00 -0300 Subject: Documentation/git-rev-parse.txt: update for new git-describe output format Signed-off-by: Cesar Eduardo Barros Signed-off-by: Junio C Hamano --- Documentation/git-rev-parse.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 5c936693d3..2921da320d 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -155,8 +155,9 @@ blobs contained in a commit. name the same commit object if there are no other object in your repository whose object name starts with dae86e. -* An output from 'git-describe'; i.e. a closest tag, followed by a - dash, a `g`, and an abbreviated object name. +* An output from 'git-describe'; i.e. a closest tag, optionally + followed by a dash and a number of commits, followed by a dash, a + `g`, and an abbreviated object name. * A symbolic ref name. E.g. 'master' typically means the commit object referenced by $GIT_DIR/refs/heads/master. If you -- cgit v1.2.1 From c5dc9a28298afb75d2be6b212d420fc89c258aa0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Jul 2008 13:47:22 -0700 Subject: git-merge-octopus: use (merge-base A (merge B C D E...)) for stepwise merge Suppose you have this topology, and you are trying to make an octopus across A, B and C (you are at C and merging A and B into your branch). The protoccol between "git merge" and merge strategies is for the former to pass common ancestor(s), '--' and then commits being merged. git-merge-octopus does not produce the final merge in one-go. It iteratively produces pairwise merges. So the first step might be to come up with a merge between B and C: o---o---o---o---C / : / o---o---o---B..(M) / / ---1---2---o---o---o---A and for that, "1" is used as the merge base, not because it is the base across A, B and C but because it is the base between B and C. For this merge, A does not matter. I drew M in parentheses and lines between B and C to it in dotted line because we actually do _not_ create a real commit --- the only thing we need is a tree object, in order to proceed to the next step. Then the final merge result is obtained by merging tree of (M) and A using their common ancestor. For that, we _could_ still use "1" as the merge base. But if you imagine a case where you started from A and M, you would _not_ pick "1" as the merge base; you would rather use "2" which is a better base for this merge. That is why git-merge-octopus ignores the merge base given by "merge" but computes its own. The comment at the end of git-merge-octopus talks about "merge reference commit", that we used to update it to common found in this round, and that that updating was pointless. After the first round of merge to produce the tree for M (but without actually creating the commit object M itself), in order to figure out the merge base used to merge that with A in the second round, we used to use A and "1" (which was merge base between B and C). That was pointless --- "merge-base A 1" is guaranteed to give a base that is no better than either "merge-base A B" or "merge-base A C". So the current code keeps using the original head (iow, MRC=C, because in this case we are starting from C and merging B and then A into it). This trickerly was necessary only because we avoided creating the extra merge commit object M. Side note. An alternative implementation could have been to actually record it as a real merge commit M, and then let the two-commit merge-base compute the base between A and M when merging A to the result of the previous round, but we avoided creating M, at the expense of potentially using suboptimal base in the later rounds. But we do not have to be that pessimistic. We can instead accumulate the commits we have merged so far in MRC, and have merge_bases_many() compute the merge base for the new head being merged and the heads we have processed so far, which can give a better base than what we currently do. Signed-off-by: Junio C Hamano --- git-merge-octopus.sh | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/git-merge-octopus.sh b/git-merge-octopus.sh index 645e1147dc..1dadbb4966 100755 --- a/git-merge-octopus.sh +++ b/git-merge-octopus.sh @@ -61,7 +61,7 @@ do exit 2 esac - common=$(git merge-base --all $MRC $SHA1) || + common=$(git merge-base --all $SHA1 $MRC) || die "Unable to find common commit with $SHA1" case "$LF$common$LF" in @@ -100,14 +100,7 @@ do next=$(git write-tree 2>/dev/null) fi - # We have merged the other branch successfully. Ideally - # we could implement OR'ed heads in merge-base, and keep - # a list of commits we have merged so far in MRC to feed - # them to merge-base, but we approximate it by keep using - # the current MRC. We used to update it to $common, which - # was incorrectly doing AND'ed merge-base here, which was - # unneeded. - + MRC="$MRC $SHA1" MRT=$next done -- cgit v1.2.1 From 9b6bf4d575e216db9240907940e9f6c619877735 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 30 Jul 2008 01:12:19 -0700 Subject: Fix merge name generation in "merge in C" When merging an early part of a branch, e.g. "git merge xyzzy~20", we were supposed to say "branch 'xyzzy' (early part)", but it incorrectly said "branch 'refs/heads/xy' (early part)" instead. The logic was supposed to first strip away "~20" part to make sure that what follows "~" is a non-zero posint, prefix it with "refs/heads/" and ask resolve_ref() if it is a ref. If it is, then we know xyzzy was a branch, and we can give the correct message. However, there were a few bugs. First of all, the logic to build this "true branch refname" did not count the characters correctly. At this point of the code, "len" is the number of trailing, non-name part of the given extended SHA-1 expression given by the user, i.e. number of bytes in "~20" in the above example. In addition, the message forgot to skip "refs/heads/" it prefixed from the output. Signed-off-by: Junio C Hamano --- builtin-merge.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin-merge.c b/builtin-merge.c index e78fa18b3a..dde0c7ed33 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -396,12 +396,12 @@ static void merge_name(const char *remote, struct strbuf *msg) struct strbuf truname = STRBUF_INIT; strbuf_addstr(&truname, "refs/heads/"); strbuf_addstr(&truname, remote); - strbuf_setlen(&truname, len+11); + strbuf_setlen(&truname, truname.len - len); if (resolve_ref(truname.buf, buf_sha, 0, 0)) { strbuf_addf(msg, "%s\t\tbranch '%s'%s of .\n", sha1_to_hex(remote_head->sha1), - truname.buf, + truname.buf + 11, (early ? " (early part)" : "")); return; } -- cgit v1.2.1 From 5354a56fe70420c147f930e0f7f1decbae685d19 Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Wed, 30 Jul 2008 13:48:33 -0400 Subject: Replace uses of "git-var" with "git var" Signed-off-by: Todd Zullinger Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 2 +- contrib/examples/git-commit.sh | 6 +++--- contrib/examples/git-tag.sh | 2 +- git-am.sh | 2 +- ident.c | 2 +- perl/Git.pm | 2 +- var.c | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index afbb294a7f..e2437f30ca 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -56,7 +56,7 @@ The --cc option must be repeated for each user you want on the cc list. --from:: Specify the sender of the emails. This will default to - the value GIT_COMMITTER_IDENT, as returned by "git-var -l". + the value GIT_COMMITTER_IDENT, as returned by "git var -l". The user will still be prompted to confirm this entry. --in-reply-to:: diff --git a/contrib/examples/git-commit.sh b/contrib/examples/git-commit.sh index 2c4a4062a5..5c72f655c7 100755 --- a/contrib/examples/git-commit.sh +++ b/contrib/examples/git-commit.sh @@ -443,7 +443,7 @@ fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG case "$signoff" in t) - sign=$(git-var GIT_COMMITTER_IDENT | sed -e ' + sign=$(git var GIT_COMMITTER_IDENT | sed -e ' s/>.*/>/ s/^/Signed-off-by: / ') @@ -535,8 +535,8 @@ esac case "$no_edit" in '') - git-var GIT_AUTHOR_IDENT > /dev/null || die - git-var GIT_COMMITTER_IDENT > /dev/null || die + git var GIT_AUTHOR_IDENT > /dev/null || die + git var GIT_COMMITTER_IDENT > /dev/null || die git_editor "$GIT_DIR/COMMIT_EDITMSG" ;; esac diff --git a/contrib/examples/git-tag.sh b/contrib/examples/git-tag.sh index e9f3a228af..2c15bc955b 100755 --- a/contrib/examples/git-tag.sh +++ b/contrib/examples/git-tag.sh @@ -164,7 +164,7 @@ git check-ref-format "tags/$name" || object=$(git rev-parse --verify --default HEAD "$@") || exit 1 type=$(git cat-file -t $object) || exit 1 -tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1 +tagger=$(git var GIT_COMMITTER_IDENT) || exit 1 test -n "$username" || username=$(git config user.signingkey) || diff --git a/git-am.sh b/git-am.sh index 6aa819280e..8f91a97eb3 100755 --- a/git-am.sh +++ b/git-am.sh @@ -291,7 +291,7 @@ fi ws=`cat "$dotest/whitespace"` if test "$(cat "$dotest/sign")" = t then - SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e ' + SIGNOFF=`git var GIT_COMMITTER_IDENT | sed -e ' s/>.*/>/ s/^/Signed-off-by: /' ` diff --git a/ident.c b/ident.c index b35504a8d2..09cf0c95c9 100644 --- a/ident.c +++ b/ident.c @@ -204,7 +204,7 @@ const char *fmt_ident(const char *name, const char *email, if ((warn_on_no_name || error_on_no_name) && name == git_default_name && env_hint) { fprintf(stderr, env_hint, au_env, co_env); - env_hint = NULL; /* warn only once, for "git-var -l" */ + env_hint = NULL; /* warn only once, for "git var -l" */ } if (error_on_no_name) die("empty ident %s <%s> not allowed", name, email); diff --git a/perl/Git.pm b/perl/Git.pm index d99e778200..087d3d0e82 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -730,7 +730,7 @@ This suite of functions retrieves and parses ident information, as stored in the commit and tag objects or produced by C (thus C can be either I or I; case is insignificant). -The C method retrieves the ident information from C +The C method retrieves the ident information from C and either returns it as a scalar string or as an array with the fields parsed. Alternatively, it can take a prepared ident string (e.g. from the commit object) and just parse it. diff --git a/var.c b/var.c index 724ba87a7c..f1eb314e89 100644 --- a/var.c +++ b/var.c @@ -5,7 +5,7 @@ */ #include "cache.h" -static const char var_usage[] = "git-var [-l | ]"; +static const char var_usage[] = "git var [-l | ]"; struct git_var { const char *name; -- cgit v1.2.1 From c4aca9ccda51badb672ab70099863072d1567267 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 30 Jul 2008 12:53:45 -0700 Subject: Fix test-parse-options "integer" test OPT_INTEGER() works on an integer, not on an unsigned long. On a big endian architecture with long larger than int, integer test gives bogus results because of this bug. Reported by H.Merijn Brand in HP-UX 64-bit environment. Signed-off-by: Junio C Hamano --- t/t0040-parse-options.sh | 11 ++++++++++- test-parse-options.c | 8 +++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index 03dbe00102..e38241c80a 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -47,6 +47,7 @@ test_expect_success 'test help' ' cat > expect << EOF boolean: 2 integer: 1729 +timestamp: 0 string: 123 abbrev: 7 verbose: 2 @@ -63,6 +64,7 @@ test_expect_success 'short options' ' cat > expect << EOF boolean: 2 integer: 1729 +timestamp: 0 string: 321 abbrev: 10 verbose: 2 @@ -88,6 +90,7 @@ test_expect_success 'missing required value' ' cat > expect << EOF boolean: 1 integer: 13 +timestamp: 0 string: 123 abbrev: 7 verbose: 0 @@ -108,6 +111,7 @@ test_expect_success 'intermingled arguments' ' cat > expect << EOF boolean: 0 integer: 2 +timestamp: 0 string: (not set) abbrev: 7 verbose: 0 @@ -135,6 +139,7 @@ test_expect_success 'ambiguously abbreviated option' ' cat > expect << EOF boolean: 0 integer: 0 +timestamp: 0 string: 123 abbrev: 7 verbose: 0 @@ -161,6 +166,7 @@ test_expect_success 'detect possible typos' ' cat > expect < expect < expect < expect <"), + OPT_DATE('t', NULL, ×tamp, "get timestamp of