diff options
-rw-r--r-- | Documentation/git-cvsserver.txt | 24 | ||||
-rw-r--r-- | Documentation/git-svnimport.txt | 5 | ||||
-rw-r--r-- | apply.c | 158 | ||||
-rw-r--r-- | cache.h | 2 | ||||
-rwxr-xr-x | contrib/gitview/gitview | 5 | ||||
-rw-r--r-- | diff-delta.c | 253 | ||||
-rw-r--r-- | environment.c | 1 | ||||
-rwxr-xr-x | git-am.sh | 13 | ||||
-rwxr-xr-x | git-annotate.perl | 11 | ||||
-rwxr-xr-x | git-cvsserver.perl | 44 | ||||
-rwxr-xr-x | git-mv.perl | 33 | ||||
-rwxr-xr-x | git-svnimport.perl | 25 | ||||
-rw-r--r-- | pack-objects.c | 2 | ||||
-rw-r--r-- | refs.c | 9 | ||||
-rwxr-xr-x | t/t8001-annotate.sh | 89 |
15 files changed, 480 insertions, 194 deletions
diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index 88f07ff15d..19c9c51cff 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -54,6 +54,30 @@ INSTALLATION of branches in git). $ cvs co -d mylocaldir master +Eclipse CVS Client Notes +------------------------ + +To get a checkout with the Eclipse CVS client: + +1. Create a new project from CVS checkout, giving it repository and module +2. Context Menu->Team->Share Project... +3. Enter the repository and module information again and click Finish +4. The Synchronize view appears. Untick "launch commit wizard" to avoid +committing the .project file, and select HEAD as the tag to synchronize to. +Update all incoming changes. + +Note that most versions of Eclipse ignore CVS_SERVER (which you can set in +the Preferences->Team->CVS->ExtConnection pane), so you may have to +rename, alias or symlink git-cvsserver to 'cvs' on the server. + +Clients known to work +--------------------- + +CVS 1.12.9 on Debian +CVS 1.11.17 on MacOSX (from Fink package) +Eclipse 3.0, 3.1.2 on MacOSX (see Eclipse CVS Client Notes) +TortoiseCVS + Operations supported -------------------- diff --git a/Documentation/git-svnimport.txt b/Documentation/git-svnimport.txt index 912a80865e..a1588138ea 100644 --- a/Documentation/git-svnimport.txt +++ b/Documentation/git-svnimport.txt @@ -82,6 +82,11 @@ When importing incrementally, you might need to edit the .git/svn2git file. "username". If encountering a commit made by a user not in the list, abort. + For convenience, this data is saved to $GIT_DIR/svn-authors + each time the -A option is provided, and read from that same + file each time git-svnimport is run with an existing GIT + repository without -A. + -m:: Attempt to detect merges based on the commit message. This option will enable default regexes that try to capture the name source @@ -34,6 +34,56 @@ static int line_termination = '\n'; static const char apply_usage[] = "git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] <patch>..."; +static enum whitespace_eol { + nowarn_whitespace, + warn_on_whitespace, + error_on_whitespace, + strip_whitespace, +} new_whitespace = warn_on_whitespace; +static int whitespace_error = 0; +static int squelch_whitespace_errors = 5; +static int applied_after_stripping = 0; +static const char *patch_input_file = NULL; + +static void parse_whitespace_option(const char *option) +{ + if (!option) { + new_whitespace = warn_on_whitespace; + return; + } + if (!strcmp(option, "warn")) { + new_whitespace = warn_on_whitespace; + return; + } + if (!strcmp(option, "nowarn")) { + new_whitespace = nowarn_whitespace; + return; + } + if (!strcmp(option, "error")) { + new_whitespace = error_on_whitespace; + return; + } + if (!strcmp(option, "error-all")) { + new_whitespace = error_on_whitespace; + squelch_whitespace_errors = 0; + return; + } + if (!strcmp(option, "strip")) { + new_whitespace = strip_whitespace; + return; + } + die("unrecognized whitespace option '%s'", option); +} + +static void set_default_whitespace_mode(const char *whitespace_option) +{ + if (!whitespace_option && !apply_default_whitespace) { + new_whitespace = (apply + ? warn_on_whitespace + : nowarn_whitespace); + } +} + /* * For "diff-stat" like behaviour, we keep track of the biggest change * we've seen, and the longest filename. That allows us to do simple @@ -815,6 +865,25 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s oldlines--; break; case '+': + /* + * We know len is at least two, since we have a '+' and + * we checked that the last character was a '\n' above. + * That is, an addition of an empty line would check + * the '+' here. Sneaky... + */ + if ((new_whitespace != nowarn_whitespace) && + isspace(line[len-2])) { + whitespace_error++; + if (squelch_whitespace_errors && + squelch_whitespace_errors < + whitespace_error) + ; + else { + fprintf(stderr, "Adds trailing whitespace.\n%s:%d:%.*s\n", + patch_input_file, + linenr, len-2, line+1); + } + } added++; newlines--; break; @@ -1092,6 +1161,28 @@ struct buffer_desc { unsigned long alloc; }; +static int apply_line(char *output, const char *patch, int plen) +{ + /* plen is number of bytes to be copied from patch, + * starting at patch+1 (patch[0] is '+'). Typically + * patch[plen] is '\n'. + */ + int add_nl_to_tail = 0; + if ((new_whitespace == strip_whitespace) && + 1 < plen && isspace(patch[plen-1])) { + if (patch[plen] == '\n') + add_nl_to_tail = 1; + plen--; + while (0 < plen && isspace(patch[plen])) + plen--; + applied_after_stripping++; + } + memcpy(output, patch + 1, plen); + if (add_nl_to_tail) + output[plen++] = '\n'; + return plen; +} + static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag) { char *buf = desc->buffer; @@ -1127,10 +1218,9 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag) break; /* Fall-through for ' ' */ case '+': - if (*patch != '+' || !no_add) { - memcpy(new + newsize, patch + 1, plen); - newsize += plen; - } + if (*patch != '+' || !no_add) + newsize += apply_line(new + newsize, patch, + plen); break; case '@': case '\\': /* Ignore it, we already handled it */ @@ -1699,7 +1789,7 @@ static int use_patch(struct patch *p) return 1; } -static int apply_patch(int fd) +static int apply_patch(int fd, const char *filename) { int newfd; unsigned long offset, size; @@ -1707,6 +1797,7 @@ static int apply_patch(int fd) struct patch *list = NULL, **listp = &list; int skipped_patch = 0; + patch_input_file = filename; if (!buffer) return -1; offset = 0; @@ -1733,6 +1824,9 @@ static int apply_patch(int fd) } newfd = -1; + if (whitespace_error && (new_whitespace == error_on_whitespace)) + apply = 0; + write_index = check_index && apply; if (write_index) newfd = hold_index_file_for_update(&cache_file, get_index_file()); @@ -1769,17 +1863,28 @@ static int apply_patch(int fd) return 0; } +static int git_apply_config(const char *var, const char *value) +{ + if (!strcmp(var, "apply.whitespace")) { + apply_default_whitespace = strdup(value); + return 0; + } + return git_default_config(var, value); +} + + int main(int argc, char **argv) { int i; int read_stdin = 1; + const char *whitespace_option = NULL; for (i = 1; i < argc; i++) { const char *arg = argv[i]; int fd; if (!strcmp(arg, "-")) { - apply_patch(0); + apply_patch(0, "<stdin>"); read_stdin = 0; continue; } @@ -1839,11 +1944,18 @@ int main(int argc, char **argv) line_termination = 0; continue; } + if (!strncmp(arg, "--whitespace=", 13)) { + whitespace_option = arg + 13; + parse_whitespace_option(arg + 13); + continue; + } if (check_index && prefix_length < 0) { prefix = setup_git_directory(); prefix_length = prefix ? strlen(prefix) : 0; - git_config(git_default_config); + git_config(git_apply_config); + if (!whitespace_option && apply_default_whitespace) + parse_whitespace_option(apply_default_whitespace); } if (0 < prefix_length) arg = prefix_filename(prefix, prefix_length, arg); @@ -1852,10 +1964,38 @@ int main(int argc, char **argv) if (fd < 0) usage(apply_usage); read_stdin = 0; - apply_patch(fd); + set_default_whitespace_mode(whitespace_option); + apply_patch(fd, arg); close(fd); } + set_default_whitespace_mode(whitespace_option); if (read_stdin) - apply_patch(0); + apply_patch(0, "<stdin>"); + if (whitespace_error) { + if (squelch_whitespace_errors && + squelch_whitespace_errors < whitespace_error) { + int squelched = + whitespace_error - squelch_whitespace_errors; + fprintf(stderr, "warning: squelched %d whitespace error%s\n", + squelched, + squelched == 1 ? "" : "s"); + } + if (new_whitespace == error_on_whitespace) + die("%d line%s add%s trailing whitespaces.", + whitespace_error, + whitespace_error == 1 ? "" : "s", + whitespace_error == 1 ? "s" : ""); + if (applied_after_stripping) + fprintf(stderr, "warning: %d line%s applied after" + " stripping trailing whitespaces.\n", + applied_after_stripping, + applied_after_stripping == 1 ? "" : "s"); + else if (whitespace_error) + fprintf(stderr, "warning: %d line%s add%s trailing" + " whitespaces.\n", + whitespace_error, + whitespace_error == 1 ? "" : "s", + whitespace_error == 1 ? "s" : ""); + } return 0; } @@ -161,11 +161,13 @@ extern int hold_index_file_for_update(struct cache_file *, const char *path); extern int commit_index_file(struct cache_file *); extern void rollback_index_file(struct cache_file *); +/* Environment bits from configuration mechanism */ extern int trust_executable_bit; extern int assume_unchanged; extern int only_use_symrefs; extern int diff_rename_limit_default; extern int shared_repository; +extern const char *apply_default_whitespace; #define GIT_REPO_VERSION 0 extern int repository_format_version; diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index ea05cd4240..de9f3f3c72 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -513,7 +513,7 @@ class GitView: scrollwin = gtk.ScrolledWindow() - scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) scrollwin.set_shadow_type(gtk.SHADOW_IN) vbox.pack_start(scrollwin, expand=True, fill=True) scrollwin.show() @@ -526,9 +526,6 @@ class GitView: self.treeview.show() cell = CellRendererGraph() - # Set the default width to 265 - # This make sure that we have nice display with large tag names - cell.set_property("width", 265) column = gtk.TreeViewColumn() column.set_resizable(True) column.pack_start(cell, expand=True) diff --git a/diff-delta.c b/diff-delta.c index c2f656ae39..2ed5984b1c 100644 --- a/diff-delta.c +++ b/diff-delta.c @@ -19,8 +19,9 @@ */ #include <stdlib.h> +#include <string.h> +#include <zlib.h> #include "delta.h" -#include "zlib.h" /* block size: min = 16, max = 64k, power of 2 */ @@ -29,149 +30,87 @@ #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define GR_PRIME 0x9e370001 -#define HASH(v, b) (((unsigned int)(v) * GR_PRIME) >> (32 - (b))) - -static unsigned int hashbits(unsigned int size) -{ - unsigned int val = 1, bits = 0; - while (val < size && bits < 32) { - val <<= 1; - bits++; - } - return bits ? bits: 1; -} - -typedef struct s_chanode { - struct s_chanode *next; - int icurr; -} chanode_t; - -typedef struct s_chastore { - int isize, nsize; - chanode_t *ancur; -} chastore_t; - -static void cha_init(chastore_t *cha, int isize, int icount) -{ - cha->isize = isize; - cha->nsize = icount * isize; - cha->ancur = NULL; -} - -static void *cha_alloc(chastore_t *cha) -{ - chanode_t *ancur; - void *data; +#define HASH(v, shift) (((unsigned int)(v) * GR_PRIME) >> (shift)) - ancur = cha->ancur; - if (!ancur || ancur->icurr == cha->nsize) { - ancur = malloc(sizeof(chanode_t) + cha->nsize); - if (!ancur) - return NULL; - ancur->icurr = 0; - ancur->next = cha->ancur; - cha->ancur = ancur; - } - - data = (void *)ancur + sizeof(chanode_t) + ancur->icurr; - ancur->icurr += cha->isize; - return data; -} - -static void cha_free(chastore_t *cha) -{ - chanode_t *cur = cha->ancur; - while (cur) { - chanode_t *tmp = cur; - cur = cur->next; - free(tmp); - } -} - -typedef struct s_bdrecord { - struct s_bdrecord *next; - unsigned int fp; +struct index { const unsigned char *ptr; -} bdrecord_t; - -typedef struct s_bdfile { - chastore_t cha; - unsigned int fphbits; - bdrecord_t **fphash; -} bdfile_t; + unsigned int val; + struct index *next; +}; -static int delta_prepare(const unsigned char *buf, int bufsize, bdfile_t *bdf) +static struct index ** delta_index(const unsigned char *buf, + unsigned long bufsize, + unsigned int *hash_shift) { - unsigned int fphbits; - int i, hsize; - const unsigned char *data, *top; - bdrecord_t *brec; - bdrecord_t **fphash; - - fphbits = hashbits(bufsize / BLK_SIZE + 1); - hsize = 1 << fphbits; - fphash = malloc(hsize * sizeof(bdrecord_t *)); - if (!fphash) - return -1; - for (i = 0; i < hsize; i++) - fphash[i] = NULL; - cha_init(&bdf->cha, sizeof(bdrecord_t), hsize / 4 + 1); - - top = buf + bufsize; - data = buf + (bufsize / BLK_SIZE) * BLK_SIZE; - if (data == top) + unsigned int hsize, hshift, entries, blksize, i; + const unsigned char *data; + struct index *entry, **hash; + void *mem; + + /* determine index hash size */ + entries = (bufsize + BLK_SIZE - 1) / BLK_SIZE; + hsize = entries / 4; + for (i = 4; (1 << i) < hsize && i < 16; i++); + hsize = 1 << i; + hshift = 32 - i; + *hash_shift = hshift; + + /* allocate lookup index */ + mem = malloc(hsize * sizeof(*hash) + entries * sizeof(*entry)); + if (!mem) + return NULL; + hash = mem; + entry = mem + hsize * sizeof(*hash); + memset(hash, 0, hsize * sizeof(*hash)); + + /* then populate it */ + data = buf + entries * BLK_SIZE - BLK_SIZE; + blksize = bufsize - (data - buf); + while (data >= buf) { + unsigned int val = adler32(0, data, blksize); + i = HASH(val, hshift); + entry->ptr = data; + entry->val = val; + entry->next = hash[i]; + hash[i] = entry++; + blksize = BLK_SIZE; data -= BLK_SIZE; + } - for ( ; data >= buf; data -= BLK_SIZE) { - brec = cha_alloc(&bdf->cha); - if (!brec) { - cha_free(&bdf->cha); - free(fphash); - return -1; - } - brec->fp = adler32(0, data, MIN(BLK_SIZE, top - data)); - brec->ptr = data; - i = HASH(brec->fp, fphbits); - brec->next = fphash[i]; - fphash[i] = brec; - } - - bdf->fphbits = fphbits; - bdf->fphash = fphash; - - return 0; -} - -static void delta_cleanup(bdfile_t *bdf) -{ - free(bdf->fphash); - cha_free(&bdf->cha); + return hash; } +/* provide the size of the copy opcode given the block offset and size */ #define COPYOP_SIZE(o, s) \ (!!(o & 0xff) + !!(o & 0xff00) + !!(o & 0xff0000) + !!(o & 0xff000000) + \ !!(s & 0xff) + !!(s & 0xff00) + 1) +/* the maximum size for any opcode */ +#define MAX_OP_SIZE COPYOP_SIZE(0xffffffff, 0xffffffff) + void *diff_delta(void *from_buf, unsigned long from_size, void *to_buf, unsigned long to_size, unsigned long *delta_size, unsigned long max_size) { - int i, outpos, outsize, inscnt, csize, msize, moff; - unsigned int fp; - const unsigned char *ref_data, *ref_top, *data, *top, *ptr1, *ptr2; - unsigned char *out, *orig; - bdrecord_t *brec; - bdfile_t bdf; + unsigned int i, outpos, outsize, inscnt, hash_shift; + const unsigned char *ref_data, *ref_top, *data, *top; + unsigned char *out; + struct index *entry, **hash; - if (!from_size || !to_size || delta_prepare(from_buf, from_size, &bdf)) + if (!from_size || !to_size) + return NULL; + hash = delta_index(from_buf, from_size, &hash_shift); + if (!hash) return NULL; - + outpos = 0; outsize = 8192; + if (max_size && outsize >= max_size) + outsize = max_size + MAX_OP_SIZE + 1; out = malloc(outsize); if (!out) { - delta_cleanup(&bdf); + free(hash); return NULL; } @@ -199,28 +138,32 @@ void *diff_delta(void *from_buf, unsigned long from_size, } inscnt = 0; - moff = 0; - while (data < top) { - msize = 0; - fp = adler32(0, data, MIN(top - data, BLK_SIZE)); - i = HASH(fp, bdf.fphbits); - for (brec = bdf.fphash[i]; brec; brec = brec->next) { - if (brec->fp == fp) { - csize = ref_top - brec->ptr; - if (csize > top - data) - csize = top - data; - for (ptr1 = brec->ptr, ptr2 = data; - csize && *ptr1 == *ptr2; - csize--, ptr1++, ptr2++); - csize = ptr1 - brec->ptr; - if (csize > msize) { - moff = brec->ptr - ref_data; - msize = csize; - if (msize >= 0x10000) { - msize = 0x10000; - break; - } + while (data < top) { + unsigned int moff = 0, msize = 0; + unsigned int blksize = MIN(top - data, BLK_SIZE); + unsigned int val = adler32(0, data, blksize); + i = HASH(val, hash_shift); + for (entry = hash[i]; entry; entry = entry->next) { + const unsigned char *ref = entry->ptr; + const unsigned char *src = data; + unsigned int ref_size = ref_top - ref; + if (entry->val != val) + continue; + if (ref_size > top - src) + ref_size = top - src; + while (ref_size && *src++ == *ref) { + ref++; + ref_size--; + } + ref_size = ref - entry->ptr; + if (ref_size > msize) { + /* this is our best match so far */ + moff = entry->ptr - ref_data; + msize = ref_size; + if (msize >= 0x10000) { + msize = 0x10000; + break; } } } @@ -235,13 +178,15 @@ void *diff_delta(void *from_buf, unsigned long from_size, inscnt = 0; } } else { + unsigned char *op; + if (inscnt) { out[outpos - inscnt - 1] = inscnt; inscnt = 0; } data += msize; - orig = out + outpos++; + op = out + outpos++; i = 0x80; if (moff & 0xff) { out[outpos++] = moff; i |= 0x01; } @@ -256,23 +201,21 @@ void *diff_delta(void *from_buf, unsigned long from_size, msize >>= 8; if (msize & 0xff) { out[outpos++] = msize; i |= 0x20; } - *orig = i; - } - - if (max_size && outpos > max_size) { - free(out); - delta_cleanup(&bdf); - return NULL; + *op = i; } - /* next time around the largest possible output is 1 + 4 + 3 */ - if (outpos > outsize - 8) { + if (outpos >= outsize - MAX_OP_SIZE) { void *tmp = out; outsize = outsize * 3 / 2; - out = realloc(out, outsize); + if (max_size && outsize >= max_size) + outsize = max_size + MAX_OP_SIZE + 1; + if (max_size && outpos > max_size) + out = NULL; + else + out = realloc(out, outsize); if (!out) { free(tmp); - delta_cleanup(&bdf); + free(hash); return NULL; } } @@ -281,7 +224,7 @@ void *diff_delta(void *from_buf, unsigned long from_size, if (inscnt) out[outpos - inscnt - 1] = inscnt; - delta_cleanup(&bdf); + free(hash); *delta_size = outpos; return out; } diff --git a/environment.c b/environment.c index 251e53ca09..16c08f0697 100644 --- a/environment.c +++ b/environment.c @@ -17,6 +17,7 @@ int only_use_symrefs = 0; int repository_format_version = 0; char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8"; int shared_repository = 0; +const char *apply_default_whitespace = NULL; static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file; @@ -100,7 +100,7 @@ fall_back_3way () { } prec=4 -dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary= +dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary= ws= while case "$#" in 0) break;; esac do @@ -133,6 +133,9 @@ do --sk|--ski|--skip) skip=t; shift ;; + --whitespace=*) + ws=$1; shift ;; + --) shift; break ;; -*) @@ -171,10 +174,11 @@ else exit 1 } - # -b, -s, -u and -k flags are kept for the resuming session after - # a patch failure. + # -b, -s, -u, -k and --whitespace flags are kept for the + # resuming session after a patch failure. # -3 and -i can and must be given when resuming. echo "$binary" >"$dotest/binary" + echo " $ws" >"$dotest/whitespace" echo "$sign" >"$dotest/sign" echo "$utf8" >"$dotest/utf8" echo "$keep" >"$dotest/keep" @@ -202,6 +206,7 @@ if test "$(cat "$dotest/keep")" = t then keep=-k fi +ws=`cat "$dotest/whitespace"` if test "$(cat "$dotest/sign")" = t then SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e ' @@ -355,7 +360,7 @@ do case "$resolved" in '') - git-apply $binary --index "$dotest/patch" + git-apply $binary --index $ws "$dotest/patch" apply_status=$? ;; t) diff --git a/git-annotate.perl b/git-annotate.perl index f9c2c6caf5..cd476c7629 100755 --- a/git-annotate.perl +++ b/git-annotate.perl @@ -31,7 +31,7 @@ our ($help, $longrev, $rename, $starting_rev, $rev_file) = (0, 0, 1); my $rc = GetOptions( "long|l" => \$longrev, "help|h" => \$help, "rename|r" => \$rename, - "rev-file|S" => \$rev_file); + "rev-file|S=s" => \$rev_file); if (!$rc or $help) { usage(); } @@ -174,7 +174,8 @@ sub git_rev_list { my $revlist; if ($rev_file) { - open($revlist, '<' . $rev_file); + open($revlist, '<' . $rev_file) + or die "Failed to open $rev_file : $!"; } else { $revlist = open_pipe("git-rev-list","--parents","--remove-empty",$rev,"--",$file) or die "Failed to exec git-rev-list: $!"; @@ -304,6 +305,12 @@ sub _git_diff_parse { } $ri++; + } elsif (m/^\\/) { + ; + # Skip \No newline at end of file. + # But this can be internationalized, so only look + # for an initial \ + } else { if (substr($_,1) ne get_line($slines,$ri) ) { die sprintf("Line %d (%d) does not match:\n|%s\n|%s\n%s => %s\n", diff --git a/git-cvsserver.perl b/git-cvsserver.perl index d20d1a8c4b..3c588c9d64 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -53,6 +53,7 @@ my $methods = { 'Entry' => \&req_Entry, 'Modified' => \&req_Modified, 'Unchanged' => \&req_Unchanged, + 'Questionable' => \&req_Questionable, 'Argument' => \&req_Argument, 'Argumentx' => \&req_Argument, 'expand-modules' => \&req_expandmodules, @@ -63,6 +64,7 @@ my $methods = { 'ci' => \&req_ci, 'diff' => \&req_diff, 'log' => \&req_log, + 'rlog' => \&req_log, 'tag' => \&req_CATCHALL, 'status' => \&req_status, 'admin' => \&req_CATCHALL, @@ -459,6 +461,22 @@ sub req_Unchanged #$log->debug("req_Unchanged : $data"); } +# Questionable filename \n +# Response expected: no. Additional data: no. +# Tell the server to check whether filename should be ignored, +# and if not, next time the server sends responses, send (in +# a M response) `?' followed by the directory and filename. +# filename must not contain `/'; it needs to be a file in the +# directory named by the most recent Directory request. +sub req_Questionable +{ + my ( $cmd, $data ) = @_; + + $state->{entries}{$state->{directory}.$data}{questionable} = 1; + + #$log->debug("req_Questionable : $data"); +} + # Argument text \n # Response expected: no. Save argument for use in a subsequent command. # Arguments accumulate until an argument-using command is given, at which @@ -568,7 +586,7 @@ sub req_co # print some information to the client print "MT +updated\n"; - print "MT text U\n"; + print "MT text U \n"; if ( defined ( $git->{dir} ) and $git->{dir} ne "./" ) { print "MT fname $checkout_path/$git->{dir}$git->{name}\n"; @@ -579,9 +597,9 @@ sub req_co print "MT -updated\n"; # instruct client we're sending a file to put in this path - print "Created $checkout_path/" . ( defined ( $git->{dir} ) ? $git->{dir} . "/" : "" ) . "\n"; + print "Created $checkout_path/" . ( defined ( $git->{dir} ) and $git->{dir} ne "./" ? $git->{dir} . "/" : "" ) . "\n"; - print $state->{CVSROOT} . "/$module/" . ( defined ( $git->{dir} ) ? $git->{dir} . "/" : "" ) . "$git->{name}\n"; + print $state->{CVSROOT} . "/$module/" . ( defined ( $git->{dir} ) and $git->{dir} ne "./" ? $git->{dir} . "/" : "" ) . "$git->{name}\n"; # this is an "entries" line print "/$git->{name}/1.$git->{revision}///\n"; @@ -612,6 +630,26 @@ sub req_update argsplit("update"); + # + # It may just be a client exploring the available heads/modukles + # in that case, list them as top level directories and leave it + # at that. Eclipse uses this technique to offer you a list of + # projects (heads in this case) to checkout. + # + if ($state->{module} eq '') { + print "E cvs update: Updating .\n"; + opendir HEADS, $state->{CVSROOT} . '/refs/heads'; + while (my $head = readdir(HEADS)) { + if (-f $state->{CVSROOT} . '/refs/heads/' . $head) { + print "E cvs update: New directory `$head'\n"; + } + } + closedir HEADS; + print "ok\n"; + return 1; + } + + # Grab a handle to the SQLite db and do any necessary updates my $updater = GITCVS::updater->new($state->{CVSROOT}, $state->{module}, $log); diff --git a/git-mv.perl b/git-mv.perl index 2ea852c918..fe9c40e1b9 100755 --- a/git-mv.perl +++ b/git-mv.perl @@ -19,25 +19,26 @@ EOT exit(1); } -my $GIT_DIR = `git rev-parse --git-dir`; -exit 1 if $?; # rev-parse would have given "not a git dir" message. -chomp($GIT_DIR); - our ($opt_n, $opt_f, $opt_h, $opt_k, $opt_v); getopts("hnfkv") || usage; usage() if $opt_h; @ARGV >= 1 or usage; +my $GIT_DIR = `git rev-parse --git-dir`; +exit 1 if $?; # rev-parse would have given "not a git dir" message. +chomp($GIT_DIR); + my (@srcArgs, @dstArgs, @srcs, @dsts); my ($src, $dst, $base, $dstDir); +# remove any trailing slash in arguments +for (@ARGV) { s/\/*$//; } + my $argCount = scalar @ARGV; if (-d $ARGV[$argCount-1]) { $dstDir = $ARGV[$argCount-1]; - # remove any trailing slash - $dstDir =~ s/\/$//; @srcArgs = @ARGV[0..$argCount-2]; - + foreach $src (@srcArgs) { $base = $src; $base =~ s/^.*\///; @@ -46,10 +47,14 @@ if (-d $ARGV[$argCount-1]) { } } else { - if ($argCount != 2) { + if ($argCount < 2) { + print "Error: need at least two arguments\n"; + exit(1); + } + if ($argCount > 2) { print "Error: moving to directory '" . $ARGV[$argCount-1] - . "' not possible; not exisiting\n"; + . "' not possible; not existing\n"; exit(1); } @srcArgs = ($ARGV[0]); @@ -57,6 +62,16 @@ else { $dstDir = ""; } +# normalize paths, needed to compare against versioned files and update-index +# also, this is nicer to end-users by doing ".//a/./b/.//./c" ==> "a/b/c" +for (@srcArgs, @dstArgs) { + s|^\./||; + s|/\./|/| while (m|/\./|); + s|//+|/|g; + # Also "a/b/../c" ==> "a/c" + 1 while (s,(^|/)[^/]+/\.\./,$1,); +} + my (@allfiles,@srcfiles,@dstfiles); my $safesrc; my (%overwritten, %srcForDst); diff --git a/git-svnimport.perl b/git-svnimport.perl index 86837edbdd..639aa41861 100755 --- a/git-svnimport.perl +++ b/git-svnimport.perl @@ -13,6 +13,7 @@ use strict; use warnings; use Getopt::Std; +use File::Copy; use File::Spec; use File::Temp qw(tempfile); use File::Path qw(mkpath); @@ -68,10 +69,16 @@ if ($opt_M) { push (@mergerx, qr/$opt_M/); } +# Absolutize filename now, since we will have chdir'ed by the time we +# get around to opening it. +$opt_A = File::Spec->rel2abs($opt_A) if $opt_A; + our %users = (); -if ($opt_A) { - die "Cannot open $opt_A\n" unless -f $opt_A; - open(my $authors,$opt_A); +our $users_file = undef; +sub read_users($) { + $users_file = File::Spec->rel2abs(@_); + die "Cannot open $users_file\n" unless -f $users_file; + open(my $authors,$users_file); while(<$authors>) { chomp; next unless /^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/; @@ -302,6 +309,14 @@ EOM -d $git_dir or die "Could not create git subdir ($git_dir).\n"; +my $default_authors = "$git_dir/svn-authors"; +if ($opt_A) { + read_users($opt_A); + copy($opt_A,$default_authors) or die "Copy failed: $!"; +} else { + read_users($default_authors) if -f $default_authors; +} + open BRANCHES,">>", "$git_dir/svn2git"; sub node_kind($$$) { @@ -498,8 +513,8 @@ sub commit { if (not defined $author) { $author_name = $author_email = "unknown"; - } elsif ($opt_A) { - die "User $author is not listed in $opt_A\n" + } elsif (defined $users_file) { + die "User $author is not listed in $users_file\n" unless exists $users{$author}; ($author_name,$author_email) = @{$users{$author}}; } elsif ($author =~ /^(.*?)\s+<(.*)>$/) { diff --git a/pack-objects.c b/pack-objects.c index 21ee572f48..136a7f5aad 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -99,7 +99,7 @@ static int reused_delta = 0; static int pack_revindex_ix(struct packed_git *p) { - unsigned long ui = (unsigned long)(long)p; + unsigned long ui = (unsigned long)p; int i; ui = ui ^ (ui >> 16); /* defeat structure alignment */ @@ -151,10 +151,15 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u break; continue; } - if (read_ref(git_path("%s", path), sha1) < 0) + if (read_ref(git_path("%s", path), sha1) < 0) { + fprintf(stderr, "%s points nowhere!", path); continue; - if (!has_sha1_file(sha1)) + } + if (!has_sha1_file(sha1)) { + fprintf(stderr, "%s does not point to a valid " + "commit object!", path); continue; + } retval = fn(path, sha1); if (retval) break; diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh new file mode 100755 index 0000000000..cae179436c --- /dev/null +++ b/t/t8001-annotate.sh @@ -0,0 +1,89 @@ +#!/bin/sh + +test_description='git-annotate' +. ./test-lib.sh + +test_expect_success \ + 'prepare reference tree' \ + 'echo "1A quick brown fox jumps over the" >file && + echo "lazy dog" >>file && + git add file + GIT_AUTHOR_NAME="A" git commit -a -m "Initial."' + +test_expect_success \ + 'check all lines blamed on A' \ + '[ $(git annotate file | awk "{print \$3}" | grep -c "A") == 2 ]' + +test_expect_success \ + 'Setup new lines blamed on B' \ + 'echo "2A quick brown fox jumps over the" >>file && + echo "lazy dog" >> file && + GIT_AUTHOR_NAME="B" git commit -a -m "Second."' + +test_expect_success \ + 'Two lines blamed on A' \ + '[ $(git annotate file | awk "{print \$3}" | grep -c "A") == 2 ]' + +test_expect_success \ + 'Two lines blamed on B' \ + '[ $(git annotate file | awk "{print \$3}" | grep -c "B") == 2 ]' + +test_expect_success \ + 'merge-setup part 1' \ + 'git checkout -b branch1 master && + echo "3A slow green fox jumps into the" >> file && + echo "well." >> file && + GIT_AUTHOR_NAME="B1" git commit -a -m "Branch1-1"' + +test_expect_success \ + 'Two lines blamed on A' \ + '[ $(git annotate file | awk "{print \$3}" | grep -c "^A$") == 2 ]' + +test_expect_success \ + 'Two lines blamed on B' \ + '[ $(git annotate file | awk "{print \$3}" | grep -c "^B$") == 2 ]' + +test_expect_success \ + 'Two lines blamed on B1' \ + '[ $(git annotate file | awk "{print \$3}" | grep -c "^B1$") == 2 ]' + +test_expect_success \ + 'merge-setup part 2' \ + 'git checkout -b branch2 master && + sed -i -e "s/2A quick brown/4A quick brown lazy dog/" file && + GIT_AUTHOR_NAME="B2" git commit -a -m "Branch2-1"' + +test_expect_success \ + 'Two lines blamed on A' \ + '[ $(git annotate file | awk "{print \$3}" | grep -c "^A$") == 2 ]' + +test_expect_success \ + 'One line blamed on B' \ + '[ $(git annotate file | awk "{print \$3}" | grep -c "^B$") == 1 ]' + +test_expect_success \ + 'One line blamed on B2' \ + '[ $(git annotate file | awk "{print \$3}" | grep -c "^B2$") == 1 ]' + + +test_expect_success \ + 'merge-setup part 3' \ + 'git pull . branch1' + +test_expect_success \ + 'Two lines blamed on A' \ + '[ $(git annotate file | awk "{print \$3}" | grep -c "^A$") == 2 ]' + +test_expect_success \ + 'One line blamed on B' \ + '[ $(git annotate file | awk "{print \$3}" | grep -c "^B$") == 1 ]' + +test_expect_success \ + 'Two lines blamed on B1' \ + '[ $(git annotate file | awk "{print \$3}" | grep -c "^B1$") == 2 ]' + +test_expect_success \ + 'One line blamed on B2' \ + '[ $(git annotate file | awk "{print \$3}" | grep -c "^B2$") == 1 ]' + +test_done |