diff options
-rw-r--r-- | Documentation/config.txt | 7 | ||||
-rw-r--r-- | Documentation/git-cvsserver.txt | 5 | ||||
-rw-r--r-- | builtin-verify-tag.c | 4 | ||||
-rwxr-xr-x | contrib/completion/git-completion.bash | 3 | ||||
-rwxr-xr-x | contrib/fast-import/git-p4 | 28 | ||||
-rw-r--r-- | diff-lib.c | 86 | ||||
-rwxr-xr-x | git-clone.sh | 6 | ||||
-rwxr-xr-x | git-cvsserver.perl | 201 | ||||
-rwxr-xr-x | git-svn.perl | 2 | ||||
-rw-r--r-- | git.c | 3 | ||||
-rw-r--r-- | help.c | 1 | ||||
-rwxr-xr-x | t/t2201-add-update-typechange.sh | 140 | ||||
-rwxr-xr-x | t/t7004-tag.sh | 15 | ||||
-rwxr-xr-x | t/t9400-git-cvsserver-server.sh | 50 |
14 files changed, 457 insertions, 94 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt index 3017d640cf..04c01c5fdc 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -661,6 +661,13 @@ gitcvs.dbuser, gitcvs.dbpass:: 'gitcvs.dbuser' supports variable substitution (see linkgit:git-cvsserver[1] for details). +gitcvs.dbTableNamePrefix:: + Database table name prefix. Prepended to the names of any + database tables used, allowing a single database to be used + for several repositories. Supports variable substitution (see + linkgit:git-cvsserver[1] for details). Any non-alphabetic + characters will be replaced with underscores. + All gitcvs variables except for 'gitcvs.allbinary' can also be specified as 'gitcvs.<access_method>.<varname>' (where 'access_method' is one of "ext" and "pserver") to make them apply only for the given diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index d3e99931d7..9cec8021b8 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -227,6 +227,11 @@ gitcvs.dbpass:: Database password. Only useful if setting `dbdriver`, since SQLite has no concept of database passwords. +gitcvs.dbTableNamePrefix:: + Database table name prefix. Supports variable substitution + (see below). Any non-alphabetic characters will be replaced + with underscores. + All variables can also be set per access method, see <<configaccessmethod,above>>. Variable substitution diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c index f3ef11fa2d..db81496b46 100644 --- a/builtin-verify-tag.c +++ b/builtin-verify-tag.c @@ -46,8 +46,10 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose) gpg.argv = args_gpg; gpg.in = -1; args_gpg[2] = path; - if (start_command(&gpg)) + if (start_command(&gpg)) { + unlink(path); return error("could not run gpg."); + } write_in_full(gpg.in, buf, len); close(gpg.in); diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 5046f69934..791e30f6d7 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -999,7 +999,8 @@ _git_config () gitcvs.enabled gitcvs.logfile gitcvs.allbinary - gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dvpass + gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dbpass + gitcvs.dbtablenameprefix gc.packrefs gc.reflogexpire gc.reflogexpireunreachable diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3cb0330ec2..d8de9f6c25 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -90,11 +90,11 @@ def getP4OpenedType(file): # Returns the perforce file type for the given file. result = read_pipe("p4 opened %s" % file) - match = re.match(".*\((.+)\)$", result) + match = re.match(".*\((.+)\)\r?$", result) if match: return match.group(1) else: - die("Could not determine file type for %s" % file) + die("Could not determine file type for %s (result: '%s')" % (file, result)) def diffTreePattern(): # This is a simple generator for the diff tree regex pattern. This could be @@ -513,6 +513,8 @@ class P4Submit(Command): template = "" inFilesSection = False for line in read_pipe_lines("p4 change -o"): + if line.endswith("\r\n"): + line = line[:-2] + "\n" if inFilesSection: if line.startswith("\t"): # path starts and ends with a tab @@ -619,8 +621,6 @@ class P4Submit(Command): setP4ExecBit(f, mode) logMessage = extractLogMessageFromGitCommit(id) - if self.isWindows: - logMessage = logMessage.replace("\n", "\r\n") logMessage = logMessage.strip() template = self.prepareSubmitTemplate() @@ -631,23 +631,25 @@ class P4Submit(Command): del(os.environ["P4DIFF"]) diff = read_pipe("p4 diff -du ...") + newdiff = "" for newFile in filesToAdd: - diff += "==== new file ====\n" - diff += "--- /dev/null\n" - diff += "+++ %s\n" % newFile + newdiff += "==== new file ====\n" + newdiff += "--- /dev/null\n" + newdiff += "+++ %s\n" % newFile f = open(newFile, "r") for line in f.readlines(): - diff += "+" + line + newdiff += "+" + line f.close() - separatorLine = "######## everything below this line is just the diff #######" - if platform.system() == "Windows": - separatorLine += "\r" - separatorLine += "\n" + separatorLine = "######## everything below this line is just the diff #######\n" [handle, fileName] = tempfile.mkstemp() tmpFile = os.fdopen(handle, "w+") - tmpFile.write(submitTemplate + separatorLine + diff) + if self.isWindows: + submitTemplate = submitTemplate.replace("\n", "\r\n") + separatorLine = separatorLine.replace("\n", "\r\n") + newdiff = newdiff.replace("\n", "\r\n") + tmpFile.write(submitTemplate + separatorLine + diff + newdiff) tmpFile.close() defaultEditor = "vi" if platform.system() == "Windows": diff --git a/diff-lib.c b/diff-lib.c index 52dbac34a4..069e4507ae 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -10,6 +10,7 @@ #include "cache-tree.h" #include "path-list.h" #include "unpack-trees.h" +#include "refs.h" /* * diff-files @@ -333,6 +334,26 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv) } return run_diff_files(revs, options); } +/* + * See if work tree has an entity that can be staged. Return 0 if so, + * return 1 if not and return -1 if error. + */ +static int check_work_tree_entity(const struct cache_entry *ce, struct stat *st, char *symcache) +{ + if (lstat(ce->name, st) < 0) { + if (errno != ENOENT && errno != ENOTDIR) + return -1; + return 1; + } + if (has_symlink_leading_path(ce->name, symcache)) + return 1; + if (S_ISDIR(st->st_mode)) { + unsigned char sub[20]; + if (resolve_gitlink_ref(ce->name, "HEAD", sub)) + return 1; + } + return 0; +} int run_diff_files(struct rev_info *revs, unsigned int option) { @@ -341,10 +362,12 @@ int run_diff_files(struct rev_info *revs, unsigned int option) int silent_on_removed = option & DIFF_SILENT_ON_REMOVED; unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED) ? CE_MATCH_RACY_IS_DIRTY : 0); + char symcache[PATH_MAX]; if (diff_unmerged_stage < 0) diff_unmerged_stage = 2; entries = active_nr; + symcache[0] = '\0'; for (i = 0; i < entries; i++) { struct stat st; unsigned int oldmode, newmode; @@ -376,16 +399,17 @@ int run_diff_files(struct rev_info *revs, unsigned int option) memset(&(dpath->parent[0]), 0, sizeof(struct combine_diff_parent)*5); - if (lstat(ce->name, &st) < 0) { - if (errno != ENOENT && errno != ENOTDIR) { + changed = check_work_tree_entity(ce, &st, symcache); + if (!changed) + dpath->mode = ce_mode_from_stat(ce, st.st_mode); + else { + if (changed < 0) { perror(ce->name); continue; } if (silent_on_removed) continue; } - else - dpath->mode = ce_mode_from_stat(ce, st.st_mode); while (i < entries) { struct cache_entry *nce = active_cache[i]; @@ -438,8 +462,10 @@ int run_diff_files(struct rev_info *revs, unsigned int option) if (ce_uptodate(ce)) continue; - if (lstat(ce->name, &st) < 0) { - if (errno != ENOENT && errno != ENOTDIR) { + + changed = check_work_tree_entity(ce, &st, symcache); + if (changed) { + if (changed < 0) { perror(ce->name); continue; } @@ -468,6 +494,11 @@ int run_diff_files(struct rev_info *revs, unsigned int option) * diff-index */ +struct oneway_unpack_data { + struct rev_info *revs; + char symcache[PATH_MAX]; +}; + /* A file entry went away or appeared */ static void diff_index_show_file(struct rev_info *revs, const char *prefix, @@ -481,7 +512,8 @@ static void diff_index_show_file(struct rev_info *revs, static int get_stat_data(struct cache_entry *ce, const unsigned char **sha1p, unsigned int *modep, - int cached, int match_missing) + int cached, int match_missing, + struct oneway_unpack_data *cbdata) { const unsigned char *sha1 = ce->sha1; unsigned int mode = ce->ce_mode; @@ -489,8 +521,11 @@ static int get_stat_data(struct cache_entry *ce, if (!cached) { int changed; struct stat st; - if (lstat(ce->name, &st) < 0) { - if (errno == ENOENT && match_missing) { + changed = check_work_tree_entity(ce, &st, cbdata->symcache); + if (changed < 0) + return -1; + else if (changed) { + if (match_missing) { *sha1p = sha1; *modep = mode; return 0; @@ -509,23 +544,25 @@ static int get_stat_data(struct cache_entry *ce, return 0; } -static void show_new_file(struct rev_info *revs, +static void show_new_file(struct oneway_unpack_data *cbdata, struct cache_entry *new, int cached, int match_missing) { const unsigned char *sha1; unsigned int mode; + struct rev_info *revs = cbdata->revs; - /* New file in the index: it might actually be different in + /* + * New file in the index: it might actually be different in * the working copy. */ - if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) + if (get_stat_data(new, &sha1, &mode, cached, match_missing, cbdata) < 0) return; diff_index_show_file(revs, "+", new, sha1, mode); } -static int show_modified(struct rev_info *revs, +static int show_modified(struct oneway_unpack_data *cbdata, struct cache_entry *old, struct cache_entry *new, int report_missing, @@ -533,8 +570,9 @@ static int show_modified(struct rev_info *revs, { unsigned int mode, oldmode; const unsigned char *sha1; + struct rev_info *revs = cbdata->revs; - if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) { + if (get_stat_data(new, &sha1, &mode, cached, match_missing, cbdata) < 0) { if (report_missing) diff_index_show_file(revs, "-", old, old->sha1, old->ce_mode); @@ -602,7 +640,8 @@ static void do_oneway_diff(struct unpack_trees_options *o, struct cache_entry *idx, struct cache_entry *tree) { - struct rev_info *revs = o->unpack_data; + struct oneway_unpack_data *cbdata = o->unpack_data; + struct rev_info *revs = cbdata->revs; int match_missing, cached; /* @@ -625,7 +664,7 @@ static void do_oneway_diff(struct unpack_trees_options *o, * Something added to the tree? */ if (!tree) { - show_new_file(revs, idx, cached, match_missing); + show_new_file(cbdata, idx, cached, match_missing); return; } @@ -638,7 +677,7 @@ static void do_oneway_diff(struct unpack_trees_options *o, } /* Show difference between old and new */ - show_modified(revs, tree, idx, 1, cached, match_missing); + show_modified(cbdata, tree, idx, 1, cached, match_missing); } static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_options *o) @@ -675,7 +714,8 @@ static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o) { struct cache_entry *idx = src[0]; struct cache_entry *tree = src[1]; - struct rev_info *revs = o->unpack_data; + struct oneway_unpack_data *cbdata = o->unpack_data; + struct rev_info *revs = cbdata->revs; if (idx && ce_stage(idx)) skip_same_name(idx, o); @@ -702,6 +742,7 @@ int run_diff_index(struct rev_info *revs, int cached) const char *tree_name; struct unpack_trees_options opts; struct tree_desc t; + struct oneway_unpack_data unpack_cb; mark_merge_entries(); @@ -711,12 +752,14 @@ int run_diff_index(struct rev_info *revs, int cached) if (!tree) return error("bad tree object %s", tree_name); + unpack_cb.revs = revs; + unpack_cb.symcache[0] = '\0'; memset(&opts, 0, sizeof(opts)); opts.head_idx = 1; opts.index_only = cached; opts.merge = 1; opts.fn = oneway_diff; - opts.unpack_data = revs; + opts.unpack_data = &unpack_cb; opts.src_index = &the_index; opts.dst_index = NULL; @@ -738,6 +781,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt) struct cache_entry *last = NULL; struct unpack_trees_options opts; struct tree_desc t; + struct oneway_unpack_data unpack_cb; /* * This is used by git-blame to run diff-cache internally; @@ -766,12 +810,14 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt) if (!tree) die("bad tree object %s", sha1_to_hex(tree_sha1)); + unpack_cb.revs = &revs; + unpack_cb.symcache[0] = '\0'; memset(&opts, 0, sizeof(opts)); opts.head_idx = 1; opts.index_only = 1; opts.merge = 1; opts.fn = oneway_diff; - opts.unpack_data = &revs; + opts.unpack_data = &unpack_cb; opts.src_index = &the_index; opts.dst_index = &the_index; diff --git a/git-clone.sh b/git-clone.sh index e981122778..2636159aaa 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -310,6 +310,9 @@ yes) mkdir -p "$GIT_DIR/objects/info" echo "$repo/objects" >>"$GIT_DIR/objects/info/alternates" else + cpio_quiet_flag="" + cpio --help 2>&1 | grep -- --quiet >/dev/null && \ + cpio_quiet_flag=--quiet l= && if test "$use_local_hardlink" = yes then @@ -330,7 +333,8 @@ yes) fi fi && cd "$repo" && - find objects -depth -print | cpio -pumd$l "$GIT_DIR/" || exit 1 + find objects -depth -print | cpio $cpio_quiet_flag -pumd$l "$GIT_DIR/" || \ + exit 1 fi git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1 ;; diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 97fa39398c..29dbfc940b 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -73,8 +73,8 @@ my $methods = { 'status' => \&req_status, 'admin' => \&req_CATCHALL, 'history' => \&req_CATCHALL, - 'watchers' => \&req_CATCHALL, - 'editors' => \&req_CATCHALL, + 'watchers' => \&req_EMPTY, + 'editors' => \&req_EMPTY, 'annotate' => \&req_annotate, 'Global_option' => \&req_Globaloption, #'annotate' => \&req_CATCHALL, @@ -199,6 +199,11 @@ sub req_CATCHALL $log->warn("Unhandled command : req_$cmd : $data"); } +# This method invariably succeeds with an empty response. +sub req_EMPTY +{ + print "ok\n"; +} # Root pathname \n # Response expected: no. Tell the server which CVSROOT to use. Note that @@ -958,6 +963,17 @@ sub req_update $meta = $updater->getmeta($filename); } + # If -p was given, "print" the contents of the requested revision. + if ( exists ( $state->{opt}{p} ) ) { + if ( defined ( $meta->{revision} ) ) { + $log->info("Printing '$filename' revision " . $meta->{revision}); + + transmitfile($meta->{filehash}, { print => 1 }); + } + + next; + } + if ( ! defined $meta ) { $meta = { @@ -1091,9 +1107,9 @@ sub req_update my $file_local = $filepart . ".mine"; system("ln","-s",$state->{entries}{$filename}{modified_filename}, $file_local); my $file_old = $filepart . "." . $oldmeta->{revision}; - transmitfile($oldmeta->{filehash}, $file_old); + transmitfile($oldmeta->{filehash}, { targetfile => $file_old }); my $file_new = $filepart . "." . $meta->{revision}; - transmitfile($meta->{filehash}, $file_new); + transmitfile($meta->{filehash}, { targetfile => $file_new }); # we need to merge with the local changes ( M=successful merge, C=conflict merge ) $log->info("Merging $file_local, $file_old, $file_new"); @@ -1423,6 +1439,8 @@ sub req_status { $filename = filecleanup($filename); + next if exists($state->{opt}{l}) && index($filename, '/', length($state->{prependdir})) >= 0; + my $meta = $updater->getmeta($filename); my $oldmeta = $meta; @@ -1466,8 +1484,10 @@ sub req_status $status ||= "Unknown"; + my ($filepart) = filenamesplit($filename); + print "M ===================================================================\n"; - print "M File: $filename\tStatus: $status\n"; + print "M File: $filepart\tStatus: $status\n"; if ( defined($state->{entries}{$filename}{revision}) ) { print "M Working revision:\t" . $state->{entries}{$filename}{revision} . "\n"; @@ -1541,14 +1561,14 @@ sub req_diff print "E File $filename at revision 1.$revision1 doesn't exist\n"; next; } - transmitfile($meta1->{filehash}, $file1); + transmitfile($meta1->{filehash}, { targetfile => $file1 }); } # otherwise we just use the working copy revision else { ( undef, $file1 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 ); $meta1 = $updater->getmeta($filename, $wrev); - transmitfile($meta1->{filehash}, $file1); + transmitfile($meta1->{filehash}, { targetfile => $file1 }); } # if we have a second -r switch, use it too @@ -1563,7 +1583,7 @@ sub req_diff next; } - transmitfile($meta2->{filehash}, $file2); + transmitfile($meta2->{filehash}, { targetfile => $file2 }); } # otherwise we just use the working copy else @@ -1576,7 +1596,7 @@ sub req_diff { ( undef, $file2 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 ); $meta2 = $updater->getmeta($filename, $wrev); - transmitfile($meta2->{filehash}, $file2); + transmitfile($meta2->{filehash}, { targetfile => $file2 }); } # We need to have retrieved something useful @@ -1708,8 +1728,7 @@ sub req_log print "M revision 1.$revision->{revision}\n"; # reformat the date for log output $revision->{modified} = sprintf('%04d/%02d/%02d %s', $3, $DATE_LIST->{$2}, $1, $4 ) if ( $revision->{modified} =~ /(\d+)\s+(\w+)\s+(\d+)\s+(\S+)/ and defined($DATE_LIST->{$2}) ); - $revision->{author} =~ s/\s+.*//; - $revision->{author} =~ s/^(.{8}).*/$1/; + $revision->{author} = cvs_author($revision->{author}); print "M date: $revision->{modified}; author: $revision->{author}; state: " . ( $revision->{filehash} eq "deleted" ? "dead" : "Exp" ) . "; lines: +2 -3\n"; my $commitmessage = $updater->commitmessage($revision->{commithash}); $commitmessage =~ s/^/M /mg; @@ -1824,8 +1843,7 @@ sub req_annotate unless ( defined ( $metadata->{$commithash} ) ) { $metadata->{$commithash} = $updater->getmeta($filename, $commithash); - $metadata->{$commithash}{author} =~ s/\s+.*//; - $metadata->{$commithash}{author} =~ s/^(.{8}).*/$1/; + $metadata->{$commithash}{author} = cvs_author($metadata->{$commithash}{author}); $metadata->{$commithash}{modified} = sprintf("%02d-%s-%02d", $1, $2, $3) if ( $metadata->{$commithash}{modified} =~ /^(\d+)\s(\w+)\s\d\d(\d\d)/ ); } printf("M 1.%-5d (%-8s %10s): %s\n", @@ -2005,14 +2023,17 @@ sub revparse return undef; } -# This method takes a file hash and does a CVS "file transfer" which transmits the -# size of the file, and then the file contents. -# If a second argument $targetfile is given, the file is instead written out to -# a file by the name of $targetfile +# This method takes a file hash and does a CVS "file transfer". Its +# exact behaviour depends on a second, optional hash table argument: +# - If $options->{targetfile}, dump the contents to that file; +# - If $options->{print}, use M/MT to transmit the contents one line +# at a time; +# - Otherwise, transmit the size of the file, followed by the file +# contents. sub transmitfile { my $filehash = shift; - my $targetfile = shift; + my $options = shift; if ( defined ( $filehash ) and $filehash eq "deleted" ) { @@ -2034,11 +2055,20 @@ sub transmitfile if ( open my $fh, '-|', "git-cat-file", "blob", $filehash ) { - if ( defined ( $targetfile ) ) + if ( defined ( $options->{targetfile} ) ) { + my $targetfile = $options->{targetfile}; open NEWFILE, ">", $targetfile or die("Couldn't open '$targetfile' for writing : $!"); print NEWFILE $_ while ( <$fh> ); close NEWFILE or die("Failed to write '$targetfile': $!"); + } elsif ( defined ( $options->{print} ) && $options->{print} ) { + while ( <$fh> ) { + if( /\n\z/ ) { + print 'M ', $_; + } else { + print 'MT text ', $_, "\n"; + } + } } else { print "$size\n"; print while ( <$fh> ); @@ -2107,6 +2137,16 @@ sub kopts_from_path } } +# Generate a CVS author name from Git author information, by taking +# the first eight characters of the user part of the email address. +sub cvs_author +{ + my $author_line = shift; + (my $author) = $author_line =~ /<([^>@]{1,8})/; + + $author; +} + package GITCVS::log; #### @@ -2311,6 +2351,14 @@ sub new bless $self, $class; + $self->{valid_tables} = {'revision' => 1, + 'revision_ix1' => 1, + 'revision_ix2' => 1, + 'head' => 1, + 'head_ix1' => 1, + 'properties' => 1, + 'commitmsgs' => 1}; + $self->{module} = $module; $self->{git_path} = $config . "/"; @@ -2326,6 +2374,8 @@ sub new $cfg->{gitcvs}{dbuser} || ""; $self->{dbpass} = $cfg->{gitcvs}{$state->{method}}{dbpass} || $cfg->{gitcvs}{dbpass} || ""; + $self->{dbtablenameprefix} = $cfg->{gitcvs}{$state->{method}}{dbtablenameprefix} || + $cfg->{gitcvs}{dbtablenameprefix} || ""; my %mapping = ( m => $module, a => $state->{method}, u => getlogin || getpwuid($<) || $<, @@ -2334,6 +2384,8 @@ sub new ); $self->{dbname} =~ s/%([mauGg])/$mapping{$1}/eg; $self->{dbuser} =~ s/%([mauGg])/$mapping{$1}/eg; + $self->{dbtablenameprefix} =~ s/%([mauGg])/$mapping{$1}/eg; + $self->{dbtablenameprefix} = mangle_tablename($self->{dbtablenameprefix}); die "Invalid char ':' in dbdriver" if $self->{dbdriver} =~ /:/; die "Invalid char ';' in dbname" if $self->{dbname} =~ /;/; @@ -2349,10 +2401,13 @@ sub new } # Construct the revision table if required - unless ( $self->{tables}{revision} ) + unless ( $self->{tables}{$self->tablename("revision")} ) { + my $tablename = $self->tablename("revision"); + my $ix1name = $self->tablename("revision_ix1"); + my $ix2name = $self->tablename("revision_ix2"); $self->{dbh}->do(" - CREATE TABLE revision ( + CREATE TABLE $tablename ( name TEXT NOT NULL, revision INTEGER NOT NULL, filehash TEXT NOT NULL, @@ -2363,20 +2418,22 @@ sub new ) "); $self->{dbh}->do(" - CREATE INDEX revision_ix1 - ON revision (name,revision) + CREATE INDEX $ix1name + ON $tablename (name,revision) "); $self->{dbh}->do(" - CREATE INDEX revision_ix2 - ON revision (name,commithash) + CREATE INDEX $ix2name + ON $tablename (name,commithash) "); } # Construct the head table if required - unless ( $self->{tables}{head} ) + unless ( $self->{tables}{$self->tablename("head")} ) { + my $tablename = $self->tablename("head"); + my $ix1name = $self->tablename("head_ix1"); $self->{dbh}->do(" - CREATE TABLE head ( + CREATE TABLE $tablename ( name TEXT NOT NULL, revision INTEGER NOT NULL, filehash TEXT NOT NULL, @@ -2387,16 +2444,17 @@ sub new ) "); $self->{dbh}->do(" - CREATE INDEX head_ix1 - ON head (name) + CREATE INDEX $ix1name + ON $tablename (name) "); } # Construct the properties table if required - unless ( $self->{tables}{properties} ) + unless ( $self->{tables}{$self->tablename("properties")} ) { + my $tablename = $self->tablename("properties"); $self->{dbh}->do(" - CREATE TABLE properties ( + CREATE TABLE $tablename ( key TEXT NOT NULL PRIMARY KEY, value TEXT ) @@ -2404,10 +2462,11 @@ sub new } # Construct the commitmsgs table if required - unless ( $self->{tables}{commitmsgs} ) + unless ( $self->{tables}{$self->tablename("commitmsgs")} ) { + my $tablename = $self->tablename("commitmsgs"); $self->{dbh}->do(" - CREATE TABLE commitmsgs ( + CREATE TABLE $tablename ( key TEXT NOT NULL PRIMARY KEY, value TEXT ) @@ -2417,6 +2476,21 @@ sub new return $self; } +=head2 tablename + +=cut +sub tablename +{ + my $self = shift; + my $name = shift; + + if (exists $self->{valid_tables}{$name}) { + return $self->{dbtablenameprefix} . $name; + } else { + return undef; + } +} + =head2 update =cut @@ -2782,8 +2856,9 @@ sub insert_rev my $modified = shift; my $author = shift; my $mode = shift; + my $tablename = $self->tablename("revision"); - my $insert_rev = $self->{dbh}->prepare_cached("INSERT INTO revision (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1); + my $insert_rev = $self->{dbh}->prepare_cached("INSERT INTO $tablename (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1); $insert_rev->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode); } @@ -2792,16 +2867,18 @@ sub insert_mergelog my $self = shift; my $key = shift; my $value = shift; + my $tablename = $self->tablename("commitmsgs"); - my $insert_mergelog = $self->{dbh}->prepare_cached("INSERT INTO commitmsgs (key, value) VALUES (?,?)",{},1); + my $insert_mergelog = $self->{dbh}->prepare_cached("INSERT INTO $tablename (key, value) VALUES (?,?)",{},1); $insert_mergelog->execute($key, $value); } sub delete_head { my $self = shift; + my $tablename = $self->tablename("head"); - my $delete_head = $self->{dbh}->prepare_cached("DELETE FROM head",{},1); + my $delete_head = $self->{dbh}->prepare_cached("DELETE FROM $tablename",{},1); $delete_head->execute(); } @@ -2815,8 +2892,9 @@ sub insert_head my $modified = shift; my $author = shift; my $mode = shift; + my $tablename = $self->tablename("head"); - my $insert_head = $self->{dbh}->prepare_cached("INSERT INTO head (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1); + my $insert_head = $self->{dbh}->prepare_cached("INSERT INTO $tablename (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1); $insert_head->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode); } @@ -2824,8 +2902,9 @@ sub _headrev { my $self = shift; my $filename = shift; + my $tablename = $self->tablename("head"); - my $db_query = $self->{dbh}->prepare_cached("SELECT filehash, revision, mode FROM head WHERE name=?",{},1); + my $db_query = $self->{dbh}->prepare_cached("SELECT filehash, revision, mode FROM $tablename WHERE name=?",{},1); $db_query->execute($filename); my ( $hash, $revision, $mode ) = $db_query->fetchrow_array; @@ -2836,8 +2915,9 @@ sub _get_prop { my $self = shift; my $key = shift; + my $tablename = $self->tablename("properties"); - my $db_query = $self->{dbh}->prepare_cached("SELECT value FROM properties WHERE key=?",{},1); + my $db_query = $self->{dbh}->prepare_cached("SELECT value FROM $tablename WHERE key=?",{},1); $db_query->execute($key); my ( $value ) = $db_query->fetchrow_array; @@ -2849,13 +2929,14 @@ sub _set_prop my $self = shift; my $key = shift; my $value = shift; + my $tablename = $self->tablename("properties"); - my $db_query = $self->{dbh}->prepare_cached("UPDATE properties SET value=? WHERE key=?",{},1); + my $db_query = $self->{dbh}->prepare_cached("UPDATE $tablename SET value=? WHERE key=?",{},1); $db_query->execute($value, $key); unless ( $db_query->rows ) { - $db_query = $self->{dbh}->prepare_cached("INSERT INTO properties (key, value) VALUES (?,?)",{},1); + $db_query = $self->{dbh}->prepare_cached("INSERT INTO $tablename (key, value) VALUES (?,?)",{},1); $db_query->execute($key, $value); } @@ -2869,10 +2950,11 @@ sub _set_prop sub gethead { my $self = shift; + my $tablename = $self->tablename("head"); return $self->{gethead_cache} if ( defined ( $self->{gethead_cache} ) ); - my $db_query = $self->{dbh}->prepare_cached("SELECT name, filehash, mode, revision, modified, commithash, author FROM head ORDER BY name ASC",{},1); + my $db_query = $self->{dbh}->prepare_cached("SELECT name, filehash, mode, revision, modified, commithash, author FROM $tablename ORDER BY name ASC",{},1); $db_query->execute(); my $tree = []; @@ -2894,8 +2976,9 @@ sub getlog { my $self = shift; my $filename = shift; + my $tablename = $self->tablename("revision"); - my $db_query = $self->{dbh}->prepare_cached("SELECT name, filehash, author, mode, revision, modified, commithash FROM revision WHERE name=? ORDER BY revision DESC",{},1); + my $db_query = $self->{dbh}->prepare_cached("SELECT name, filehash, author, mode, revision, modified, commithash FROM $tablename WHERE name=? ORDER BY revision DESC",{},1); $db_query->execute($filename); my $tree = []; @@ -2919,19 +3002,21 @@ sub getmeta my $self = shift; my $filename = shift; my $revision = shift; + my $tablename_rev = $self->tablename("revision"); + my $tablename_head = $self->tablename("head"); my $db_query; if ( defined($revision) and $revision =~ /^\d+$/ ) { - $db_query = $self->{dbh}->prepare_cached("SELECT * FROM revision WHERE name=? AND revision=?",{},1); + $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_rev WHERE name=? AND revision=?",{},1); $db_query->execute($filename, $revision); } elsif ( defined($revision) and $revision =~ /^[a-zA-Z0-9]{40}$/ ) { - $db_query = $self->{dbh}->prepare_cached("SELECT * FROM revision WHERE name=? AND commithash=?",{},1); + $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_rev WHERE name=? AND commithash=?",{},1); $db_query->execute($filename, $revision); } else { - $db_query = $self->{dbh}->prepare_cached("SELECT * FROM head WHERE name=?",{},1); + $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_head WHERE name=?",{},1); $db_query->execute($filename); } @@ -2947,11 +3032,12 @@ sub commitmessage { my $self = shift; my $commithash = shift; + my $tablename = $self->tablename("commitmsgs"); die("Need commithash") unless ( defined($commithash) and $commithash =~ /^[a-zA-Z0-9]{40}$/ ); my $db_query; - $db_query = $self->{dbh}->prepare_cached("SELECT value FROM commitmsgs WHERE key=?",{},1); + $db_query = $self->{dbh}->prepare_cached("SELECT value FROM $tablename WHERE key=?",{},1); $db_query->execute($commithash); my ( $message ) = $db_query->fetchrow_array; @@ -2979,9 +3065,10 @@ sub gethistory { my $self = shift; my $filename = shift; + my $tablename = $self->tablename("revision"); my $db_query; - $db_query = $self->{dbh}->prepare_cached("SELECT revision, filehash, commithash FROM revision WHERE name=? ORDER BY revision DESC",{},1); + $db_query = $self->{dbh}->prepare_cached("SELECT revision, filehash, commithash FROM $tablename WHERE name=? ORDER BY revision DESC",{},1); $db_query->execute($filename); return $db_query->fetchall_arrayref; @@ -3001,9 +3088,10 @@ sub gethistorydense { my $self = shift; my $filename = shift; + my $tablename = $self->tablename("revision"); my $db_query; - $db_query = $self->{dbh}->prepare_cached("SELECT revision, filehash, commithash FROM revision WHERE name=? AND filehash!='deleted' ORDER BY revision DESC",{},1); + $db_query = $self->{dbh}->prepare_cached("SELECT revision, filehash, commithash FROM $tablename WHERE name=? AND filehash!='deleted' ORDER BY revision DESC",{},1); $db_query->execute($filename); return $db_query->fetchall_arrayref; @@ -3061,4 +3149,19 @@ sub mangle_dirname { return $dirname; } +=head2 mangle_tablename + +create a string from a that is suitable to use as part of an SQL table +name, mainly by converting all chars except \w to _ + +=cut +sub mangle_tablename { + my $tablename = shift; + return unless defined $tablename; + + $tablename =~ s/[^\w_]/_/g; + + return $tablename; +} + 1; diff --git a/git-svn.perl b/git-svn.perl index 0c2b791eab..1b44cbe106 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1900,7 +1900,7 @@ sub prop_walk { foreach (sort keys %$dirent) { next if $dirent->{$_}->{kind} != $SVN::Node::dir; - $self->prop_walk($path . '/' . $_, $rev, $sub); + $self->prop_walk($p . $_, $rev, $sub); } } @@ -148,8 +148,9 @@ static int handle_alias(int *argcp, const char ***argv) const char** new_argv; const char *alias_command; char *alias_string; + int unused_nongit; - subdir = setup_git_directory_gently(NULL); + subdir = setup_git_directory_gently(&unused_nongit); alias_command = (*argv)[0]; alias_string = alias_lookup(alias_command); @@ -30,6 +30,7 @@ static struct option builtin_help_options[] = { HELP_FORMAT_WEB), OPT_SET_INT('i', "info", &help_format, "show info page", HELP_FORMAT_INFO), + OPT_END(), }; static const char * const builtin_help_usage[] = { diff --git a/t/t2201-add-update-typechange.sh b/t/t2201-add-update-typechange.sh new file mode 100755 index 0000000000..e15e3eb81b --- /dev/null +++ b/t/t2201-add-update-typechange.sh @@ -0,0 +1,140 @@ +#!/bin/sh + +test_description='more git add -u' + +. ./test-lib.sh + +_z40=0000000000000000000000000000000000000000 + +test_expect_success setup ' + >xyzzy && + _empty=$(git hash-object --stdin <xyzzy) && + >yomin && + >caskly && + ln -s frotz nitfol && + mkdir rezrov && + >rezrov/bozbar && + git add caskly xyzzy yomin nitfol rezrov/bozbar && + + test_tick && + git commit -m initial + +' + +test_expect_success modify ' + rm -f xyzzy yomin nitfol caskly && + # caskly disappears (not a submodule) + mkdir caskly && + # nitfol changes from symlink to regular + >nitfol && + # rezrov/bozbar disappears + rm -fr rezrov && + ln -s xyzzy rezrov && + # xyzzy disappears (not a submodule) + mkdir xyzzy && + echo gnusto >xyzzy/bozbar && + # yomin gets replaced with a submodule + mkdir yomin && + >yomin/yomin && + ( + cd yomin && + git init && + git add yomin && + git commit -m "sub initial" + ) && + yomin=$(GIT_DIR=yomin/.git git rev-parse HEAD) && + # yonk is added and then turned into a submodule + # this should appear as T in diff-files and as A in diff-index + >yonk && + git add yonk && + rm -f yonk && + mkdir yonk && + >yonk/yonk && + ( + cd yonk && + git init && + git add yonk && + git commit -m "sub initial" + ) && + yonk=$(GIT_DIR=yonk/.git git rev-parse HEAD) && + # zifmia is added and then removed + # this should appear in diff-files but not in diff-index. + >zifmia && + git add zifmia && + rm -f zifmia && + mkdir zifmia && + { + git ls-tree -r HEAD | + sed -e "s/^/:/" -e " + / caskly/{ + s/ caskly/ $_z40 D&/ + s/blob/000000/ + } + / nitfol/{ + s/ nitfol/ $_z40 T&/ + s/blob/100644/ + } + / rezrov.bozbar/{ + s/ rezrov.bozbar/ $_z40 D&/ + s/blob/000000/ + } + / xyzzy/{ + s/ xyzzy/ $_z40 D&/ + s/blob/000000/ + } + / yomin/{ + s/ yomin/ $_z40 T&/ + s/blob/160000/ + } + " + } >expect && + { + cat expect + echo ":100644 160000 $_empty $_z40 T yonk" + echo ":100644 000000 $_empty $_z40 D zifmia" + } >expect-files && + { + cat expect + echo ":000000 160000 $_z40 $_z40 A yonk" + } >expect-index && + { + echo "100644 $_empty 0 nitfol" + echo "160000 $yomin 0 yomin" + echo "160000 $yonk 0 yonk" + } >expect-final +' + +test_expect_success diff-files ' + git diff-files --raw >actual && + diff -u expect-files actual +' + +test_expect_success diff-index ' + git diff-index --raw HEAD -- >actual && + diff -u expect-index actual +' + +test_expect_success 'add -u' ' + rm -f ".git/saved-index" && + cp -p ".git/index" ".git/saved-index" && + git add -u && + git ls-files -s >actual && + diff -u expect-final actual +' + +test_expect_success 'commit -a' ' + if test -f ".git/saved-index" + then + rm -f ".git/index" && + mv ".git/saved-index" ".git/index" + fi && + git commit -m "second" -a && + git ls-files -s >actual && + diff -u expect-final actual && + rm -f .git/index && + git read-tree HEAD && + git ls-files -s >actual && + diff -u expect-final actual +' + +test_done diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 75cd33bde8..1a7141ecd7 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -578,6 +578,14 @@ test_expect_success \ git diff expect actual ' +# subsequent tests require gpg; check if it is available +gpg --version >/dev/null +if [ $? -eq 127 ]; then + echo "gpg not found - skipping tag signing and verification tests" + test_done + exit +fi + # trying to verify annotated non-signed tags: test_expect_success \ @@ -600,13 +608,6 @@ test_expect_success \ # creating and verifying signed tags: -gpg --version >/dev/null -if [ $? -eq 127 ]; then - echo "Skipping signed tags tests, because gpg was not found" - test_done - exit -fi - # As said here: http://www.gnupg.org/documentation/faqs.html#q6.19 # the gpg version 1.0.6 didn't parse trust packets correctly, so for # that version, creation of signed tags using the generated key fails. diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index b91b151417..166b43f783 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -420,4 +420,54 @@ test_expect_success 'cvs update (merge no-op)' \ GIT_CONFIG="$git_config" cvs -Q update && diff -q merge ../merge' +cd "$WORKDIR" +test_expect_success 'cvs update (-p)' ' + touch really-empty && + echo Line 1 > no-lf && + echo -n Line 2 >> no-lf && + git add really-empty no-lf && + git commit -q -m "Update -p test" && + git push gitcvs.git >/dev/null && + cd cvswork && + GIT_CONFIG="$git_config" cvs update && + rm -f failures && + for i in merge no-lf empty really-empty; do + GIT_CONFIG="$git_config" cvs update -p "$i" >$i.out + diff $i.out ../$i >>failures 2>&1 + done && + test -z "$(cat failures)" +' + +#------------ +# CVS STATUS +#------------ + +cd "$WORKDIR" +test_expect_success 'cvs status' ' + mkdir status.dir && + echo Line > status.dir/status.file && + echo Line > status.file && + git add status.dir status.file && + git commit -q -m "Status test" && + git push gitcvs.git >/dev/null && + cd cvswork && + GIT_CONFIG="$git_config" cvs update && + GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out && + test $(wc -l <../out) = 2 +' + +cd "$WORKDIR" +test_expect_success 'cvs status (nonrecursive)' ' + cd cvswork && + GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out && + test $(wc -l <../out) = 1 +' + +cd "$WORKDIR" +test_expect_success 'cvs status (no subdirs in header)' ' + cd cvswork && + GIT_CONFIG="$git_config" cvs status | grep ^File: >../out && + ! grep / <../out +' + test_done |