From 2c3c4399477533329579ca6b84824ef0b125914f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 5 Sep 2007 13:01:37 -0700 Subject: Implement git gc --auto This implements a new option "git gc --auto". When gc.auto is set to a positive value, and the object database has accumulated roughly that many number of loose objects, this runs a lightweight version of "git gc". The primary difference from the full "git gc" is that it does not pass "-a" option to "git repack", which means we do not try to repack _everything_, but only repack incrementally. We still do "git prune-packed". The default threshold is arbitrarily set by yours truly to: - not trigger it for fully unpacked git v0.99 history; - do trigger it for fully unpacked git v1.0.0 history; - not trigger it for incremental update to git v1.0.0 starting from fully packed git v0.99 history. This patch does not add invocation of the "auto repacking". It is left to key Porcelain commands that could produce tons of loose objects to add a call to "git gc --auto" after they are done their work. Signed-off-by: Junio C Hamano diff --git a/builtin-gc.c b/builtin-gc.c index 9397482..093b3dd 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -20,6 +20,7 @@ static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]"; static int pack_refs = 1; static int aggressive_window = -1; +static int gc_auto_threshold = 6700; #define MAX_ADD 10 static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL}; @@ -28,6 +29,8 @@ static const char *argv_repack[MAX_ADD] = {"repack", "-a", "-d", "-l", NULL}; static const char *argv_prune[] = {"prune", NULL}; static const char *argv_rerere[] = {"rerere", "gc", NULL}; +static const char *argv_repack_auto[] = {"repack", "-d", "-l", NULL}; + static int gc_config(const char *var, const char *value) { if (!strcmp(var, "gc.packrefs")) { @@ -41,6 +44,10 @@ static int gc_config(const char *var, const char *value) aggressive_window = git_config_int(var, value); return 0; } + if (!strcmp(var, "gc.auto")) { + gc_auto_threshold = git_config_int(var, value); + return 0; + } return git_default_config(var, value); } @@ -57,10 +64,49 @@ static void append_option(const char **cmd, const char *opt, int max_length) cmd[i] = NULL; } +static int need_to_gc(void) +{ + /* + * Quickly check if a "gc" is needed, by estimating how + * many loose objects there are. Because SHA-1 is evenly + * distributed, we can check only one and get a reasonable + * estimate. + */ + char path[PATH_MAX]; + const char *objdir = get_object_directory(); + DIR *dir; + struct dirent *ent; + int auto_threshold; + int num_loose = 0; + int needed = 0; + + if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) { + warning("insanely long object directory %.*s", 50, objdir); + return 0; + } + dir = opendir(path); + if (!dir) + return 0; + + auto_threshold = (gc_auto_threshold + 255) / 256; + while ((ent = readdir(dir)) != NULL) { + if (strspn(ent->d_name, "0123456789abcdef") != 38 || + ent->d_name[38] != '\0') + continue; + if (++num_loose > auto_threshold) { + needed = 1; + break; + } + } + closedir(dir); + return needed; +} + int cmd_gc(int argc, const char **argv, const char *prefix) { int i; int prune = 0; + int auto_gc = 0; char buf[80]; git_config(gc_config); @@ -82,12 +128,28 @@ int cmd_gc(int argc, const char **argv, const char *prefix) } continue; } - /* perhaps other parameters later... */ + if (!strcmp(arg, "--auto")) { + if (gc_auto_threshold <= 0) + return 0; + auto_gc = 1; + continue; + } break; } if (i != argc) usage(builtin_gc_usage); + if (auto_gc) { + /* + * Auto-gc should be least intrusive as possible. + */ + prune = 0; + for (i = 0; i < ARRAY_SIZE(argv_repack_auto); i++) + argv_repack[i] = argv_repack_auto[i]; + if (!need_to_gc()) + return 0; + } + if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD)) return error(FAILED_RUN, argv_pack_refs[0]); -- cgit v0.10.2-6-g49f6 From d4bb43ee273528064192848165f93f8fc3512be1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 5 Sep 2007 14:59:59 -0700 Subject: Invoke "git gc --auto" from commit, merge, am and rebase. The point of auto gc is to pack new objects created in loose format, so a good rule of thumb is where we do update-ref after creating a new commit. Signed-off-by: Junio C Hamano diff --git a/git-am.sh b/git-am.sh index 6809aa0..4db4701 100755 --- a/git-am.sh +++ b/git-am.sh @@ -466,6 +466,8 @@ do "$GIT_DIR"/hooks/post-applypatch fi + git gc --auto + go_next done diff --git a/git-commit.sh b/git-commit.sh index 1d04f1f..d22d35e 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -652,6 +652,7 @@ git rerere if test "$ret" = 0 then + git gc --auto if test -x "$GIT_DIR"/hooks/post-commit then "$GIT_DIR"/hooks/post-commit diff --git a/git-merge.sh b/git-merge.sh index 3a01db0..697bec2 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -82,6 +82,7 @@ finish () { ;; *) git update-ref -m "$rlogm" HEAD "$1" "$head" || exit 1 + git gc --auto ;; esac ;; diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index abc2b1c..8258b7a 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -307,6 +307,8 @@ do_next () { rm -rf "$DOTEST" && warn "Successfully rebased and updated $HEADNAME." + git gc --auto + exit } -- cgit v0.10.2-6-g49f6 From b449f4cfc972929b638b90d375b8960c37790618 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:05 +0200 Subject: Rework strbuf API and semantics. The gory details are explained in strbuf.h. The change of semantics this patch enforces is that the embeded buffer has always a '\0' character after its last byte, to always make it a C-string. The offs-by-one changes are all related to that very change. A strbuf can be used to store byte arrays, or as an extended string library. The `buf' member can be passed to any C legacy string function, because strbuf operations always ensure there is a terminating \0 at the end of the buffer, not accounted in the `len' field of the structure. A strbuf can be used to generate a string/buffer whose final size is not really known, and then "strbuf_detach" can be used to get the built buffer, and keep the wrapping "strbuf" structure usable for further work again. Other interesting feature: strbuf_grow(sb, size) ensure that there is enough allocated space in `sb' to put `size' new octets of data in the buffer. It helps avoiding reallocating data for nothing when the problem the strbuf helps to solve has a known typical size. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/archive-tar.c b/archive-tar.c index 66fe3e3..a0763c5 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -166,7 +166,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1)); } else { if (verbose) - fprintf(stderr, "%.*s\n", path->len, path->buf); + fprintf(stderr, "%.*s\n", (int)path->len, path->buf); if (S_ISDIR(mode) || S_ISGITLINK(mode)) { *header.typeflag = TYPEFLAG_DIR; mode = (mode | 0777) & ~tar_umask; diff --git a/fast-import.c b/fast-import.c index 078079d..2f7baf4 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1595,7 +1595,7 @@ static void read_next_command(void) } else { struct recent_command *rc; - command_buf.buf = NULL; + strbuf_detach(&command_buf); read_line(&command_buf, stdin, '\n'); if (command_buf.eof) return; @@ -1649,7 +1649,6 @@ static void *cmd_data (size_t *size) size_t sz = 8192, term_len = command_buf.len - 5 - 2; length = 0; buffer = xmalloc(sz); - command_buf.buf = NULL; for (;;) { read_line(&command_buf, stdin, '\n'); if (command_buf.eof) @@ -1657,11 +1656,11 @@ static void *cmd_data (size_t *size) if (term_len == command_buf.len && !strcmp(term, command_buf.buf)) break; - ALLOC_GROW(buffer, length + command_buf.len, sz); + ALLOC_GROW(buffer, length + command_buf.len + 1, sz); memcpy(buffer + length, command_buf.buf, - command_buf.len - 1); - length += command_buf.len - 1; + command_buf.len); + length += command_buf.len; buffer[length++] = '\n'; } free(term); @@ -2101,7 +2100,7 @@ static void cmd_new_commit(void) } /* file_change* */ - while (!command_buf.eof && command_buf.len > 1) { + while (!command_buf.eof && command_buf.len > 0) { if (!prefixcmp(command_buf.buf, "M ")) file_change_m(b); else if (!prefixcmp(command_buf.buf, "D ")) @@ -2256,7 +2255,7 @@ static void cmd_reset_branch(void) else b = new_branch(sp); read_next_command(); - if (!cmd_from(b) && command_buf.len > 1) + if (!cmd_from(b) && command_buf.len > 0) unread_command_buf = 1; } @@ -2273,7 +2272,7 @@ static void cmd_checkpoint(void) static void cmd_progress(void) { - fwrite(command_buf.buf, 1, command_buf.len - 1, stdout); + fwrite(command_buf.buf, 1, command_buf.len, stdout); fputc('\n', stdout); fflush(stdout); skip_optional_lf(); diff --git a/mktree.c b/mktree.c index d86dde8..86de5eb 100644 --- a/mktree.c +++ b/mktree.c @@ -92,7 +92,6 @@ int main(int ac, char **av) strbuf_init(&sb); while (1) { - int len; char *ptr, *ntr; unsigned mode; enum object_type type; @@ -101,7 +100,6 @@ int main(int ac, char **av) read_line(&sb, stdin, line_termination); if (sb.eof) break; - len = sb.len; ptr = sb.buf; /* Input is non-recursive ls-tree output format * mode SP type SP sha1 TAB name @@ -111,7 +109,7 @@ int main(int ac, char **av) die("input format error: %s", sb.buf); ptr = ntr + 1; /* type */ ntr = strchr(ptr, ' '); - if (!ntr || sb.buf + len <= ntr + 41 || + if (!ntr || sb.buf + sb.len <= ntr + 40 || ntr[41] != '\t' || get_sha1_hex(ntr + 1, sha1)) die("input format error: %s", sb.buf); diff --git a/strbuf.c b/strbuf.c index e33d06b..7136de1 100644 --- a/strbuf.c +++ b/strbuf.c @@ -2,40 +2,113 @@ #include "strbuf.h" void strbuf_init(struct strbuf *sb) { - sb->buf = NULL; - sb->eof = sb->alloc = sb->len = 0; + memset(sb, 0, sizeof(*sb)); } -static void strbuf_begin(struct strbuf *sb) { +void strbuf_release(struct strbuf *sb) { free(sb->buf); + memset(sb, 0, sizeof(*sb)); +} + +void strbuf_reset(struct strbuf *sb) { + if (sb->len) + strbuf_setlen(sb, 0); + sb->eof = 0; +} + +char *strbuf_detach(struct strbuf *sb) { + char *res = sb->buf; strbuf_init(sb); + return res; +} + +void strbuf_grow(struct strbuf *sb, size_t extra) { + if (sb->len + extra + 1 <= sb->len) + die("you want to use way too much memory"); + ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); +} + +void strbuf_add(struct strbuf *sb, const void *data, size_t len) { + strbuf_grow(sb, len); + memcpy(sb->buf + sb->len, data, len); + strbuf_setlen(sb, sb->len + len); +} + +void strbuf_addf(struct strbuf *sb, const char *fmt, ...) { + int len; + va_list ap; + + va_start(ap, fmt); + len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); + va_end(ap); + if (len < 0) { + len = 0; + } + if (len >= strbuf_avail(sb)) { + strbuf_grow(sb, len); + va_start(ap, fmt); + len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); + va_end(ap); + if (len >= strbuf_avail(sb)) { + die("this should not happen, your snprintf is broken"); + } + } + strbuf_setlen(sb, sb->len + len); } -static void inline strbuf_add(struct strbuf *sb, int ch) { - if (sb->alloc <= sb->len) { - sb->alloc = sb->alloc * 3 / 2 + 16; - sb->buf = xrealloc(sb->buf, sb->alloc); +size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) { + size_t res; + + strbuf_grow(sb, size); + res = fread(sb->buf + sb->len, 1, size, f); + if (res > 0) { + strbuf_setlen(sb, sb->len + res); } - sb->buf[sb->len++] = ch; + return res; } -static void strbuf_end(struct strbuf *sb) { - strbuf_add(sb, 0); +ssize_t strbuf_read(struct strbuf *sb, int fd) +{ + size_t oldlen = sb->len; + + for (;;) { + ssize_t cnt; + + strbuf_grow(sb, 8192); + cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1); + if (cnt < 0) { + strbuf_setlen(sb, oldlen); + return -1; + } + if (!cnt) + break; + sb->len += cnt; + } + + sb->buf[sb->len] = '\0'; + return sb->len - oldlen; } void read_line(struct strbuf *sb, FILE *fp, int term) { int ch; - strbuf_begin(sb); if (feof(fp)) { + strbuf_release(sb); sb->eof = 1; return; } + + strbuf_reset(sb); while ((ch = fgetc(fp)) != EOF) { if (ch == term) break; - strbuf_add(sb, ch); + strbuf_grow(sb, 1); + sb->buf[sb->len++] = ch; } - if (ch == EOF && sb->len == 0) + if (ch == EOF && sb->len == 0) { + strbuf_release(sb); sb->eof = 1; - strbuf_end(sb); + } + + strbuf_grow(sb, 1); + sb->buf[sb->len] = '\0'; } diff --git a/strbuf.h b/strbuf.h index 74cc012..b40dc99 100644 --- a/strbuf.h +++ b/strbuf.h @@ -1,13 +1,95 @@ #ifndef STRBUF_H #define STRBUF_H + +/* + * Strbuf's can be use in many ways: as a byte array, or to store arbitrary + * long, overflow safe strings. + * + * Strbufs has some invariants that are very important to keep in mind: + * + * 1. the ->buf member is always malloc-ed, hence strbuf's can be used to + * build complex strings/buffers whose final size isn't easily known. + * + * It is legal to copy the ->buf pointer away. Though if you want to reuse + * the strbuf after that, setting ->buf to NULL isn't legal. + * `strbuf_detach' is the operation that detachs a buffer from its shell + * while keeping the shell valid wrt its invariants. + * + * 2. the ->buf member is a byte array that has at least ->len + 1 bytes + * allocated. The extra byte is used to store a '\0', allowing the ->buf + * member to be a valid C-string. Every strbuf function ensure this + * invariant is preserved. + * + * Note that it is OK to "play" with the buffer directly if you work it + * that way: + * + * strbuf_grow(sb, SOME_SIZE); + * // ... here the memory areay starting at sb->buf, and of length + * // sb_avail(sb) is all yours, and you are sure that sb_avail(sb) is at + * // least SOME_SIZE + * strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE); + * + * Of course, SOME_OTHER_SIZE must be smaller or equal to sb_avail(sb). + * + * Doing so is safe, though if it has to be done in many places, adding the + * missing API to the strbuf module is the way to go. + * + * XXX: do _not_ assume that the area that is yours is of size ->alloc - 1 + * even if it's true in the current implementation. Alloc is somehow a + * "private" member that should not be messed with. + */ + +#include + struct strbuf { - int alloc; - int len; + size_t alloc; + size_t len; int eof; char *buf; }; +#define STRBUF_INIT { 0, 0, 0, NULL } + +/*----- strbuf life cycle -----*/ extern void strbuf_init(struct strbuf *); +extern void strbuf_release(struct strbuf *); +extern void strbuf_reset(struct strbuf *); +extern char *strbuf_detach(struct strbuf *); + +/*----- strbuf size related -----*/ +static inline size_t strbuf_avail(struct strbuf *sb) { + return sb->alloc ? sb->alloc - sb->len - 1 : 0; +} +static inline void strbuf_setlen(struct strbuf *sb, size_t len) { + assert (len < sb->alloc); + sb->len = len; + sb->buf[len] = '\0'; +} + +extern void strbuf_grow(struct strbuf *, size_t); + +/*----- add data in your buffer -----*/ +static inline void strbuf_addch(struct strbuf *sb, int c) { + strbuf_grow(sb, 1); + sb->buf[sb->len++] = c; + sb->buf[sb->len] = '\0'; +} + +extern void strbuf_add(struct strbuf *, const void *, size_t); +static inline void strbuf_addstr(struct strbuf *sb, const char *s) { + strbuf_add(sb, s, strlen(s)); +} +static inline void strbuf_addbuf(struct strbuf *sb, struct strbuf *sb2) { + strbuf_add(sb, sb2->buf, sb2->len); +} + +__attribute__((format(printf,2,3))) +extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); + +extern size_t strbuf_fread(struct strbuf *, size_t, FILE *); +/* XXX: if read fails, any partial read is undone */ +extern ssize_t strbuf_read(struct strbuf *, int fd); + extern void read_line(struct strbuf *, FILE *, int); #endif /* STRBUF_H */ -- cgit v0.10.2-6-g49f6 From 7a604f16b71e3bfd1c6e30d400f05be918e5376e Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:06 +0200 Subject: Simplify strbuf uses in archive-tar.c using strbuf API This is just cleaner way to deal with strbufs, using its API rather than reinventing it in the module (e.g. strbuf_append_string is just the plain strbuf_addstr function, and it was used to perform what strbuf_addch does anyways). Signed-off-by: Junio C Hamano diff --git a/archive-tar.c b/archive-tar.c index a0763c5..c84d7c0 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -78,19 +78,6 @@ static void write_trailer(void) } } -static void strbuf_append_string(struct strbuf *sb, const char *s) -{ - int slen = strlen(s); - int total = sb->len + slen; - if (total + 1 > sb->alloc) { - sb->buf = xrealloc(sb->buf, total + 1); - sb->alloc = total + 1; - } - memcpy(sb->buf + sb->len, s, slen); - sb->len = total; - sb->buf[total] = '\0'; -} - /* * pax extended header records have the format "%u %s=%s\n". %u contains * the size of the whole string (including the %u), the first %s is the @@ -100,26 +87,17 @@ static void strbuf_append_string(struct strbuf *sb, const char *s) static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword, const char *value, unsigned int valuelen) { - char *p; - int len, total, tmp; + int len, tmp; /* "%u %s=%s\n" */ len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1; for (tmp = len; tmp > 9; tmp /= 10) len++; - total = sb->len + len; - if (total > sb->alloc) { - sb->buf = xrealloc(sb->buf, total); - sb->alloc = total; - } - - p = sb->buf; - p += sprintf(p, "%u %s=", len, keyword); - memcpy(p, value, valuelen); - p += valuelen; - *p = '\n'; - sb->len = total; + strbuf_grow(sb, len); + strbuf_addf(sb, "%u %s=", len, keyword); + strbuf_add(sb, value, valuelen); + strbuf_addch(sb, '\n'); } static unsigned int ustar_header_chksum(const struct ustar_header *header) @@ -153,8 +131,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, struct strbuf ext_header; memset(&header, 0, sizeof(header)); - ext_header.buf = NULL; - ext_header.len = ext_header.alloc = 0; + strbuf_init(&ext_header); if (!sha1) { *header.typeflag = TYPEFLAG_GLOBAL_HEADER; @@ -225,8 +202,8 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, if (ext_header.len > 0) { write_entry(sha1, NULL, 0, ext_header.buf, ext_header.len); - free(ext_header.buf); } + strbuf_release(&ext_header); write_blocked(&header, sizeof(header)); if (S_ISREG(mode) && buffer && size > 0) write_blocked(buffer, size); @@ -235,11 +212,11 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, static void write_global_extended_header(const unsigned char *sha1) { struct strbuf ext_header; - ext_header.buf = NULL; - ext_header.len = ext_header.alloc = 0; + + strbuf_init(&ext_header); strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40); write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len); - free(ext_header.buf); + strbuf_release(&ext_header); } static int git_tar_config(const char *var, const char *value) @@ -260,28 +237,18 @@ static int write_tar_entry(const unsigned char *sha1, const char *base, int baselen, const char *filename, unsigned mode, int stage) { - static struct strbuf path; + static struct strbuf path = STRBUF_INIT; int filenamelen = strlen(filename); void *buffer; enum object_type type; unsigned long size; - if (!path.alloc) { - path.buf = xmalloc(PATH_MAX); - path.alloc = PATH_MAX; - path.len = path.eof = 0; - } - if (path.alloc < baselen + filenamelen + 1) { - free(path.buf); - path.buf = xmalloc(baselen + filenamelen + 1); - path.alloc = baselen + filenamelen + 1; - } - memcpy(path.buf, base, baselen); - memcpy(path.buf + baselen, filename, filenamelen); - path.len = baselen + filenamelen; - path.buf[path.len] = '\0'; + strbuf_grow(&path, MAX(PATH_MAX, baselen + filenamelen + 1)); + strbuf_reset(&path); + strbuf_add(&path, base, baselen); + strbuf_add(&path, filename, filenamelen); if (S_ISDIR(mode) || S_ISGITLINK(mode)) { - strbuf_append_string(&path, "/"); + strbuf_addch(&path, '/'); buffer = NULL; size = 0; } else { -- cgit v0.10.2-6-g49f6 From 4a241d79c9020dcafb1f254774bcab200171ab46 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:07 +0200 Subject: fast-import: Use strbuf API, and simplify cmd_data() This patch features the use of strbuf_detach, and prevent the programmer to mess with allocation directly. The code is as efficent as before, just more concise and more straightforward. Signed-off-by: Junio C Hamano diff --git a/fast-import.c b/fast-import.c index 2f7baf4..74ff0fd 100644 --- a/fast-import.c +++ b/fast-import.c @@ -340,7 +340,7 @@ static struct tag *last_tag; /* Input stream parsing */ static whenspec_type whenspec = WHENSPEC_RAW; -static struct strbuf command_buf; +static struct strbuf command_buf = STRBUF_INIT; static int unread_command_buf; static struct recent_command cmd_hist = {&cmd_hist, &cmd_hist, NULL}; static struct recent_command *cmd_tail = &cmd_hist; @@ -1638,17 +1638,16 @@ static void cmd_mark(void) static void *cmd_data (size_t *size) { - size_t length; - char *buffer; + struct strbuf buffer; + strbuf_init(&buffer); if (prefixcmp(command_buf.buf, "data ")) die("Expected 'data n' command, found: %s", command_buf.buf); if (!prefixcmp(command_buf.buf + 5, "<<")) { char *term = xstrdup(command_buf.buf + 5 + 2); - size_t sz = 8192, term_len = command_buf.len - 5 - 2; - length = 0; - buffer = xmalloc(sz); + size_t term_len = command_buf.len - 5 - 2; + for (;;) { read_line(&command_buf, stdin, '\n'); if (command_buf.eof) @@ -1656,21 +1655,18 @@ static void *cmd_data (size_t *size) if (term_len == command_buf.len && !strcmp(term, command_buf.buf)) break; - ALLOC_GROW(buffer, length + command_buf.len + 1, sz); - memcpy(buffer + length, - command_buf.buf, - command_buf.len); - length += command_buf.len; - buffer[length++] = '\n'; + strbuf_addbuf(&buffer, &command_buf); + strbuf_addch(&buffer, '\n'); } free(term); } else { - size_t n = 0; + size_t n = 0, length; + length = strtoul(command_buf.buf + 5, NULL, 10); - buffer = xmalloc(length); + while (n < length) { - size_t s = fread(buffer + n, 1, length - n, stdin); + size_t s = strbuf_fread(&buffer, length - n, stdin); if (!s && feof(stdin)) die("EOF in data (%lu bytes remaining)", (unsigned long)(length - n)); @@ -1679,8 +1675,8 @@ static void *cmd_data (size_t *size) } skip_optional_lf(); - *size = length; - return buffer; + *size = buffer.len; + return strbuf_detach(&buffer); } static int validate_raw_date(const char *src, char *result, int maxlen) -- cgit v0.10.2-6-g49f6 From d52bc66152834dff3fb5f32a54f6ed57730f58c6 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:08 +0200 Subject: mktree: Simplify write_tree() using strbuf API Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/mktree.c b/mktree.c index 86de5eb..2e84889 100644 --- a/mktree.c +++ b/mktree.c @@ -44,30 +44,23 @@ static int ent_compare(const void *a_, const void *b_) static void write_tree(unsigned char *sha1) { - char *buffer; - unsigned long size, offset; + struct strbuf buf; + size_t size; int i; qsort(entries, used, sizeof(*entries), ent_compare); for (size = i = 0; i < used; i++) size += 32 + entries[i]->len; - buffer = xmalloc(size); - offset = 0; + strbuf_init(&buf); + strbuf_grow(&buf, size); for (i = 0; i < used; i++) { struct treeent *ent = entries[i]; - - if (offset + ent->len + 100 < size) { - size = alloc_nr(offset + ent->len + 100); - buffer = xrealloc(buffer, size); - } - offset += sprintf(buffer + offset, "%o ", ent->mode); - offset += sprintf(buffer + offset, "%s", ent->name); - buffer[offset++] = 0; - hashcpy((unsigned char*)buffer + offset, ent->sha1); - offset += 20; + strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0'); + strbuf_add(&buf, ent->sha1, 20); } - write_sha1_file(buffer, offset, tree_type, sha1); + + write_sha1_file(buf.buf, buf.len, tree_type, sha1); } static const char mktree_usage[] = "git-mktree [-z]"; -- cgit v0.10.2-6-g49f6 From af6eb82262e35687aa8f00d688e327cb845973fa Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:09 +0200 Subject: Use strbuf API in apply, blame, commit-tree and diff Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/builtin-apply.c b/builtin-apply.c index 976ec77..90e328e 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -12,6 +12,7 @@ #include "blob.h" #include "delta.h" #include "builtin.h" +#include "strbuf.h" /* * --check turns on checking that the working tree matches the @@ -181,34 +182,21 @@ static void say_patch_name(FILE *output, const char *pre, struct patch *patch, c static void *read_patch_file(int fd, unsigned long *sizep) { - unsigned long size = 0, alloc = CHUNKSIZE; - void *buffer = xmalloc(alloc); + struct strbuf buf; - for (;;) { - ssize_t nr = alloc - size; - if (nr < 1024) { - alloc += CHUNKSIZE; - buffer = xrealloc(buffer, alloc); - nr = alloc - size; - } - nr = xread(fd, (char *) buffer + size, nr); - if (!nr) - break; - if (nr < 0) - die("git-apply: read returned %s", strerror(errno)); - size += nr; - } - *sizep = size; + strbuf_init(&buf); + if (strbuf_read(&buf, fd) < 0) + die("git-apply: read returned %s", strerror(errno)); + *sizep = buf.len; /* * Make sure that we have some slop in the buffer * so that we can do speculative "memcmp" etc, and * see to it that it is NUL-filled. */ - if (alloc < size + SLOP) - buffer = xrealloc(buffer, size + SLOP); - memset((char *) buffer + size, 0, SLOP); - return buffer; + strbuf_grow(&buf, SLOP); + memset(buf.buf + buf.len, 0, SLOP); + return strbuf_detach(&buf); } static unsigned long linelen(const char *buffer, unsigned long size) diff --git a/builtin-blame.c b/builtin-blame.c index dc88a95..1b1e6da 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -18,6 +18,7 @@ #include "cache-tree.h" #include "path-list.h" #include "mailmap.h" +#include "strbuf.h" static char blame_usage[] = "git-blame [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-w] [-L n,m] [-S ] [-M] [-C] [-C] [--contents ] [--incremental] [commit] [--] file\n" @@ -2001,11 +2002,10 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con struct commit *commit; struct origin *origin; unsigned char head_sha1[20]; - char *buf; + struct strbuf buf; const char *ident; int fd; time_t now; - unsigned long fin_size; int size, len; struct cache_entry *ce; unsigned mode; @@ -2023,9 +2023,11 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con origin = make_origin(commit, path); + strbuf_init(&buf); if (!contents_from || strcmp("-", contents_from)) { struct stat st; const char *read_from; + unsigned long fin_size; if (contents_from) { if (stat(contents_from, &st) < 0) @@ -2038,19 +2040,19 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con read_from = path; } fin_size = xsize_t(st.st_size); - buf = xmalloc(fin_size+1); mode = canon_mode(st.st_mode); switch (st.st_mode & S_IFMT) { case S_IFREG: fd = open(read_from, O_RDONLY); if (fd < 0) die("cannot open %s", read_from); - if (read_in_full(fd, buf, fin_size) != fin_size) + if (strbuf_read(&buf, fd) != xsize_t(st.st_size)) die("cannot read %s", read_from); break; case S_IFLNK: - if (readlink(read_from, buf, fin_size+1) != fin_size) + if (readlink(read_from, buf.buf, buf.alloc) != fin_size) die("cannot readlink %s", read_from); + buf.len = fin_size; break; default: die("unsupported file type %s", read_from); @@ -2059,26 +2061,13 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con else { /* Reading from stdin */ contents_from = "standard input"; - buf = NULL; - fin_size = 0; mode = 0; - while (1) { - ssize_t cnt = 8192; - buf = xrealloc(buf, fin_size + cnt); - cnt = xread(0, buf + fin_size, cnt); - if (cnt < 0) - die("read error %s from stdin", - strerror(errno)); - if (!cnt) - break; - fin_size += cnt; - } - buf = xrealloc(buf, fin_size + 1); + if (strbuf_read(&buf, 0) < 0) + die("read error %s from stdin", strerror(errno)); } - buf[fin_size] = 0; - origin->file.ptr = buf; - origin->file.size = fin_size; - pretend_sha1_file(buf, fin_size, OBJ_BLOB, origin->blob_sha1); + origin->file.ptr = buf.buf; + origin->file.size = buf.len; + pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1); commit->util = origin; /* diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c index ccbcbe3..bc9502c 100644 --- a/builtin-commit-tree.c +++ b/builtin-commit-tree.c @@ -8,42 +8,13 @@ #include "tree.h" #include "builtin.h" #include "utf8.h" +#include "strbuf.h" #define BLOCKING (1ul << 14) /* * FIXME! Share the code with "write-tree.c" */ -static void init_buffer(char **bufp, unsigned int *sizep) -{ - *bufp = xmalloc(BLOCKING); - *sizep = 0; -} - -static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...) -{ - char one_line[2048]; - va_list args; - int len; - unsigned long alloc, size, newsize; - char *buf; - - va_start(args, fmt); - len = vsnprintf(one_line, sizeof(one_line), fmt, args); - va_end(args); - size = *sizep; - newsize = size + len + 1; - alloc = (size + 32767) & ~32767; - buf = *bufp; - if (newsize > alloc) { - alloc = (newsize + 32767) & ~32767; - buf = xrealloc(buf, alloc); - *bufp = buf; - } - *sizep = newsize - 1; - memcpy(buf + size, one_line, len); -} - static void check_valid(unsigned char *sha1, enum object_type expect) { enum object_type type = sha1_object_info(sha1, NULL); @@ -87,9 +58,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) int parents = 0; unsigned char tree_sha1[20]; unsigned char commit_sha1[20]; - char comment[1000]; - char *buffer; - unsigned int size; + struct strbuf buffer; int encoding_is_utf8; git_config(git_default_config); @@ -118,8 +87,9 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) /* Not having i18n.commitencoding is the same as having utf-8 */ encoding_is_utf8 = is_encoding_utf8(git_commit_encoding); - init_buffer(&buffer, &size); - add_buffer(&buffer, &size, "tree %s\n", sha1_to_hex(tree_sha1)); + strbuf_init(&buffer); + strbuf_grow(&buffer, 8192); /* should avoid reallocs for the headers */ + strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree_sha1)); /* * NOTE! This ordering means that the same exact tree merged with a @@ -127,26 +97,24 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) * if everything else stays the same. */ for (i = 0; i < parents; i++) - add_buffer(&buffer, &size, "parent %s\n", sha1_to_hex(parent_sha1[i])); + strbuf_addf(&buffer, "parent %s\n", sha1_to_hex(parent_sha1[i])); /* Person/date information */ - add_buffer(&buffer, &size, "author %s\n", git_author_info(1)); - add_buffer(&buffer, &size, "committer %s\n", git_committer_info(1)); + strbuf_addf(&buffer, "author %s\n", git_author_info(1)); + strbuf_addf(&buffer, "committer %s\n", git_committer_info(1)); if (!encoding_is_utf8) - add_buffer(&buffer, &size, - "encoding %s\n", git_commit_encoding); - add_buffer(&buffer, &size, "\n"); + strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding); + strbuf_addch(&buffer, '\n'); /* And add the comment */ - while (fgets(comment, sizeof(comment), stdin) != NULL) - add_buffer(&buffer, &size, "%s", comment); + if (strbuf_read(&buffer, 0) < 0) + die("git-commit-tree: read returned %s", strerror(errno)); /* And check the encoding */ - buffer[size] = '\0'; - if (encoding_is_utf8 && !is_utf8(buffer)) + if (encoding_is_utf8 && !is_utf8(buffer.buf)) fprintf(stderr, commit_utf8_warn); - if (!write_sha1_file(buffer, size, commit_type, commit_sha1)) { + if (!write_sha1_file(buffer.buf, buffer.len, commit_type, commit_sha1)) { printf("%s\n", sha1_to_hex(commit_sha1)); return 0; } diff --git a/diff.c b/diff.c index 0d30d05..c054b23 100644 --- a/diff.c +++ b/diff.c @@ -9,6 +9,7 @@ #include "xdiff-interface.h" #include "color.h" #include "attr.h" +#include "strbuf.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -1545,26 +1546,16 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int static int populate_from_stdin(struct diff_filespec *s) { -#define INCREMENT 1024 - char *buf; - unsigned long size; - ssize_t got; - - size = 0; - buf = NULL; - while (1) { - buf = xrealloc(buf, size + INCREMENT); - got = xread(0, buf + size, INCREMENT); - if (!got) - break; /* EOF */ - if (got < 0) - return error("error while reading from stdin %s", + struct strbuf buf; + + strbuf_init(&buf); + if (strbuf_read(&buf, 0) < 0) + return error("error while reading from stdin %s", strerror(errno)); - size += got; - } + s->should_munmap = 0; - s->data = buf; - s->size = size; + s->size = buf.len; + s->data = strbuf_detach(&buf); s->should_free = 1; return 0; } -- cgit v0.10.2-6-g49f6 From 19b358e8daafdfe5a5d25ff7972e041493b05156 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:10 +0200 Subject: Use strbuf API in buitin-rerere.c Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/builtin-rerere.c b/builtin-rerere.c index 29d057c..98d7702 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -1,6 +1,7 @@ #include "builtin.h" #include "cache.h" #include "path-list.h" +#include "strbuf.h" #include "xdiff/xdiff.h" #include "xdiff-interface.h" @@ -66,41 +67,20 @@ static int write_rr(struct path_list *rr, int out_fd) return commit_lock_file(&write_lock); } -struct buffer { - char *ptr; - int nr, alloc; -}; - -static void append_line(struct buffer *buffer, const char *line) -{ - int len = strlen(line); - - if (buffer->nr + len > buffer->alloc) { - buffer->alloc = alloc_nr(buffer->nr + len); - buffer->ptr = xrealloc(buffer->ptr, buffer->alloc); - } - memcpy(buffer->ptr + buffer->nr, line, len); - buffer->nr += len; -} - -static void clear_buffer(struct buffer *buffer) -{ - free(buffer->ptr); - buffer->ptr = NULL; - buffer->nr = buffer->alloc = 0; -} - static int handle_file(const char *path, unsigned char *sha1, const char *output) { SHA_CTX ctx; char buf[1024]; int hunk = 0, hunk_no = 0; - struct buffer minus = { NULL, 0, 0 }, plus = { NULL, 0, 0 }; - struct buffer *one = &minus, *two = + + struct strbuf minus, plus; + struct strbuf *one = &minus, *two = + FILE *f = fopen(path, "r"); FILE *out; + strbuf_init(&minus); + strbuf_init(&plus); + if (!f) return error("Could not open %s", path); @@ -122,36 +102,36 @@ static int handle_file(const char *path, else if (!prefixcmp(buf, "=======")) hunk = 2; else if (!prefixcmp(buf, ">>>>>>> ")) { - int one_is_longer = (one->nr > two->nr); - int common_len = one_is_longer ? two->nr : one->nr; - int cmp = memcmp(one->ptr, two->ptr, common_len); + int one_is_longer = (one->len > two->len); + int common_len = one_is_longer ? two->len : one->len; + int cmp = memcmp(one->buf, two->buf, common_len); hunk_no++; hunk = 0; if ((cmp > 0) || ((cmp == 0) && one_is_longer)) { - struct buffer *swap = one; + struct strbuf *swap = one; one = two; two = swap; } if (out) { fputs("<<<<<<<\n", out); - fwrite(one->ptr, one->nr, 1, out); + fwrite(one->buf, one->len, 1, out); fputs("=======\n", out); - fwrite(two->ptr, two->nr, 1, out); + fwrite(two->buf, two->len, 1, out); fputs(">>>>>>>\n", out); } if (sha1) { - SHA1_Update(&ctx, one->ptr, one->nr); + SHA1_Update(&ctx, one->buf, one->len); SHA1_Update(&ctx, "\0", 1); - SHA1_Update(&ctx, two->ptr, two->nr); + SHA1_Update(&ctx, two->buf, two->len); SHA1_Update(&ctx, "\0", 1); } - clear_buffer(one); - clear_buffer(two); + strbuf_release(one); + strbuf_release(two); } else if (hunk == 1) - append_line(one, buf); + strbuf_addstr(one, buf); else if (hunk == 2) - append_line(two, buf); + strbuf_addstr(two, buf); else if (out) fputs(buf, out); } -- cgit v0.10.2-6-g49f6 From 5242bcbb638f031818e9ebd4467c8e55d5a06bfb Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:11 +0200 Subject: Use strbuf API in cache-tree.c Should even be marginally faster. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/cache-tree.c b/cache-tree.c index 077f034..76af6f5 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "strbuf.h" #include "tree.h" #include "cache-tree.h" @@ -235,8 +236,7 @@ static int update_one(struct cache_tree *it, int missing_ok, int dryrun) { - unsigned long size, offset; - char *buffer; + struct strbuf buffer; int i; if (0 <= it->entry_count && has_sha1_file(it->sha1)) @@ -293,9 +293,8 @@ static int update_one(struct cache_tree *it, /* * Then write out the tree object for this level. */ - size = 8192; - buffer = xmalloc(size); - offset = 0; + strbuf_init(&buffer); + strbuf_grow(&buffer, 8192); for (i = 0; i < entries; i++) { struct cache_entry *ce = cache[i]; @@ -332,15 +331,9 @@ static int update_one(struct cache_tree *it, if (!ce->ce_mode) continue; /* entry being removed */ - if (size < offset + entlen + 100) { - size = alloc_nr(offset + entlen + 100); - buffer = xrealloc(buffer, size); - } - offset += sprintf(buffer + offset, - "%o %.*s", mode, entlen, path + baselen); - buffer[offset++] = 0; - hashcpy((unsigned char*)buffer + offset, sha1); - offset += 20; + strbuf_grow(&buffer, entlen + 100); + strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0'); + strbuf_add(&buffer, sha1, 20); #if DEBUG fprintf(stderr, "cache-tree update-one %o %.*s\n", @@ -349,10 +342,10 @@ static int update_one(struct cache_tree *it, } if (dryrun) - hash_sha1_file(buffer, offset, tree_type, it->sha1); + hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1); else - write_sha1_file(buffer, offset, tree_type, it->sha1); - free(buffer); + write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1); + strbuf_release(&buffer); it->entry_count = i; #if DEBUG fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n", @@ -378,12 +371,10 @@ int cache_tree_update(struct cache_tree *it, return 0; } -static void *write_one(struct cache_tree *it, +static void write_one(struct cache_tree *it, char *path, int pathlen, - char *buffer, - unsigned long *size, - unsigned long *offset) + struct strbuf *buffer) { int i; @@ -393,13 +384,9 @@ static void *write_one(struct cache_tree *it, * tree-sha1 (missing if invalid) * subtree_nr "cache-tree" entries for subtrees. */ - if (*size < *offset + pathlen + 100) { - *size = alloc_nr(*offset + pathlen + 100); - buffer = xrealloc(buffer, *size); - } - *offset += sprintf(buffer + *offset, "%.*s%c%d %d\n", - pathlen, path, 0, - it->entry_count, it->subtree_nr); + strbuf_grow(buffer, pathlen + 100); + strbuf_add(buffer, path, pathlen); + strbuf_addf(buffer, "%c%d %d\n", 0, it->entry_count, it->subtree_nr); #if DEBUG if (0 <= it->entry_count) @@ -412,8 +399,7 @@ static void *write_one(struct cache_tree *it, #endif if (0 <= it->entry_count) { - hashcpy((unsigned char*)buffer + *offset, it->sha1); - *offset += 20; + strbuf_add(buffer, it->sha1, 20); } for (i = 0; i < it->subtree_nr; i++) { struct cache_tree_sub *down = it->down[i]; @@ -423,21 +409,20 @@ static void *write_one(struct cache_tree *it, prev->name, prev->namelen) <= 0) die("fatal - unsorted cache subtree"); } - buffer = write_one(down->cache_tree, down->name, down->namelen, - buffer, size, offset); + write_one(down->cache_tree, down->name, down->namelen, buffer); } - return buffer; } void *cache_tree_write(struct cache_tree *root, unsigned long *size_p) { char path[PATH_MAX]; - unsigned long size = 8192; - char *buffer = xmalloc(size); + struct strbuf buffer; - *size_p = 0; path[0] = 0; - return write_one(root, path, 0, buffer, &size, size_p); + strbuf_init(&buffer); + write_one(root, path, 0, &buffer); + *size_p = buffer.len; + return strbuf_detach(&buffer); } static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) -- cgit v0.10.2-6-g49f6 From f1696ee398e92bcea3cdc7b3da85d8e0f77f6c50 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 10 Sep 2007 12:35:04 +0200 Subject: Strbuf API extensions and fixes. * Add strbuf_rtrim to remove trailing spaces. * Add strbuf_insert to insert data at a given position. * Off-by one fix in strbuf_addf: strbuf_avail() does not counts the final \0 so the overflow test for snprintf is the strict comparison. This is not critical as the growth mechanism chosen will always allocate _more_ memory than asked, so the second test will not fail. It's some kind of miracle though. * Add size extension hints for strbuf_init and strbuf_read. If 0, default applies, else: + initial buffer has the given size for strbuf_init. + first growth checks it has at least this size rather than the default 8192. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/archive-tar.c b/archive-tar.c index 0612bb6..cc94cf3 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -132,7 +132,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, struct strbuf ext_header; memset(&header, 0, sizeof(header)); - strbuf_init(&ext_header); + strbuf_init(&ext_header, 0); if (!sha1) { *header.typeflag = TYPEFLAG_GLOBAL_HEADER; @@ -214,7 +214,7 @@ static void write_global_extended_header(const unsigned char *sha1) { struct strbuf ext_header; - strbuf_init(&ext_header); + strbuf_init(&ext_header, 0); strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40); write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len); strbuf_release(&ext_header); diff --git a/builtin-apply.c b/builtin-apply.c index 90e328e..988e85f 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -184,8 +184,8 @@ static void *read_patch_file(int fd, unsigned long *sizep) { struct strbuf buf; - strbuf_init(&buf); - if (strbuf_read(&buf, fd) < 0) + strbuf_init(&buf, 0); + if (strbuf_read(&buf, fd, 0) < 0) die("git-apply: read returned %s", strerror(errno)); *sizep = buf.len; diff --git a/builtin-blame.c b/builtin-blame.c index 1b1e6da..b004f06 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -2023,7 +2023,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con origin = make_origin(commit, path); - strbuf_init(&buf); + strbuf_init(&buf, 0); if (!contents_from || strcmp("-", contents_from)) { struct stat st; const char *read_from; @@ -2046,7 +2046,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con fd = open(read_from, O_RDONLY); if (fd < 0) die("cannot open %s", read_from); - if (strbuf_read(&buf, fd) != xsize_t(st.st_size)) + if (strbuf_read(&buf, fd, 0) != xsize_t(st.st_size)) die("cannot read %s", read_from); break; case S_IFLNK: @@ -2062,7 +2062,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con /* Reading from stdin */ contents_from = "standard input"; mode = 0; - if (strbuf_read(&buf, 0) < 0) + if (strbuf_read(&buf, 0, 0) < 0) die("read error %s from stdin", strerror(errno)); } origin->file.ptr = buf.buf; diff --git a/builtin-checkout-index.c b/builtin-checkout-index.c index 75377b9..153ba7d 100644 --- a/builtin-checkout-index.c +++ b/builtin-checkout-index.c @@ -274,7 +274,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) struct strbuf buf; if (all) die("git-checkout-index: don't mix '--all' and '--stdin'"); - strbuf_init(&buf); + strbuf_init(&buf, 0); while (1) { char *path_name; const char *p; diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c index bc9502c..325334f 100644 --- a/builtin-commit-tree.c +++ b/builtin-commit-tree.c @@ -87,8 +87,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) /* Not having i18n.commitencoding is the same as having utf-8 */ encoding_is_utf8 = is_encoding_utf8(git_commit_encoding); - strbuf_init(&buffer); - strbuf_grow(&buffer, 8192); /* should avoid reallocs for the headers */ + strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */ strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree_sha1)); /* @@ -107,7 +106,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) strbuf_addch(&buffer, '\n'); /* And add the comment */ - if (strbuf_read(&buffer, 0) < 0) + if (strbuf_read(&buffer, 0, 0) < 0) die("git-commit-tree: read returned %s", strerror(errno)); /* And check the encoding */ diff --git a/builtin-rerere.c b/builtin-rerere.c index 98d7702..826d346 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -78,8 +78,8 @@ static int handle_file(const char *path, FILE *f = fopen(path, "r"); FILE *out; - strbuf_init(&minus); - strbuf_init(&plus); + strbuf_init(&minus, 0); + strbuf_init(&plus, 0); if (!f) return error("Could not open %s", path); diff --git a/builtin-update-index.c b/builtin-update-index.c index a7a4574..9240a28 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -303,7 +303,7 @@ static void update_one(const char *path, const char *prefix, int prefix_length) static void read_index_info(int line_termination) { struct strbuf buf; - strbuf_init(&buf); + strbuf_init(&buf, 0); while (1) { char *ptr, *tab; char *path_name; @@ -716,7 +716,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) } if (read_from_stdin) { struct strbuf buf; - strbuf_init(&buf); + strbuf_init(&buf, 0); while (1) { char *path_name; const char *p; diff --git a/cache-tree.c b/cache-tree.c index 76af6f5..8f53c99 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -293,8 +293,7 @@ static int update_one(struct cache_tree *it, /* * Then write out the tree object for this level. */ - strbuf_init(&buffer); - strbuf_grow(&buffer, 8192); + strbuf_init(&buffer, 8192); for (i = 0; i < entries; i++) { struct cache_entry *ce = cache[i]; @@ -419,7 +418,7 @@ void *cache_tree_write(struct cache_tree *root, unsigned long *size_p) struct strbuf buffer; path[0] = 0; - strbuf_init(&buffer); + strbuf_init(&buffer, 0); write_one(root, path, 0, &buffer); *size_p = buffer.len; return strbuf_detach(&buffer); diff --git a/diff.c b/diff.c index 26d7bb9..7290309 100644 --- a/diff.c +++ b/diff.c @@ -1548,8 +1548,8 @@ static int populate_from_stdin(struct diff_filespec *s) { struct strbuf buf; - strbuf_init(&buf); - if (strbuf_read(&buf, 0) < 0) + strbuf_init(&buf, 0); + if (strbuf_read(&buf, 0, 0) < 0) return error("error while reading from stdin %s", strerror(errno)); diff --git a/fast-import.c b/fast-import.c index 74ff0fd..2c0bfb9 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1640,7 +1640,7 @@ static void *cmd_data (size_t *size) { struct strbuf buffer; - strbuf_init(&buffer); + strbuf_init(&buffer, 0); if (prefixcmp(command_buf.buf, "data ")) die("Expected 'data n' command, found: %s", command_buf.buf); @@ -2318,7 +2318,7 @@ int main(int argc, const char **argv) git_config(git_default_config); alloc_objects(object_entry_alloc); - strbuf_init(&command_buf); + strbuf_init(&command_buf, 0); atom_table = xcalloc(atom_table_sz, sizeof(struct atom_str*)); branch_table = xcalloc(branch_table_sz, sizeof(struct branch*)); avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*)); diff --git a/fetch.c b/fetch.c index 811be87..dd6ed9e 100644 --- a/fetch.c +++ b/fetch.c @@ -218,7 +218,7 @@ int pull_targets_stdin(char ***target, const char ***write_ref) int targets = 0, targets_alloc = 0; struct strbuf buf; *target = NULL; *write_ref = NULL; - strbuf_init(&buf); + strbuf_init(&buf, 0); while (1) { char *rf_one = NULL; char *tg_one; diff --git a/mktree.c b/mktree.c index 2e84889..3891cd9 100644 --- a/mktree.c +++ b/mktree.c @@ -51,9 +51,8 @@ static void write_tree(unsigned char *sha1) qsort(entries, used, sizeof(*entries), ent_compare); for (size = i = 0; i < used; i++) size += 32 + entries[i]->len; - strbuf_init(&buf); - strbuf_grow(&buf, size); + strbuf_init(&buf, size); for (i = 0; i < used; i++) { struct treeent *ent = entries[i]; strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0'); @@ -83,7 +82,7 @@ int main(int ac, char **av) av++; } - strbuf_init(&sb); + strbuf_init(&sb, 0); while (1) { char *ptr, *ntr; unsigned mode; diff --git a/strbuf.c b/strbuf.c index 7136de1..d919047 100644 --- a/strbuf.c +++ b/strbuf.c @@ -1,8 +1,10 @@ #include "cache.h" #include "strbuf.h" -void strbuf_init(struct strbuf *sb) { +void strbuf_init(struct strbuf *sb, size_t hint) { memset(sb, 0, sizeof(*sb)); + if (hint) + strbuf_grow(sb, hint); } void strbuf_release(struct strbuf *sb) { @@ -18,7 +20,7 @@ void strbuf_reset(struct strbuf *sb) { char *strbuf_detach(struct strbuf *sb) { char *res = sb->buf; - strbuf_init(sb); + strbuf_init(sb, 0); return res; } @@ -28,6 +30,24 @@ void strbuf_grow(struct strbuf *sb, size_t extra) { ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); } +void strbuf_rtrim(struct strbuf *sb) +{ + while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1])) + sb->len--; + sb->buf[sb->len] = '\0'; +} + +void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len) { + strbuf_grow(sb, len); + if (pos >= sb->len) { + pos = sb->len; + } else { + memmove(sb->buf + pos + len, sb->buf + pos, sb->len - pos); + } + memcpy(sb->buf + pos, data, len); + strbuf_setlen(sb, sb->len + len); +} + void strbuf_add(struct strbuf *sb, const void *data, size_t len) { strbuf_grow(sb, len); memcpy(sb->buf + sb->len, data, len); @@ -44,12 +64,12 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...) { if (len < 0) { len = 0; } - if (len >= strbuf_avail(sb)) { + if (len > strbuf_avail(sb)) { strbuf_grow(sb, len); va_start(ap, fmt); len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); va_end(ap); - if (len >= strbuf_avail(sb)) { + if (len > strbuf_avail(sb)) { die("this should not happen, your snprintf is broken"); } } @@ -67,14 +87,14 @@ size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) { return res; } -ssize_t strbuf_read(struct strbuf *sb, int fd) +ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint) { size_t oldlen = sb->len; + strbuf_grow(sb, hint ? hint : 8192); for (;;) { ssize_t cnt; - strbuf_grow(sb, 8192); cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1); if (cnt < 0) { strbuf_setlen(sb, oldlen); @@ -83,6 +103,7 @@ ssize_t strbuf_read(struct strbuf *sb, int fd) if (!cnt) break; sb->len += cnt; + strbuf_grow(sb, 8192); } sb->buf[sb->len] = '\0'; diff --git a/strbuf.h b/strbuf.h index b40dc99..21fc111 100644 --- a/strbuf.h +++ b/strbuf.h @@ -51,7 +51,7 @@ struct strbuf { #define STRBUF_INIT { 0, 0, 0, NULL } /*----- strbuf life cycle -----*/ -extern void strbuf_init(struct strbuf *); +extern void strbuf_init(struct strbuf *, size_t); extern void strbuf_release(struct strbuf *); extern void strbuf_reset(struct strbuf *); extern char *strbuf_detach(struct strbuf *); @@ -68,6 +68,9 @@ static inline void strbuf_setlen(struct strbuf *sb, size_t len) { extern void strbuf_grow(struct strbuf *, size_t); +/*----- content related -----*/ +extern void strbuf_rtrim(struct strbuf *); + /*----- add data in your buffer -----*/ static inline void strbuf_addch(struct strbuf *sb, int c) { strbuf_grow(sb, 1); @@ -75,6 +78,9 @@ static inline void strbuf_addch(struct strbuf *sb, int c) { sb->buf[sb->len] = '\0'; } +/* inserts after pos, or appends if pos >= sb->len */ +extern void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t); + extern void strbuf_add(struct strbuf *, const void *, size_t); static inline void strbuf_addstr(struct strbuf *sb, const char *s) { strbuf_add(sb, s, strlen(s)); @@ -88,7 +94,7 @@ extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); extern size_t strbuf_fread(struct strbuf *, size_t, FILE *); /* XXX: if read fails, any partial read is undone */ -extern ssize_t strbuf_read(struct strbuf *, int fd); +extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint); extern void read_line(struct strbuf *, FILE *, int); -- cgit v0.10.2-6-g49f6 From 4acfd1b799acf43642a28a22cc794266c25129ef Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 10 Sep 2007 12:35:05 +0200 Subject: Change semantics of interpolate to work like snprintf. Also fix many off-by-ones and a useless memset. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/commit.c b/commit.c index 99f65ce..25781cc 100644 --- a/commit.c +++ b/commit.c @@ -923,15 +923,14 @@ long format_commit_message(const struct commit *commit, const void *format, do { char *buf = *buf_p; - unsigned long space = *space_p; + unsigned long len; - space = interpolate(buf, space, format, + len = interpolate(buf, *space_p, format, table, ARRAY_SIZE(table)); - if (!space) + if (len < *space_p) break; - buf = xrealloc(buf, space); + ALLOC_GROW(buf, len + 1, *space_p); *buf_p = buf; - *space_p = space; } while (1); interp_clear_table(table, ARRAY_SIZE(table)); diff --git a/interpolate.c b/interpolate.c index 0082677..3de5832 100644 --- a/interpolate.c +++ b/interpolate.c @@ -44,9 +44,8 @@ void interp_clear_table(struct interp *table, int ninterps) * { "%%", "%"}, * } * - * Returns 0 on a successful substitution pass that fits in result, - * Returns a number of bytes needed to hold the full substituted - * string otherwise. + * Returns the length of the substituted string (not including the final \0). + * Like with snprintf, if the result is >= reslen, then it overflowed. */ unsigned long interpolate(char *result, unsigned long reslen, @@ -61,8 +60,6 @@ unsigned long interpolate(char *result, unsigned long reslen, int i; char c; - memset(result, 0, reslen); - while ((c = *src)) { if (c == '%') { /* Try to match an interpolation string. */ @@ -78,9 +75,9 @@ unsigned long interpolate(char *result, unsigned long reslen, value = interps[i].value; valuelen = strlen(value); - if (newlen + valuelen + 1 < reslen) { + if (newlen + valuelen < reslen) { /* Substitute. */ - strncpy(dest, value, valuelen); + memcpy(dest, value, valuelen); dest += valuelen; } newlen += valuelen; @@ -95,8 +92,9 @@ unsigned long interpolate(char *result, unsigned long reslen, newlen++; } - if (newlen + 1 < reslen) - return 0; - else - return newlen + 2; + /* XXX: the previous loop always keep room for the ending NUL, + we just need to check if there was room for a NUL in the first place */ + if (reslen > 0) + *dest = '\0'; + return newlen; } -- cgit v0.10.2-6-g49f6 From 674d1727305211f7ade4ade70440220f74f55162 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 10 Sep 2007 12:35:06 +0200 Subject: Rework pretty_print_commit to use strbufs instead of custom buffers. Also remove the "len" parameter, as: (1) it was used as a max boundary, and every caller used ~0u (2) we check for final NUL no matter what, so it doesn't help for speed. As a result most of the pp_* function takes 3 arguments less, and we need a lot less local variables, this makes the code way more readable, and easier to extend if needed. This patch also fixes some spacing and cosmetic issues. This patch also fixes (as a side effect) a memory leak intoruced in builtin-archive.c at commit df4a394f (fmt was xmalloc'ed and not free'd) Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/builtin-archive.c b/builtin-archive.c index a90c65c..b50d5ad 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -84,14 +84,16 @@ static int run_remote_archiver(const char *remote, int argc, static void *format_subst(const struct commit *commit, const char *format, unsigned long *sizep) { - unsigned long len = *sizep, result_len = 0; + unsigned long len = *sizep; const char *a = format; - char *result = NULL; + struct strbuf result; + struct strbuf fmt; + + strbuf_init(&result, 0); + strbuf_init(&fmt, 0); for (;;) { const char *b, *c; - char *fmt, *formatted = NULL; - unsigned long a_len, fmt_len, formatted_len, allocated = 0; b = memmem(a, len, "$Format:", 8); if (!b || a + len < b + 9) @@ -100,32 +102,23 @@ static void *format_subst(const struct commit *commit, const char *format, if (!c) break; - a_len = b - a; - fmt_len = c - b - 8; - fmt = xmalloc(fmt_len + 1); - memcpy(fmt, b + 8, fmt_len); - fmt[fmt_len] = '\0'; - - formatted_len = format_commit_message(commit, fmt, &formatted, - &allocated); - free(fmt); - result = xrealloc(result, result_len + a_len + formatted_len); - memcpy(result + result_len, a, a_len); - memcpy(result + result_len + a_len, formatted, formatted_len); - result_len += a_len + formatted_len; + strbuf_reset(&fmt); + strbuf_add(&fmt, b + 8, c - b - 8); + + strbuf_add(&result, a, b - a); + format_commit_message(commit, fmt.buf, &result); len -= c + 1 - a; a = c + 1; } - if (result && len) { - result = xrealloc(result, result_len + len); - memcpy(result + result_len, a, len); - result_len += len; + if (result.len && len) { + strbuf_add(&result, a, len); } - *sizep = result_len; + *sizep = result.len; - return result; + strbuf_release(&fmt); + return strbuf_detach(&result); } static void *convert_to_archive(const char *path, diff --git a/builtin-branch.c b/builtin-branch.c index 5f5c182..3da8b55 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -268,23 +268,22 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, } if (verbose) { - char *subject = NULL; - unsigned long subject_len = 0; + struct strbuf subject; const char *sub = " **** invalid ref ****"; + strbuf_init(&subject, 0); + commit = lookup_commit(item->sha1); if (commit && !parse_commit(commit)) { - pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, - &subject, &subject_len, 0, - NULL, NULL, 0); - sub = subject; + pretty_print_commit(CMIT_FMT_ONELINE, commit, + &subject, 0, NULL, NULL, 0); + sub = subject.buf; } printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color), maxwidth, item->name, branch_get_color(COLOR_BRANCH_RESET), find_unique_abbrev(item->sha1, abbrev), sub); - if (subject) - free(subject); + strbuf_release(&subject); } else { printf("%c %s%s%s\n", c, branch_get_color(color), item->name, branch_get_color(COLOR_BRANCH_RESET)); diff --git a/builtin-log.c b/builtin-log.c index fa81c25..e1d3e7d 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -763,13 +763,13 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) sign = '-'; if (verbose) { - char *buf = NULL; - unsigned long buflen = 0; - pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, - &buf, &buflen, 0, NULL, NULL, 0); + struct strbuf buf; + strbuf_init(&buf, 0); + pretty_print_commit(CMIT_FMT_ONELINE, commit, + &buf, 0, NULL, NULL, 0); printf("%c %s %s\n", sign, - sha1_to_hex(commit->object.sha1), buf); - free(buf); + sha1_to_hex(commit->object.sha1), buf.buf); + strbuf_release(&buf); } else { printf("%c %s\n", sign, diff --git a/builtin-rev-list.c b/builtin-rev-list.c index ac551d5..4fd4b6b 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -80,13 +80,12 @@ static void show_commit(struct commit *commit) putchar('\n'); if (revs.verbose_header) { - char *buf = NULL; - unsigned long buflen = 0; - pretty_print_commit(revs.commit_format, commit, ~0, - &buf, &buflen, - revs.abbrev, NULL, NULL, revs.date_mode); - printf("%s%c", buf, hdr_termination); - free(buf); + struct strbuf buf; + strbuf_init(&buf, 0); + pretty_print_commit(revs.commit_format, commit, + &buf, revs.abbrev, NULL, NULL, revs.date_mode); + printf("%s%c", buf.buf, hdr_termination); + strbuf_release(&buf); } maybe_flush_or_die(stdout, "stdout"); if (commit->parents) { diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 4fa87f6..07a0c23 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -259,16 +259,15 @@ static void join_revs(struct commit_list **list_p, static void show_one_commit(struct commit *commit, int no_name) { - char *pretty = NULL; + struct strbuf pretty; const char *pretty_str = "(unavailable)"; - unsigned long pretty_len = 0; struct commit_name *name = commit->util; + strbuf_init(&pretty, 0); if (commit->object.parsed) { - pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, - &pretty, &pretty_len, - 0, NULL, NULL, 0); - pretty_str = pretty; + pretty_print_commit(CMIT_FMT_ONELINE, commit, + &pretty, 0, NULL, NULL, 0); + pretty_str = pretty.buf; } if (!prefixcmp(pretty_str, "[PATCH] ")) pretty_str += 8; @@ -289,7 +288,7 @@ static void show_one_commit(struct commit *commit, int no_name) find_unique_abbrev(commit->object.sha1, 7)); } puts(pretty_str); - free(pretty); + strbuf_release(&pretty); } static char *ref_name[MAX_REVS + 1]; diff --git a/commit.c b/commit.c index 25781cc..6602e2c 100644 --- a/commit.c +++ b/commit.c @@ -458,11 +458,11 @@ void clear_commit_marks(struct commit *commit, unsigned int mark) /* * Generic support for pretty-printing the header */ -static int get_one_line(const char *msg, unsigned long len) +static int get_one_line(const char *msg) { int ret = 0; - while (len--) { + for (;;) { char c = *msg++; if (!c) break; @@ -485,31 +485,24 @@ static int is_rfc2047_special(char ch) return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_')); } -static int add_rfc2047(char *buf, const char *line, int len, +static void add_rfc2047(struct strbuf *sb, const char *line, int len, const char *encoding) { - char *bp = buf; - int i, needquote; - char q_encoding[128]; - const char *q_encoding_fmt = "=?%s?q?"; + int i, last; - for (i = needquote = 0; !needquote && i < len; i++) { + for (i = 0; i < len; i++) { int ch = line[i]; if (non_ascii(ch)) - needquote++; - if ((i + 1 < len) && - (ch == '=' && line[i+1] == '?')) - needquote++; + goto needquote; + if ((i + 1 < len) && (ch == '=' && line[i+1] == '?')) + goto needquote; } - if (!needquote) - return sprintf(buf, "%.*s", len, line); - - i = snprintf(q_encoding, sizeof(q_encoding), q_encoding_fmt, encoding); - if (sizeof(q_encoding) < i) - die("Insanely long encoding name %s", encoding); - memcpy(bp, q_encoding, i); - bp += i; - for (i = 0; i < len; i++) { + strbuf_add(sb, line, len); + return; + +needquote: + strbuf_addf(sb, "=?%s?q?", encoding); + for (i = last = 0; i < len; i++) { unsigned ch = line[i] & 0xFF; /* * We encode ' ' using '=20' even though rfc2047 @@ -518,15 +511,13 @@ static int add_rfc2047(char *buf, const char *line, int len, * leave the underscore in place. */ if (is_rfc2047_special(ch) || ch == ' ') { - sprintf(bp, "=%02X", ch); - bp += 3; + strbuf_add(sb, line + last, i - last); + strbuf_addf(sb, "=%02X", ch); + last = i + 1; } - else - *bp++ = ch; } - memcpy(bp, "?=", 2); - bp += 2; - return bp - buf; + strbuf_add(sb, line + last, len - last); + strbuf_addstr(sb, "?="); } static unsigned long bound_rfc2047(unsigned long len, const char *encoding) @@ -537,21 +528,21 @@ static unsigned long bound_rfc2047(unsigned long len, const char *encoding) return len * 3 + elen + 100; } -static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, +static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, const char *line, enum date_mode dmode, const char *encoding) { char *date; int namelen; unsigned long time; - int tz, ret; + int tz; const char *filler = " "; if (fmt == CMIT_FMT_ONELINE) - return 0; + return; date = strchr(line, '>'); if (!date) - return 0; + return; namelen = ++date - line; time = strtoul(date, &date, 10); tz = strtol(date, NULL, 10); @@ -560,42 +551,35 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, char *name_tail = strchr(line, '<'); int display_name_length; if (!name_tail) - return 0; + return; while (line < name_tail && isspace(name_tail[-1])) name_tail--; display_name_length = name_tail - line; filler = ""; - strcpy(buf, "From: "); - ret = strlen(buf); - ret += add_rfc2047(buf + ret, line, display_name_length, - encoding); - memcpy(buf + ret, name_tail, namelen - display_name_length); - ret += namelen - display_name_length; - buf[ret++] = '\n'; + strbuf_addstr(sb, "From: "); + add_rfc2047(sb, line, display_name_length, encoding); + strbuf_add(sb, name_tail, namelen - display_name_length); + strbuf_addch(sb, '\n'); } else { - ret = sprintf(buf, "%s: %.*s%.*s\n", what, + strbuf_addf(sb, "%s: %.*s%.*s\n", what, (fmt == CMIT_FMT_FULLER) ? 4 : 0, filler, namelen, line); } switch (fmt) { case CMIT_FMT_MEDIUM: - ret += sprintf(buf + ret, "Date: %s\n", - show_date(time, tz, dmode)); + strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode)); break; case CMIT_FMT_EMAIL: - ret += sprintf(buf + ret, "Date: %s\n", - show_date(time, tz, DATE_RFC2822)); + strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822)); break; case CMIT_FMT_FULLER: - ret += sprintf(buf + ret, "%sDate: %s\n", what, - show_date(time, tz, dmode)); + strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode)); break; default: /* notin' */ break; } - return ret; } static int is_empty_line(const char *line, int *len_p) @@ -607,16 +591,16 @@ static int is_empty_line(const char *line, int *len_p) return !len; } -static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *commit, int abbrev) +static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb, + const struct commit *commit, int abbrev) { struct commit_list *parent = commit->parents; - int offset; if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) || !parent || !parent->next) - return 0; + return; - offset = sprintf(buf, "Merge:"); + strbuf_addstr(sb, "Merge:"); while (parent) { struct commit *p = parent->item; @@ -629,10 +613,9 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com dots = (abbrev && strlen(hex) != 40) ? "..." : ""; parent = parent->next; - offset += sprintf(buf + offset, " %s%s", hex, dots); + strbuf_addf(sb, " %s%s", hex, dots); } - buf[offset++] = '\n'; - return offset; + strbuf_addch(sb, '\n'); } static char *get_header(const struct commit *commit, const char *key) @@ -787,8 +770,8 @@ static void fill_person(struct interp *table, const char *msg, int len) interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601)); } -long format_commit_message(const struct commit *commit, const void *format, - char **buf_p, unsigned long *space_p) +void format_commit_message(const struct commit *commit, + const void *format, struct strbuf *sb) { struct interp table[] = { { "%H" }, /* commit hash */ @@ -841,6 +824,7 @@ long format_commit_message(const struct commit *commit, const void *format, }; struct commit_list *p; char parents[1024]; + unsigned long len; int i; enum { HEADER, SUBJECT, BODY } state; const char *msg = commit->buffer; @@ -921,20 +905,15 @@ long format_commit_message(const struct commit *commit, const void *format, if (!table[i].value) interp_set_entry(table, i, ""); - do { - char *buf = *buf_p; - unsigned long len; - - len = interpolate(buf, *space_p, format, - table, ARRAY_SIZE(table)); - if (len < *space_p) - break; - ALLOC_GROW(buf, len + 1, *space_p); - *buf_p = buf; - } while (1); + len = interpolate(sb->buf + sb->len, strbuf_avail(sb), + format, table, ARRAY_SIZE(table)); + if (len > strbuf_avail(sb)) { + strbuf_grow(sb, len); + interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1, + format, table, ARRAY_SIZE(table)); + } + strbuf_setlen(sb, sb->len + len); interp_clear_table(table, ARRAY_SIZE(table)); - - return strlen(*buf_p); } static void pp_header(enum cmit_fmt fmt, @@ -943,34 +922,24 @@ static void pp_header(enum cmit_fmt fmt, const char *encoding, const struct commit *commit, const char **msg_p, - unsigned long *len_p, - unsigned long *ofs_p, - char **buf_p, - unsigned long *space_p) + struct strbuf *sb) { int parents_shown = 0; for (;;) { const char *line = *msg_p; - char *dst; - int linelen = get_one_line(*msg_p, *len_p); - unsigned long len; + int linelen = get_one_line(*msg_p); if (!linelen) return; *msg_p += linelen; - *len_p -= linelen; if (linelen == 1) /* End of header */ return; - ALLOC_GROW(*buf_p, linelen + *ofs_p + 20, *space_p); - dst = *buf_p + *ofs_p; - if (fmt == CMIT_FMT_RAW) { - memcpy(dst, line, linelen); - *ofs_p += linelen; + strbuf_add(sb, line, linelen); continue; } @@ -988,10 +957,8 @@ static void pp_header(enum cmit_fmt fmt, parent = parent->next, num++) ; /* with enough slop */ - num = *ofs_p + num * 50 + 20; - ALLOC_GROW(*buf_p, num, *space_p); - dst = *buf_p + *ofs_p; - *ofs_p += add_merge_info(fmt, dst, commit, abbrev); + strbuf_grow(sb, num * 50 + 20); + add_merge_info(fmt, sb, commit, abbrev); parents_shown = 1; } @@ -1001,129 +968,99 @@ static void pp_header(enum cmit_fmt fmt, * FULLER shows both authors and dates. */ if (!memcmp(line, "author ", 7)) { - len = linelen; + unsigned long len = linelen; if (fmt == CMIT_FMT_EMAIL) len = bound_rfc2047(linelen, encoding); - ALLOC_GROW(*buf_p, *ofs_p + len + 80, *space_p); - dst = *buf_p + *ofs_p; - *ofs_p += add_user_info("Author", fmt, dst, - line + 7, dmode, encoding); + strbuf_grow(sb, len + 80); + add_user_info("Author", fmt, sb, line + 7, dmode, encoding); } if (!memcmp(line, "committer ", 10) && (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) { - len = linelen; + unsigned long len = linelen; if (fmt == CMIT_FMT_EMAIL) len = bound_rfc2047(linelen, encoding); - ALLOC_GROW(*buf_p, *ofs_p + len + 80, *space_p); - dst = *buf_p + *ofs_p; - *ofs_p += add_user_info("Commit", fmt, dst, - line + 10, dmode, encoding); + strbuf_grow(sb, len + 80); + add_user_info("Commit", fmt, sb, line + 10, dmode, encoding); } } } static void pp_title_line(enum cmit_fmt fmt, const char **msg_p, - unsigned long *len_p, - unsigned long *ofs_p, - char **buf_p, - unsigned long *space_p, - int indent, + struct strbuf *sb, const char *subject, const char *after_subject, const char *encoding, int plain_non_ascii) { - char *title; - unsigned long title_alloc, title_len; + struct strbuf title; unsigned long len; - title_len = 0; - title_alloc = 80; - title = xmalloc(title_alloc); + strbuf_init(&title, 80); + for (;;) { const char *line = *msg_p; - int linelen = get_one_line(line, *len_p); - *msg_p += linelen; - *len_p -= linelen; + int linelen = get_one_line(line); + *msg_p += linelen; if (!linelen || is_empty_line(line, &linelen)) break; - if (title_alloc <= title_len + linelen + 2) { - title_alloc = title_len + linelen + 80; - title = xrealloc(title, title_alloc); - } - len = 0; - if (title_len) { + strbuf_grow(&title, linelen + 2); + if (title.len) { if (fmt == CMIT_FMT_EMAIL) { - len++; - title[title_len++] = '\n'; + strbuf_addch(&title, '\n'); } - len++; - title[title_len++] = ' '; + strbuf_addch(&title, ' '); } - memcpy(title + title_len, line, linelen); - title_len += linelen; + strbuf_add(&title, line, linelen); } /* Enough slop for the MIME header and rfc2047 */ - len = bound_rfc2047(title_len, encoding)+ 1000; + len = bound_rfc2047(title.len, encoding) + 1000; if (subject) len += strlen(subject); if (after_subject) len += strlen(after_subject); if (encoding) len += strlen(encoding); - ALLOC_GROW(*buf_p, title_len + *ofs_p + len, *space_p); + strbuf_grow(sb, title.len + len); if (subject) { - len = strlen(subject); - memcpy(*buf_p + *ofs_p, subject, len); - *ofs_p += len; - *ofs_p += add_rfc2047(*buf_p + *ofs_p, - title, title_len, encoding); + strbuf_addstr(sb, subject); + add_rfc2047(sb, title.buf, title.len, encoding); } else { - memcpy(*buf_p + *ofs_p, title, title_len); - *ofs_p += title_len; + strbuf_addbuf(sb, &title); } - (*buf_p)[(*ofs_p)++] = '\n'; + strbuf_addch(sb, '\n'); + if (plain_non_ascii) { const char *header_fmt = "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=%s\n" "Content-Transfer-Encoding: 8bit\n"; - *ofs_p += snprintf(*buf_p + *ofs_p, - *space_p - *ofs_p, - header_fmt, encoding); + strbuf_addf(sb, header_fmt, encoding); } if (after_subject) { - len = strlen(after_subject); - memcpy(*buf_p + *ofs_p, after_subject, len); - *ofs_p += len; + strbuf_addstr(sb, after_subject); } - free(title); if (fmt == CMIT_FMT_EMAIL) { - ALLOC_GROW(*buf_p, *ofs_p + 20, *space_p); - (*buf_p)[(*ofs_p)++] = '\n'; + strbuf_addch(sb, '\n'); } + strbuf_release(&title); } static void pp_remainder(enum cmit_fmt fmt, const char **msg_p, - unsigned long *len_p, - unsigned long *ofs_p, - char **buf_p, - unsigned long *space_p, + struct strbuf *sb, int indent) { int first = 1; for (;;) { const char *line = *msg_p; - int linelen = get_one_line(line, *len_p); + int linelen = get_one_line(line); *msg_p += linelen; - *len_p -= linelen; if (!linelen) break; @@ -1136,36 +1073,32 @@ static void pp_remainder(enum cmit_fmt fmt, } first = 0; - ALLOC_GROW(*buf_p, *ofs_p + linelen + indent + 20, *space_p); + strbuf_grow(sb, linelen + indent + 20); if (indent) { - memset(*buf_p + *ofs_p, ' ', indent); - *ofs_p += indent; + memset(sb->buf + sb->len, ' ', indent); + strbuf_setlen(sb, sb->len + indent); } - memcpy(*buf_p + *ofs_p, line, linelen); - *ofs_p += linelen; - (*buf_p)[(*ofs_p)++] = '\n'; + strbuf_add(sb, line, linelen); + strbuf_addch(sb, '\n'); } } -unsigned long pretty_print_commit(enum cmit_fmt fmt, - const struct commit *commit, - unsigned long len, - char **buf_p, unsigned long *space_p, - int abbrev, const char *subject, - const char *after_subject, +void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, + struct strbuf *sb, int abbrev, + const char *subject, const char *after_subject, enum date_mode dmode) { - unsigned long offset = 0; unsigned long beginning_of_body; int indent = 4; const char *msg = commit->buffer; int plain_non_ascii = 0; char *reencoded; const char *encoding; - char *buf; - if (fmt == CMIT_FMT_USERFORMAT) - return format_commit_message(commit, user_format, buf_p, space_p); + if (fmt == CMIT_FMT_USERFORMAT) { + format_commit_message(commit, user_format, sb); + return; + } encoding = (git_log_output_encoding ? git_log_output_encoding @@ -1175,7 +1108,6 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, reencoded = logmsg_reencode(commit, encoding); if (reencoded) { msg = reencoded; - len = strlen(reencoded); } if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) @@ -1190,14 +1122,13 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, if (fmt == CMIT_FMT_EMAIL && !after_subject) { int i, ch, in_body; - for (in_body = i = 0; (ch = msg[i]) && i < len; i++) { + for (in_body = i = 0; (ch = msg[i]); i++) { if (!in_body) { /* author could be non 7-bit ASCII but * the log may be so; skip over the * header part first. */ - if (ch == '\n' && - i + 1 < len && msg[i+1] == '\n') + if (ch == '\n' && msg[i+1] == '\n') in_body = 1; } else if (non_ascii(ch)) { @@ -1207,59 +1138,44 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, } } - pp_header(fmt, abbrev, dmode, encoding, - commit, &msg, &len, - &offset, buf_p, space_p); + pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb); if (fmt != CMIT_FMT_ONELINE && !subject) { - ALLOC_GROW(*buf_p, offset + 20, *space_p); - (*buf_p)[offset++] = '\n'; + strbuf_addch(sb, '\n'); } /* Skip excess blank lines at the beginning of body, if any... */ for (;;) { - int linelen = get_one_line(msg, len); + int linelen = get_one_line(msg); int ll = linelen; if (!linelen) break; if (!is_empty_line(msg, &ll)) break; msg += linelen; - len -= linelen; } /* These formats treat the title line specially. */ - if (fmt == CMIT_FMT_ONELINE - || fmt == CMIT_FMT_EMAIL) - pp_title_line(fmt, &msg, &len, &offset, - buf_p, space_p, indent, - subject, after_subject, encoding, - plain_non_ascii); - - beginning_of_body = offset; - if (fmt != CMIT_FMT_ONELINE) - pp_remainder(fmt, &msg, &len, &offset, - buf_p, space_p, indent); - - while (offset && isspace((*buf_p)[offset-1])) - offset--; + if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) + pp_title_line(fmt, &msg, sb, subject, + after_subject, encoding, plain_non_ascii); - ALLOC_GROW(*buf_p, offset + 20, *space_p); - buf = *buf_p; + beginning_of_body = sb->len; + if (fmt != CMIT_FMT_ONELINE) + pp_remainder(fmt, &msg, sb, indent); + strbuf_rtrim(sb); /* Make sure there is an EOLN for the non-oneline case */ if (fmt != CMIT_FMT_ONELINE) - buf[offset++] = '\n'; + strbuf_addch(sb, '\n'); /* * The caller may append additional body text in e-mail * format. Make sure we did not strip the blank line * between the header and the body. */ - if (fmt == CMIT_FMT_EMAIL && offset <= beginning_of_body) - buf[offset++] = '\n'; - buf[offset] = '\0'; + if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body) + strbuf_addch(sb, '\n'); free(reencoded); - return offset; } struct commit *pop_commit(struct commit_list **stack) @@ -1338,12 +1254,12 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, next=next->next; } /* - * find the tips - * - * tips are nodes not reachable from any other node in the list - * - * the tips serve as a starting set for the work queue. - */ + * find the tips + * + * tips are nodes not reachable from any other node in the list + * + * the tips serve as a starting set for the work queue. + */ next=*list; insert = &work; while (next) { @@ -1370,9 +1286,9 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, if (pn) { /* * parents are only enqueued for emission - * when all their children have been emitted thereby - * guaranteeing topological order. - */ + * when all their children have been emitted thereby + * guaranteeing topological order. + */ pn->indegree--; if (!pn->indegree) { if (!lifo) @@ -1384,9 +1300,9 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, parents=parents->next; } /* - * work_item is a commit all of whose children - * have already been emitted. we can emit it now. - */ + * work_item is a commit all of whose children + * have already been emitted. we can emit it now. + */ *pptr = work_node->list_item; pptr = &(*pptr)->next; *pptr = NULL; @@ -1482,8 +1398,7 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two) } struct commit_list *get_merge_bases(struct commit *one, - struct commit *two, - int cleanup) + struct commit *two, int cleanup) { struct commit_list *list; struct commit **rslt; diff --git a/commit.h b/commit.h index a8d7661..b779de8 100644 --- a/commit.h +++ b/commit.h @@ -3,6 +3,7 @@ #include "object.h" #include "tree.h" +#include "strbuf.h" #include "decorate.h" struct commit_list { @@ -61,8 +62,12 @@ enum cmit_fmt { }; extern enum cmit_fmt get_commit_format(const char *arg); -extern long format_commit_message(const struct commit *commit, const void *template, char **buf_p, unsigned long *space_p); -extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char **buf_p, unsigned long *space_p, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode); +extern void format_commit_message(const struct commit *commit, + const void *format, struct strbuf *sb); +extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*, + struct strbuf *, + int abbrev, const char *subject, + const char *after_subject, enum date_mode); /** Removes the first commit from a list sorted by date, and adds all * of its parents. diff --git a/log-tree.c b/log-tree.c index a642371..3e5e6ac 100644 --- a/log-tree.c +++ b/log-tree.c @@ -79,25 +79,14 @@ static int detect_any_signoff(char *letter, int size) return seen_head && seen_name; } -static unsigned long append_signoff(char **buf_p, unsigned long *buf_sz_p, - unsigned long at, const char *signoff) +static void append_signoff(struct strbuf *sb, const char *signoff) { static const char signed_off_by[] = "Signed-off-by: "; size_t signoff_len = strlen(signoff); int has_signoff = 0; char *cp; - char *buf; - unsigned long buf_sz; - - buf = *buf_p; - buf_sz = *buf_sz_p; - if (buf_sz <= at + strlen(signed_off_by) + signoff_len + 3) { - buf_sz += strlen(signed_off_by) + signoff_len + 3; - buf = xrealloc(buf, buf_sz); - *buf_p = buf; - *buf_sz_p = buf_sz; - } - cp = buf; + + cp = sb->buf; /* First see if we already have the sign-off by the signer */ while ((cp = strstr(cp, signed_off_by))) { @@ -105,29 +94,25 @@ static unsigned long append_signoff(char **buf_p, unsigned long *buf_sz_p, has_signoff = 1; cp += strlen(signed_off_by); - if (cp + signoff_len >= buf + at) + if (cp + signoff_len >= sb->buf + sb->len) break; if (strncmp(cp, signoff, signoff_len)) continue; if (!isspace(cp[signoff_len])) continue; /* we already have him */ - return at; + return; } if (!has_signoff) - has_signoff = detect_any_signoff(buf, at); + has_signoff = detect_any_signoff(sb->buf, sb->len); if (!has_signoff) - buf[at++] = '\n'; - - strcpy(buf + at, signed_off_by); - at += strlen(signed_off_by); - strcpy(buf + at, signoff); - at += signoff_len; - buf[at++] = '\n'; - buf[at] = 0; - return at; + strbuf_addch(sb, '\n'); + + strbuf_addstr(sb, signed_off_by); + strbuf_add(sb, signoff, signoff_len); + strbuf_addch(sb, '\n'); } static unsigned int digits_in_number(unsigned int number) @@ -142,14 +127,12 @@ static unsigned int digits_in_number(unsigned int number) void show_log(struct rev_info *opt, const char *sep) { - char *msgbuf = NULL; - unsigned long msgbuf_len = 0; + struct strbuf msgbuf; struct log_info *log = opt->loginfo; struct commit *commit = log->commit, *parent = log->parent; int abbrev = opt->diffopt.abbrev; int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40; const char *extra; - int len; const char *subject = NULL, *extra_headers = opt->extra_headers; opt->loginfo = NULL; @@ -288,18 +271,17 @@ void show_log(struct rev_info *opt, const char *sep) /* * And then the pretty-printed message itself */ - len = pretty_print_commit(opt->commit_format, commit, ~0u, - &msgbuf, &msgbuf_len, abbrev, subject, - extra_headers, opt->date_mode); + strbuf_init(&msgbuf, 0); + pretty_print_commit(opt->commit_format, commit, &msgbuf, + abbrev, subject, extra_headers, opt->date_mode); if (opt->add_signoff) - len = append_signoff(&msgbuf, &msgbuf_len, len, - opt->add_signoff); + append_signoff(&msgbuf, opt->add_signoff); if (opt->show_log_size) - printf("log size %i\n", len); + printf("log size %i\n", (int)msgbuf.len); - printf("%s%s%s", msgbuf, extra, sep); - free(msgbuf); + printf("%s%s%s", msgbuf.buf, extra, sep); + strbuf_release(&msgbuf); } int log_tree_diff_flush(struct rev_info *opt) -- cgit v0.10.2-6-g49f6 From b655d46bb2d8aa12f3203f93c323b41df161fd26 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 10 Sep 2007 12:35:07 +0200 Subject: Use strbuf_read in builtin-fetch-tool.c. xrealloc.use --; Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index 24c7e6f..90bdc32 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -2,27 +2,16 @@ #include "cache.h" #include "refs.h" #include "commit.h" - -#define CHUNK_SIZE 1024 +#include "strbuf.h" static char *get_stdin(void) { - size_t offset = 0; - char *data = xmalloc(CHUNK_SIZE); - - while (1) { - ssize_t cnt = xread(0, data + offset, CHUNK_SIZE); - if (cnt < 0) - die("error reading standard input: %s", - strerror(errno)); - if (cnt == 0) { - data[offset] = 0; - break; - } - offset += cnt; - data = xrealloc(data, offset + CHUNK_SIZE); + struct strbuf buf; + strbuf_init(&buf, 0); + if (strbuf_read(&buf, 0, 1024) < 0) { + die("error reading standard input: %s", strerror(errno)); } - return data; + return strbuf_detach(&buf); } static void show_new(enum object_type type, unsigned char *sha1_new) -- cgit v0.10.2-6-g49f6 From 635d043f30c60f5ec8121bd94304e529f90407c7 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 10 Sep 2007 12:35:08 +0200 Subject: Use strbufs to in read_message (imap-send.c), custom buffer--. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/imap-send.c b/imap-send.c index a5a0696..ecd4216 100644 --- a/imap-send.c +++ b/imap-send.c @@ -23,6 +23,7 @@ */ #include "cache.h" +#include "strbuf.h" typedef struct store_conf { char *name; @@ -1160,28 +1161,18 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) static int read_message( FILE *f, msg_data_t *msg ) { - int len, r; + struct strbuf buf; - memset( msg, 0, sizeof *msg ); - len = CHUNKSIZE; - msg->data = xmalloc( len+1 ); - msg->data[0] = 0; - - while(!feof( f )) { - if (msg->len >= len) { - void *p; - len += CHUNKSIZE; - p = xrealloc(msg->data, len+1); - if (!p) - break; - msg->data = p; - } - r = fread( &msg->data[msg->len], 1, len - msg->len, f ); - if (r <= 0) + memset(msg, 0, sizeof(*msg)); + strbuf_init(&buf, 0); + + do { + if (strbuf_fread(&buf, CHUNKSIZE, f) <= 0) break; - msg->len += r; - } - msg->data[msg->len] = 0; + } while (!feof(f)); + + msg->len = buf.len; + msg->data = strbuf_detach(&buf); return msg->len; } -- cgit v0.10.2-6-g49f6 From fd17f5b5f77716bf90098c6e49e3cf7fd9f56306 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 10 Sep 2007 12:35:09 +0200 Subject: Replace all read_fd use with strbuf_read, and get rid of it. This brings builtin-stripspace, builtin-tag and mktag to use strbufs. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/builtin-stripspace.c b/builtin-stripspace.c index 916355c..c4cf2f0 100644 --- a/builtin-stripspace.c +++ b/builtin-stripspace.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "strbuf.h" /* * Returns the length of a line, without trailing spaces. @@ -74,26 +75,22 @@ size_t stripspace(char *buffer, size_t length, int skip_comments) int cmd_stripspace(int argc, const char **argv, const char *prefix) { - char *buffer; - unsigned long size; + struct strbuf buf; int strip_comments = 0; if (argc > 1 && (!strcmp(argv[1], "-s") || !strcmp(argv[1], "--strip-comments"))) strip_comments = 1; - size = 1024; - buffer = xmalloc(size); - if (read_fd(0, &buffer, &size)) { - free(buffer); + strbuf_init(&buf, 0); + if (strbuf_read(&buf, 0, 1024) < 0) die("could not read the input"); - } - size = stripspace(buffer, size, strip_comments); - write_or_die(1, buffer, size); - if (size) - putc('\n', stdout); + strbuf_setlen(&buf, stripspace(buf.buf, buf.len, strip_comments)); + if (buf.len) + strbuf_addch(&buf, '\n'); - free(buffer); + write_or_die(1, buf.buf, buf.len); + strbuf_release(&buf); return 0; } diff --git a/builtin-tag.c b/builtin-tag.c index 3a9d2ee..9f29342 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -8,6 +8,7 @@ #include "cache.h" #include "builtin.h" +#include "strbuf.h" #include "refs.h" #include "tag.h" #include "run-command.h" @@ -17,7 +18,7 @@ static const char builtin_tag_usage[] = static char signingkey[1000]; -static void launch_editor(const char *path, char **buffer, unsigned long *len) +static void launch_editor(const char *path, struct strbuf *buffer) { const char *editor, *terminal; struct child_process child; @@ -55,10 +56,8 @@ static void launch_editor(const char *path, char **buffer, unsigned long *len) fd = open(path, O_RDONLY); if (fd < 0) die("could not open '%s': %s", path, strerror(errno)); - if (read_fd(fd, buffer, len)) { - free(*buffer); - die("could not read message file '%s': %s", - path, strerror(errno)); + if (strbuf_read(buffer, fd, 0) < 0) { + die("could not read message file '%s': %s", path, strerror(errno)); } close(fd); } @@ -184,7 +183,7 @@ static int verify_tag(const char *name, const char *ref, return 0; } -static ssize_t do_sign(char *buffer, size_t size, size_t max) +static int do_sign(struct strbuf *buffer) { struct child_process gpg; const char *args[4]; @@ -216,22 +215,22 @@ static ssize_t do_sign(char *buffer, size_t size, size_t max) if (start_command(&gpg)) return error("could not run gpg."); - if (write_in_full(gpg.in, buffer, size) != size) { + if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) { close(gpg.in); finish_command(&gpg); return error("gpg did not accept the tag data"); } close(gpg.in); gpg.close_in = 0; - len = read_in_full(gpg.out, buffer + size, max - size); + len = strbuf_read(buffer, gpg.out, 1024); if (finish_command(&gpg) || !len || len < 0) return error("gpg failed to sign the tag"); - if (len == max - size) + if (len < 0) return error("could not read the entire signature from gpg."); - return size + len; + return 0; } static const char tag_template[] = @@ -254,15 +253,13 @@ static int git_tag_config(const char *var, const char *value) return git_default_config(var, value); } -#define MAX_SIGNATURE_LENGTH 1024 -/* message must be NULL or allocated, it will be reallocated and freed */ static void create_tag(const unsigned char *object, const char *tag, - char *message, int sign, unsigned char *result) + struct strbuf *buf, int message, int sign, + unsigned char *result) { enum object_type type; - char header_buf[1024], *buffer = NULL; - int header_len, max_size; - unsigned long size = 0; + char header_buf[1024]; + int header_len; type = sha1_object_info(object, NULL); if (type <= OBJ_NONE) @@ -294,53 +291,40 @@ 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, &buffer, &size); + launch_editor(path, buf); unlink(path); free(path); } - else { - buffer = message; - size = strlen(message); - } - size = stripspace(buffer, size, 1); + strbuf_setlen(buf, stripspace(buf->buf, buf->len, 1)); - if (!message && !size) + if (!message && !buf->len) die("no tag message?"); /* insert the header and add the '\n' if needed: */ - max_size = header_len + size + (sign ? MAX_SIGNATURE_LENGTH : 0) + 1; - buffer = xrealloc(buffer, max_size); - if (size) - buffer[size++] = '\n'; - memmove(buffer + header_len, buffer, size); - memcpy(buffer, header_buf, header_len); - size += header_len; - - if (sign) { - ssize_t r = do_sign(buffer, size, max_size); - if (r < 0) - die("unable to sign the tag"); - size = r; - } + if (buf->len) + strbuf_addch(buf, '\n'); + strbuf_insert(buf, 0, header_buf, header_len); - if (write_sha1_file(buffer, size, tag_type, result) < 0) + if (sign && do_sign(buf) < 0) + die("unable to sign the tag"); + if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 0) die("unable to write tag file"); - free(buffer); } int cmd_tag(int argc, const char **argv, const char *prefix) { + struct strbuf buf; unsigned char object[20], prev[20]; - int annotate = 0, sign = 0, force = 0, lines = 0; - char *message = NULL; + int annotate = 0, sign = 0, force = 0, lines = 0, message = 0; char ref[PATH_MAX]; const char *object_ref, *tag; int i; struct ref_lock *lock; git_config(git_tag_config); + strbuf_init(&buf, 0); for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -376,11 +360,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix) die("option -m needs an argument."); if (message) die("only one -F or -m option is allowed."); - message = xstrdup(argv[i]); + strbuf_addstr(&buf, argv[i]); + message = 1; continue; } if (!strcmp(arg, "-F")) { - unsigned long len; int fd; annotate = 1; @@ -398,12 +382,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix) die("could not open '%s': %s", argv[i], strerror(errno)); } - len = 1024; - message = xmalloc(len); - if (read_fd(fd, &message, &len)) { - free(message); + if (strbuf_read(&buf, fd, 1024) < 0) { die("cannot read %s", argv[i]); } + message = 1; continue; } if (!strcmp(arg, "-u")) { @@ -451,7 +433,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) die("tag '%s' already exists", tag); if (annotate) - create_tag(object, tag, message, sign, object); + create_tag(object, tag, &buf, message, sign, object); lock = lock_any_ref_for_update(ref, prev, 0); if (!lock) @@ -459,5 +441,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (write_ref_sha1(lock, object, NULL) < 0) die("%s: cannot update the ref", ref); + strbuf_release(&buf); return 0; } diff --git a/cache.h b/cache.h index 493983c..231f81d 100644 --- a/cache.h +++ b/cache.h @@ -269,7 +269,6 @@ extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int); extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path); -extern int read_fd(int fd, char **return_buf, unsigned long *return_size); extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object); extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); diff --git a/mktag.c b/mktag.c index 38acd5a..7567f9e 100644 --- a/mktag.c +++ b/mktag.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "strbuf.h" #include "tag.h" /* @@ -111,8 +112,7 @@ static int verify_tag(char *buffer, unsigned long size) int main(int argc, char **argv) { - unsigned long size = 4096; - char *buffer = xmalloc(size); + struct strbuf buf; unsigned char result_sha1[20]; if (argc != 1) @@ -120,21 +120,20 @@ int main(int argc, char **argv) setup_git_directory(); - if (read_fd(0, &buffer, &size)) { - free(buffer); + strbuf_init(&buf, 0); + if (strbuf_read(&buf, 0, 4096) < 0) { die("could not read from stdin"); } /* Verify it for some basic sanity: it needs to start with "object \ntype\ntagger " */ - if (verify_tag(buffer, size) < 0) + if (verify_tag(buf.buf, buf.len) < 0) die("invalid tag signature file"); - if (write_sha1_file(buffer, size, tag_type, result_sha1) < 0) + if (write_sha1_file(buf.buf, buf.len, tag_type, result_sha1) < 0) die("unable to write tag file"); - free(buffer); - + strbuf_release(&buf); printf("%s\n", sha1_to_hex(result_sha1)); return 0; } diff --git a/sha1_file.c b/sha1_file.c index 9978a58..aea1096 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -14,6 +14,7 @@ #include "tag.h" #include "tree.h" #include "refs.h" +#include "strbuf.h" #ifndef O_NOATIME #if defined(__linux__) && (defined(__i386__) || defined(__PPC__)) @@ -2302,68 +2303,25 @@ int has_sha1_file(const unsigned char *sha1) return find_sha1_file(sha1, &st) ? 1 : 0; } -/* - * reads from fd as long as possible into a supplied buffer of size bytes. - * If necessary the buffer's size is increased using realloc() - * - * returns 0 if anything went fine and -1 otherwise - * - * The buffer is always NUL-terminated, not including it in returned size. - * - * NOTE: both buf and size may change, but even when -1 is returned - * you still have to free() it yourself. - */ -int read_fd(int fd, char **return_buf, unsigned long *return_size) -{ - char *buf = *return_buf; - unsigned long size = *return_size; - ssize_t iret; - unsigned long off = 0; - - if (!buf || size <= 1) { - size = 1024; - buf = xrealloc(buf, size); - } - - do { - iret = xread(fd, buf + off, (size - 1) - off); - if (iret > 0) { - off += iret; - if (off == size - 1) { - size = alloc_nr(size); - buf = xrealloc(buf, size); - } - } - } while (iret > 0); - - buf[off] = '\0'; - - *return_buf = buf; - *return_size = off; - - if (iret < 0) - return -1; - return 0; -} - int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) { - unsigned long size = 4096; - char *buf = xmalloc(size); + struct strbuf buf; int ret; - if (read_fd(fd, &buf, &size)) { - free(buf); + strbuf_init(&buf, 0); + if (strbuf_read(&buf, fd, 4096) < 0) { + strbuf_release(&buf); return -1; } if (!type) type = blob_type; if (write_object) - ret = write_sha1_file(buf, size, type, sha1); + ret = write_sha1_file(buf.buf, buf.len, type, sha1); else - ret = hash_sha1_file(buf, size, type, sha1); - free(buf); + ret = hash_sha1_file(buf.buf, buf.len, type, sha1); + strbuf_release(&buf); + return ret; } -- cgit v0.10.2-6-g49f6 From 760da9607ee08e9dd495dee993262bb857694ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 14 Sep 2007 00:13:06 +0200 Subject: archive: fix subst file generation Before the strbuf conversion, result was a char pointer. The if statement checked for it being not NULL, which meant that no "$Format:...$" string had been found and no replacement had to be made. format_subst() returned NULL in that case -- the caller then simply kept the original file content, as it was unaffected by the expansion. The length of the string being 0 is not the same as the string being NULL (expansion to an empty string vs. no expansion at all), so checking result.len != 0 is not a full replacement for the old NULL check. However, I doubt the subtle optimization explained above resulted in a notable speed-up anyway. Simplify the code and add the tail of the file to the expanded string unconditionally. [jc: added a test to expose the breakage this fixes] Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano diff --git a/builtin-archive.c b/builtin-archive.c index b50d5ad..6fa424d 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -111,9 +111,7 @@ static void *format_subst(const struct commit *commit, const char *format, a = c + 1; } - if (result.len && len) { - strbuf_add(&result, a, len); - } + strbuf_add(&result, a, len); *sizep = result.len; diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 42e28ab..dca2067 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -36,7 +36,8 @@ test_expect_success \ echo simple textfile >a/a && mkdir a/bin && cp /bin/sh a/bin && - printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile && + printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 && + printf "A not substituted O" >a/substfile2 && ln -s a a/l1 && (p=long_path_to_a_file && cd a && for depth in 1 2 3 4 5; do mkdir $p && cd $p; done && @@ -108,20 +109,22 @@ test_expect_success \ 'diff -r a c/prefix/a' test_expect_success \ - 'create an archive with a substfile' \ - 'echo substfile export-subst >a/.gitattributes && + 'create an archive with a substfiles' \ + 'echo "substfile?" export-subst >a/.gitattributes && git archive HEAD >f.tar && rm a/.gitattributes' test_expect_success \ - 'extract substfile' \ + 'extract substfiles' \ '(mkdir f && cd f && $TAR xf -) f/a/substfile.expected && - diff f/a/substfile.expected f/a/substfile' + >f/a/substfile1.expected && + diff f/a/substfile1.expected f/a/substfile1 && + diff a/substfile2 f/a/substfile2 +' test_expect_success \ 'git archive --format=zip' \ -- cgit v0.10.2-6-g49f6 From 917c9a713397b16671ed5b1f1c159515bcfa389e Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sat, 15 Sep 2007 15:56:50 +0200 Subject: New strbuf APIs: splice and attach. * strbuf_splice replace a portion of the buffer with another. * strbuf_attach replace a strbuf buffer with the given one, that should be malloc'ed. Then it enforces strbuf's invariants. If alloc > len, then this function has negligible cost, else it will perform a realloc, possibly with a cost. Also some style issues are fixed now. Signed-off-by: Pierre Habouzit Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/strbuf.c b/strbuf.c index d919047..ff551ac 100644 --- a/strbuf.c +++ b/strbuf.c @@ -1,30 +1,45 @@ #include "cache.h" #include "strbuf.h" -void strbuf_init(struct strbuf *sb, size_t hint) { +void strbuf_init(struct strbuf *sb, size_t hint) +{ memset(sb, 0, sizeof(*sb)); if (hint) strbuf_grow(sb, hint); } -void strbuf_release(struct strbuf *sb) { +void strbuf_release(struct strbuf *sb) +{ free(sb->buf); memset(sb, 0, sizeof(*sb)); } -void strbuf_reset(struct strbuf *sb) { +void strbuf_reset(struct strbuf *sb) +{ if (sb->len) strbuf_setlen(sb, 0); sb->eof = 0; } -char *strbuf_detach(struct strbuf *sb) { +char *strbuf_detach(struct strbuf *sb) +{ char *res = sb->buf; strbuf_init(sb, 0); return res; } -void strbuf_grow(struct strbuf *sb, size_t extra) { +void strbuf_attach(struct strbuf *sb, void *buf, size_t len, size_t alloc) +{ + strbuf_release(sb); + sb->buf = buf; + sb->len = len; + sb->alloc = alloc; + strbuf_grow(sb, 0); + sb->buf[sb->len] = '\0'; +} + +void strbuf_grow(struct strbuf *sb, size_t extra) +{ if (sb->len + extra + 1 <= sb->len) die("you want to use way too much memory"); ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); @@ -37,24 +52,44 @@ void strbuf_rtrim(struct strbuf *sb) sb->buf[sb->len] = '\0'; } -void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len) { +void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len) +{ strbuf_grow(sb, len); - if (pos >= sb->len) { - pos = sb->len; - } else { - memmove(sb->buf + pos + len, sb->buf + pos, sb->len - pos); - } + if (pos > sb->len) + die("`pos' is too far after the end of the buffer"); + memmove(sb->buf + pos + len, sb->buf + pos, sb->len - pos); memcpy(sb->buf + pos, data, len); strbuf_setlen(sb, sb->len + len); } -void strbuf_add(struct strbuf *sb, const void *data, size_t len) { +void strbuf_splice(struct strbuf *sb, size_t pos, size_t len, + const void *data, size_t dlen) +{ + if (pos + len < pos) + die("you want to use way too much memory"); + if (pos > sb->len) + die("`pos' is too far after the end of the buffer"); + if (pos + len > sb->len) + die("`pos + len' is too far after the end of the buffer"); + + if (dlen >= len) + strbuf_grow(sb, dlen - len); + memmove(sb->buf + pos + dlen, + sb->buf + pos + len, + sb->len - pos - len); + memcpy(sb->buf + pos, data, dlen); + strbuf_setlen(sb, sb->len + dlen - len); +} + +void strbuf_add(struct strbuf *sb, const void *data, size_t len) +{ strbuf_grow(sb, len); memcpy(sb->buf + sb->len, data, len); strbuf_setlen(sb, sb->len + len); } -void strbuf_addf(struct strbuf *sb, const char *fmt, ...) { +void strbuf_addf(struct strbuf *sb, const char *fmt, ...) +{ int len; va_list ap; @@ -76,7 +111,8 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...) { strbuf_setlen(sb, sb->len + len); } -size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) { +size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) +{ size_t res; strbuf_grow(sb, size); @@ -110,7 +146,8 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint) return sb->len - oldlen; } -void read_line(struct strbuf *sb, FILE *fp, int term) { +void read_line(struct strbuf *sb, FILE *fp, int term) +{ int ch; if (feof(fp)) { strbuf_release(sb); diff --git a/strbuf.h b/strbuf.h index 21fc111..f163c63 100644 --- a/strbuf.h +++ b/strbuf.h @@ -55,6 +55,7 @@ extern void strbuf_init(struct strbuf *, size_t); extern void strbuf_release(struct strbuf *); extern void strbuf_reset(struct strbuf *); extern char *strbuf_detach(struct strbuf *); +extern void strbuf_attach(struct strbuf *, void *, size_t, size_t); /*----- strbuf size related -----*/ static inline size_t strbuf_avail(struct strbuf *sb) { @@ -81,6 +82,10 @@ static inline void strbuf_addch(struct strbuf *sb, int c) { /* inserts after pos, or appends if pos >= sb->len */ extern void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t); +/* splice pos..pos+len with given data */ +extern void strbuf_splice(struct strbuf *, size_t pos, size_t len, + const void *, size_t); + extern void strbuf_add(struct strbuf *, const void *, size_t); static inline void strbuf_addstr(struct strbuf *sb, const char *s) { strbuf_add(sb, s, strlen(s)); -- cgit v0.10.2-6-g49f6 From 5ecd293d1420bf641a927a015877950f4d79c067 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 16 Sep 2007 15:51:04 +0200 Subject: Rewrite convert_to_{git,working_tree} to use strbuf's. * Now, those functions take an "out" strbuf argument, where they store their result if any. In that case, it also returns 1, else it returns 0. * those functions support "in place" editing, in the sense that it's OK to call them this way: convert_to_git(path, sb->buf, sb->len, sb); When doable, conversions are done in place for real, else the strbuf content is just replaced with the new one, transparentely for the caller. If you want to create a new filter working this way, being the accumulation of filter1, filter2, ... filtern, then your meta_filter would be: int meta_filter(..., const char *src, size_t len, struct strbuf *sb) { int ret = 0; ret |= filter1(...., src, len, sb); if (ret) { src = sb->buf; len = sb->len; } ret |= filter2(...., src, len, sb); if (ret) { src = sb->buf; len = sb->len; } .... return ret | filtern(..., src, len, sb); } That's why subfilters the convert_to_* functions called were also rewritten to work this way. Signed-off-by: Pierre Habouzit Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/builtin-apply.c b/builtin-apply.c index 988e85f..e5c29eb 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -1446,8 +1446,7 @@ static int read_old_data(struct stat *st, const char *path, char **buf_p, unsign { int fd; unsigned long got; - unsigned long nsize; - char *nbuf; + struct strbuf nbuf; unsigned long size = *size_p; char *buf = *buf_p; @@ -1466,13 +1465,12 @@ static int read_old_data(struct stat *st, const char *path, char **buf_p, unsign got += ret; } close(fd); - nsize = got; - nbuf = convert_to_git(path, buf, &nsize); - if (nbuf) { + strbuf_init(&nbuf, 0); + if (convert_to_git(path, buf, size, &nbuf)) { free(buf); - *buf_p = nbuf; - *alloc_p = nsize; - *size_p = nsize; + *buf_p = nbuf.buf; + *alloc_p = nbuf.alloc; + *size_p = nbuf.len; } return got != size; default: @@ -2439,7 +2437,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size) { int fd; - char *nbuf; + struct strbuf nbuf; if (S_ISGITLINK(mode)) { struct stat st; @@ -2458,9 +2456,11 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, if (fd < 0) return -1; - nbuf = convert_to_working_tree(path, buf, &size); - if (nbuf) - buf = nbuf; + strbuf_init(&nbuf, 0); + if (convert_to_working_tree(path, buf, size, &nbuf)) { + size = nbuf.len; + buf = nbuf.buf; + } while (size) { int written = xwrite(fd, buf, size); @@ -2473,8 +2473,7 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, } if (close(fd) < 0) die("closing file %s: %s", path, strerror(errno)); - if (nbuf) - free(nbuf); + strbuf_release(&nbuf); return 0; } diff --git a/builtin-archive.c b/builtin-archive.c index 6fa424d..843a9e3 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -81,22 +81,21 @@ static int run_remote_archiver(const char *remote, int argc, return !!rv; } -static void *format_subst(const struct commit *commit, const char *format, - unsigned long *sizep) +static void format_subst(const struct commit *commit, + const char *src, size_t len, + struct strbuf *buf) { - unsigned long len = *sizep; - const char *a = format; - struct strbuf result; + char *to_free = NULL; struct strbuf fmt; - strbuf_init(&result, 0); + if (src == buf->buf) + to_free = strbuf_detach(buf); strbuf_init(&fmt, 0); - for (;;) { const char *b, *c; - b = memmem(a, len, "$Format:", 8); - if (!b || a + len < b + 9) + b = memmem(src, len, "$Format:", 8); + if (!b || src + len < b + 9) break; c = memchr(b + 8, '$', len - 8); if (!c) @@ -105,62 +104,57 @@ static void *format_subst(const struct commit *commit, const char *format, strbuf_reset(&fmt); strbuf_add(&fmt, b + 8, c - b - 8); - strbuf_add(&result, a, b - a); - format_commit_message(commit, fmt.buf, &result); - len -= c + 1 - a; - a = c + 1; + strbuf_add(buf, src, b - src); + format_commit_message(commit, fmt.buf, buf); + len -= c + 1 - src; + src = c + 1; } - - strbuf_add(&result, a, len); - - *sizep = result.len; - + strbuf_add(buf, src, len); strbuf_release(&fmt); - return strbuf_detach(&result); + free(to_free); } -static void *convert_to_archive(const char *path, - const void *src, unsigned long *sizep, - const struct commit *commit) +static int convert_to_archive(const char *path, + const void *src, size_t len, + struct strbuf *buf, + const struct commit *commit) { static struct git_attr *attr_export_subst; struct git_attr_check check[1]; if (!commit) - return NULL; + return 0; - if (!attr_export_subst) - attr_export_subst = git_attr("export-subst", 12); + if (!attr_export_subst) + attr_export_subst = git_attr("export-subst", 12); check[0].attr = attr_export_subst; if (git_checkattr(path, ARRAY_SIZE(check), check)) - return NULL; + return 0; if (!ATTR_TRUE(check[0].value)) - return NULL; + return 0; - return format_subst(commit, src, sizep); + format_subst(commit, src, len, buf); + return 1; } void *sha1_file_to_archive(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, - unsigned long *size, + unsigned long *sizep, const struct commit *commit) { - void *buffer, *converted; + void *buffer; - buffer = read_sha1_file(sha1, type, size); + buffer = read_sha1_file(sha1, type, sizep); if (buffer && S_ISREG(mode)) { - converted = convert_to_working_tree(path, buffer, size); - if (converted) { - free(buffer); - buffer = converted; - } - - converted = convert_to_archive(path, buffer, size, commit); - if (converted) { - free(buffer); - buffer = converted; - } + struct strbuf buf; + + strbuf_init(&buf, 0); + strbuf_attach(&buf, buffer, *sizep, *sizep + 1); + convert_to_working_tree(path, buf.buf, buf.len, &buf); + convert_to_archive(path, buf.buf, buf.len, &buf, commit); + *sizep = buf.len; + buffer = buf.buf; } return buffer; diff --git a/cache.h b/cache.h index 231f81d..37eb57e 100644 --- a/cache.h +++ b/cache.h @@ -2,6 +2,7 @@ #define CACHE_H #include "git-compat-util.h" +#include "strbuf.h" #include SHA1_HEADER #include @@ -589,8 +590,9 @@ extern void trace_printf(const char *format, ...); extern void trace_argv_printf(const char **argv, int count, const char *format, ...); /* convert.c */ -extern char *convert_to_git(const char *path, const char *src, unsigned long *sizep); -extern char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep); +/* returns 1 if *dst was used */ +extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst); +extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst); /* diff.c */ extern int diff_auto_refresh_index; diff --git a/convert.c b/convert.c index d77c8eb..00a341c 100644 --- a/convert.c +++ b/convert.c @@ -1,6 +1,7 @@ #include "cache.h" #include "attr.h" #include "run-command.h" +#include "strbuf.h" /* * convert.c - convert a file when checking it out and checking it in. @@ -80,24 +81,19 @@ static int is_binary(unsigned long size, struct text_stat *stats) return 0; } -static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep, int action) +static int crlf_to_git(const char *path, const char *src, size_t len, + struct strbuf *buf, int action) { - char *buffer, *dst; - unsigned long size, nsize; struct text_stat stats; + char *dst; - if ((action == CRLF_BINARY) || !auto_crlf) - return NULL; - - size = *sizep; - if (!size) - return NULL; - - gather_stats(src, size, &stats); + if ((action == CRLF_BINARY) || !auto_crlf || !len) + return 0; + gather_stats(src, len, &stats); /* No CR? Nothing to convert, regardless. */ if (!stats.cr) - return NULL; + return 0; if (action == CRLF_GUESS) { /* @@ -106,24 +102,17 @@ static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep * stuff? */ if (stats.cr != stats.crlf) - return NULL; + return 0; /* * And add some heuristics for binary vs text, of course... */ - if (is_binary(size, &stats)) - return NULL; + if (is_binary(len, &stats)) + return 0; } - /* - * Ok, allocate a new buffer, fill it in, and return it - * to let the caller know that we switched buffers. - */ - nsize = size - stats.crlf; - buffer = xmalloc(nsize); - *sizep = nsize; - - dst = buffer; + strbuf_grow(buf, len); + dst = buf->buf; if (action == CRLF_GUESS) { /* * If we guessed, we already know we rejected a file with @@ -134,71 +123,72 @@ static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep unsigned char c = *src++; if (c != '\r') *dst++ = c; - } while (--size); + } while (--len); } else { do { unsigned char c = *src++; - if (! (c == '\r' && (1 < size && *src == '\n'))) + if (! (c == '\r' && (1 < len && *src == '\n'))) *dst++ = c; - } while (--size); + } while (--len); } - - return buffer; + strbuf_setlen(buf, dst - buf->buf); + return 1; } -static char *crlf_to_worktree(const char *path, const char *src, unsigned long *sizep, int action) +static int crlf_to_worktree(const char *path, const char *src, size_t len, + struct strbuf *buf, int action) { - char *buffer, *dst; - unsigned long size, nsize; + char *to_free = NULL; struct text_stat stats; - unsigned char last; if ((action == CRLF_BINARY) || (action == CRLF_INPUT) || auto_crlf <= 0) - return NULL; + return 0; - size = *sizep; - if (!size) - return NULL; + if (!len) + return 0; - gather_stats(src, size, &stats); + gather_stats(src, len, &stats); /* No LF? Nothing to convert, regardless. */ if (!stats.lf) - return NULL; + return 0; /* Was it already in CRLF format? */ if (stats.lf == stats.crlf) - return NULL; + return 0; if (action == CRLF_GUESS) { /* If we have any bare CR characters, we're not going to touch it */ if (stats.cr != stats.crlf) - return NULL; + return 0; - if (is_binary(size, &stats)) - return NULL; + if (is_binary(len, &stats)) + return 0; } - /* - * Ok, allocate a new buffer, fill it in, and return it - * to let the caller know that we switched buffers. - */ - nsize = size + stats.lf - stats.crlf; - buffer = xmalloc(nsize); - *sizep = nsize; - last = 0; - - dst = buffer; - do { - unsigned char c = *src++; - if (c == '\n' && last != '\r') - *dst++ = '\r'; - *dst++ = c; - last = c; - } while (--size); - - return buffer; + /* are we "faking" in place editing ? */ + if (src == buf->buf) + to_free = strbuf_detach(buf); + + strbuf_grow(buf, len + stats.lf - stats.crlf); + for (;;) { + const char *nl = memchr(src, '\n', len); + if (!nl) + break; + if (nl > src && nl[-1] == '\r') { + strbuf_add(buf, src, nl + 1 - src); + } else { + strbuf_add(buf, src, nl - src); + strbuf_addstr(buf, "\r\n"); + } + len -= nl + 1 - src; + src = nl + 1; + } + strbuf_add(buf, src, len); + + free(to_free); + return 1; } static int filter_buffer(const char *path, const char *src, @@ -246,8 +236,8 @@ static int filter_buffer(const char *path, const char *src, return (write_err || status); } -static char *apply_filter(const char *path, const char *src, - unsigned long *sizep, const char *cmd) +static int apply_filter(const char *path, const char *src, size_t len, + struct strbuf *dst, const char *cmd) { /* * Create a pipeline to have the command filter the buffer's @@ -255,21 +245,19 @@ static char *apply_filter(const char *path, const char *src, * * (child --> cmd) --> us */ - const int SLOP = 4096; int pipe_feed[2]; - int status; - char *dst; - unsigned long dstsize, dstalloc; + int status, ret = 1; struct child_process child_process; + struct strbuf nbuf; if (!cmd) - return NULL; + return 0; memset(&child_process, 0, sizeof(child_process)); if (pipe(pipe_feed) < 0) { error("cannot create pipe to run external filter %s", cmd); - return NULL; + return 0; } fflush(NULL); @@ -278,54 +266,37 @@ static char *apply_filter(const char *path, const char *src, error("cannot fork to run external filter %s", cmd); close(pipe_feed[0]); close(pipe_feed[1]); - return NULL; + return 0; } if (!child_process.pid) { dup2(pipe_feed[1], 1); close(pipe_feed[0]); close(pipe_feed[1]); - exit(filter_buffer(path, src, *sizep, cmd)); + exit(filter_buffer(path, src, len, cmd)); } close(pipe_feed[1]); - dstalloc = *sizep; - dst = xmalloc(dstalloc); - dstsize = 0; - - while (1) { - ssize_t numread = xread(pipe_feed[0], dst + dstsize, - dstalloc - dstsize); - - if (numread <= 0) { - if (!numread) - break; - error("read from external filter %s failed", cmd); - free(dst); - dst = NULL; - break; - } - dstsize += numread; - if (dstalloc <= dstsize + SLOP) { - dstalloc = dstsize + SLOP; - dst = xrealloc(dst, dstalloc); - } + strbuf_init(&nbuf, 0); + if (strbuf_read(&nbuf, pipe_feed[0], len) < 0) { + error("read from external filter %s failed", cmd); + ret = 0; } if (close(pipe_feed[0])) { - error("read from external filter %s failed", cmd); - free(dst); - dst = NULL; + ret = error("read from external filter %s failed", cmd); + ret = 0; } - status = finish_command(&child_process); if (status) { - error("external filter %s failed %d", cmd, -status); - free(dst); - dst = NULL; + ret = error("external filter %s failed %d", cmd, -status); + ret = 0; } - if (dst) - *sizep = dstsize; - return dst; + if (ret) { + *dst = nbuf; + } else { + strbuf_release(&nbuf); + } + return ret; } static struct convert_driver { @@ -449,137 +420,104 @@ static int count_ident(const char *cp, unsigned long size) return cnt; } -static char *ident_to_git(const char *path, const char *src, unsigned long *sizep, int ident) +static int ident_to_git(const char *path, const char *src, size_t len, + struct strbuf *buf, int ident) { - int cnt; - unsigned long size; - char *dst, *buf; + char *dst, *dollar; - if (!ident) - return NULL; - size = *sizep; - cnt = count_ident(src, size); - if (!cnt) - return NULL; - buf = xmalloc(size); - - for (dst = buf; size; size--) { - char ch = *src++; - *dst++ = ch; - if ((ch == '$') && (3 <= size) && - !memcmp("Id:", src, 3)) { - unsigned long rem = size - 3; - const char *cp = src + 3; - do { - ch = *cp++; - if (ch == '$') - break; - rem--; - } while (rem); - if (!rem) - continue; + if (!ident || !count_ident(src, len)) + return 0; + + strbuf_grow(buf, len); + dst = buf->buf; + for (;;) { + dollar = memchr(src, '$', len); + if (!dollar) + break; + memcpy(dst, src, dollar + 1 - src); + dst += dollar + 1 - src; + len -= dollar + 1 - src; + src = dollar + 1; + + if (len > 3 && !memcmp(src, "Id:", 3)) { + dollar = memchr(src + 3, '$', len - 3); + if (!dollar) + break; memcpy(dst, "Id$", 3); dst += 3; - size -= (cp - src); - src = cp; + len -= dollar + 1 - src; + src = dollar + 1; } } - - *sizep = dst - buf; - return buf; + memcpy(dst, src, len); + strbuf_setlen(buf, dst + len - buf->buf); + return 1; } -static char *ident_to_worktree(const char *path, const char *src, unsigned long *sizep, int ident) +static int ident_to_worktree(const char *path, const char *src, size_t len, + struct strbuf *buf, int ident) { - int cnt; - unsigned long size; - char *dst, *buf; unsigned char sha1[20]; + char *to_free = NULL, *dollar; + int cnt; if (!ident) - return NULL; + return 0; - size = *sizep; - cnt = count_ident(src, size); + cnt = count_ident(src, len); if (!cnt) - return NULL; + return 0; - hash_sha1_file(src, size, "blob", sha1); - buf = xmalloc(size + cnt * 43); - - for (dst = buf; size; size--) { - const char *cp; - /* Fetch next source character, move the pointer on */ - char ch = *src++; - /* Copy the current character to the destination */ - *dst++ = ch; - /* If the current character is "$" or there are less than three - * remaining bytes or the two bytes following this one are not - * "Id", then simply read the next character */ - if ((ch != '$') || (size < 3) || memcmp("Id", src, 2)) - continue; - /* - * Here when - * - There are more than 2 bytes remaining - * - The current three bytes are "$Id" - * with - * - ch == "$" - * - src[0] == "I" - */ + /* are we "faking" in place editing ? */ + if (src == buf->buf) + to_free = strbuf_detach(buf); + hash_sha1_file(src, len, "blob", sha1); - /* - * It's possible that an expanded Id has crept its way into the - * repository, we cope with that by stripping the expansion out - */ - if (src[2] == ':') { - /* Expanded keywords have "$Id:" at the front */ + strbuf_grow(buf, len + cnt * 43); + for (;;) { + /* step 1: run to the next '$' */ + dollar = memchr(src, '$', len); + if (!dollar) + break; + strbuf_add(buf, src, dollar + 1 - src); + len -= dollar + 1 - src; + src = dollar + 1; - /* discard up to but not including the closing $ */ - unsigned long rem = size - 3; - /* Point at first byte after the ":" */ - cp = src + 3; - /* - * Throw away characters until either - * - we reach a "$" - * - we run out of bytes (rem == 0) - */ - do { - ch = *cp; - if (ch == '$') - break; - cp++; - rem--; - } while (rem); - /* If the above finished because it ran out of characters, then - * this is an incomplete keyword, so don't run the expansion */ - if (!rem) - continue; - } else if (src[2] == '$') - cp = src + 2; - else - /* Anything other than "$Id:XXX$" or $Id$ and we skip the - * expansion */ + /* step 2: does it looks like a bit like Id:xxx$ or Id$ ? */ + if (len < 3 || memcmp("Id", src, 2)) continue; - /* cp is now pointing at the last $ of the keyword */ - - memcpy(dst, "Id: ", 4); - dst += 4; - memcpy(dst, sha1_to_hex(sha1), 40); - dst += 40; - *dst++ = ' '; + /* step 3: skip over Id$ or Id:xxxxx$ */ + if (src[2] == '$') { + src += 3; + len -= 3; + } else if (src[2] == ':') { + /* + * It's possible that an expanded Id has crept its way into the + * repository, we cope with that by stripping the expansion out + */ + dollar = memchr(src + 3, '$', len - 3); + if (!dollar) { + /* incomplete keyword, no more '$', so just quit the loop */ + break; + } - /* Adjust for the characters we've discarded */ - size -= (cp - src); - src = cp; + len -= dollar + 1 - src; + src = dollar + 1; + } else { + /* it wasn't a "Id$" or "Id:xxxx$" */ + continue; + } - /* Copy the final "$" */ - *dst++ = *src++; - size--; + /* step 4: substitute */ + strbuf_addstr(buf, "Id: "); + strbuf_add(buf, sha1_to_hex(sha1), 40); + strbuf_addstr(buf, " $"); } + strbuf_add(buf, src, len); - *sizep = dst - buf; - return buf; + free(to_free); + return 1; } static int git_path_check_crlf(const char *path, struct git_attr_check *check) @@ -618,13 +556,12 @@ static int git_path_check_ident(const char *path, struct git_attr_check *check) return !!ATTR_TRUE(value); } -char *convert_to_git(const char *path, const char *src, unsigned long *sizep) +int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst) { struct git_attr_check check[3]; int crlf = CRLF_GUESS; - int ident = 0; + int ident = 0, ret = 0; char *filter = NULL; - char *buf, *buf2; setup_convert_check(check); if (!git_checkattr(path, ARRAY_SIZE(check), check)) { @@ -636,30 +573,25 @@ char *convert_to_git(const char *path, const char *src, unsigned long *sizep) filter = drv->clean; } - buf = apply_filter(path, src, sizep, filter); - - buf2 = crlf_to_git(path, buf ? buf : src, sizep, crlf); - if (buf2) { - free(buf); - buf = buf2; + ret |= apply_filter(path, src, len, dst, filter); + if (ret) { + src = dst->buf; + len = dst->len; } - - buf2 = ident_to_git(path, buf ? buf : src, sizep, ident); - if (buf2) { - free(buf); - buf = buf2; + ret |= crlf_to_git(path, src, len, dst, crlf); + if (ret) { + src = dst->buf; + len = dst->len; } - - return buf; + return ret | ident_to_git(path, src, len, dst, ident); } -char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep) +int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst) { struct git_attr_check check[3]; int crlf = CRLF_GUESS; - int ident = 0; + int ident = 0, ret = 0; char *filter = NULL; - char *buf, *buf2; setup_convert_check(check); if (!git_checkattr(path, ARRAY_SIZE(check), check)) { @@ -671,19 +603,15 @@ char *convert_to_working_tree(const char *path, const char *src, unsigned long * filter = drv->smudge; } - buf = ident_to_worktree(path, src, sizep, ident); - - buf2 = crlf_to_worktree(path, buf ? buf : src, sizep, crlf); - if (buf2) { - free(buf); - buf = buf2; + ret |= ident_to_worktree(path, src, len, dst, ident); + if (ret) { + src = dst->buf; + len = dst->len; } - - buf2 = apply_filter(path, buf ? buf : src, sizep, filter); - if (buf2) { - free(buf); - buf = buf2; + ret |= crlf_to_worktree(path, src, len, dst, crlf); + if (ret) { + src = dst->buf; + len = dst->len; } - - return buf; + return ret | apply_filter(path, src, len, dst, filter); } diff --git a/diff.c b/diff.c index 7290309..f41bcd9 100644 --- a/diff.c +++ b/diff.c @@ -1600,10 +1600,9 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) if (!s->sha1_valid || reuse_worktree_file(s->path, s->sha1, 0)) { + struct strbuf buf; struct stat st; int fd; - char *buf; - unsigned long size; if (!strcmp(s->path, "-")) return populate_from_stdin(s); @@ -1644,13 +1643,12 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) /* * Convert from working tree format to canonical git format */ - size = s->size; - buf = convert_to_git(s->path, s->data, &size); - if (buf) { + strbuf_init(&buf, 0); + if (convert_to_git(s->path, s->data, s->size, &buf)) { munmap(s->data, s->size); s->should_munmap = 0; - s->data = buf; - s->size = size; + s->data = buf.buf; + s->size = buf.len; s->should_free = 1; } } diff --git a/entry.c b/entry.c index fc3a506..4a8c73b 100644 --- a/entry.c +++ b/entry.c @@ -104,7 +104,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout long wrote; switch (ntohl(ce->ce_mode) & S_IFMT) { - char *buf, *new; + char *new; + struct strbuf buf; unsigned long size; case S_IFREG: @@ -116,10 +117,11 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout /* * Convert from git internal format to working tree format */ - buf = convert_to_working_tree(ce->name, new, &size); - if (buf) { + strbuf_init(&buf, 0); + if (convert_to_working_tree(ce->name, new, size, &buf)) { free(new); - new = buf; + new = buf.buf; + size = buf.len; } if (to_tempfile) { diff --git a/sha1_file.c b/sha1_file.c index aea1096..64b5b46 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2343,12 +2343,12 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, * Convert blobs to git internal format */ if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) { - unsigned long nsize = size; - char *nbuf = convert_to_git(path, buf, &nsize); - if (nbuf) { + struct strbuf nbuf; + strbuf_init(&nbuf, 0); + if (convert_to_git(path, buf, size, &nbuf)) { munmap(buf, size); - size = nsize; - buf = nbuf; + size = nbuf.len; + buf = nbuf.buf; re_allocated = 1; } } -- cgit v0.10.2-6-g49f6 From ba3ed09728cb25e004d3b732de14fca8aeb602f6 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sat, 15 Sep 2007 15:56:50 +0200 Subject: Now that cache.h needs strbuf.h, remove useless includes. Signed-off-by: Pierre Habouzit Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/archive-tar.c b/archive-tar.c index cc94cf3..a87bc4b 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -3,7 +3,6 @@ */ #include "cache.h" #include "commit.h" -#include "strbuf.h" #include "tar.h" #include "builtin.h" #include "archive.h" diff --git a/builtin-apply.c b/builtin-apply.c index e5c29eb..1256716 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -12,7 +12,6 @@ #include "blob.h" #include "delta.h" #include "builtin.h" -#include "strbuf.h" /* * --check turns on checking that the working tree matches the diff --git a/builtin-blame.c b/builtin-blame.c index b004f06..e364b6c 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -18,7 +18,6 @@ #include "cache-tree.h" #include "path-list.h" #include "mailmap.h" -#include "strbuf.h" static char blame_usage[] = "git-blame [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-w] [-L n,m] [-S ] [-M] [-C] [-C] [--contents ] [--incremental] [commit] [--] file\n" diff --git a/builtin-checkout-index.c b/builtin-checkout-index.c index 153ba7d..85e8efe 100644 --- a/builtin-checkout-index.c +++ b/builtin-checkout-index.c @@ -38,7 +38,6 @@ */ #include "builtin.h" #include "cache.h" -#include "strbuf.h" #include "quote.h" #include "cache-tree.h" diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c index 325334f..88b0ab3 100644 --- a/builtin-commit-tree.c +++ b/builtin-commit-tree.c @@ -8,7 +8,6 @@ #include "tree.h" #include "builtin.h" #include "utf8.h" -#include "strbuf.h" #define BLOCKING (1ul << 14) diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index 90bdc32..514a3cc 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -2,7 +2,6 @@ #include "cache.h" #include "refs.h" #include "commit.h" -#include "strbuf.h" static char *get_stdin(void) { diff --git a/builtin-rerere.c b/builtin-rerere.c index 826d346..58288f6 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -1,7 +1,6 @@ #include "builtin.h" #include "cache.h" #include "path-list.h" -#include "strbuf.h" #include "xdiff/xdiff.h" #include "xdiff-interface.h" diff --git a/builtin-stripspace.c b/builtin-stripspace.c index c4cf2f0..1ce2847 100644 --- a/builtin-stripspace.c +++ b/builtin-stripspace.c @@ -1,6 +1,5 @@ #include "builtin.h" #include "cache.h" -#include "strbuf.h" /* * Returns the length of a line, without trailing spaces. diff --git a/builtin-tag.c b/builtin-tag.c index 9f29342..82ebda1 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -8,7 +8,6 @@ #include "cache.h" #include "builtin.h" -#include "strbuf.h" #include "refs.h" #include "tag.h" #include "run-command.h" diff --git a/builtin-update-index.c b/builtin-update-index.c index 9240a28..1091f1b 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -4,7 +4,6 @@ * Copyright (C) Linus Torvalds, 2005 */ #include "cache.h" -#include "strbuf.h" #include "quote.h" #include "cache-tree.h" #include "tree-walk.h" diff --git a/cache-tree.c b/cache-tree.c index 8f53c99..5471844 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -1,5 +1,4 @@ #include "cache.h" -#include "strbuf.h" #include "tree.h" #include "cache-tree.h" diff --git a/convert.c b/convert.c index 00a341c..508d30b 100644 --- a/convert.c +++ b/convert.c @@ -1,7 +1,6 @@ #include "cache.h" #include "attr.h" #include "run-command.h" -#include "strbuf.h" /* * convert.c - convert a file when checking it out and checking it in. diff --git a/diff.c b/diff.c index f41bcd9..56b672c 100644 --- a/diff.c +++ b/diff.c @@ -9,7 +9,6 @@ #include "xdiff-interface.h" #include "color.h" #include "attr.h" -#include "strbuf.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 diff --git a/fast-import.c b/fast-import.c index 2c0bfb9..1866d34 100644 --- a/fast-import.c +++ b/fast-import.c @@ -149,7 +149,6 @@ Format of STDIN stream: #include "pack.h" #include "refs.h" #include "csum-file.h" -#include "strbuf.h" #include "quote.h" #define PACK_ID_BITS 16 diff --git a/fetch.c b/fetch.c index dd6ed9e..c256e6f 100644 --- a/fetch.c +++ b/fetch.c @@ -6,7 +6,6 @@ #include "tag.h" #include "blob.h" #include "refs.h" -#include "strbuf.h" int get_tree = 0; int get_history = 0; diff --git a/imap-send.c b/imap-send.c index ecd4216..86e4a0f 100644 --- a/imap-send.c +++ b/imap-send.c @@ -23,7 +23,6 @@ */ #include "cache.h" -#include "strbuf.h" typedef struct store_conf { char *name; diff --git a/mktag.c b/mktag.c index 7567f9e..b05260c 100644 --- a/mktag.c +++ b/mktag.c @@ -1,5 +1,4 @@ #include "cache.h" -#include "strbuf.h" #include "tag.h" /* diff --git a/mktree.c b/mktree.c index 3891cd9..5dab4bd 100644 --- a/mktree.c +++ b/mktree.c @@ -4,7 +4,6 @@ * Copyright (c) Junio C Hamano, 2006 */ #include "cache.h" -#include "strbuf.h" #include "quote.h" #include "tree.h" diff --git a/sha1_file.c b/sha1_file.c index 64b5b46..59325d4 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -14,7 +14,6 @@ #include "tag.h" #include "tree.h" #include "refs.h" -#include "strbuf.h" #ifndef O_NOATIME #if defined(__linux__) && (defined(__i386__) || defined(__PPC__)) diff --git a/strbuf.c b/strbuf.c index ff551ac..c5f9e2a 100644 --- a/strbuf.c +++ b/strbuf.c @@ -1,5 +1,4 @@ #include "cache.h" -#include "strbuf.h" void strbuf_init(struct strbuf *sb, size_t hint) { -- cgit v0.10.2-6-g49f6 From c7f9cb14286fff23bfe65033d4ec63f84c77f554 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 16 Sep 2007 18:54:42 +0200 Subject: builtin-apply: use strbuf's instead of buffer_desc's. Signed-off-by: Pierre Habouzit Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/builtin-apply.c b/builtin-apply.c index 1256716..61b5131 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -1441,37 +1441,28 @@ static void show_stats(struct patch *patch) free(qname); } -static int read_old_data(struct stat *st, const char *path, char **buf_p, unsigned long *alloc_p, unsigned long *size_p) +static int read_old_data(struct stat *st, const char *path, struct strbuf *buf) { int fd; - unsigned long got; - struct strbuf nbuf; - unsigned long size = *size_p; - char *buf = *buf_p; switch (st->st_mode & S_IFMT) { case S_IFLNK: - return readlink(path, buf, size) != size; + strbuf_grow(buf, st->st_size); + if (readlink(path, buf->buf, st->st_size) != st->st_size) + return -1; + strbuf_setlen(buf, st->st_size); + return 0; case S_IFREG: fd = open(path, O_RDONLY); if (fd < 0) return error("unable to open %s", path); - got = 0; - for (;;) { - ssize_t ret = xread(fd, buf + got, size - got); - if (ret <= 0) - break; - got += ret; + if (strbuf_read(buf, fd, st->st_size) < 0) { + close(fd); + return -1; } close(fd); - strbuf_init(&nbuf, 0); - if (convert_to_git(path, buf, size, &nbuf)) { - free(buf); - *buf_p = nbuf.buf; - *alloc_p = nbuf.alloc; - *size_p = nbuf.len; - } - return got != size; + convert_to_git(path, buf->buf, buf->len, buf); + return 0; default: return -1; } @@ -1576,12 +1567,6 @@ static void remove_last_line(const char **rbuf, int *rsize) *rsize = offset + 1; } -struct buffer_desc { - char *buffer; - unsigned long size; - unsigned long alloc; -}; - static int apply_line(char *output, const char *patch, int plen) { /* plen is number of bytes to be copied from patch, @@ -1651,10 +1636,9 @@ static int apply_line(char *output, const char *patch, int plen) return output + plen - buf; } -static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, int inaccurate_eof) +static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int inaccurate_eof) { int match_beginning, match_end; - char *buf = desc->buffer; const char *patch = frag->patch; int offset, size = frag->size; char *old = xmalloc(size); @@ -1765,24 +1749,17 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i lines = 0; pos = frag->newpos; for (;;) { - offset = find_offset(buf, desc->size, + offset = find_offset(buf->buf, buf->len, oldlines, oldsize, pos, &lines); - if (match_end && offset + oldsize != desc->size) + if (match_end && offset + oldsize != buf->len) offset = -1; if (match_beginning && offset) offset = -1; if (offset >= 0) { - int diff; - unsigned long size, alloc; - if (new_whitespace == strip_whitespace && - (desc->size - oldsize - offset == 0)) /* end of file? */ + (buf->len - oldsize - offset == 0)) /* end of file? */ newsize -= new_blank_lines_at_end; - diff = newsize - oldsize; - size = desc->size + diff; - alloc = desc->alloc; - /* Warn if it was necessary to reduce the number * of context lines. */ @@ -1792,19 +1769,8 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i " to apply fragment at %d\n", leading, trailing, pos + lines); - if (size > alloc) { - alloc = size + 8192; - desc->alloc = alloc; - buf = xrealloc(buf, alloc); - desc->buffer = buf; - } - desc->size = size; - memmove(buf + offset + newsize, - buf + offset + oldsize, - size - offset - newsize); - memcpy(buf + offset, newlines, newsize); + strbuf_splice(buf, offset, oldsize, newlines, newsize); offset = 0; - break; } @@ -1840,12 +1806,11 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i return offset; } -static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch) +static int apply_binary_fragment(struct strbuf *buf, struct patch *patch) { - unsigned long dst_size; struct fragment *fragment = patch->fragments; - void *data; - void *result; + unsigned long len; + void *dst; /* Binary patch is irreversible without the optional second hunk */ if (apply_in_reverse) { @@ -1856,29 +1821,24 @@ static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch) ? patch->new_name : patch->old_name); fragment = fragment->next; } - data = (void*) fragment->patch; switch (fragment->binary_patch_method) { case BINARY_DELTA_DEFLATED: - result = patch_delta(desc->buffer, desc->size, - data, - fragment->size, - &dst_size); - free(desc->buffer); - desc->buffer = result; - break; + dst = patch_delta(buf->buf, buf->len, fragment->patch, + fragment->size, &len); + if (!dst) + return -1; + /* XXX patch_delta NUL-terminates */ + strbuf_attach(buf, dst, len, len + 1); + return 0; case BINARY_LITERAL_DEFLATED: - free(desc->buffer); - desc->buffer = data; - dst_size = fragment->size; - break; + strbuf_reset(buf); + strbuf_add(buf, fragment->patch, fragment->size); + return 0; } - if (!desc->buffer) - return -1; - desc->size = desc->alloc = dst_size; - return 0; + return -1; } -static int apply_binary(struct buffer_desc *desc, struct patch *patch) +static int apply_binary(struct strbuf *buf, struct patch *patch) { const char *name = patch->old_name ? patch->old_name : patch->new_name; unsigned char sha1[20]; @@ -1897,7 +1857,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch) /* See if the old one matches what the patch * applies to. */ - hash_sha1_file(desc->buffer, desc->size, blob_type, sha1); + hash_sha1_file(buf->buf, buf->len, blob_type, sha1); if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix)) return error("the patch applies to '%s' (%s), " "which does not match the " @@ -1906,16 +1866,14 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch) } else { /* Otherwise, the old one must be empty. */ - if (desc->size) + if (buf->len) return error("the patch applies to an empty " "'%s' but it is not empty", name); } get_sha1_hex(patch->new_sha1_prefix, sha1); if (is_null_sha1(sha1)) { - free(desc->buffer); - desc->alloc = desc->size = 0; - desc->buffer = NULL; + strbuf_release(buf); return 0; /* deletion patch */ } @@ -1923,43 +1881,44 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch) /* We already have the postimage */ enum object_type type; unsigned long size; + char *result; - free(desc->buffer); - desc->buffer = read_sha1_file(sha1, &type, &size); - if (!desc->buffer) + result = read_sha1_file(sha1, &type, &size); + if (!result) return error("the necessary postimage %s for " "'%s' cannot be read", patch->new_sha1_prefix, name); - desc->alloc = desc->size = size; - } - else { - /* We have verified desc matches the preimage; + /* XXX read_sha1_file NUL-terminates */ + strbuf_attach(buf, result, size, size + 1); + } else { + /* We have verified buf matches the preimage; * apply the patch data to it, which is stored * in the patch->fragments->{patch,size}. */ - if (apply_binary_fragment(desc, patch)) + if (apply_binary_fragment(buf, patch)) return error("binary patch does not apply to '%s'", name); /* verify that the result matches */ - hash_sha1_file(desc->buffer, desc->size, blob_type, sha1); + hash_sha1_file(buf->buf, buf->len, blob_type, sha1); if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix)) - return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", name, patch->new_sha1_prefix, sha1_to_hex(sha1)); + return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", + name, patch->new_sha1_prefix, sha1_to_hex(sha1)); } return 0; } -static int apply_fragments(struct buffer_desc *desc, struct patch *patch) +static int apply_fragments(struct strbuf *buf, struct patch *patch) { struct fragment *frag = patch->fragments; const char *name = patch->old_name ? patch->old_name : patch->new_name; if (patch->is_binary) - return apply_binary(desc, patch); + return apply_binary(buf, patch); while (frag) { - if (apply_one_fragment(desc, frag, patch->inaccurate_eof)) { + if (apply_one_fragment(buf, frag, patch->inaccurate_eof)) { error("patch failed: %s:%ld", name, frag->oldpos); if (!apply_with_reject) return -1; @@ -1970,76 +1929,57 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch) return 0; } -static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p, - unsigned long *size_p) +static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf) { if (!ce) return 0; if (S_ISGITLINK(ntohl(ce->ce_mode))) { - *buf_p = xmalloc(100); - *size_p = snprintf(*buf_p, 100, - "Subproject commit %s\n", sha1_to_hex(ce->sha1)); + strbuf_grow(buf, 100); + strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1)); } else { enum object_type type; - *buf_p = read_sha1_file(ce->sha1, &type, size_p); - if (!*buf_p) + unsigned long sz; + char *result; + + result = read_sha1_file(ce->sha1, &type, &sz); + if (!result) return -1; + /* XXX read_sha1_file NUL-terminates */ + strbuf_attach(buf, result, sz, sz + 1); } return 0; } static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) { - char *buf; - unsigned long size, alloc; - struct buffer_desc desc; + struct strbuf buf; - size = 0; - alloc = 0; - buf = NULL; + strbuf_init(&buf, 0); if (cached) { - if (read_file_or_gitlink(ce, &buf, &size)) + if (read_file_or_gitlink(ce, &buf)) return error("read of %s failed", patch->old_name); - alloc = size; } else if (patch->old_name) { if (S_ISGITLINK(patch->old_mode)) { - if (ce) - read_file_or_gitlink(ce, &buf, &size); - else { + if (ce) { + read_file_or_gitlink(ce, &buf); + } else { /* * There is no way to apply subproject * patch without looking at the index. */ patch->fragments = NULL; - size = 0; } - } - else { - size = xsize_t(st->st_size); - alloc = size + 8192; - buf = xmalloc(alloc); - if (read_old_data(st, patch->old_name, - &buf, &alloc, &size)) - return error("read of %s failed", - patch->old_name); + } else { + if (read_old_data(st, patch->old_name, &buf)) + return error("read of %s failed", patch->old_name); } } - desc.size = size; - desc.alloc = alloc; - desc.buffer = buf; - - if (apply_fragments(&desc, patch) < 0) + if (apply_fragments(&buf, patch) < 0) return -1; /* note with --reject this succeeds. */ - - /* NUL terminate the result */ - if (desc.alloc <= desc.size) - desc.buffer = xrealloc(desc.buffer, desc.size + 1); - desc.buffer[desc.size] = 0; - - patch->result = desc.buffer; - patch->resultsize = desc.size; + patch->result = buf.buf; + patch->resultsize = buf.len; if (0 < patch->is_delete && patch->resultsize) return error("removal patch leaves file contents"); @@ -2460,19 +2400,11 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, size = nbuf.len; buf = nbuf.buf; } + write_or_die(fd, buf, size); + strbuf_release(&nbuf); - while (size) { - int written = xwrite(fd, buf, size); - if (written < 0) - die("writing file %s: %s", path, strerror(errno)); - if (!written) - die("out of space writing file %s", path); - buf += written; - size -= written; - } if (close(fd) < 0) die("closing file %s: %s", path, strerror(errno)); - strbuf_release(&nbuf); return 0; } -- cgit v0.10.2-6-g49f6 From a08f23ab3eab67aac7163a284038f45fa99ef66f Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sat, 15 Sep 2007 23:50:12 +0200 Subject: Refactor replace_encoding_header. * Be more clever in how we search for "encoding ...\n": parse for real instead of the sloppy strstr's. * use strbuf_splice to do the substring replacements. Signed-off-by: Pierre Habouzit Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/commit.c b/commit.c index 6602e2c..13af933 100644 --- a/commit.c +++ b/commit.c @@ -648,47 +648,34 @@ static char *get_header(const struct commit *commit, const char *key) static char *replace_encoding_header(char *buf, const char *encoding) { - char *encoding_header = strstr(buf, "\nencoding "); - char *header_end = strstr(buf, "\n\n"); - char *end_of_encoding_header; - int encoding_header_pos; - int encoding_header_len; - int new_len; - int need_len; - int buflen = strlen(buf) + 1; - - if (!header_end) - header_end = buf + buflen; - if (!encoding_header || encoding_header >= header_end) - return buf; - encoding_header++; - end_of_encoding_header = strchr(encoding_header, '\n'); - if (!end_of_encoding_header) + struct strbuf tmp; + size_t start, len; + char *cp = buf; + + /* guess if there is an encoding header before a \n\n */ + while (strncmp(cp, "encoding ", strlen("encoding "))) { + cp = strchr(cp, '\n'); + if (!cp || *++cp == '\n') + return buf; + } + start = cp - buf; + cp = strchr(cp, '\n'); + if (!cp) return buf; /* should not happen but be defensive */ - end_of_encoding_header++; - - encoding_header_len = end_of_encoding_header - encoding_header; - encoding_header_pos = encoding_header - buf; + len = cp + 1 - (buf + start); + strbuf_init(&tmp, 0); + strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1); if (is_encoding_utf8(encoding)) { /* we have re-coded to UTF-8; drop the header */ - memmove(encoding_header, end_of_encoding_header, - buflen - (encoding_header_pos + encoding_header_len)); - return buf; - } - new_len = strlen(encoding); - need_len = new_len + strlen("encoding \n"); - if (encoding_header_len < need_len) { - buf = xrealloc(buf, buflen + (need_len - encoding_header_len)); - encoding_header = buf + encoding_header_pos; - end_of_encoding_header = encoding_header + encoding_header_len; + strbuf_splice(&tmp, start, len, NULL, 0); + } else { + /* just replaces XXXX in 'encoding XXXX\n' */ + strbuf_splice(&tmp, start + strlen("encoding "), + len - strlen("encoding \n"), + encoding, strlen(encoding)); } - memmove(end_of_encoding_header + (need_len - encoding_header_len), - end_of_encoding_header, - buflen - (encoding_header_pos + encoding_header_len)); - memcpy(encoding_header + 9, encoding, strlen(encoding)); - encoding_header[9 + new_len] = '\n'; - return buf; + return tmp.buf; } static char *logmsg_reencode(const struct commit *commit, -- cgit v0.10.2-6-g49f6 From 8b6087fb25068d6af927f112a93dc056930f3108 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 16 Sep 2007 10:19:01 +0200 Subject: Remove preemptive allocations. Careful profiling shows that we spend more time guessing what pattern allocation will have, whereas we can delay it only at the point where add_rfc2047 will be used and don't allocate huge memory area for the many cases where it's not. Signed-off-by: Pierre Habouzit Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/commit.c b/commit.c index 13af933..85889f9 100644 --- a/commit.c +++ b/commit.c @@ -501,6 +501,7 @@ static void add_rfc2047(struct strbuf *sb, const char *line, int len, return; needquote: + strbuf_grow(sb, len * 3 + strlen(encoding) + 100); strbuf_addf(sb, "=?%s?q?", encoding); for (i = last = 0; i < len; i++) { unsigned ch = line[i] & 0xFF; @@ -520,14 +521,6 @@ needquote: strbuf_addstr(sb, "?="); } -static unsigned long bound_rfc2047(unsigned long len, const char *encoding) -{ - /* upper bound of q encoded string of length 'len' */ - unsigned long elen = strlen(encoding); - - return len * 3 + elen + 100; -} - static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, const char *line, enum date_mode dmode, const char *encoding) @@ -560,8 +553,7 @@ static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb add_rfc2047(sb, line, display_name_length, encoding); strbuf_add(sb, name_tail, namelen - display_name_length); strbuf_addch(sb, '\n'); - } - else { + } else { strbuf_addf(sb, "%s: %.*s%.*s\n", what, (fmt == CMIT_FMT_FULLER) ? 4 : 0, filler, namelen, line); @@ -955,19 +947,12 @@ static void pp_header(enum cmit_fmt fmt, * FULLER shows both authors and dates. */ if (!memcmp(line, "author ", 7)) { - unsigned long len = linelen; - if (fmt == CMIT_FMT_EMAIL) - len = bound_rfc2047(linelen, encoding); - strbuf_grow(sb, len + 80); + strbuf_grow(sb, linelen + 80); add_user_info("Author", fmt, sb, line + 7, dmode, encoding); } - if (!memcmp(line, "committer ", 10) && (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) { - unsigned long len = linelen; - if (fmt == CMIT_FMT_EMAIL) - len = bound_rfc2047(linelen, encoding); - strbuf_grow(sb, len + 80); + strbuf_grow(sb, linelen + 80); add_user_info("Commit", fmt, sb, line + 10, dmode, encoding); } } @@ -982,7 +967,6 @@ static void pp_title_line(enum cmit_fmt fmt, int plain_non_ascii) { struct strbuf title; - unsigned long len; strbuf_init(&title, 80); @@ -1004,16 +988,7 @@ static void pp_title_line(enum cmit_fmt fmt, strbuf_add(&title, line, linelen); } - /* Enough slop for the MIME header and rfc2047 */ - len = bound_rfc2047(title.len, encoding) + 1000; - if (subject) - len += strlen(subject); - if (after_subject) - len += strlen(after_subject); - if (encoding) - len += strlen(encoding); - - strbuf_grow(sb, title.len + len); + strbuf_grow(sb, title.len + 1024); if (subject) { strbuf_addstr(sb, subject); add_rfc2047(sb, title.buf, title.len, encoding); -- cgit v0.10.2-6-g49f6 From 000dfd3f6e3f61e15ccfd4cecb3a51624adfbf38 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 16 Sep 2007 23:15:19 -0700 Subject: Export matches_pack_name() and fix its return value The function sounds boolean; make it behave as one, not "0 for success, non-zero for failure". Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index 70abbd5..3fa5b8e 100644 --- a/cache.h +++ b/cache.h @@ -529,6 +529,7 @@ extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsign extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep); extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t); extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *); +extern int matches_pack_name(struct packed_git *p, const char *name); /* Dumb servers support */ extern int update_server_info(int); diff --git a/sha1_file.c b/sha1_file.c index 9978a58..5801c3e 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1684,22 +1684,22 @@ off_t find_pack_entry_one(const unsigned char *sha1, return 0; } -static int matches_pack_name(struct packed_git *p, const char *ig) +int matches_pack_name(struct packed_git *p, const char *name) { const char *last_c, *c; - if (!strcmp(p->pack_name, ig)) - return 0; + if (!strcmp(p->pack_name, name)) + return 1; for (c = p->pack_name, last_c = c; *c;) if (*c == '/') last_c = ++c; else ++c; - if (!strcmp(last_c, ig)) - return 0; + if (!strcmp(last_c, name)) + return 1; - return 1; + return 0; } static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed) @@ -1717,7 +1717,7 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, cons if (ignore_packed) { const char **ig; for (ig = ignore_packed; *ig; ig++) - if (!matches_pack_name(p, *ig)) + if (matches_pack_name(p, *ig)) break; if (*ig) goto next; -- cgit v0.10.2-6-g49f6 From 08cdfb13374f31b0c1c47444f55042e7b72c3190 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 16 Sep 2007 23:20:07 -0700 Subject: pack-objects --keep-unreachable This new option is meant to be used in conjunction with the options "git repack -a -d" usually invokes the underlying pack-objects with. When this option is given, objects unreachable from the refs in packs named with --unpacked= option are added to the resulting pack, in addition to the reachable objects that are not in packs marked with *.keep files. Signed-off-by: Junio C Hamano diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 12509fa..ba7c8da 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -21,7 +21,7 @@ git-pack-objects [{ -q | --progress | --all-progress }] \n\ [--window=N] [--window-memory=N] [--depth=N] \n\ [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\ [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\ - [--stdout | base-name] [object.sha1, OBJ_COMMIT, NULL, 0); + commit->object.flags |= OBJECT_ADDED; } static void show_object(struct object_array_entry *p) { add_preferred_base_object(p->name); add_object_entry(p->item->sha1, p->item->type, p->name, 0); + p->item->flags |= OBJECT_ADDED; } static void show_edge(struct commit *commit) @@ -1641,6 +1645,86 @@ static void show_edge(struct commit *commit) add_preferred_base(commit->object.sha1); } +struct in_pack_object { + off_t offset; + struct object *object; +}; + +struct in_pack { + int alloc; + int nr; + struct in_pack_object *array; +}; + +static void mark_in_pack_object(struct object *object, struct packed_git *p, struct in_pack *in_pack) +{ + in_pack->array[in_pack->nr].offset = find_pack_entry_one(object->sha1, p); + in_pack->array[in_pack->nr].object = object; + in_pack->nr++; +} + +/* + * Compare the objects in the offset order, in order to emulate the + * "git-rev-list --objects" output that produced the pack originally. + */ +static int ofscmp(const void *a_, const void *b_) +{ + struct in_pack_object *a = (struct in_pack_object *)a_; + struct in_pack_object *b = (struct in_pack_object *)b_; + + if (a->offset < b->offset) + return -1; + else if (a->offset > b->offset) + return 1; + else + return hashcmp(a->object->sha1, b->object->sha1); +} + +static void add_objects_in_unpacked_packs(struct rev_info *revs) +{ + struct packed_git *p; + struct in_pack in_pack; + uint32_t i; + + memset(&in_pack, 0, sizeof(in_pack)); + + for (p = packed_git; p; p = p->next) { + const unsigned char *sha1; + struct object *o; + + for (i = 0; i < revs->num_ignore_packed; i++) { + if (matches_pack_name(p, revs->ignore_packed[i])) + break; + } + if (revs->num_ignore_packed <= i) + continue; + if (open_pack_index(p)) + die("cannot open pack index"); + + ALLOC_GROW(in_pack.array, + in_pack.nr + p->num_objects, + in_pack.alloc); + + for (i = 0; i < p->num_objects; i++) { + sha1 = nth_packed_object_sha1(p, i); + o = lookup_unknown_object(sha1); + if (!(o->flags & OBJECT_ADDED)) + mark_in_pack_object(o, p, &in_pack); + o->flags |= OBJECT_ADDED; + } + } + + if (in_pack.nr) { + qsort(in_pack.array, in_pack.nr, sizeof(in_pack.array[0]), + ofscmp); + for (i = 0; i < in_pack.nr; i++) { + struct object *o = in_pack.array[i].object; + add_object_entry(o->sha1, o->type, "", 0); + } + } + free(in_pack.array); +} + static void get_object_list(int ac, const char **av) { struct rev_info revs; @@ -1672,6 +1756,9 @@ static void get_object_list(int ac, const char **av) prepare_revision_walk(&revs); mark_edges_uninteresting(revs.commits, &revs, show_edge); traverse_commit_list(&revs, show_commit, show_object); + + if (keep_unreachable) + add_objects_in_unpacked_packs(&revs); } static int adjust_perm(const char *path, mode_t mode) @@ -1789,6 +1876,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) use_internal_rev_list = 1; continue; } + if (!strcmp("--keep-unreachable", arg)) { + keep_unreachable = 1; + continue; + } if (!strcmp("--unpacked", arg) || !prefixcmp(arg, "--unpacked=") || !strcmp("--reflog", arg) || -- cgit v0.10.2-6-g49f6 From 65aa53029a32a1ad36523f3e7a1bb933d4494805 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 16 Sep 2007 23:24:07 -0700 Subject: repack -A -d: use --keep-unreachable when repacking This is a safer variant of "repack -a -d" that does not drop unreachable objects that are in packs. Signed-off-by: Junio C Hamano diff --git a/git-repack.sh b/git-repack.sh index 156c5e8..633b902 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -3,17 +3,19 @@ # Copyright (c) 2005 Linus Torvalds # -USAGE='[-a] [-d] [-f] [-l] [-n] [-q] [--max-pack-size=N] [--window=N] [--window-memory=N] [--depth=N]' +USAGE='[-a|-A] [-d] [-f] [-l] [-n] [-q] [--max-pack-size=N] [--window=N] [--window-memory=N] [--depth=N]' SUBDIRECTORY_OK='Yes' . git-sh-setup -no_update_info= all_into_one= remove_redundant= +no_update_info= all_into_one= remove_redundant= keep_unreachable= local= quiet= no_reuse= extra= while case "$#" in 0) break ;; esac do case "$1" in -n) no_update_info=t ;; -a) all_into_one=t ;; + -A) all_into_one=t + keep_unreachable=--keep-unreachable ;; -d) remove_redundant=t ;; -q) quiet=-q ;; -f) no_reuse=--no-reuse-object ;; @@ -59,7 +61,13 @@ case ",$all_into_one," in fi done fi - [ -z "$args" ] && args='--unpacked --incremental' + if test -z "$args" + then + args='--unpacked --incremental' + elif test -n "$keep_unreachable" + then + args="$args $keep_unreachable" + fi ;; esac -- cgit v0.10.2-6-g49f6 From caf9de2f46471dc25180bf519c07537c00a68dda Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 17 Sep 2007 00:37:06 -0700 Subject: git-gc --auto: move threshold check to need_to_gc() function. That is where we decide if we are going to run gc automatically. Signed-off-by: Junio C Hamano diff --git a/builtin-gc.c b/builtin-gc.c index 093b3dd..f046a2a 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -80,6 +80,13 @@ static int need_to_gc(void) int num_loose = 0; int needed = 0; + /* + * Setting gc.auto to 0 or negative can disable the + * automatic gc + */ + if (gc_auto_threshold <= 0) + return 0; + if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) { warning("insanely long object directory %.*s", 50, objdir); return 0; @@ -129,8 +136,6 @@ int cmd_gc(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--auto")) { - if (gc_auto_threshold <= 0) - return 0; auto_gc = 1; continue; } -- cgit v0.10.2-6-g49f6 From e9831e83e063844b90cf9e525d0003715dd8b395 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 17 Sep 2007 00:39:52 -0700 Subject: git-gc --auto: add documentation. This documents the auto-packing of loose objects performed by git-gc --auto. Signed-off-by: Junio C Hamano diff --git a/Documentation/config.txt b/Documentation/config.txt index 866e053..6b6553d 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -439,6 +439,13 @@ gc.aggressiveWindow:: algorithm used by 'git gc --aggressive'. This defaults to 10. +gc.auto:: + When there are approximately more than this many loose + objects in the repository, `git gc --auto` will pack them. + Some Porcelain commands use this command to perform a + light-weight garbage collection from time to time. Setting + this to 0 disables it. + gc.packrefs:: `git gc` does not run `git pack-refs` in a bare repository by default so that older dumb-transport clients can still fetch diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt index c7742ca..40c1ce4 100644 --- a/Documentation/git-gc.txt +++ b/Documentation/git-gc.txt @@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository SYNOPSIS -------- -'git-gc' [--prune] [--aggressive] +'git-gc' [--prune] [--aggressive] [--auto] DESCRIPTION ----------- @@ -43,6 +43,15 @@ OPTIONS persistent, so this option only needs to be used occasionally; every few hundred changesets or so. +--auto:: + With this option, `git gc` checks if there are too many + loose objects in the repository and runs + gitlink:git-repack[1] with `-d -l` option to pack them. + The threshold is set with `gc.auto` configuration + variable, and can be disabled by setting it to 0. Some + Porcelain commands use this after they perform operation + that could create many loose objects automatically. + Configuration ------------- -- cgit v0.10.2-6-g49f6 From a087cc9819d5790a0aeb42c2bd74f781c555e8d6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 17 Sep 2007 00:44:17 -0700 Subject: git-gc --auto: protect ourselves from accumulated cruft Deciding to run "repack -d -l" when there are too many loose objects would backfire when there are too many loose objects that are unreachable, because repacking that way would never improve the situation. Detect that case by checking the number of loose objects again after automatic garbage collection runs, and issue an warning to run "prune" manually. Signed-off-by: Junio C Hamano diff --git a/builtin-gc.c b/builtin-gc.c index f046a2a..bf29f5e 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -64,7 +64,7 @@ static void append_option(const char **cmd, const char *opt, int max_length) cmd[i] = NULL; } -static int need_to_gc(void) +static int too_many_loose_objects(void) { /* * Quickly check if a "gc" is needed, by estimating how @@ -80,13 +80,6 @@ static int need_to_gc(void) int num_loose = 0; int needed = 0; - /* - * Setting gc.auto to 0 or negative can disable the - * automatic gc - */ - if (gc_auto_threshold <= 0) - return 0; - if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) { warning("insanely long object directory %.*s", 50, objdir); return 0; @@ -109,6 +102,18 @@ static int need_to_gc(void) return needed; } +static int need_to_gc(void) +{ + /* + * Setting gc.auto to 0 or negative can disable the + * automatic gc + */ + if (gc_auto_threshold <= 0) + return 0; + + return too_many_loose_objects(); +} + int cmd_gc(int argc, const char **argv, const char *prefix) { int i; @@ -170,5 +175,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (run_command_v_opt(argv_rerere, RUN_GIT_CMD)) return error(FAILED_RUN, argv_rerere[0]); + if (auto_gc && too_many_loose_objects()) + warning("There are too many unreachable loose objects; " + "run 'git prune' to remove them."); + return 0; } -- cgit v0.10.2-6-g49f6 From 95143f9e686dee144e0ff4a20190b923e20e1b64 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 17 Sep 2007 00:48:39 -0700 Subject: git-gc --auto: restructure the way "repack" command line is built. We used to build the command line to run repack outside of need_to_gc() but with the next patch we would want to tweak the command line depending on the nature of need. Signed-off-by: Junio C Hamano diff --git a/builtin-gc.c b/builtin-gc.c index bf29f5e..34ce35b 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -29,8 +29,6 @@ static const char *argv_repack[MAX_ADD] = {"repack", "-a", "-d", "-l", NULL}; static const char *argv_prune[] = {"prune", NULL}; static const char *argv_rerere[] = {"rerere", "gc", NULL}; -static const char *argv_repack_auto[] = {"repack", "-d", "-l", NULL}; - static int gc_config(const char *var, const char *value) { if (!strcmp(var, "gc.packrefs")) { @@ -104,6 +102,8 @@ static int too_many_loose_objects(void) static int need_to_gc(void) { + int ac = 0; + /* * Setting gc.auto to 0 or negative can disable the * automatic gc @@ -111,7 +111,14 @@ static int need_to_gc(void) if (gc_auto_threshold <= 0) return 0; - return too_many_loose_objects(); + if (!too_many_loose_objects()) + return 0; + + argv_repack[ac++] = "repack"; + argv_repack[ac++] = "-d"; + argv_repack[ac++] = "-l"; + argv_repack[ac++] = NULL; + return 1; } int cmd_gc(int argc, const char **argv, const char *prefix) @@ -154,8 +161,6 @@ int cmd_gc(int argc, const char **argv, const char *prefix) * Auto-gc should be least intrusive as possible. */ prune = 0; - for (i = 0; i < ARRAY_SIZE(argv_repack_auto); i++) - argv_repack[i] = argv_repack_auto[i]; if (!need_to_gc()) return 0; } -- cgit v0.10.2-6-g49f6 From 17815501a8f95c080891acd9537514adfe17c80e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 17 Sep 2007 00:55:13 -0700 Subject: git-gc --auto: run "repack -A -d -l" as necessary. This teaches "git-gc --auto" to consolidate many packs into one without losing unreachable objects in them by using "repack -A" when there are too many packfiles that are not marked with *.keep in the repository. gc.autopacklimit configuration can be used to set the maximum number of packs a repository is allowed to have before this mechanism kicks in. Signed-off-by: Junio C Hamano diff --git a/Documentation/config.txt b/Documentation/config.txt index 6b6553d..b0390f8 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -446,6 +446,12 @@ gc.auto:: light-weight garbage collection from time to time. Setting this to 0 disables it. +gc.autopacklimit:: + When there are more than this many packs that are not + marked with `*.keep` file in the repository, `git gc + --auto` consolidates them into one larger pack. Setting + this to 0 disables this. + gc.packrefs:: `git gc` does not run `git pack-refs` in a bare repository by default so that older dumb-transport clients can still fetch diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt index 40c1ce4..b9d5660 100644 --- a/Documentation/git-gc.txt +++ b/Documentation/git-gc.txt @@ -47,10 +47,15 @@ OPTIONS With this option, `git gc` checks if there are too many loose objects in the repository and runs gitlink:git-repack[1] with `-d -l` option to pack them. - The threshold is set with `gc.auto` configuration + The threshold for loose objects is set with `gc.auto` configuration variable, and can be disabled by setting it to 0. Some Porcelain commands use this after they perform operation that could create many loose objects automatically. + Additionally, when there are too many packs are present, + they are consolidated into one larger pack by running + the `git-repack` command with `-A` option. The + threshold for number of packs is set with + `gc.autopacklimit` configuration variable. Configuration ------------- diff --git a/builtin-gc.c b/builtin-gc.c index 34ce35b..23ad2b6 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -21,6 +21,7 @@ static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]"; static int pack_refs = 1; static int aggressive_window = -1; static int gc_auto_threshold = 6700; +static int gc_auto_pack_limit = 20; #define MAX_ADD 10 static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL}; @@ -46,6 +47,10 @@ static int gc_config(const char *var, const char *value) gc_auto_threshold = git_config_int(var, value); return 0; } + if (!strcmp(var, "gc.autopacklimit")) { + gc_auto_pack_limit = git_config_int(var, value); + return 0; + } return git_default_config(var, value); } @@ -78,6 +83,9 @@ static int too_many_loose_objects(void) int num_loose = 0; int needed = 0; + if (gc_auto_threshold <= 0) + return 0; + if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) { warning("insanely long object directory %.*s", 50, objdir); return 0; @@ -100,21 +108,61 @@ static int too_many_loose_objects(void) return needed; } +static int too_many_packs(void) +{ + struct packed_git *p; + int cnt; + + if (gc_auto_pack_limit <= 0) + return 0; + + prepare_packed_git(); + for (cnt = 0, p = packed_git; p; p = p->next) { + char path[PATH_MAX]; + size_t len; + int keep; + + if (!p->pack_local) + continue; + len = strlen(p->pack_name); + if (PATH_MAX <= len + 1) + continue; /* oops, give up */ + memcpy(path, p->pack_name, len-5); + memcpy(path + len - 5, ".keep", 6); + keep = access(p->pack_name, F_OK) && (errno == ENOENT); + if (keep) + continue; + /* + * Perhaps check the size of the pack and count only + * very small ones here? + */ + cnt++; + } + return gc_auto_pack_limit <= cnt; +} + static int need_to_gc(void) { int ac = 0; /* - * Setting gc.auto to 0 or negative can disable the - * automatic gc + * Setting gc.auto and gc.autopacklimit to 0 or negative can + * disable the automatic gc. */ - if (gc_auto_threshold <= 0) - return 0; - - if (!too_many_loose_objects()) + if (gc_auto_threshold <= 0 && gc_auto_pack_limit <= 0) return 0; + /* + * If there are too many loose objects, but not too many + * packs, we run "repack -d -l". If there are too many packs, + * we run "repack -A -d -l". Otherwise we tell the caller + * there is no need. + */ argv_repack[ac++] = "repack"; + if (too_many_packs()) + argv_repack[ac++] = "-A"; + else if (!too_many_loose_objects()) + return 0; argv_repack[ac++] = "-d"; argv_repack[ac++] = "-l"; argv_repack[ac++] = NULL; -- cgit v0.10.2-6-g49f6 From e6c019d0b0140fae1cdfd661746cbe319b6c3670 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 17 Sep 2007 11:19:04 +0200 Subject: Drop strbuf's 'eof' marker, and make read_line a first class citizen. read_line is now strbuf_getline, and is a first class citizen, it returns 0 when reading a line worked, EOF else. The ->eof marker was used non-locally by fast-import.c, mimic the same behaviour using a static int in "read_next_command", that now returns -1 on EOF, and avoids to call strbuf_getline when it's in EOF state. Also no longer automagically strbuf_release the buffer, it's counter intuitive and breaks fast-import in a very subtle way. Note: being at EOF implies that command_buf.len == 0. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/builtin-checkout-index.c b/builtin-checkout-index.c index 85e8efe..a18ecc4 100644 --- a/builtin-checkout-index.c +++ b/builtin-checkout-index.c @@ -277,9 +277,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) while (1) { char *path_name; const char *p; - - read_line(&buf, stdin, line_termination); - if (buf.eof) + if (strbuf_getline(&buf, stdin, line_termination) == EOF) break; if (line_termination && buf.buf[0] == '"') path_name = unquote_c_style(buf.buf, NULL); @@ -292,6 +290,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) if (path_name != buf.buf) free(path_name); } + strbuf_release(&buf); } if (all) diff --git a/builtin-update-index.c b/builtin-update-index.c index 1091f1b..45e33f5 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -327,8 +327,7 @@ static void read_index_info(int line_termination) * This format is to put higher order stages into the * index file and matches git-ls-files --stage output. */ - read_line(&buf, stdin, line_termination); - if (buf.eof) + if (strbuf_getline(&buf, stdin, line_termination) == EOF) break; errno = 0; @@ -391,6 +390,7 @@ static void read_index_info(int line_termination) bad_line: die("malformed index info %s", buf.buf); } + strbuf_release(&buf); } static const char update_index_usage[] = @@ -719,8 +719,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) while (1) { char *path_name; const char *p; - read_line(&buf, stdin, line_termination); - if (buf.eof) + if (strbuf_getline(&buf, stdin, line_termination) == EOF) break; if (line_termination && buf.buf[0] == '"') path_name = unquote_c_style(buf.buf, NULL); @@ -735,6 +734,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) if (path_name != buf.buf) free(path_name); } + strbuf_release(&buf); } finish: diff --git a/fast-import.c b/fast-import.c index 1866d34..da04566 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1584,20 +1584,25 @@ static void dump_marks(void) mark_file, strerror(errno)); } -static void read_next_command(void) +static int read_next_command(void) { + static int stdin_eof = 0; + + if (stdin_eof) { + unread_command_buf = 0; + return EOF; + } + do { if (unread_command_buf) { unread_command_buf = 0; - if (command_buf.eof) - return; } else { struct recent_command *rc; strbuf_detach(&command_buf); - read_line(&command_buf, stdin, '\n'); - if (command_buf.eof) - return; + stdin_eof = strbuf_getline(&command_buf, stdin, '\n'); + if (stdin_eof) + return EOF; rc = rc_free; if (rc) @@ -1616,6 +1621,8 @@ static void read_next_command(void) cmd_tail = rc; } } while (command_buf.buf[0] == '#'); + + return 0; } static void skip_optional_lf(void) @@ -1648,8 +1655,7 @@ static void *cmd_data (size_t *size) size_t term_len = command_buf.len - 5 - 2; for (;;) { - read_line(&command_buf, stdin, '\n'); - if (command_buf.eof) + if (strbuf_getline(&command_buf, stdin, '\n') == EOF) die("EOF in data (terminator '%s' not found)", term); if (term_len == command_buf.len && !strcmp(term, command_buf.buf)) @@ -2095,7 +2101,7 @@ static void cmd_new_commit(void) } /* file_change* */ - while (!command_buf.eof && command_buf.len > 0) { + while (command_buf.len > 0) { if (!prefixcmp(command_buf.buf, "M ")) file_change_m(b); else if (!prefixcmp(command_buf.buf, "D ")) @@ -2110,7 +2116,8 @@ static void cmd_new_commit(void) unread_command_buf = 1; break; } - read_next_command(); + if (read_next_command() == EOF) + break; } /* build the tree and the commit */ @@ -2375,11 +2382,8 @@ int main(int argc, const char **argv) prepare_packed_git(); start_packfile(); set_die_routine(die_nicely); - for (;;) { - read_next_command(); - if (command_buf.eof) - break; - else if (!strcmp("blob", command_buf.buf)) + while (read_next_command() != EOF) { + if (!strcmp("blob", command_buf.buf)) cmd_new_blob(); else if (!prefixcmp(command_buf.buf, "commit ")) cmd_new_commit(); diff --git a/fetch.c b/fetch.c index c256e6f..b1c1f07 100644 --- a/fetch.c +++ b/fetch.c @@ -222,8 +222,7 @@ int pull_targets_stdin(char ***target, const char ***write_ref) char *rf_one = NULL; char *tg_one; - read_line(&buf, stdin, '\n'); - if (buf.eof) + if (strbuf_getline(&buf, stdin, '\n') == EOF) break; tg_one = buf.buf; rf_one = strchr(tg_one, '\t'); @@ -239,6 +238,7 @@ int pull_targets_stdin(char ***target, const char ***write_ref) (*write_ref)[targets] = rf_one ? xstrdup(rf_one) : NULL; targets++; } + strbuf_release(&buf); return targets; } diff --git a/mktree.c b/mktree.c index 5dab4bd..9c137de 100644 --- a/mktree.c +++ b/mktree.c @@ -88,8 +88,7 @@ int main(int ac, char **av) enum object_type type; char *path; - read_line(&sb, stdin, line_termination); - if (sb.eof) + if (strbuf_getline(&sb, stdin, line_termination) == EOF) break; ptr = sb.buf; /* Input is non-recursive ls-tree output format @@ -121,6 +120,7 @@ int main(int ac, char **av) if (path != ntr) free(path); } + strbuf_release(&sb); write_tree(sha1); puts(sha1_to_hex(sha1)); exit(0); diff --git a/strbuf.c b/strbuf.c index c5f9e2a..59383ac 100644 --- a/strbuf.c +++ b/strbuf.c @@ -17,7 +17,6 @@ void strbuf_reset(struct strbuf *sb) { if (sb->len) strbuf_setlen(sb, 0); - sb->eof = 0; } char *strbuf_detach(struct strbuf *sb) @@ -145,14 +144,13 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint) return sb->len - oldlen; } -void read_line(struct strbuf *sb, FILE *fp, int term) +int strbuf_getline(struct strbuf *sb, FILE *fp, int term) { int ch; - if (feof(fp)) { - strbuf_release(sb); - sb->eof = 1; - return; - } + + strbuf_grow(sb, 0); + if (feof(fp)) + return EOF; strbuf_reset(sb); while ((ch = fgetc(fp)) != EOF) { @@ -161,11 +159,9 @@ void read_line(struct strbuf *sb, FILE *fp, int term) strbuf_grow(sb, 1); sb->buf[sb->len++] = ch; } - if (ch == EOF && sb->len == 0) { - strbuf_release(sb); - sb->eof = 1; - } + if (ch == EOF && sb->len == 0) + return EOF; - strbuf_grow(sb, 1); sb->buf[sb->len] = '\0'; + return 0; } diff --git a/strbuf.h b/strbuf.h index f163c63..b2cbd97 100644 --- a/strbuf.h +++ b/strbuf.h @@ -44,11 +44,10 @@ struct strbuf { size_t alloc; size_t len; - int eof; char *buf; }; -#define STRBUF_INIT { 0, 0, 0, NULL } +#define STRBUF_INIT { 0, 0, NULL } /*----- strbuf life cycle -----*/ extern void strbuf_init(struct strbuf *, size_t); @@ -101,6 +100,6 @@ extern size_t strbuf_fread(struct strbuf *, size_t, FILE *); /* XXX: if read fails, any partial read is undone */ extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint); -extern void read_line(struct strbuf *, FILE *, int); +extern int strbuf_getline(struct strbuf *, FILE *, int); #endif /* STRBUF_H */ -- cgit v0.10.2-6-g49f6 From eec813cfc6409a1b72399acf7ac08283faad7b8f Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 17 Sep 2007 13:48:17 +0200 Subject: fast-import was using dbuf's, replace them with strbuf's. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/fast-import.c b/fast-import.c index da04566..e456eab 100644 --- a/fast-import.c +++ b/fast-import.c @@ -250,12 +250,6 @@ struct tag unsigned char sha1[20]; }; -struct dbuf -{ - void *buffer; - size_t capacity; -}; - struct hash_list { struct hash_list *next; @@ -323,8 +317,8 @@ static unsigned int tree_entry_alloc = 1000; static void *avail_tree_entry; static unsigned int avail_tree_table_sz = 100; static struct avail_tree_content **avail_tree_table; -static struct dbuf old_tree; -static struct dbuf new_tree; +static struct strbuf old_tree = STRBUF_INIT; +static struct strbuf new_tree = STRBUF_INIT; /* Branch data */ static unsigned long max_active_branches = 5; @@ -346,7 +340,7 @@ static struct recent_command *cmd_tail = &cmd_hist; static struct recent_command *rc_free; static unsigned int cmd_save = 100; static uintmax_t next_mark; -static struct dbuf new_data; +static struct strbuf new_data = STRBUF_INIT; static void write_branch_report(FILE *rpt, struct branch *b) { @@ -566,17 +560,6 @@ static char *pool_strdup(const char *s) return r; } -static void size_dbuf(struct dbuf *b, size_t maxlen) -{ - if (b->buffer) { - if (b->capacity >= maxlen) - return; - free(b->buffer); - } - b->capacity = ((maxlen / 1024) + 1) * 1024; - b->buffer = xmalloc(b->capacity); -} - static void insert_mark(uintmax_t idnum, struct object_entry *oe) { struct mark_set *s = marks; @@ -1005,8 +988,7 @@ static size_t encode_header( static int store_object( enum object_type type, - void *dat, - size_t datlen, + struct strbuf *dat, struct last_object *last, unsigned char *sha1out, uintmax_t mark) @@ -1020,10 +1002,10 @@ static int store_object( z_stream s; hdrlen = sprintf((char*)hdr,"%s %lu", typename(type), - (unsigned long)datlen) + 1; + (unsigned long)dat->len) + 1; SHA1_Init(&c); SHA1_Update(&c, hdr, hdrlen); - SHA1_Update(&c, dat, datlen); + SHA1_Update(&c, dat->buf, dat->len); SHA1_Final(sha1, &c); if (sha1out) hashcpy(sha1out, sha1); @@ -1044,9 +1026,9 @@ static int store_object( if (last && last->data && last->depth < max_depth) { delta = diff_delta(last->data, last->len, - dat, datlen, + dat->buf, dat->len, &deltalen, 0); - if (delta && deltalen >= datlen) { + if (delta && deltalen >= dat->len) { free(delta); delta = NULL; } @@ -1059,8 +1041,8 @@ static int store_object( s.next_in = delta; s.avail_in = deltalen; } else { - s.next_in = dat; - s.avail_in = datlen; + s.next_in = (void *)dat->buf; + s.avail_in = dat->len; } s.avail_out = deflateBound(&s, s.avail_in); s.next_out = out = xmalloc(s.avail_out); @@ -1083,8 +1065,8 @@ static int store_object( memset(&s, 0, sizeof(s)); deflateInit(&s, zlib_compression_level); - s.next_in = dat; - s.avail_in = datlen; + s.next_in = (void *)dat->buf; + s.avail_in = dat->len; s.avail_out = deflateBound(&s, s.avail_in); s.next_out = out = xrealloc(out, s.avail_out); while (deflate(&s, Z_FINISH) == Z_OK) @@ -1118,7 +1100,7 @@ static int store_object( } else { if (last) last->depth = 0; - hdrlen = encode_header(type, datlen, hdr); + hdrlen = encode_header(type, dat->len, hdr); write_or_die(pack_data->pack_fd, hdr, hdrlen); pack_size += hdrlen; } @@ -1131,9 +1113,9 @@ static int store_object( if (last) { if (!last->no_free) free(last->data); - last->data = dat; last->offset = e->offset; - last->len = datlen; + last->data = dat->buf; + last->len = dat->len; } return 0; } @@ -1229,14 +1211,10 @@ static int tecmp1 (const void *_a, const void *_b) b->name->str_dat, b->name->str_len, b->versions[1].mode); } -static void mktree(struct tree_content *t, - int v, - unsigned long *szp, - struct dbuf *b) +static void mktree(struct tree_content *t, int v, struct strbuf *b) { size_t maxlen = 0; unsigned int i; - char *c; if (!v) qsort(t->entries,t->entry_count,sizeof(t->entries[0]),tecmp0); @@ -1248,27 +1226,22 @@ static void mktree(struct tree_content *t, maxlen += t->entries[i]->name->str_len + 34; } - size_dbuf(b, maxlen); - c = b->buffer; + strbuf_reset(b); + strbuf_grow(b, maxlen); for (i = 0; i < t->entry_count; i++) { struct tree_entry *e = t->entries[i]; if (!e->versions[v].mode) continue; - c += sprintf(c, "%o", (unsigned int)e->versions[v].mode); - *c++ = ' '; - strcpy(c, e->name->str_dat); - c += e->name->str_len + 1; - hashcpy((unsigned char*)c, e->versions[v].sha1); - c += 20; + strbuf_addf(b, "%o %s%c", (unsigned int)e->versions[v].mode, + e->name->str_dat, '\0'); + strbuf_add(b, e->versions[v].sha1, 20); } - *szp = c - (char*)b->buffer; } static void store_tree(struct tree_entry *root) { struct tree_content *t = root->tree; unsigned int i, j, del; - unsigned long new_len; struct last_object lo; struct object_entry *le; @@ -1288,16 +1261,16 @@ static void store_tree(struct tree_entry *root) lo.depth = 0; lo.no_free = 0; } else { - mktree(t, 0, &lo.len, &old_tree); - lo.data = old_tree.buffer; + mktree(t, 0, &old_tree); + lo.len = old_tree.len; + lo.data = old_tree.buf; lo.offset = le->offset; lo.depth = t->delta_depth; lo.no_free = 1; } - mktree(t, 1, &new_len, &new_tree); - store_object(OBJ_TREE, new_tree.buffer, new_len, - &lo, root->versions[1].sha1, 0); + mktree(t, 1, &new_tree); + store_object(OBJ_TREE, &new_tree, &lo, root->versions[1].sha1, 0); t->delta_depth = lo.depth; for (i = 0, j = 0, del = 0; i < t->entry_count; i++) { @@ -1642,11 +1615,10 @@ static void cmd_mark(void) next_mark = 0; } -static void *cmd_data (size_t *size) +static void cmd_data(struct strbuf *sb) { - struct strbuf buffer; + strbuf_reset(sb); - strbuf_init(&buffer, 0); if (prefixcmp(command_buf.buf, "data ")) die("Expected 'data n' command, found: %s", command_buf.buf); @@ -1660,8 +1632,8 @@ static void *cmd_data (size_t *size) if (term_len == command_buf.len && !strcmp(term, command_buf.buf)) break; - strbuf_addbuf(&buffer, &command_buf); - strbuf_addch(&buffer, '\n'); + strbuf_addbuf(sb, &command_buf); + strbuf_addch(sb, '\n'); } free(term); } @@ -1671,7 +1643,7 @@ static void *cmd_data (size_t *size) length = strtoul(command_buf.buf + 5, NULL, 10); while (n < length) { - size_t s = strbuf_fread(&buffer, length - n, stdin); + size_t s = strbuf_fread(sb, length - n, stdin); if (!s && feof(stdin)) die("EOF in data (%lu bytes remaining)", (unsigned long)(length - n)); @@ -1680,8 +1652,6 @@ static void *cmd_data (size_t *size) } skip_optional_lf(); - *size = buffer.len; - return strbuf_detach(&buffer); } static int validate_raw_date(const char *src, char *result, int maxlen) @@ -1744,15 +1714,14 @@ static char *parse_ident(const char *buf) static void cmd_new_blob(void) { - size_t l; - void *d; + struct strbuf buf; read_next_command(); cmd_mark(); - d = cmd_data(&l); - - if (store_object(OBJ_BLOB, d, l, &last_blob, NULL, next_mark)) - free(d); + strbuf_init(&buf, 0); + cmd_data(&buf); + if (store_object(OBJ_BLOB, &buf, &last_blob, NULL, next_mark)) + strbuf_release(&buf); } static void unload_one_branch(void) @@ -1848,14 +1817,15 @@ static void file_change_m(struct branch *b) } if (inline_data) { - size_t l; - void *d; + struct strbuf buf; + if (!p_uq) p = p_uq = xstrdup(p); read_next_command(); - d = cmd_data(&l); - if (store_object(OBJ_BLOB, d, l, &last_blob, sha1, 0)) - free(d); + strbuf_init(&buf, 0); + cmd_data(&buf); + if (store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0)) + strbuf_release(&buf); } else if (oe) { if (oe->type != OBJ_BLOB) die("Not a blob (actually a %s): %s", @@ -2062,9 +2032,8 @@ static struct hash_list *cmd_merge(unsigned int *count) static void cmd_new_commit(void) { + static struct strbuf msg = STRBUF_INIT; struct branch *b; - void *msg; - size_t msglen; char *sp; char *author = NULL; char *committer = NULL; @@ -2089,7 +2058,7 @@ static void cmd_new_commit(void) } if (!committer) die("Expected committer but didn't get one"); - msg = cmd_data(&msglen); + cmd_data(&msg); read_next_command(); cmd_from(b); merge_list = cmd_merge(&merge_count); @@ -2124,46 +2093,39 @@ static void cmd_new_commit(void) store_tree(&b->branch_tree); hashcpy(b->branch_tree.versions[0].sha1, b->branch_tree.versions[1].sha1); - size_dbuf(&new_data, 114 + msglen - + merge_count * 49 - + (author - ? strlen(author) + strlen(committer) - : 2 * strlen(committer))); - sp = new_data.buffer; - sp += sprintf(sp, "tree %s\n", + + strbuf_reset(&new_data); + strbuf_addf(&new_data, "tree %s\n", sha1_to_hex(b->branch_tree.versions[1].sha1)); if (!is_null_sha1(b->sha1)) - sp += sprintf(sp, "parent %s\n", sha1_to_hex(b->sha1)); + strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(b->sha1)); while (merge_list) { struct hash_list *next = merge_list->next; - sp += sprintf(sp, "parent %s\n", sha1_to_hex(merge_list->sha1)); + strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(merge_list->sha1)); free(merge_list); merge_list = next; } - sp += sprintf(sp, "author %s\n", author ? author : committer); - sp += sprintf(sp, "committer %s\n", committer); - *sp++ = '\n'; - memcpy(sp, msg, msglen); - sp += msglen; + strbuf_addf(&new_data, + "author %s\n" + "committer %s\n" + "\n", + author ? author : committer, committer); + strbuf_addbuf(&new_data, &msg); free(author); free(committer); - free(msg); - if (!store_object(OBJ_COMMIT, - new_data.buffer, sp - (char*)new_data.buffer, - NULL, b->sha1, next_mark)) + if (!store_object(OBJ_COMMIT, &new_data, NULL, b->sha1, next_mark)) b->pack_id = pack_id; b->last_commit = object_count_by_type[OBJ_COMMIT]; } static void cmd_new_tag(void) { + static struct strbuf msg = STRBUF_INIT; char *sp; const char *from; char *tagger; struct branch *s; - void *msg; - size_t msglen; struct tag *t; uintmax_t from_mark = 0; unsigned char sha1[20]; @@ -2214,24 +2176,21 @@ static void cmd_new_tag(void) /* tag payload/message */ read_next_command(); - msg = cmd_data(&msglen); + cmd_data(&msg); /* build the tag object */ - size_dbuf(&new_data, 67+strlen(t->name)+strlen(tagger)+msglen); - sp = new_data.buffer; - sp += sprintf(sp, "object %s\n", sha1_to_hex(sha1)); - sp += sprintf(sp, "type %s\n", commit_type); - sp += sprintf(sp, "tag %s\n", t->name); - sp += sprintf(sp, "tagger %s\n", tagger); - *sp++ = '\n'; - memcpy(sp, msg, msglen); - sp += msglen; + strbuf_reset(&new_data); + strbuf_addf(&new_data, + "object %s\n" + "type %s\n" + "tag %s\n" + "tagger %s\n" + "\n", + sha1_to_hex(sha1), commit_type, t->name, tagger); + strbuf_addbuf(&new_data, &msg); free(tagger); - free(msg); - if (store_object(OBJ_TAG, new_data.buffer, - sp - (char*)new_data.buffer, - NULL, t->sha1, 0)) + if (store_object(OBJ_TAG, &new_data, NULL, t->sha1, 0)) t->pack_id = MAX_PACK_ID; else t->pack_id = pack_id; -- cgit v0.10.2-6-g49f6 From 0557656930d41f10c90bccf90e3c5bd87bd53661 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 17 Sep 2007 14:00:38 +0200 Subject: fast-import optimization: Now that cmd_data acts on a strbuf, make last_object stashed buffer be a strbuf as well. On new stash, don't free the last stashed buffer, rather swap it with the one you will stash, this way, callers of store_object can act on static strbufs, and at some point, fast-import won't allocate new memory for objects buffers. Signed-off-by: Junio C Hamano diff --git a/fast-import.c b/fast-import.c index e456eab..e2a4834 100644 --- a/fast-import.c +++ b/fast-import.c @@ -182,11 +182,10 @@ struct mark_set struct last_object { - void *data; - unsigned long len; + struct strbuf data; uint32_t offset; unsigned int depth; - unsigned no_free:1; + unsigned no_swap : 1; }; struct mem_pool @@ -310,7 +309,7 @@ static struct mark_set *marks; static const char* mark_file; /* Our last blob */ -static struct last_object last_blob; +static struct last_object last_blob = { STRBUF_INIT, 0, 0, 0 }; /* Tree management */ static unsigned int tree_entry_alloc = 1000; @@ -950,9 +949,7 @@ static void end_packfile(void) free(old_p); /* We can't carry a delta across packfiles. */ - free(last_blob.data); - last_blob.data = NULL; - last_blob.len = 0; + strbuf_release(&last_blob.data); last_blob.offset = 0; last_blob.depth = 0; } @@ -1024,8 +1021,8 @@ static int store_object( return 1; } - if (last && last->data && last->depth < max_depth) { - delta = diff_delta(last->data, last->len, + if (last && last->data.buf && last->depth < max_depth) { + delta = diff_delta(last->data.buf, last->data.len, dat->buf, dat->len, &deltalen, 0); if (delta && deltalen >= dat->len) { @@ -1111,11 +1108,14 @@ static int store_object( free(out); free(delta); if (last) { - if (!last->no_free) - free(last->data); + if (last->no_swap) { + last->data = *dat; + } else { + struct strbuf tmp = *dat; + *dat = last->data; + last->data = tmp; + } last->offset = e->offset; - last->data = dat->buf; - last->len = dat->len; } return 0; } @@ -1242,7 +1242,7 @@ static void store_tree(struct tree_entry *root) { struct tree_content *t = root->tree; unsigned int i, j, del; - struct last_object lo; + struct last_object lo = { STRBUF_INIT, 0, 0, /* no_swap */ 1 }; struct object_entry *le; if (!is_null_sha1(root->versions[1].sha1)) @@ -1254,19 +1254,11 @@ static void store_tree(struct tree_entry *root) } le = find_object(root->versions[0].sha1); - if (!S_ISDIR(root->versions[0].mode) - || !le - || le->pack_id != pack_id) { - lo.data = NULL; - lo.depth = 0; - lo.no_free = 0; - } else { + if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) { mktree(t, 0, &old_tree); - lo.len = old_tree.len; - lo.data = old_tree.buf; + lo.data = old_tree; lo.offset = le->offset; lo.depth = t->delta_depth; - lo.no_free = 1; } mktree(t, 1, &new_tree); @@ -1714,14 +1706,12 @@ static char *parse_ident(const char *buf) static void cmd_new_blob(void) { - struct strbuf buf; + static struct strbuf buf = STRBUF_INIT; read_next_command(); cmd_mark(); - strbuf_init(&buf, 0); cmd_data(&buf); - if (store_object(OBJ_BLOB, &buf, &last_blob, NULL, next_mark)) - strbuf_release(&buf); + store_object(OBJ_BLOB, &buf, &last_blob, NULL, next_mark); } static void unload_one_branch(void) @@ -1817,15 +1807,13 @@ static void file_change_m(struct branch *b) } if (inline_data) { - struct strbuf buf; + static struct strbuf buf = STRBUF_INIT; if (!p_uq) p = p_uq = xstrdup(p); read_next_command(); - strbuf_init(&buf, 0); cmd_data(&buf); - if (store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0)) - strbuf_release(&buf); + store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0); } else if (oe) { if (oe->type != OBJ_BLOB) die("Not a blob (actually a %s): %s", -- cgit v0.10.2-6-g49f6 From 68d3025a805097ec148ec6e9b0b54a5db1ef138e Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sat, 15 Sep 2007 23:53:05 +0200 Subject: Add xmemdupz() that duplicates a block of memory, and NUL terminates it. A lot of places in git's code use code like: char *res; len = ... find length of an interesting segment in src ...; res = xmalloc(len + 1); memcpy(res, src, len); res[len] = '\0'; return res; A new function xmemdupz() captures the allocation, copy and NUL termination. Existing xstrndup() is reimplemented in terms of this new function. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/git-compat-util.h b/git-compat-util.h index 1bfbdeb..f23d934 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -211,19 +211,20 @@ static inline void *xmalloc(size_t size) return ret; } -static inline char *xstrndup(const char *str, size_t len) +static inline void *xmemdupz(const void *data, size_t len) { - char *p; - - p = memchr(str, '\0', len); - if (p) - len = p - str; - p = xmalloc(len + 1); - memcpy(p, str, len); + char *p = xmalloc(len + 1); + memcpy(p, data, len); p[len] = '\0'; return p; } +static inline char *xstrndup(const char *str, size_t len) +{ + char *p = memchr(str, '\0', len); + return xmemdupz(str, p ? p - str : len); +} + static inline void *xrealloc(void *ptr, size_t size) { void *ret = realloc(ptr, size); -- cgit v0.10.2-6-g49f6 From 46232915d5ac84fb033ad395bbf161c9645c42c9 Mon Sep 17 00:00:00 2001 From: Josh England Date: Tue, 11 Sep 2007 10:59:03 -0600 Subject: Add post-merge hook, related documentation, and tests. The post-merge hook enables one to hook in for `git pull` operations in order to check and/or change attributes of a work tree from the hook. As an example, it can be used in combination with a pre-commit hook to save/restore file ownership and permissions data (or file ACLs) within the repository and transparently update the working tree after a `git pull` operation. Signed-off-by: Josh England Signed-off-by: Junio C Hamano diff --git a/Documentation/hooks.txt b/Documentation/hooks.txt index c39edc5..50535a7 100644 --- a/Documentation/hooks.txt +++ b/Documentation/hooks.txt @@ -87,6 +87,18 @@ parameter, and is invoked after a commit is made. This hook is meant primarily for notification, and cannot affect the outcome of `git-commit`. +post-merge +----------- + +This hook is invoked by `git-merge`, which happens when a `git pull` +is done on a local repository. The hook takes a single parameter, a status +flag specifying whether or not the merge being done was a squash merge. +This hook cannot affect the outcome of `git-merge`. + +This hook can be used in conjunction with a corresponding pre-commit hook to +save and restore any form of metadata associated with the working tree +(eg: permissions/ownership, ACLS, etc). + [[pre-receive]] pre-receive ----------- diff --git a/git-merge.sh b/git-merge.sh index 3a01db0..66e48b3 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -97,6 +97,19 @@ finish () { fi ;; esac + + # Run a post-merge hook + if test -x "$GIT_DIR"/hooks/post-merge + then + case "$squash" in + t) + "$GIT_DIR"/hooks/post-merge 1 + ;; + '') + "$GIT_DIR"/hooks/post-merge 0 + ;; + esac + fi } merge_name () { diff --git a/t/t5402-post-merge-hook.sh b/t/t5402-post-merge-hook.sh new file mode 100755 index 0000000..1c4b0b3 --- /dev/null +++ b/t/t5402-post-merge-hook.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# +# Copyright (c) 2006 Josh England +# + +test_description='Test the post-merge hook.' +. ./test-lib.sh + +test_expect_success setup ' + echo Data for commit0. >a && + git update-index --add a && + tree0=$(git write-tree) && + commit0=$(echo setup | git commit-tree $tree0) && + echo Changed data for commit1. >a && + git update-index a && + tree1=$(git write-tree) && + commit1=$(echo modify | git commit-tree $tree1 -p $commit0) && + git update-ref refs/heads/master $commit0 && + git-clone ./. clone1 && + GIT_DIR=clone1/.git git update-index --add a && + git-clone ./. clone2 && + GIT_DIR=clone2/.git git update-index --add a +' + +for clone in 1 2; do + cat >clone${clone}/.git/hooks/post-merge <<'EOF' +#!/bin/sh +echo $@ >> $GIT_DIR/post-merge.args +EOF + chmod u+x clone${clone}/.git/hooks/post-merge +done + +test_expect_failure 'post-merge does not run for up-to-date ' ' + GIT_DIR=clone1/.git git merge $commit0 && + test -e clone1/.git/post-merge.args +' + +test_expect_success 'post-merge runs as expected ' ' + GIT_DIR=clone1/.git git merge $commit1 && + test -e clone1/.git/post-merge.args +' + +test_expect_success 'post-merge from normal merge receives the right argument ' ' + grep 0 clone1/.git/post-merge.args +' + +test_expect_success 'post-merge from squash merge runs as expected ' ' + GIT_DIR=clone2/.git git merge --squash $commit1 && + test -e clone2/.git/post-merge.args +' + +test_expect_success 'post-merge from squash merge receives the right argument ' ' + grep 1 clone2/.git/post-merge.args +' + +test_done -- cgit v0.10.2-6-g49f6 From af6fb4c822a5a65c7671d810127171759dff38f6 Mon Sep 17 00:00:00 2001 From: Josh England Date: Tue, 11 Sep 2007 10:59:04 -0600 Subject: Added example hook script to save/restore permissions/ownership. Usage info is emebed in the script, but the gist of it is to run the script from a pre-commit hook to save permissions/ownership data to a file and check that file into the repository. Then, a post_merge hook reads the file and updates working tree permissions/ownership. All updates are transparent to the user (although there is a --verbose option). Merge conflicts are handled in the "read" phase (in pre-commit), and the script aborts the commit and tells you how to fix things in the case of a merge conflict in the metadata file. This same idea could be extended to handle file ACLs or other file metadata if desired. Signed-off-by: Josh England Signed-off-by: Junio C Hamano diff --git a/Documentation/hooks.txt b/Documentation/hooks.txt index 50535a7..58b9547 100644 --- a/Documentation/hooks.txt +++ b/Documentation/hooks.txt @@ -97,7 +97,8 @@ This hook cannot affect the outcome of `git-merge`. This hook can be used in conjunction with a corresponding pre-commit hook to save and restore any form of metadata associated with the working tree -(eg: permissions/ownership, ACLS, etc). +(eg: permissions/ownership, ACLS, etc). See contrib/hooks/setgitperms.perl +for an example of how to do this. [[pre-receive]] pre-receive diff --git a/contrib/hooks/setgitperms.perl b/contrib/hooks/setgitperms.perl new file mode 100644 index 0000000..5e3b89d --- /dev/null +++ b/contrib/hooks/setgitperms.perl @@ -0,0 +1,213 @@ +#!/usr/bin/perl +# +# Copyright (c) 2006 Josh England +# +# This script can be used to save/restore full permissions and ownership data +# within a git working tree. +# +# To save permissions/ownership data, place this script in your .git/hooks +# directory and enable a `pre-commit` hook with the following lines: +# #!/bin/sh +# . git-sh-setup +# $GIT_DIR/hooks/setgitperms.perl -r +# +# To restore permissions/ownership data, place this script in your .git/hooks +# directory and enable a `post-merge` hook with the following lines: +# #!/bin/sh +# . git-sh-setup +# $GIT_DIR/hooks/setgitperms.perl -w +# +use strict; +use Getopt::Long; +use File::Find; +use File::Basename; + +my $usage = +"Usage: setgitperms.perl [OPTION]... <--read|--write> +This program uses a file `.gitmeta` to store/restore permissions and uid/gid +info for all files/dirs tracked by git in the repository. + +---------------------------------Read Mode------------------------------------- +-r, --read Reads perms/etc from working dir into a .gitmeta file +-s, --stdout Output to stdout instead of .gitmeta +-d, --diff Show unified diff of perms file (XOR with --stdout) + +---------------------------------Write Mode------------------------------------ +-w, --write Modify perms/etc in working dir to match the .gitmeta file +-v, --verbose Be verbose + +\n"; + +my ($stdout, $showdiff, $verbose, $read_mode, $write_mode); + +if ((@ARGV < 0) || !GetOptions( + "stdout", \$stdout, + "diff", \$showdiff, + "read", \$read_mode, + "write", \$write_mode, + "verbose", \$verbose, + )) { die $usage; } +die $usage unless ($read_mode xor $write_mode); + +my $topdir = `git-rev-parse --show-cdup` or die "\n"; chomp $topdir; +my $gitdir = $topdir . '.git'; +my $gitmeta = $topdir . '.gitmeta'; + +if ($write_mode) { + # Update the working dir permissions/ownership based on data from .gitmeta + open (IN, "<$gitmeta") or die "Could not open $gitmeta for reading: $!\n"; + while (defined ($_ = )) { + chomp; + if (/^(.*) mode=(\S+)\s+uid=(\d+)\s+gid=(\d+)/) { + # Compare recorded perms to actual perms in the working dir + my ($path, $mode, $uid, $gid) = ($1, $2, $3, $4); + my $fullpath = $topdir . $path; + my (undef,undef,$wmode,undef,$wuid,$wgid) = lstat($fullpath); + $wmode = sprintf "%04o", $wmode & 07777; + if ($mode ne $wmode) { + $verbose && print "Updating permissions on $path: old=$wmode, new=$mode\n"; + chmod oct($mode), $fullpath; + } + if ($uid != $wuid || $gid != $wgid) { + if ($verbose) { + # Print out user/group names instead of uid/gid + my $pwname = getpwuid($uid); + my $grpname = getgrgid($gid); + my $wpwname = getpwuid($wuid); + my $wgrpname = getgrgid($wgid); + $pwname = $uid if !defined $pwname; + $grpname = $gid if !defined $grpname; + $wpwname = $wuid if !defined $wpwname; + $wgrpname = $wgid if !defined $wgrpname; + + print "Updating uid/gid on $path: old=$wpwname/$wgrpname, new=$pwname/$grpname\n"; + } + chown $uid, $gid, $fullpath; + } + } + else { + warn "Invalid input format in $gitmeta:\n\t$_\n"; + } + } + close IN; +} +elsif ($read_mode) { + # Handle merge conflicts in the .gitperms file + if (-e "$gitdir/MERGE_MSG") { + if (`grep ====== $gitmeta`) { + # Conflict not resolved -- abort the commit + print "PERMISSIONS/OWNERSHIP CONFLICT\n"; + print " Resolve the conflict in the $gitmeta file and then run\n"; + print " `.git/hooks/setgitperms.perl --write` to reconcile.\n"; + exit 1; + } + elsif (`grep $gitmeta $gitdir/MERGE_MSG`) { + # A conflict in .gitmeta has been manually resolved. Verify that + # the working dir perms matches the current .gitmeta perms for + # each file/dir that conflicted. + # This is here because a `setgitperms.perl --write` was not + # performed due to a merge conflict, so permissions/ownership + # may not be consistent with the manually merged .gitmeta file. + my @conflict_diff = `git show \$(cat $gitdir/MERGE_HEAD)`; + my @conflict_files; + my $metadiff = 0; + + # Build a list of files that conflicted from the .gitmeta diff + foreach my $line (@conflict_diff) { + if ($line =~ m|^diff --git a/$gitmeta b/$gitmeta|) { + $metadiff = 1; + } + elsif ($line =~ /^diff --git/) { + $metadiff = 0; + } + elsif ($metadiff && $line =~ /^\+(.*) mode=/) { + push @conflict_files, $1; + } + } + + # Verify that each conflict file now has permissions consistent + # with the .gitmeta file + foreach my $file (@conflict_files) { + my $absfile = $topdir . $file; + my $gm_entry = `grep "^$file mode=" $gitmeta`; + if ($gm_entry =~ /mode=(\d+) uid=(\d+) gid=(\d+)/) { + my ($gm_mode, $gm_uid, $gm_gid) = ($1, $2, $3); + my (undef,undef,$mode,undef,$uid,$gid) = lstat("$absfile"); + $mode = sprintf("%04o", $mode & 07777); + if (($gm_mode ne $mode) || ($gm_uid != $uid) + || ($gm_gid != $gid)) { + print "PERMISSIONS/OWNERSHIP CONFLICT\n"; + print " Mismatch found for file: $file\n"; + print " Run `.git/hooks/setgitperms.perl --write` to reconcile.\n"; + exit 1; + } + } + else { + print "Warning! Permissions/ownership no longer being tracked for file: $file\n"; + } + } + } + } + + # No merge conflicts -- write out perms/ownership data to .gitmeta file + unless ($stdout) { + open (OUT, ">$gitmeta.tmp") or die "Could not open $gitmeta.tmp for writing: $!\n"; + } + + my @files = `git-ls-files`; + my %dirs; + + foreach my $path (@files) { + chomp $path; + # We have to manually add stats for parent directories + my $parent = dirname($path); + while (!exists $dirs{$parent}) { + $dirs{$parent} = 1; + next if $parent eq '.'; + printstats($parent); + $parent = dirname($parent); + } + # Now the git-tracked file + printstats($path); + } + + # diff the temporary metadata file to see if anything has changed + # If no metadata has changed, don't overwrite the real file + # This is just so `git commit -a` doesn't try to commit a bogus update + unless ($stdout) { + if (! -e $gitmeta) { + rename "$gitmeta.tmp", $gitmeta; + } + else { + my $diff = `diff -U 0 $gitmeta $gitmeta.tmp`; + if ($diff ne '') { + rename "$gitmeta.tmp", $gitmeta; + } + else { + unlink "$gitmeta.tmp"; + } + if ($showdiff) { + print $diff; + } + } + close OUT; + } + # Make sure the .gitmeta file is tracked + system("git add $gitmeta"); +} + + +sub printstats { + my $path = $_[0]; + $path =~ s/@/\@/g; + my (undef,undef,$mode,undef,$uid,$gid) = lstat($path); + $path =~ s/%/\%/g; + if ($stdout) { + print $path; + printf " mode=%04o uid=$uid gid=$gid\n", $mode & 07777; + } + else { + print OUT $path; + printf OUT " mode=%04o uid=$uid gid=$gid\n", $mode & 07777; + } +} -- cgit v0.10.2-6-g49f6 From 7a98869935a3682b5ff9679c92bf9bc12f87d8c5 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 17 Sep 2007 23:34:06 +0100 Subject: apply: get rid of --index-info in favor of --build-fake-ancestor git-am used "git apply -z --index-info" to find the original versions of the files touched by the diff, to be able to do an inexpensive three-way merge. This operation makes only sense in a repository, since the index information in the diff refers to blobs, which have to be present in the current repository. Therefore, teach "git apply" a mode to write out the result as an index file to begin with, obviating the need for scripts to do it themselves. The sole user for --index-info is "git am" is converted to use --build-fake-ancestor in this patch. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt index 4c7e3a2..c1c54bf 100644 --- a/Documentation/git-apply.txt +++ b/Documentation/git-apply.txt @@ -10,7 +10,7 @@ SYNOPSIS -------- [verse] 'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] - [--apply] [--no-add] [--index-info] [-R | --reverse] + [--apply] [--no-add] [--build-fake-ancestor ] [-R | --reverse] [--allow-binary-replacement | --binary] [--reject] [-z] [-pNUM] [-CNUM] [--inaccurate-eof] [--cached] [--whitespace=] @@ -63,12 +63,15 @@ OPTIONS cached data, apply the patch, and store the result in the index, without using the working tree. This implies '--index'. ---index-info:: +--build-fake-ancestor :: Newer git-diff output has embedded 'index information' for each blob to help identify the original version that the patch applies to. When this flag is given, and if - the original version of the blob is available locally, - outputs information about them to the standard output. + the original versions of the blobs is available locally, + builds a temporary index containing those blobs. ++ +When a pure mode change is encountered (which has no index information), +the information is read from the current index instead. -R, --reverse:: Apply the patch in reverse. diff --git a/builtin-apply.c b/builtin-apply.c index 86d89a4..6777231 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -41,7 +41,7 @@ static int apply_in_reverse; static int apply_with_reject; static int apply_verbosely; static int no_add; -static int show_index_info; +static const char *fake_ancestor; static int line_termination = '\n'; static unsigned long p_context = ULONG_MAX; static const char apply_usage[] = @@ -2248,9 +2248,12 @@ static int get_current_sha1(const char *path, unsigned char *sha1) return 0; } -static void show_index_list(struct patch *list) +/* Build an index that contains the just the files needed for a 3way merge */ +static void build_fake_ancestor(struct patch *list, const char *filename) { struct patch *patch; + struct index_state result = { 0 }; + int fd; /* Once we start supporting the reverse patch, it may be * worth showing the new sha1 prefix, but until then... @@ -2258,11 +2261,12 @@ static void show_index_list(struct patch *list) for (patch = list; patch; patch = patch->next) { const unsigned char *sha1_ptr; unsigned char sha1[20]; + struct cache_entry *ce; const char *name; name = patch->old_name ? patch->old_name : patch->new_name; if (0 < patch->is_new) - sha1_ptr = null_sha1; + continue; else if (get_sha1(patch->old_sha1_prefix, sha1)) /* git diff has no index line for mode/type changes */ if (!patch->lines_added && !patch->lines_deleted) { @@ -2277,13 +2281,16 @@ static void show_index_list(struct patch *list) else sha1_ptr = sha1; - printf("%06o %s ",patch->old_mode, sha1_to_hex(sha1_ptr)); - if (line_termination && quote_c_style(name, NULL, NULL, 0)) - quote_c_style(name, NULL, stdout, 0); - else - fputs(name, stdout); - putchar(line_termination); + ce = make_cache_entry(patch->old_mode, sha1_ptr, name, 0, 0); + if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD)) + die ("Could not add %s to temporary index", name); } + + fd = open(filename, O_WRONLY | O_CREAT, 0666); + if (fd < 0 || write_index(&result, fd) || close(fd)) + die ("Could not write temporary index to %s", filename); + + discard_index(&result); } static void stat_patch_list(struct patch *patch) @@ -2803,8 +2810,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) if (apply && write_out_results(list, skipped_patch)) exit(1); - if (show_index_info) - show_index_list(list); + if (fake_ancestor) + build_fake_ancestor(list, fake_ancestor); if (diffstat) stat_patch_list(list); @@ -2912,9 +2919,11 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) apply = 1; continue; } - if (!strcmp(arg, "--index-info")) { + if (!strcmp(arg, "--build-fake-ancestor")) { apply = 0; - show_index_info = 1; + if (++i >= argc) + die ("need a filename"); + fake_ancestor = argv[i]; continue; } if (!strcmp(arg, "-z")) { diff --git a/git-am.sh b/git-am.sh index 6809aa0..5599b93 100755 --- a/git-am.sh +++ b/git-am.sh @@ -62,10 +62,8 @@ fall_back_3way () { mkdir "$dotest/patch-merge-tmp-dir" # First see if the patch records the index info that we can use. - git apply -z --index-info "$dotest/patch" \ - >"$dotest/patch-merge-index-info" && - GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \ - git update-index -z --index-info <"$dotest/patch-merge-index-info" && + git apply --build-fake-ancestor "$dotest/patch-merge-tmp-index" \ + "$dotest/patch" && GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \ git write-tree >"$dotest/patch-merge-base+" || cannot_fallback "Repository lacks necessary blobs to fall back on 3-way merge." -- cgit v0.10.2-6-g49f6 From 611360443ef4d9e2f4def1e2c97727dd04e0d244 Mon Sep 17 00:00:00 2001 From: James Bowes Date: Tue, 5 Jun 2007 19:25:23 -0400 Subject: remote: add 'rm' subcommand Introduce git-remote rm which will: - Remove the remote config entry for . - Remove any config entries for tracking branches of . - Remove any stored remote branches of . Signed-off-by: James Bowes Signed-off-by: Junio C Hamano diff --git a/git-remote.perl b/git-remote.perl index f6f283e..f513a8a 100755 --- a/git-remote.perl +++ b/git-remote.perl @@ -316,6 +316,34 @@ sub update_remote { } } +sub rm_remote { + my ($name) = @_; + if (!exists $remote->{$name}) { + print STDERR "No such remote $name\n"; + return; + } + + $git->command('config', '--remove-section', "remote.$name"); + + eval { + my @trackers = $git->command('config', '--get-regexp', + 'branch.*.remote', $name); + for (@trackers) { + /^branch\.(.*)?\.remote/; + $git->config('--unset', "branch.$1.remote"); + $git->config('--unset', "branch.$1.merge"); + } + }; + + + my @refs = $git->command('for-each-ref', + '--format=%(refname) %(objectname)', "refs/remotes/$name"); + for (@refs) { + ($ref, $object) = split; + $git->command(qw(update-ref -d), $ref, $object); + } +} + sub add_usage { print STDERR "Usage: git remote add [-f] [-t track]* [-m master] \n"; exit(1); @@ -422,9 +450,19 @@ elsif ($ARGV[0] eq 'add') { } add_remote($ARGV[1], $ARGV[2], \%opts); } +elsif ($ARGV[0] eq 'rm') { + if (@ARGV <= 1) { + print STDERR "Usage: git remote rm \n"; + } + else { + rm_remote($ARGV[1]); + } + exit(1); +} else { print STDERR "Usage: git remote\n"; print STDERR " git remote add \n"; + print STDERR " git remote rm \n"; print STDERR " git remote show \n"; print STDERR " git remote prune \n"; print STDERR " git remote update [group]\n"; -- cgit v0.10.2-6-g49f6 From 1b4cbb5d5880091b1f4980e7a3f5cac276352bee Mon Sep 17 00:00:00 2001 From: James Bowes Date: Sat, 7 Jul 2007 11:22:43 -0400 Subject: remote: document the 'rm' subcommand Signed-off-by: James Bowes Signed-off-by: Junio C Hamano diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index 94b9f17..027ba11 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -11,6 +11,7 @@ SYNOPSIS [verse] 'git-remote' 'git-remote' add [-t ] [-m ] [-f] [--mirror] +'git-remote' rm 'git-remote' show 'git-remote' prune 'git-remote' update [group] @@ -50,6 +51,11 @@ In mirror mode, enabled with `--mirror`, the refs will not be stored in the 'refs/remotes/' namespace, but in 'refs/heads/'. This option only makes sense in bare repositories. +'rm':: + +Remove the remote named . All remote tracking branches and +configuration settings for the remote are removed. + 'show':: Gives some information about the remote . -- cgit v0.10.2-6-g49f6 From 182af8343c307436bb5364309aa6d4d46fa5911d Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 16 Sep 2007 00:32:36 +0200 Subject: Use xmemdupz() in many places. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/attr.c b/attr.c index 1293993..92704a3 100644 --- a/attr.c +++ b/attr.c @@ -160,12 +160,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp, else if (!equals) e->setto = ATTR__TRUE; else { - char *value; - int vallen = ep - equals; - value = xmalloc(vallen); - memcpy(value, equals+1, vallen-1); - value[vallen-1] = 0; - e->setto = value; + e->setto = xmemdupz(equals + 1, ep - equals - 1); } e->attr = git_attr(cp, len); } diff --git a/builtin-add.c b/builtin-add.c index 0d7d0ce..f9a6580 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -71,12 +71,8 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec, baselen = common_prefix(pathspec); path = "."; base = ""; - if (baselen) { - char *common = xmalloc(baselen + 1); - memcpy(common, *pathspec, baselen); - common[baselen] = 0; - path = base = common; - } + if (baselen) + path = base = xmemdupz(*pathspec, baselen); /* Read the directory and prune it */ read_directory(dir, path, base, baselen, pathspec); diff --git a/builtin-apply.c b/builtin-apply.c index f953c5b..512c9b6 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -293,11 +293,7 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) return def; } - name = xmalloc(len + 1); - memcpy(name, start, len); - name[len] = 0; - free(def); - return name; + return xmemdupz(start, len); } static int count_slashes(const char *cp) @@ -687,10 +683,7 @@ static char *git_header_name(char *line, int llen) break; } if (second[len] == '\n' && !memcmp(name, second, len)) { - char *ret = xmalloc(len + 1); - memcpy(ret, name, len); - ret[len] = 0; - return ret; + return xmemdupz(name, len); } } } diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index 514a3cc..349b59c 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -222,19 +222,15 @@ static char *find_local_name(const char *remote_name, const char *refs, } if (!strncmp(remote_name, ref, len) && ref[len] == ':') { const char *local_part = ref + len + 1; - char *ret; int retlen; if (!next) retlen = strlen(local_part); else retlen = next - local_part; - ret = xmalloc(retlen + 1); - memcpy(ret, local_part, retlen); - ret[retlen] = 0; *force_p = single_force; *not_for_merge_p = not_for_merge; - return ret; + return xmemdupz(local_part, retlen); } ref = next; } diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index ae60fcc..8a3c962 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -140,12 +140,10 @@ static int handle_line(char *line) if (!strcmp(".", src) || !strcmp(src, origin)) { int len = strlen(origin); if (origin[0] == '\'' && origin[len - 1] == '\'') { - char *new_origin = xmalloc(len - 1); - memcpy(new_origin, origin + 1, len - 2); - new_origin[len - 2] = 0; - origin = new_origin; - } else + origin = xmemdupz(origin + 1, len - 2); + } else { origin = xstrdup(origin); + } } else { char *new_origin = xmalloc(strlen(origin) + strlen(src) + 5); sprintf(new_origin, "%s of %s", origin, src); @@ -211,14 +209,11 @@ static void shortlog(const char *name, unsigned char *sha1, bol += 2; eol = strchr(bol, '\n'); - if (eol) { - int len = eol - bol; - oneline = xmalloc(len + 1); - memcpy(oneline, bol, len); - oneline[len] = 0; - } else + oneline = xmemdupz(bol, eol - bol); + } else { oneline = xstrdup(bol); + } append_to_list(&subjects, oneline, NULL); } diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index 0afa1c5..725c1df 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -87,7 +87,6 @@ static int used_atom_cnt, sort_atom_limit, need_tagged; static int parse_atom(const char *atom, const char *ep) { const char *sp; - char *n; int i, at; sp = atom; @@ -120,10 +119,7 @@ static int parse_atom(const char *atom, const char *ep) (sizeof *used_atom) * used_atom_cnt); used_atom_type = xrealloc(used_atom_type, (sizeof(*used_atom_type) * used_atom_cnt)); - n = xmalloc(ep - atom + 1); - memcpy(n, atom, ep - atom); - n[ep-atom] = 0; - used_atom[at] = n; + used_atom[at] = xmemdupz(atom, ep - atom); used_atom_type[at] = valid_atom[i].cmp_type; return at; } @@ -305,46 +301,28 @@ static const char *find_wholine(const char *who, int wholen, const char *buf, un static const char *copy_line(const char *buf) { const char *eol = strchr(buf, '\n'); - char *line; - int len; if (!eol) return ""; - len = eol - buf; - line = xmalloc(len + 1); - memcpy(line, buf, len); - line[len] = 0; - return line; + return xmemdupz(buf, eol - buf); } static const char *copy_name(const char *buf) { - const char *eol = strchr(buf, '\n'); - const char *eoname = strstr(buf, " <"); - char *line; - int len; - if (!(eoname && eol && eoname < eol)) - return ""; - len = eoname - buf; - line = xmalloc(len + 1); - memcpy(line, buf, len); - line[len] = 0; - return line; + const char *cp; + for (cp = buf; *cp != '\n'; cp++) { + if (!strncmp(cp, " <", 2)) + return xmemdupz(buf, cp - buf); + } + return ""; } static const char *copy_email(const char *buf) { const char *email = strchr(buf, '<'); const char *eoemail = strchr(email, '>'); - char *line; - int len; if (!email || !eoemail) return ""; - eoemail++; - len = eoemail - email; - line = xmalloc(len + 1); - memcpy(line, email, len); - line[len] = 0; - return line; + return xmemdupz(email, eoemail + 1 - email); } static void grab_date(const char *buf, struct atom_value *v) diff --git a/builtin-log.c b/builtin-log.c index 60819d1..e8b982d 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -441,8 +441,6 @@ static const char *clean_message_id(const char *msg_id) { char ch; const char *a, *z, *m; - char *n; - size_t len; m = msg_id; while ((ch = *m) && (isspace(ch) || (ch == '<'))) @@ -458,11 +456,7 @@ static const char *clean_message_id(const char *msg_id) die("insane in-reply-to: %s", msg_id); if (++z == m) return a; - len = z - a; - n = xmalloc(len + 1); - memcpy(n, a, len); - n[len] = 0; - return n; + return xmemdupz(a, z - a); } int cmd_format_patch(int argc, const char **argv, const char *prefix) @@ -541,9 +535,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) endpos = strchr(committer, '>'); if (!endpos) die("bogos committer info %s\n", committer); - add_signoff = xmalloc(endpos - committer + 2); - memcpy(add_signoff, committer, endpos - committer + 1); - add_signoff[endpos - committer + 1] = 0; + add_signoff = xmemdupz(committer, endpos - committer + 1); } else if (!strcmp(argv[i], "--attach")) { rev.mime_boundary = git_version_string; diff --git a/builtin-ls-files.c b/builtin-ls-files.c index 6c1db86..48dd3f7 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -299,7 +299,6 @@ static void prune_cache(const char *prefix) static const char *verify_pathspec(const char *prefix) { const char **p, *n, *prev; - char *real_prefix; unsigned long max; prev = NULL; @@ -326,14 +325,8 @@ static const char *verify_pathspec(const char *prefix) if (prefix_offset > max || memcmp(prev, prefix, prefix_offset)) die("git-ls-files: cannot generate relative filenames containing '..'"); - real_prefix = NULL; prefix_len = max; - if (max) { - real_prefix = xmalloc(max + 1); - memcpy(real_prefix, prev, max); - real_prefix[max] = 0; - } - return real_prefix; + return max ? xmemdupz(prev, max) : NULL; } /* diff --git a/builtin-mv.c b/builtin-mv.c index b95b7d2..b944651 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -22,10 +22,7 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec, for (i = 0; i < count; i++) { int length = strlen(result[i]); if (length > 0 && result[i][length - 1] == '/') { - char *without_slash = xmalloc(length); - memcpy(without_slash, result[i], length - 1); - without_slash[length - 1] = '\0'; - result[i] = without_slash; + result[i] = xmemdupz(result[i], length - 1); } if (base_name) { const char *last_slash = strrchr(result[i], '/'); diff --git a/builtin-revert.c b/builtin-revert.c index 499bbe7..a655c8e 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -168,9 +168,7 @@ static void set_author_ident_env(const char *message) char *line, *pend, *email, *timestamp; p += 7; - line = xmalloc(eol + 1 - p); - memcpy(line, p, eol - p); - line[eol - p] = '\0'; + line = xmemdupz(p, eol - p); email = strchr(line, '<'); if (!email) die ("Could not extract author email from %s", diff --git a/builtin-shortlog.c b/builtin-shortlog.c index 16af619..3fe7546 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -39,10 +39,7 @@ static void insert_author_oneline(struct path_list *list, while (authorlen > 0 && isspace(author[authorlen - 1])) authorlen--; - buffer = xmalloc(authorlen + 1); - memcpy(buffer, author, authorlen); - buffer[authorlen] = '\0'; - + buffer = xmemdupz(author, authorlen); item = path_list_insert(buffer, list); if (item->util == NULL) item->util = xcalloc(1, sizeof(struct path_list)); @@ -66,13 +63,9 @@ static void insert_author_oneline(struct path_list *list, oneline++; onelinelen--; } - while (onelinelen > 0 && isspace(oneline[onelinelen - 1])) onelinelen--; - - buffer = xmalloc(onelinelen + 1); - memcpy(buffer, oneline, onelinelen); - buffer[onelinelen] = '\0'; + buffer = xmemdupz(oneline, onelinelen); if (dot3) { int dot3len = strlen(dot3); diff --git a/commit.c b/commit.c index 85889f9..f86fa77 100644 --- a/commit.c +++ b/commit.c @@ -628,11 +628,7 @@ static char *get_header(const struct commit *commit, const char *key) if (eol - line > key_len && !strncmp(line, key, key_len) && line[key_len] == ' ') { - int len = eol - line - key_len; - char *ret = xmalloc(len); - memcpy(ret, line + key_len + 1, len - 1); - ret[len - 1] = '\0'; - return ret; + return xmemdupz(line + key_len + 1, eol - line - key_len - 1); } line = next; } @@ -709,7 +705,7 @@ static void fill_person(struct interp *table, const char *msg, int len) start = end + 1; while (end > 0 && isspace(msg[end - 1])) end--; - table[0].value = xstrndup(msg, end); + table[0].value = xmemdupz(msg, end); if (start >= len) return; @@ -721,7 +717,7 @@ static void fill_person(struct interp *table, const char *msg, int len) if (end >= len) return; - table[1].value = xstrndup(msg + start, end - start); + table[1].value = xmemdupz(msg + start, end - start); /* parse date */ for (start = end + 1; start < len && isspace(msg[start]); start++) @@ -732,7 +728,7 @@ static void fill_person(struct interp *table, const char *msg, int len) if (msg + start == ep) return; - table[5].value = xstrndup(msg + start, ep - (msg + start)); + table[5].value = xmemdupz(msg + start, ep - (msg + start)); /* parse tz */ for (start = ep - msg + 1; start < len && isspace(msg[start]); start++) @@ -859,7 +855,7 @@ void format_commit_message(const struct commit *commit, ; /* do nothing */ if (state == SUBJECT) { - table[ISUBJECT].value = xstrndup(msg + i, eol - i); + table[ISUBJECT].value = xmemdupz(msg + i, eol - i); i = eol; } if (i == eol) { @@ -875,7 +871,7 @@ void format_commit_message(const struct commit *commit, msg + i + 10, eol - i - 10); else if (!prefixcmp(msg + i, "encoding ")) table[IENCODING].value = - xstrndup(msg + i + 9, eol - i - 9); + xmemdupz(msg + i + 9, eol - i - 9); i = eol; } if (msg[i]) diff --git a/connect.c b/connect.c index 8b1e993..1653a0e 100644 --- a/connect.c +++ b/connect.c @@ -393,9 +393,7 @@ static int git_proxy_command_options(const char *var, const char *value) if (matchlen == 4 && !memcmp(value, "none", 4)) matchlen = 0; - git_proxy_command = xmalloc(matchlen + 1); - memcpy(git_proxy_command, value, matchlen); - git_proxy_command[matchlen] = 0; + git_proxy_command = xmemdupz(value, matchlen); } return 0; } diff --git a/convert.c b/convert.c index 508d30b..79c9df2 100644 --- a/convert.c +++ b/convert.c @@ -323,13 +323,8 @@ static int read_convert_config(const char *var, const char *value) if (!strncmp(drv->name, name, namelen) && !drv->name[namelen]) break; if (!drv) { - char *namebuf; drv = xcalloc(1, sizeof(struct convert_driver)); - namebuf = xmalloc(namelen + 1); - memcpy(namebuf, name, namelen); - namebuf[namelen] = 0; - drv->name = namebuf; - drv->next = NULL; + drv->name = xmemdupz(name, namelen); *user_convert_tail = drv; user_convert_tail = &(drv->next); } diff --git a/diff.c b/diff.c index a5b69ed..2216d75 100644 --- a/diff.c +++ b/diff.c @@ -83,13 +83,8 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val if (!strncmp(drv->name, name, namelen) && !drv->name[namelen]) break; if (!drv) { - char *namebuf; drv = xcalloc(1, sizeof(struct ll_diff_driver)); - namebuf = xmalloc(namelen + 1); - memcpy(namebuf, name, namelen); - namebuf[namelen] = 0; - drv->name = namebuf; - drv->next = NULL; + drv->name = xmemdupz(name, namelen); if (!user_diff_tail) user_diff_tail = &user_diff; *user_diff_tail = drv; @@ -126,12 +121,8 @@ static int parse_funcname_pattern(const char *var, const char *ep, const char *v if (!strncmp(pp->name, name, namelen) && !pp->name[namelen]) break; if (!pp) { - char *namebuf; pp = xcalloc(1, sizeof(*pp)); - namebuf = xmalloc(namelen + 1); - memcpy(namebuf, name, namelen); - namebuf[namelen] = 0; - pp->name = namebuf; + pp->name = xmemdupz(name, namelen); pp->next = funcname_pattern_list; funcname_pattern_list = pp; } diff --git a/diffcore-order.c b/diffcore-order.c index 2a4bd82..23e9385 100644 --- a/diffcore-order.c +++ b/diffcore-order.c @@ -48,11 +48,8 @@ static void prepare_order(const char *orderfile) if (*ep == '\n') { *ep = 0; order[cnt] = cp; - } - else { - order[cnt] = xmalloc(ep-cp+1); - memcpy(order[cnt], cp, ep-cp); - order[cnt][ep-cp] = 0; + } else { + order[cnt] = xmemdupz(cp, ep - cp); } cnt++; } diff --git a/fast-import.c b/fast-import.c index e2a4834..f990658 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1864,9 +1864,7 @@ static void file_change_cr(struct branch *b, int rename) endp = strchr(s, ' '); if (!endp) die("Missing space after source: %s", command_buf.buf); - s_uq = xmalloc(endp - s + 1); - memcpy(s_uq, s, endp - s); - s_uq[endp - s] = 0; + s_uq = xmemdupz(s, endp - s); } s = s_uq; diff --git a/http-push.c b/http-push.c index 7c3720f..276e1eb 100644 --- a/http-push.c +++ b/http-push.c @@ -1271,10 +1271,7 @@ xml_cdata(void *userData, const XML_Char *s, int len) { struct xml_ctx *ctx = (struct xml_ctx *)userData; free(ctx->cdata); - ctx->cdata = xmalloc(len + 1); - /* NB: 's' is not null-terminated, can not use strlcpy here */ - memcpy(ctx->cdata, s, len); - ctx->cdata[len] = '\0'; + ctx->cdata = xmemdupz(s, len); } static struct remote_lock *lock_remote(const char *path, long timeout) @@ -2172,9 +2169,7 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1) /* If it's a symref, set the refname; otherwise try for a sha1 */ if (!prefixcmp((char *)buffer.buffer, "ref: ")) { - *symref = xmalloc(buffer.posn - 5); - memcpy(*symref, (char *)buffer.buffer + 5, buffer.posn - 6); - (*symref)[buffer.posn - 6] = '\0'; + *symref = xmemdupz((char *)buffer.buffer + 5, buffer.posn - 6); } else { get_sha1_hex(buffer.buffer, sha1); } diff --git a/imap-send.c b/imap-send.c index 86e4a0f..905d097 100644 --- a/imap-send.c +++ b/imap-send.c @@ -623,9 +623,7 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level ) goto bail; cur->len = s - p; s++; - cur->val = xmalloc( cur->len + 1 ); - memcpy( cur->val, p, cur->len ); - cur->val[cur->len] = 0; + cur->val = xmemdupz(p, cur->len); } else { /* atom */ p = s; @@ -633,12 +631,10 @@ 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 { - cur->val = xmalloc( cur->len + 1 ); - memcpy( cur->val, p, cur->len ); - cur->val[cur->len] = 0; + } else { + cur->val = xmemdupz(p, cur->len); } } @@ -1221,13 +1217,7 @@ split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs ) if (p) msg->len = &p[1] - data; - msg->data = xmalloc( msg->len + 1 ); - if (!msg->data) - return 0; - - memcpy( msg->data, data, msg->len ); - msg->data[ msg->len ] = 0; - + msg->data = xmemdupz(data, msg->len); *ofs += msg->len; return 1; } diff --git a/merge-recursive.c b/merge-recursive.c index 19d5f3b..14b56c2 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -432,19 +432,15 @@ static int update_stages(const char *path, struct diff_filespec *o, static int remove_path(const char *name) { - int ret, len; + int ret; char *slash, *dirs; ret = unlink(name); if (ret) return ret; - len = strlen(name); - dirs = xmalloc(len+1); - memcpy(dirs, name, len); - dirs[len] = '\0'; + dirs = xstrdup(name); while ((slash = strrchr(name, '/'))) { *slash = '\0'; - len = slash - name; if (rmdir(name) != 0) break; } @@ -578,9 +574,7 @@ static void update_file_flags(const unsigned char *sha, flush_buffer(fd, buf, size); close(fd); } else if (S_ISLNK(mode)) { - char *lnk = xmalloc(size + 1); - memcpy(lnk, buf, size); - lnk[size] = '\0'; + char *lnk = xmemdupz(buf, size); mkdir_p(path, 0777); unlink(path); symlink(lnk, path); @@ -872,14 +866,9 @@ static int read_merge_config(const char *var, const char *value) if (!strncmp(fn->name, name, namelen) && !fn->name[namelen]) break; if (!fn) { - char *namebuf; fn = xcalloc(1, sizeof(struct ll_merge_driver)); - namebuf = xmalloc(namelen + 1); - memcpy(namebuf, name, namelen); - namebuf[namelen] = 0; - fn->name = namebuf; + fn->name = xmemdupz(name, namelen); fn->fn = ll_ext_merge; - fn->next = NULL; *ll_user_merge_tail = fn; ll_user_merge_tail = &(fn->next); } diff --git a/refs.c b/refs.c index 7fb3350..07e260c 100644 --- a/refs.c +++ b/refs.c @@ -1246,15 +1246,11 @@ int create_symref(const char *ref_target, const char *refs_heads_master, static char *ref_msg(const char *line, const char *endp) { const char *ep; - char *msg; - line += 82; - for (ep = line; ep < endp && *ep != '\n'; ep++) - ; - msg = xmalloc(ep - line + 1); - memcpy(msg, line, ep - line); - msg[ep - line] = 0; - return msg; + ep = memchr(line, '\n', endp - line); + if (!ep) + ep = endp; + return xmemdupz(line, ep - line); } int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt) diff --git a/sha1_file.c b/sha1_file.c index 59325d4..385c5d8 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1491,11 +1491,8 @@ found_cache_entry: ent->lru.next->prev = ent->lru.prev; ent->lru.prev->next = ent->lru.next; delta_base_cached -= ent->size; - } - else { - ret = xmalloc(ent->size + 1); - memcpy(ret, ent->data, ent->size); - ((char *)ret)[ent->size] = 0; + } else { + ret = xmemdupz(ent->data, ent->size); } *type = ent->type; *base_size = ent->size; @@ -1872,12 +1869,9 @@ void *read_sha1_file(const unsigned char *sha1, enum object_type *type, co = find_cached_object(sha1); if (co) { - buf = xmalloc(co->size + 1); - memcpy(buf, co->buf, co->size); - ((char*)buf)[co->size] = 0; *type = co->type; *size = co->size; - return buf; + return xmemdupz(co->buf, co->size); } buf = read_packed_sha1(sha1, type, size); diff --git a/tag.c b/tag.c index bbacd59..f62bcdd 100644 --- a/tag.c +++ b/tag.c @@ -68,9 +68,7 @@ int parse_tag_buffer(struct tag *item, void *data, unsigned long size) memcpy(type, type_line + 5, typelen); type[typelen] = '\0'; taglen = sig_line - tag_line - strlen("tag \n"); - item->tag = xmalloc(taglen + 1); - memcpy(item->tag, tag_line + 4, taglen); - item->tag[taglen] = '\0'; + item->tag = xmemdupz(tag_line + 4, taglen); if (!strcmp(type, blob_type)) { item->tagged = &lookup_blob(sha1)->object; -- cgit v0.10.2-6-g49f6 From ca0328354afbf4ec6941cb4312bfc6908ab06d73 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Sep 2007 01:37:50 -0700 Subject: builtin-apply.c: fix a tiny leak introduced during xmemdupz() conversion. Signed-off-by: Junio C Hamano diff --git a/builtin-apply.c b/builtin-apply.c index 512c9b6..db337f1 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -291,6 +291,7 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) int deflen = strlen(def); if (deflen < len && !strncmp(start, def, deflen)) return def; + free(def); } return xmemdupz(start, len); -- cgit v0.10.2-6-g49f6 From 6b30852ded5074652f4304a303521932235c62db Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Sep 2007 01:52:59 -0700 Subject: builtin-for-each-ref.c::copy_name() - do not overstep the buffer. This was introduced during xmemdupz() conversion. Signed-off-by: Junio C Hamano diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index 725c1df..e868a4b 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -309,7 +309,7 @@ static const char *copy_line(const char *buf) static const char *copy_name(const char *buf) { const char *cp; - for (cp = buf; *cp != '\n'; cp++) { + for (cp = buf; *cp && *cp != '\n'; cp++) { if (!strncmp(cp, " <", 2)) return xmemdupz(buf, cp - buf); } -- cgit v0.10.2-6-g49f6 From 17ed158021ead9cb056f692fc35ff3fcde96a747 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 20 Sep 2007 07:23:01 +0200 Subject: rev-list --bisect: Fix best == NULL case. Earlier commit ce0cbad77 broke rev-list --bisect to cause it segfault when the resulting set is empty. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 899a31d..3894633 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -436,10 +436,10 @@ static struct commit_list *find_bisection(struct commit_list *list, /* Do the real work of finding bisection commit. */ best = do_find_bisection(list, nr, weights); - if (best) + if (best) { best->next = NULL; - - *reaches = weight(best); + *reaches = weight(best); + } free(weights); return best; -- cgit v0.10.2-6-g49f6 From e03e05ff73a6f6b4f32eb5cd13b4f1e43275e42f Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 20 Sep 2007 00:42:10 +0200 Subject: Fix the expansion pattern of the pseudo-static path buffer. Signed-off-by: Pierre Habouzit diff --git a/archive-tar.c b/archive-tar.c index a87bc4b..e1bced5 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -238,15 +238,14 @@ static int write_tar_entry(const unsigned char *sha1, const char *filename, unsigned mode, int stage) { static struct strbuf path = STRBUF_INIT; - int filenamelen = strlen(filename); void *buffer; enum object_type type; unsigned long size; - strbuf_grow(&path, MAX(PATH_MAX, baselen + filenamelen + 1)); strbuf_reset(&path); + strbuf_grow(&path, PATH_MAX); strbuf_add(&path, base, baselen); - strbuf_add(&path, filename, filenamelen); + strbuf_addstr(&path, filename); if (S_ISDIR(mode) || S_ISGITLINK(mode)) { strbuf_addch(&path, '/'); buffer = NULL; -- cgit v0.10.2-6-g49f6 From 19247e5510279f018f8358a72b38cc5aa62fac8a Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 20 Sep 2007 10:43:11 +0200 Subject: nfv?asprintf are broken without va_copy, workaround them. * drop nfasprintf. * move nfvasprintf into imap-send.c back, and let it work on a 8k buffer, and die() in case of overflow. It should be enough for imap commands, if someone cares about imap-send, he's welcomed to fix it properly. * replace nfvasprintf use in merge-recursive with a copy of the strbuf_addf logic, it's one place, we'll live with it. To ease the change, output_buffer string list is replaced with a strbuf ;) * rework trace.c to call vsnprintf itself. It's used to format strerror()s and git command names, it should never be more than a few octets long, let it work on a 8k static buffer with vsnprintf or die loudly. Signed-off-by: Pierre Habouzit diff --git a/cache.h b/cache.h index 8650d62..916ee51 100644 --- a/cache.h +++ b/cache.h @@ -585,8 +585,6 @@ extern void *alloc_object_node(void); extern void alloc_report(void); /* trace.c */ -extern int nfasprintf(char **str, const char *fmt, ...); -extern int nfvasprintf(char **str, const char *fmt, va_list va); extern void trace_printf(const char *format, ...); extern void trace_argv_printf(const char **argv, int count, const char *format, ...); diff --git a/imap-send.c b/imap-send.c index 905d097..e95cdde 100644 --- a/imap-send.c +++ b/imap-send.c @@ -105,6 +105,19 @@ static void free_generic_messages( message_t * ); static int nfsnprintf( char *buf, int blen, const char *fmt, ... ); +static int nfvasprintf(char **strp, const char *fmt, va_list ap) +{ + int len; + char tmp[8192]; + + len = vsnprintf(tmp, sizeof(tmp), fmt, ap); + if (len < 0) + die("Fatal: Out of memory\n"); + if (len >= sizeof(tmp)) + die("imap command overflow !\n"); + *strp = xmemdupz(tmp, len); + return len; +} static void arc4_init( void ); static unsigned char arc4_getbyte( void ); diff --git a/merge-recursive.c b/merge-recursive.c index 14b56c2..86767e6 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -85,63 +85,58 @@ struct stage_data unsigned processed:1; }; -struct output_buffer -{ - struct output_buffer *next; - char *str; -}; - static struct path_list current_file_set = {NULL, 0, 0, 1}; static struct path_list current_directory_set = {NULL, 0, 0, 1}; static int call_depth = 0; static int verbosity = 2; static int buffer_output = 1; -static struct output_buffer *output_list, *output_end; +static struct strbuf obuf = STRBUF_INIT; -static int show (int v) +static int show(int v) { return (!call_depth && verbosity >= v) || verbosity >= 5; } -static void output(int v, const char *fmt, ...) +static void flush_output(void) { - va_list args; - va_start(args, fmt); - if (buffer_output && show(v)) { - struct output_buffer *b = xmalloc(sizeof(*b)); - nfvasprintf(&b->str, fmt, args); - b->next = NULL; - if (output_end) - output_end->next = b; - else - output_list = b; - output_end = b; - } else if (show(v)) { - int i; - for (i = call_depth; i--;) - fputs(" ", stdout); - vfprintf(stdout, fmt, args); - fputc('\n', stdout); + if (obuf.len) { + fputs(obuf.buf, stdout); + strbuf_reset(&obuf); } - va_end(args); } -static void flush_output(void) +static void output(int v, const char *fmt, ...) { - struct output_buffer *b, *n; - for (b = output_list; b; b = n) { - int i; - for (i = call_depth; i--;) - fputs(" ", stdout); - fputs(b->str, stdout); - fputc('\n', stdout); - n = b->next; - free(b->str); - free(b); + int len; + va_list ap; + + if (!show(v)) + return; + + strbuf_grow(&obuf, call_depth * 2 + 2); + memset(obuf.buf + obuf.len, ' ', call_depth * 2); + strbuf_setlen(&obuf, obuf.len + call_depth * 2); + + va_start(ap, fmt); + len = vsnprintf(obuf.buf + obuf.len, strbuf_avail(&obuf), fmt, ap); + va_end(ap); + + if (len < 0) + len = 0; + if (len >= strbuf_avail(&obuf)) { + strbuf_grow(&obuf, len + 2); + va_start(ap, fmt); + len = vsnprintf(obuf.buf + obuf.len, strbuf_avail(&obuf), fmt, ap); + va_end(ap); + if (len >= strbuf_avail(&obuf)) { + die("this should not happen, your snprintf is broken"); + } } - output_list = NULL; - output_end = NULL; + strbuf_setlen(&obuf, obuf.len + len); + strbuf_add(&obuf, "\n", 1); + if (!buffer_output) + flush_output(); } static void output_commit_title(struct commit *commit) diff --git a/trace.c b/trace.c index 7961a27..91548a5 100644 --- a/trace.c +++ b/trace.c @@ -25,33 +25,6 @@ #include "cache.h" #include "quote.h" -/* Stolen from "imap-send.c". */ -int nfvasprintf(char **strp, const char *fmt, va_list ap) -{ - int len; - char tmp[1024]; - - if ((len = vsnprintf(tmp, sizeof(tmp), fmt, ap)) < 0 || - !(*strp = xmalloc(len + 1))) - die("Fatal: Out of memory\n"); - if (len >= (int)sizeof(tmp)) - vsprintf(*strp, fmt, ap); - else - memcpy(*strp, tmp, len + 1); - return len; -} - -int nfasprintf(char **str, const char *fmt, ...) -{ - int rc; - va_list args; - - va_start(args, fmt); - rc = nfvasprintf(str, fmt, args); - va_end(args); - return rc; -} - /* Get a trace file descriptor from GIT_TRACE env variable. */ static int get_trace_fd(int *need_close) { @@ -89,63 +62,54 @@ static int get_trace_fd(int *need_close) static const char err_msg[] = "Could not trace into fd given by " "GIT_TRACE environment variable"; -void trace_printf(const char *format, ...) +void trace_printf(const char *fmt, ...) { - char *trace_str; - va_list rest; - int need_close = 0; - int fd = get_trace_fd(&need_close); + char buf[8192]; + va_list ap; + int fd, len, need_close = 0; + fd = get_trace_fd(&need_close); if (!fd) return; - va_start(rest, format); - nfvasprintf(&trace_str, format, rest); - va_end(rest); - - write_or_whine_pipe(fd, trace_str, strlen(trace_str), err_msg); - - free(trace_str); + va_start(ap, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if (len >= sizeof(buf)) + die("unreasonnable trace length"); + write_or_whine_pipe(fd, buf, len, err_msg); if (need_close) close(fd); } -void trace_argv_printf(const char **argv, int count, const char *format, ...) +void trace_argv_printf(const char **argv, int count, const char *fmt, ...) { - char *argv_str, *format_str, *trace_str; - size_t argv_len, format_len, trace_len; - va_list rest; - int need_close = 0; - int fd = get_trace_fd(&need_close); + char buf[8192]; + va_list ap; + char *argv_str; + size_t argv_len; + int fd, len, need_close = 0; + fd = get_trace_fd(&need_close); if (!fd) return; + va_start(ap, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if (len >= sizeof(buf)) + die("unreasonnable trace length"); + /* Get the argv string. */ argv_str = sq_quote_argv(argv, count); argv_len = strlen(argv_str); - /* Get the formated string. */ - va_start(rest, format); - nfvasprintf(&format_str, format, rest); - va_end(rest); - - /* Allocate buffer for trace string. */ - format_len = strlen(format_str); - trace_len = argv_len + format_len + 1; /* + 1 for \n */ - trace_str = xmalloc(trace_len + 1); - - /* Copy everything into the trace string. */ - strncpy(trace_str, format_str, format_len); - strncpy(trace_str + format_len, argv_str, argv_len); - strcpy(trace_str + trace_len - 1, "\n"); - - write_or_whine_pipe(fd, trace_str, trace_len, err_msg); + write_or_whine_pipe(fd, buf, len, err_msg); + write_or_whine_pipe(fd, argv_str, argv_len, err_msg); + write_or_whine_pipe(fd, "\n", 1, err_msg); free(argv_str); - free(format_str); - free(trace_str); if (need_close) close(fd); -- cgit v0.10.2-6-g49f6 From c76689df6c64a1e987bd779bd71a2042b5131fb9 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 20 Sep 2007 00:42:12 +0200 Subject: strbuf API additions and enhancements. Add strbuf_remove, change strbuf_insert: As both are special cases of strbuf_splice, implement them as such. gcc is able to do the math and generate almost optimal code this way. Add strbuf_swap: Exchange the values of its arguments. Use it in fast-import.c Also fix spacing issues in strbuf.h Signed-off-by: Pierre Habouzit diff --git a/commit.c b/commit.c index f86fa77..55b08ec 100644 --- a/commit.c +++ b/commit.c @@ -656,7 +656,7 @@ static char *replace_encoding_header(char *buf, const char *encoding) strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1); if (is_encoding_utf8(encoding)) { /* we have re-coded to UTF-8; drop the header */ - strbuf_splice(&tmp, start, len, NULL, 0); + strbuf_remove(&tmp, start, len); } else { /* just replaces XXXX in 'encoding XXXX\n' */ strbuf_splice(&tmp, start + strlen("encoding "), diff --git a/fast-import.c b/fast-import.c index f990658..eddae22 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1111,9 +1111,7 @@ static int store_object( if (last->no_swap) { last->data = *dat; } else { - struct strbuf tmp = *dat; - *dat = last->data; - last->data = tmp; + strbuf_swap(&last->data, dat); } last->offset = e->offset; } diff --git a/strbuf.c b/strbuf.c index 59383ac..dcb725d 100644 --- a/strbuf.c +++ b/strbuf.c @@ -50,16 +50,6 @@ void strbuf_rtrim(struct strbuf *sb) sb->buf[sb->len] = '\0'; } -void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len) -{ - strbuf_grow(sb, len); - if (pos > sb->len) - die("`pos' is too far after the end of the buffer"); - memmove(sb->buf + pos + len, sb->buf + pos, sb->len - pos); - memcpy(sb->buf + pos, data, len); - strbuf_setlen(sb, sb->len + len); -} - void strbuf_splice(struct strbuf *sb, size_t pos, size_t len, const void *data, size_t dlen) { @@ -79,6 +69,16 @@ void strbuf_splice(struct strbuf *sb, size_t pos, size_t len, strbuf_setlen(sb, sb->len + dlen - len); } +void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len) +{ + strbuf_splice(sb, pos, 0, data, len); +} + +void strbuf_remove(struct strbuf *sb, size_t pos, size_t len) +{ + strbuf_splice(sb, pos, len, NULL, 0); +} + void strbuf_add(struct strbuf *sb, const void *data, size_t len) { strbuf_grow(sb, len); diff --git a/strbuf.h b/strbuf.h index b2cbd97..567e2d1 100644 --- a/strbuf.h +++ b/strbuf.h @@ -55,15 +55,20 @@ extern void strbuf_release(struct strbuf *); extern void strbuf_reset(struct strbuf *); extern char *strbuf_detach(struct strbuf *); extern void strbuf_attach(struct strbuf *, void *, size_t, size_t); +static inline void strbuf_swap(struct strbuf *a, struct strbuf *b) { + struct strbuf tmp = *a; + *a = *b; + *b = tmp; +} /*----- strbuf size related -----*/ static inline size_t strbuf_avail(struct strbuf *sb) { - return sb->alloc ? sb->alloc - sb->len - 1 : 0; + return sb->alloc ? sb->alloc - sb->len - 1 : 0; } static inline void strbuf_setlen(struct strbuf *sb, size_t len) { - assert (len < sb->alloc); - sb->len = len; - sb->buf[len] = '\0'; + assert (len < sb->alloc); + sb->len = len; + sb->buf[len] = '\0'; } extern void strbuf_grow(struct strbuf *, size_t); @@ -78,12 +83,12 @@ static inline void strbuf_addch(struct strbuf *sb, int c) { sb->buf[sb->len] = '\0'; } -/* inserts after pos, or appends if pos >= sb->len */ extern void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t); +extern void strbuf_remove(struct strbuf *, size_t pos, size_t len); /* splice pos..pos+len with given data */ extern void strbuf_splice(struct strbuf *, size_t pos, size_t len, - const void *, size_t); + const void *, size_t); extern void strbuf_add(struct strbuf *, const void *, size_t); static inline void strbuf_addstr(struct strbuf *sb, const char *s) { -- cgit v0.10.2-6-g49f6 From 7fb1011e610a28518959b1d2d48cea17ecc32048 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 20 Sep 2007 00:42:14 +0200 Subject: Rework unquote_c_style to work on a strbuf. If the gain is not obvious in the diffstat, the resulting code is more readable, _and_ in checkout-index/update-index we now reuse the same buffer to unquote strings instead of always freeing/mallocing. This also is more coherent with the next patch that reworks quoting functions. The quoting function is also made more efficient scanning for backslashes and treating portions of strings without a backslash at once. Signed-off-by: Pierre Habouzit diff --git a/builtin-apply.c b/builtin-apply.c index db337f1..1cf68ed 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -231,35 +231,33 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) { int len; const char *start = line; - char *name; if (*line == '"') { + struct strbuf name; + /* Proposed "new-style" GNU patch/diff format; see * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2 */ - name = unquote_c_style(line, NULL); - if (name) { - char *cp = name; - while (p_value) { + strbuf_init(&name, 0); + if (!unquote_c_style(&name, line, NULL)) { + char *cp; + + for (cp = name.buf; p_value; p_value--) { cp = strchr(cp, '/'); if (!cp) break; cp++; - p_value--; } if (cp) { /* name can later be freed, so we need * to memmove, not just return cp */ - memmove(name, cp, strlen(cp) + 1); + strbuf_remove(&name, 0, cp - name.buf); free(def); - return name; - } - else { - free(name); - name = NULL; + return name.buf; } } + strbuf_release(&name); } for (;;) { @@ -567,29 +565,30 @@ static const char *stop_at_slash(const char *line, int llen) */ static char *git_header_name(char *line, int llen) { - int len; const char *name; const char *second = NULL; + size_t len; line += strlen("diff --git "); llen -= strlen("diff --git "); if (*line == '"') { const char *cp; - char *first = unquote_c_style(line, &second); - if (!first) - return NULL; + struct strbuf first; + struct strbuf sp; + + strbuf_init(&first, 0); + strbuf_init(&sp, 0); + + if (unquote_c_style(&first, line, &second)) + goto free_and_fail1; /* advance to the first slash */ - cp = stop_at_slash(first, strlen(first)); - if (!cp || cp == first) { - /* we do not accept absolute paths */ - free_first_and_fail: - free(first); - return NULL; - } - len = strlen(cp+1); - memmove(first, cp+1, len+1); /* including NUL */ + cp = stop_at_slash(first.buf, first.len); + /* we do not accept absolute paths */ + if (!cp || cp == first.buf) + goto free_and_fail1; + strbuf_remove(&first, 0, cp + 1 - first.buf); /* second points at one past closing dq of name. * find the second name. @@ -598,40 +597,40 @@ static char *git_header_name(char *line, int llen) second++; if (line + llen <= second) - goto free_first_and_fail; + goto free_and_fail1; if (*second == '"') { - char *sp = unquote_c_style(second, NULL); - if (!sp) - goto free_first_and_fail; - cp = stop_at_slash(sp, strlen(sp)); - if (!cp || cp == sp) { - free_both_and_fail: - free(sp); - goto free_first_and_fail; - } + if (unquote_c_style(&sp, second, NULL)) + goto free_and_fail1; + cp = stop_at_slash(sp.buf, sp.len); + if (!cp || cp == sp.buf) + goto free_and_fail1; /* They must match, otherwise ignore */ - if (strcmp(cp+1, first)) - goto free_both_and_fail; - free(sp); - return first; + if (strcmp(cp + 1, first.buf)) + goto free_and_fail1; + strbuf_release(&sp); + return first.buf; } /* unquoted second */ cp = stop_at_slash(second, line + llen - second); if (!cp || cp == second) - goto free_first_and_fail; + goto free_and_fail1; cp++; - if (line + llen - cp != len + 1 || - memcmp(first, cp, len)) - goto free_first_and_fail; - return first; + if (line + llen - cp != first.len + 1 || + memcmp(first.buf, cp, first.len)) + goto free_and_fail1; + return first.buf; + + free_and_fail1: + strbuf_release(&first); + strbuf_release(&sp); + return NULL; } /* unquoted first name */ name = stop_at_slash(line, llen); if (!name || name == line) return NULL; - name++; /* since the first name is unquoted, a dq if exists must be @@ -639,28 +638,30 @@ static char *git_header_name(char *line, int llen) */ for (second = name; second < line + llen; second++) { if (*second == '"') { - const char *cp = second; + struct strbuf sp; const char *np; - char *sp = unquote_c_style(second, NULL); - - if (!sp) - return NULL; - np = stop_at_slash(sp, strlen(sp)); - if (!np || np == sp) { - free_second_and_fail: - free(sp); - return NULL; - } + + strbuf_init(&sp, 0); + if (unquote_c_style(&sp, second, NULL)) + goto free_and_fail2; + + np = stop_at_slash(sp.buf, sp.len); + if (!np || np == sp.buf) + goto free_and_fail2; np++; - len = strlen(np); - if (len < cp - name && + + len = sp.buf + sp.len - np; + if (len < second - name && !strncmp(np, name, len) && isspace(name[len])) { /* Good */ - memmove(sp, np, len + 1); - return sp; + strbuf_remove(&sp, 0, np - sp.buf); + return sp.buf; } - goto free_second_and_fail; + + free_and_fail2: + strbuf_release(&sp); + return NULL; } } diff --git a/builtin-checkout-index.c b/builtin-checkout-index.c index a18ecc4..e6264c4 100644 --- a/builtin-checkout-index.c +++ b/builtin-checkout-index.c @@ -270,26 +270,27 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) } if (read_from_stdin) { - struct strbuf buf; + struct strbuf buf, nbuf; + if (all) die("git-checkout-index: don't mix '--all' and '--stdin'"); + strbuf_init(&buf, 0); - while (1) { - char *path_name; + strbuf_init(&nbuf, 0); + while (strbuf_getline(&buf, stdin, line_termination) != EOF) { const char *p; - if (strbuf_getline(&buf, stdin, line_termination) == EOF) - break; - if (line_termination && buf.buf[0] == '"') - path_name = unquote_c_style(buf.buf, NULL); - else - path_name = buf.buf; - p = prefix_path(prefix, prefix_length, path_name); + if (line_termination && buf.buf[0] == '"') { + strbuf_reset(&nbuf); + if (unquote_c_style(&nbuf, buf.buf, NULL)) + die("line is badly quoted"); + strbuf_swap(&buf, &nbuf); + } + p = prefix_path(prefix, prefix_length, buf.buf); checkout_file(p, prefix_length); - if (p < path_name || p > path_name + strlen(path_name)) + if (p < buf.buf || p > buf.buf + buf.len) free((char *)p); - if (path_name != buf.buf) - free(path_name); } + strbuf_release(&nbuf); strbuf_release(&buf); } diff --git a/builtin-update-index.c b/builtin-update-index.c index acd5ab5..c76879e 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -295,8 +295,11 @@ static void update_one(const char *path, const char *prefix, int prefix_length) static void read_index_info(int line_termination) { struct strbuf buf; + struct strbuf uq; + strbuf_init(&buf, 0); - while (1) { + strbuf_init(&uq, 0); + while (strbuf_getline(&buf, stdin, line_termination) != EOF) { char *ptr, *tab; char *path_name; unsigned char sha1[20]; @@ -320,9 +323,6 @@ static void read_index_info(int line_termination) * This format is to put higher order stages into the * index file and matches git-ls-files --stage output. */ - if (strbuf_getline(&buf, stdin, line_termination) == EOF) - break; - errno = 0; ul = strtoul(buf.buf, &ptr, 8); if (ptr == buf.buf || *ptr != ' ' @@ -347,15 +347,17 @@ static void read_index_info(int line_termination) if (get_sha1_hex(tab - 40, sha1) || tab[-41] != ' ') goto bad_line; - if (line_termination && ptr[0] == '"') - path_name = unquote_c_style(ptr, NULL); - else - path_name = ptr; + path_name = ptr; + if (line_termination && path_name[0] == '"') { + strbuf_reset(&uq); + if (unquote_c_style(&uq, path_name, NULL)) { + die("git-update-index: bad quoting of path name"); + } + path_name = uq.buf; + } if (!verify_path(path_name)) { fprintf(stderr, "Ignoring path %s\n", path_name); - if (path_name != ptr) - free(path_name); continue; } @@ -383,6 +385,7 @@ static void read_index_info(int line_termination) die("malformed index info %s", buf.buf); } strbuf_release(&buf); + strbuf_release(&uq); } static const char update_index_usage[] = @@ -705,26 +708,26 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) free((char*)p); } if (read_from_stdin) { - struct strbuf buf; + struct strbuf buf, nbuf; + strbuf_init(&buf, 0); - while (1) { - char *path_name; + strbuf_init(&nbuf, 0); + while (strbuf_getline(&buf, stdin, line_termination) != EOF) { const char *p; - if (strbuf_getline(&buf, stdin, line_termination) == EOF) - break; - if (line_termination && buf.buf[0] == '"') - path_name = unquote_c_style(buf.buf, NULL); - else - path_name = buf.buf; - p = prefix_path(prefix, prefix_length, path_name); + if (line_termination && buf.buf[0] == '"') { + strbuf_reset(&nbuf); + if (unquote_c_style(&nbuf, buf.buf, NULL)) + die("line is badly quoted"); + strbuf_swap(&buf, &nbuf); + } + p = prefix_path(prefix, prefix_length, buf.buf); update_one(p, NULL, 0); if (set_executable_bit) chmod_path(set_executable_bit, p); - if (p < path_name || p > path_name + strlen(path_name)) - free((char*) p); - if (path_name != buf.buf) - free(path_name); + if (p < buf.buf || p > buf.buf + buf.len) + free((char *)p); } + strbuf_release(&nbuf); strbuf_release(&buf); } diff --git a/fast-import.c b/fast-import.c index eddae22..a870a44 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1759,7 +1759,7 @@ static void load_branch(struct branch *b) static void file_change_m(struct branch *b) { const char *p = command_buf.buf + 2; - char *p_uq; + static struct strbuf uq = STRBUF_INIT; const char *endp; struct object_entry *oe = oe; unsigned char sha1[20]; @@ -1797,18 +1797,20 @@ static void file_change_m(struct branch *b) if (*p++ != ' ') die("Missing space after SHA1: %s", command_buf.buf); - p_uq = unquote_c_style(p, &endp); - if (p_uq) { + strbuf_reset(&uq); + if (!unquote_c_style(&uq, p, &endp)) { if (*endp) die("Garbage after path in: %s", command_buf.buf); - p = p_uq; + p = uq.buf; } if (inline_data) { static struct strbuf buf = STRBUF_INIT; - if (!p_uq) - p = p_uq = xstrdup(p); + if (p != uq.buf) { + strbuf_addstr(&uq, p); + p = uq.buf; + } read_next_command(); cmd_data(&buf); store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0); @@ -1826,56 +1828,54 @@ static void file_change_m(struct branch *b) } tree_content_set(&b->branch_tree, p, sha1, S_IFREG | mode, NULL); - free(p_uq); } static void file_change_d(struct branch *b) { const char *p = command_buf.buf + 2; - char *p_uq; + static struct strbuf uq = STRBUF_INIT; const char *endp; - p_uq = unquote_c_style(p, &endp); - if (p_uq) { + strbuf_reset(&uq); + if (!unquote_c_style(&uq, p, &endp)) { if (*endp) die("Garbage after path in: %s", command_buf.buf); - p = p_uq; + p = uq.buf; } tree_content_remove(&b->branch_tree, p, NULL); - free(p_uq); } static void file_change_cr(struct branch *b, int rename) { const char *s, *d; - char *s_uq, *d_uq; + static struct strbuf s_uq = STRBUF_INIT; + static struct strbuf d_uq = STRBUF_INIT; const char *endp; struct tree_entry leaf; s = command_buf.buf + 2; - s_uq = unquote_c_style(s, &endp); - if (s_uq) { + strbuf_reset(&s_uq); + if (!unquote_c_style(&s_uq, s, &endp)) { if (*endp != ' ') die("Missing space after source: %s", command_buf.buf); - } - else { + } else { endp = strchr(s, ' '); if (!endp) die("Missing space after source: %s", command_buf.buf); - s_uq = xmemdupz(s, endp - s); + strbuf_add(&s_uq, s, endp - s); } - s = s_uq; + s = s_uq.buf; endp++; if (!*endp) die("Missing dest: %s", command_buf.buf); d = endp; - d_uq = unquote_c_style(d, &endp); - if (d_uq) { + strbuf_reset(&d_uq); + if (!unquote_c_style(&d_uq, d, &endp)) { if (*endp) die("Garbage after dest in: %s", command_buf.buf); - d = d_uq; + d = d_uq.buf; } memset(&leaf, 0, sizeof(leaf)); @@ -1889,9 +1889,6 @@ static void file_change_cr(struct branch *b, int rename) leaf.versions[1].sha1, leaf.versions[1].mode, leaf.tree); - - free(s_uq); - free(d_uq); } static void file_change_deleteall(struct branch *b) diff --git a/mktree.c b/mktree.c index 9c137de..e0da110 100644 --- a/mktree.c +++ b/mktree.c @@ -66,6 +66,7 @@ static const char mktree_usage[] = "git-mktree [-z]"; int main(int ac, char **av) { struct strbuf sb; + struct strbuf p_uq; unsigned char sha1[20]; int line_termination = '\n'; @@ -82,14 +83,13 @@ int main(int ac, char **av) } strbuf_init(&sb, 0); - while (1) { + strbuf_init(&p_uq, 0); + while (strbuf_getline(&sb, stdin, line_termination) != EOF) { char *ptr, *ntr; unsigned mode; enum object_type type; char *path; - if (strbuf_getline(&sb, stdin, line_termination) == EOF) - break; ptr = sb.buf; /* Input is non-recursive ls-tree output format * mode SP type SP sha1 TAB name @@ -109,18 +109,21 @@ int main(int ac, char **av) *ntr++ = 0; /* now at the beginning of SHA1 */ if (type != type_from_string(ptr)) die("object type %s mismatch (%s)", ptr, typename(type)); - ntr += 41; /* at the beginning of name */ - if (line_termination && ntr[0] == '"') - path = unquote_c_style(ntr, NULL); - else - path = ntr; - append_to_tree(mode, sha1, path); + path = ntr + 41; /* at the beginning of name */ + if (line_termination && path[0] == '"') { + strbuf_reset(&p_uq); + if (unquote_c_style(&p_uq, path, NULL)) { + die("invalid quoting"); + } + path = p_uq.buf; + } - if (path != ntr) - free(path); + append_to_tree(mode, sha1, path); } + strbuf_release(&p_uq); strbuf_release(&sb); + write_tree(sha1); puts(sha1_to_hex(sha1)); exit(0); diff --git a/quote.c b/quote.c index d88bf75..7771c9c 100644 --- a/quote.c +++ b/quote.c @@ -239,73 +239,71 @@ int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq) /* * C-style name unquoting. * - * Quoted should point at the opening double quote. Returns - * an allocated memory that holds unquoted name, which the caller - * should free when done. Updates endp pointer to point at - * one past the ending double quote if given. + * Quoted should point at the opening double quote. + * + Returns 0 if it was able to unquote the string properly, and appends the + * result in the strbuf `sb'. + * + Returns -1 in case of error, and doesn't touch the strbuf. Though note + * that this function will allocate memory in the strbuf, so calling + * strbuf_release is mandatory whichever result unquote_c_style returns. + * + * Updates endp pointer to point at one past the ending double quote if given. */ - -char *unquote_c_style(const char *quoted, const char **endp) +int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp) { - const char *sp; - char *name = NULL, *outp = NULL; - int count = 0, ch, ac; - -#undef EMIT -#define EMIT(c) (outp ? (*outp++ = (c)) : (count++)) + size_t oldlen = sb->len, len; + int ch, ac; if (*quoted++ != '"') - return NULL; + return -1; + + for (;;) { + len = strcspn(quoted, "\"\\"); + strbuf_add(sb, quoted, len); + quoted += len; - while (1) { - /* first pass counts and allocates, second pass fills */ - for (sp = quoted; (ch = *sp++) != '"'; ) { - if (ch == '\\') { - switch (ch = *sp++) { - case 'a': ch = '\a'; break; - case 'b': ch = '\b'; break; - case 'f': ch = '\f'; break; - case 'n': ch = '\n'; break; - case 'r': ch = '\r'; break; - case 't': ch = '\t'; break; - case 'v': ch = '\v'; break; - - case '\\': case '"': - break; /* verbatim */ - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - /* octal */ + switch (*quoted++) { + case '"': + if (endp) + *endp = quoted + 1; + return 0; + case '\\': + break; + default: + goto error; + } + + switch ((ch = *quoted++)) { + case 'a': ch = '\a'; break; + case 'b': ch = '\b'; break; + case 'f': ch = '\f'; break; + case 'n': ch = '\n'; break; + case 'r': ch = '\r'; break; + case 't': ch = '\t'; break; + case 'v': ch = '\v'; break; + + case '\\': case '"': + break; /* verbatim */ + + /* octal values with first digit over 4 overflow */ + case '0': case '1': case '2': case '3': ac = ((ch - '0') << 6); - if ((ch = *sp++) < '0' || '7' < ch) - return NULL; + if ((ch = *quoted++) < '0' || '7' < ch) + goto error; ac |= ((ch - '0') << 3); - if ((ch = *sp++) < '0' || '7' < ch) - return NULL; + if ((ch = *quoted++) < '0' || '7' < ch) + goto error; ac |= (ch - '0'); ch = ac; break; default: - return NULL; /* malformed */ - } + goto error; } - EMIT(ch); + strbuf_addch(sb, ch); } - if (name) { - *outp = 0; - if (endp) - *endp = sp; - return name; - } - outp = name = xmalloc(count + 1); - } + error: + strbuf_setlen(sb, oldlen); + return -1; } void write_name_quoted(const char *prefix, int prefix_len, diff --git a/quote.h b/quote.h index 8a59cc5..f48a3fc 100644 --- a/quote.h +++ b/quote.h @@ -43,9 +43,9 @@ extern int add_to_string(char **ptrp, int *sizep, const char *str, int quote); */ extern char *sq_dequote(char *); +extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp); extern int quote_c_style(const char *name, char *outbuf, FILE *outfp, int nodq); -extern char *unquote_c_style(const char *quoted, const char **endp); extern void write_name_quoted(const char *prefix, int prefix_len, const char *name, int quote, FILE *out); -- cgit v0.10.2-6-g49f6 From 663af3422a648e87945e4d8c0cc3e13671f2bbde Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 20 Sep 2007 00:42:15 +0200 Subject: Full rework of quote_c_style and write_name_quoted. * quote_c_style works on a strbuf instead of a wild buffer. * quote_c_style is now clever enough to not add double quotes if not needed. * write_name_quoted inherits those advantages, but also take a different set of arguments. Now instead of asking for quotes or not, you pass a "terminator". If it's \0 then we assume you don't want to escape, else C escaping is performed. In any case, the terminator is also appended to the stream. It also no longer takes the prefix/prefix_len arguments, as it's seldomly used, and makes some optimizations harder. * write_name_quotedpfx is created to work like write_name_quoted and take the prefix/prefix_len arguments. Thanks to those API changes, diff.c has somehow lost weight, thanks to the removal of functions that were wrappers around the old write_name_quoted trying to give it a semantics like the new one, but performing a lot of allocations for this goal. Now we always write directly to the stream, no intermediate allocation is performed. As a side effect of the refactor in builtin-apply.c, the length of the bar graphs in diffstats are not affected anymore by the fact that the path was clipped. Signed-off-by: Pierre Habouzit diff --git a/builtin-apply.c b/builtin-apply.c index 1cf68ed..01c9d60 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -163,15 +163,14 @@ static void say_patch_name(FILE *output, const char *pre, struct patch *patch, c fputs(pre, output); if (patch->old_name && patch->new_name && strcmp(patch->old_name, patch->new_name)) { - write_name_quoted(NULL, 0, patch->old_name, 1, output); + quote_c_style(patch->old_name, NULL, output, 0); fputs(" => ", output); - write_name_quoted(NULL, 0, patch->new_name, 1, output); - } - else { + quote_c_style(patch->new_name, NULL, output, 0); + } else { const char *n = patch->new_name; if (!n) n = patch->old_name; - write_name_quoted(NULL, 0, n, 1, output); + quote_c_style(n, NULL, output, 0); } fputs(post, output); } @@ -1379,61 +1378,50 @@ static const char minuses[]= "-------------------------------------------------- static void show_stats(struct patch *patch) { - const char *prefix = ""; - char *name = patch->new_name; - char *qname = NULL; - int len, max, add, del, total; - - if (!name) - name = patch->old_name; + struct strbuf qname; + char *cp = patch->new_name ? patch->new_name : patch->old_name; + int max, add, del; - if (0 < (len = quote_c_style(name, NULL, NULL, 0))) { - qname = xmalloc(len + 1); - quote_c_style(name, qname, NULL, 0); - name = qname; - } + strbuf_init(&qname, 0); + quote_c_style(cp, &qname, NULL, 0); /* * "scale" the filename */ - len = strlen(name); max = max_len; if (max > 50) max = 50; - if (len > max) { - char *slash; - prefix = "..."; - max -= 3; - name += len - max; - slash = strchr(name, '/'); - if (slash) - name = slash; + + if (qname.len > max) { + cp = strchr(qname.buf + qname.len + 3 - max, '/'); + if (!cp) + cp = qname.buf + qname.len + 3 - max; + strbuf_splice(&qname, 0, cp - qname.buf, "...", 3); + } + + if (patch->is_binary) { + printf(" %-*s | Bin\n", max, qname.buf); + strbuf_release(&qname); + return; } - len = max; + + printf(" %-*s |", max, qname.buf); + strbuf_release(&qname); /* * scale the add/delete */ - max = max_change; - if (max + len > 70) - max = 70 - len; - + max = max + max_change > 70 ? 70 - max : max_change; add = patch->lines_added; del = patch->lines_deleted; - total = add + del; if (max_change > 0) { - total = (total * max + max_change / 2) / max_change; + int total = ((add + del) * max + max_change / 2) / max_change; add = (add * max + max_change / 2) / max_change; del = total - add; } - if (patch->is_binary) - printf(" %s%-*s | Bin\n", prefix, len, name); - else - printf(" %s%-*s |%5d %.*s%.*s\n", prefix, - len, name, patch->lines_added + patch->lines_deleted, - add, pluses, del, minuses); - free(qname); + printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted, + add, pluses, del, minuses); } static int read_old_data(struct stat *st, const char *path, struct strbuf *buf) @@ -2228,13 +2216,8 @@ static void numstat_patch_list(struct patch *patch) if (patch->is_binary) printf("-\t-\t"); else - printf("%d\t%d\t", - patch->lines_added, patch->lines_deleted); - if (line_termination && quote_c_style(name, NULL, NULL, 0)) - quote_c_style(name, NULL, stdout, 0); - else - fputs(name, stdout); - putchar(line_termination); + printf("%d\t%d\t", patch->lines_added, patch->lines_deleted); + write_name_quoted(name, stdout, line_termination); } } diff --git a/builtin-blame.c b/builtin-blame.c index e364b6c..16c0ca8 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -1430,8 +1430,7 @@ static void get_commit_info(struct commit *commit, static void write_filename_info(const char *path) { printf("filename "); - write_name_quoted(NULL, 0, path, 1, stdout); - putchar('\n'); + write_name_quoted(path, stdout, '\n'); } /* diff --git a/builtin-check-attr.c b/builtin-check-attr.c index d949733..6afdfa1 100644 --- a/builtin-check-attr.c +++ b/builtin-check-attr.c @@ -56,7 +56,7 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix) else if (ATTR_UNSET(value)) value = "unspecified"; - write_name_quoted("", 0, argv[i], 1, stdout); + quote_c_style(argv[i], NULL, stdout, 0); printf(": %s: %s\n", argv[j+1], value); } } diff --git a/builtin-checkout-index.c b/builtin-checkout-index.c index e6264c4..70d619d 100644 --- a/builtin-checkout-index.c +++ b/builtin-checkout-index.c @@ -66,9 +66,7 @@ static void write_tempfile_record(const char *name, int prefix_length) fputs(topath[checkout_stage], stdout); putchar('\t'); - write_name_quoted("", 0, name + prefix_length, - line_termination, stdout); - putchar(line_termination); + write_name_quoted(name + prefix_length, stdout, line_termination); for (i = 0; i < 4; i++) { topath[i][0] = 0; diff --git a/builtin-ls-files.c b/builtin-ls-files.c index 48dd3f7..2e6f43b 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -84,8 +84,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) return; fputs(tag, stdout); - write_name_quoted("", 0, ent->name + offset, line_terminator, stdout); - putchar(line_terminator); + write_name_quoted(ent->name + offset, stdout, line_terminator); } static void show_other_files(struct dir_struct *dir) @@ -208,21 +207,15 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce) if (!show_stage) { fputs(tag, stdout); - write_name_quoted("", 0, ce->name + offset, - line_terminator, stdout); - putchar(line_terminator); - } - else { + } else { printf("%s%06o %s %d\t", tag, ntohl(ce->ce_mode), abbrev ? find_unique_abbrev(ce->sha1,abbrev) : sha1_to_hex(ce->sha1), ce_stage(ce)); - write_name_quoted("", 0, ce->name + offset, - line_terminator, stdout); - putchar(line_terminator); } + write_name_quoted(ce->name + offset, stdout, line_terminator); } static void show_files(struct dir_struct *dir, const char *prefix) diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c index cb4be4f..7abe333 100644 --- a/builtin-ls-tree.c +++ b/builtin-ls-tree.c @@ -112,10 +112,8 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen, abbrev ? find_unique_abbrev(sha1, abbrev) : sha1_to_hex(sha1)); } - write_name_quoted(base + chomp_prefix, baselen - chomp_prefix, - pathname, - line_termination, stdout); - putchar(line_termination); + write_name_quotedpfx(base + chomp_prefix, baselen - chomp_prefix, + pathname, stdout, line_termination); return retval; } diff --git a/combine-diff.c b/combine-diff.c index ef62234..fe5a2a1 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -650,10 +650,7 @@ static void dump_quoted_path(const char *prefix, const char *path, const char *c_meta, const char *c_reset) { printf("%s%s", c_meta, prefix); - if (quote_c_style(path, NULL, NULL, 0)) - quote_c_style(path, NULL, stdout, 0); - else - printf("%s", path); + quote_c_style(path, NULL, stdout, 0); printf("%s\n", c_reset); } @@ -900,16 +897,7 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct re putchar(inter_name_termination); } - if (line_termination) { - if (quote_c_style(p->path, NULL, NULL, 0)) - quote_c_style(p->path, NULL, stdout, 0); - else - printf("%s", p->path); - putchar(line_termination); - } - else { - printf("%s%c", p->path, line_termination); - } + write_name_quoted(p->path, stdout, line_termination); } void show_combined_diff(struct combine_diff_path *p, diff --git a/diff.c b/diff.c index 2216d75..fb6d077 100644 --- a/diff.c +++ b/diff.c @@ -181,44 +181,23 @@ int git_diff_ui_config(const char *var, const char *value) return git_default_config(var, value); } -static char *quote_one(const char *str) -{ - int needlen; - char *xp; - - if (!str) - return NULL; - needlen = quote_c_style(str, NULL, NULL, 0); - if (!needlen) - return xstrdup(str); - xp = xmalloc(needlen + 1); - quote_c_style(str, xp, NULL, 0); - return xp; -} - static char *quote_two(const char *one, const char *two) { int need_one = quote_c_style(one, NULL, NULL, 1); int need_two = quote_c_style(two, NULL, NULL, 1); - char *xp; + struct strbuf res; + strbuf_init(&res, 0); if (need_one + need_two) { - if (!need_one) need_one = strlen(one); - if (!need_two) need_one = strlen(two); - - xp = xmalloc(need_one + need_two + 3); - xp[0] = '"'; - quote_c_style(one, xp + 1, NULL, 1); - quote_c_style(two, xp + need_one + 1, NULL, 1); - strcpy(xp + need_one + need_two + 1, "\""); - return xp; + strbuf_addch(&res, '"'); + quote_c_style(one, &res, NULL, 1); + quote_c_style(two, &res, NULL, 1); + strbuf_addch(&res, '"'); + } else { + strbuf_addstr(&res, one); + strbuf_addstr(&res, two); } - need_one = strlen(one); - need_two = strlen(two); - xp = xmalloc(need_one + need_two + 1); - strcpy(xp, one); - strcpy(xp + need_one, two); - return xp; + return res.buf; } static const char *external_diff(void) @@ -670,27 +649,20 @@ static char *pprint_rename(const char *a, const char *b) { const char *old = a; const char *new = b; - char *name = NULL; + struct strbuf name; int pfx_length, sfx_length; int len_a = strlen(a); int len_b = strlen(b); + int a_midlen, b_midlen; int qlen_a = quote_c_style(a, NULL, NULL, 0); int qlen_b = quote_c_style(b, NULL, NULL, 0); + strbuf_init(&name, 0); if (qlen_a || qlen_b) { - if (qlen_a) len_a = qlen_a; - if (qlen_b) len_b = qlen_b; - name = xmalloc( len_a + len_b + 5 ); - if (qlen_a) - quote_c_style(a, name, NULL, 0); - else - memcpy(name, a, len_a); - memcpy(name + len_a, " => ", 4); - if (qlen_b) - quote_c_style(b, name + len_a + 4, NULL, 0); - else - memcpy(name + len_a + 4, b, len_b + 1); - return name; + quote_c_style(a, &name, NULL, 0); + strbuf_addstr(&name, " => "); + quote_c_style(b, &name, NULL, 0); + return name.buf; } /* Find common prefix */ @@ -719,24 +691,26 @@ static char *pprint_rename(const char *a, const char *b) * pfx{sfx-a => sfx-b} * name-a => name-b */ + a_midlen = len_a - pfx_length - sfx_length; + b_midlen = len_b - pfx_length - sfx_length; + if (a_midlen < 0) + a_midlen = 0; + if (b_midlen < 0) + b_midlen = 0; + + strbuf_grow(&name, pfx_length + a_midlen + b_midlen + sfx_length + 7); if (pfx_length + sfx_length) { - int a_midlen = len_a - pfx_length - sfx_length; - int b_midlen = len_b - pfx_length - sfx_length; - if (a_midlen < 0) a_midlen = 0; - if (b_midlen < 0) b_midlen = 0; - - name = xmalloc(pfx_length + a_midlen + b_midlen + sfx_length + 7); - sprintf(name, "%.*s{%.*s => %.*s}%s", - pfx_length, a, - a_midlen, a + pfx_length, - b_midlen, b + pfx_length, - a + len_a - sfx_length); + strbuf_add(&name, a, pfx_length); + strbuf_addch(&name, '{'); } - else { - name = xmalloc(len_a + len_b + 5); - sprintf(name, "%s => %s", a, b); + strbuf_add(&name, a + pfx_length, a_midlen); + strbuf_addstr(&name, " => "); + strbuf_add(&name, b + pfx_length, b_midlen); + if (pfx_length + sfx_length) { + strbuf_addch(&name, '}'); + strbuf_add(&name, a + len_a - sfx_length, sfx_length); } - return name; + return name.buf; } struct diffstat_t { @@ -849,12 +823,13 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) int change = file->added + file->deleted; if (!file->is_renamed) { /* renames are already quoted by pprint_rename */ - len = quote_c_style(file->name, NULL, NULL, 0); - if (len) { - char *qname = xmalloc(len + 1); - quote_c_style(file->name, qname, NULL, 0); + struct strbuf buf; + strbuf_init(&buf, 0); + if (quote_c_style(file->name, &buf, NULL, 0)) { free(file->name); - file->name = qname; + file->name = buf.buf; + } else { + strbuf_release(&buf); } } @@ -992,12 +967,12 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options) printf("-\t-\t"); else printf("%d\t%d\t", file->added, file->deleted); - if (options->line_termination && !file->is_renamed && - quote_c_style(file->name, NULL, NULL, 0)) - quote_c_style(file->name, NULL, stdout, 0); - else + if (!file->is_renamed) { + write_name_quoted(file->name, stdout, options->line_termination); + } else { fputs(file->name, stdout); - putchar(options->line_termination); + putchar(options->line_termination); + } } } @@ -1941,50 +1916,46 @@ static int similarity_index(struct diff_filepair *p) static void run_diff(struct diff_filepair *p, struct diff_options *o) { const char *pgm = external_diff(); - char msg[PATH_MAX*2+300], *xfrm_msg; - struct diff_filespec *one; - struct diff_filespec *two; + struct strbuf msg; + char *xfrm_msg; + struct diff_filespec *one = p->one; + struct diff_filespec *two = p->two; const char *name; const char *other; - char *name_munged, *other_munged; int complete_rewrite = 0; - int len; + if (DIFF_PAIR_UNMERGED(p)) { - /* unmerged */ run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0); return; } - name = p->one->path; + name = p->one->path; other = (strcmp(name, p->two->path) ? p->two->path : NULL); - name_munged = quote_one(name); - other_munged = quote_one(other); - one = p->one; two = p->two; - diff_fill_sha1_info(one); diff_fill_sha1_info(two); - len = 0; + strbuf_init(&msg, PATH_MAX * 2 + 300); switch (p->status) { case DIFF_STATUS_COPIED: - len += snprintf(msg + len, sizeof(msg) - len, - "similarity index %d%%\n" - "copy from %s\n" - "copy to %s\n", - similarity_index(p), name_munged, other_munged); + strbuf_addf(&msg, "similarity index %d%%", similarity_index(p)); + strbuf_addstr(&msg, "\ncopy from "); + quote_c_style(name, &msg, NULL, 0); + strbuf_addstr(&msg, "\ncopy to "); + quote_c_style(other, &msg, NULL, 0); + strbuf_addch(&msg, '\n'); break; case DIFF_STATUS_RENAMED: - len += snprintf(msg + len, sizeof(msg) - len, - "similarity index %d%%\n" - "rename from %s\n" - "rename to %s\n", - similarity_index(p), name_munged, other_munged); + strbuf_addf(&msg, "similarity index %d%%", similarity_index(p)); + strbuf_addstr(&msg, "\nrename from "); + quote_c_style(name, &msg, NULL, 0); + strbuf_addstr(&msg, "\nrename to "); + quote_c_style(other, &msg, NULL, 0); + strbuf_addch(&msg, '\n'); break; case DIFF_STATUS_MODIFIED: if (p->score) { - len += snprintf(msg + len, sizeof(msg) - len, - "dissimilarity index %d%%\n", + strbuf_addf(&msg, "dissimilarity index %d%%\n", similarity_index(p)); complete_rewrite = 1; break; @@ -2004,19 +1975,17 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two))) abbrev = 40; } - len += snprintf(msg + len, sizeof(msg) - len, - "index %.*s..%.*s", + strbuf_addf(&msg, "index %.*s..%.*s", abbrev, sha1_to_hex(one->sha1), abbrev, sha1_to_hex(two->sha1)); if (one->mode == two->mode) - len += snprintf(msg + len, sizeof(msg) - len, - " %06o", one->mode); - len += snprintf(msg + len, sizeof(msg) - len, "\n"); + strbuf_addf(&msg, " %06o", one->mode); + strbuf_addch(&msg, '\n'); } - if (len) - msg[--len] = 0; - xfrm_msg = len ? msg : NULL; + if (msg.len) + strbuf_setlen(&msg, msg.len - 1); + xfrm_msg = msg.len ? msg.buf : NULL; if (!pgm && DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) && @@ -2035,8 +2004,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o, complete_rewrite); - free(name_munged); - free(other_munged); + strbuf_release(&msg); } static void run_diffstat(struct diff_filepair *p, struct diff_options *o, @@ -2492,72 +2460,30 @@ const char *diff_unique_abbrev(const unsigned char *sha1, int len) return sha1_to_hex(sha1); } -static void diff_flush_raw(struct diff_filepair *p, - struct diff_options *options) +static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt) { - int two_paths; - char status[10]; - int abbrev = options->abbrev; - const char *path_one, *path_two; - int inter_name_termination = '\t'; - int line_termination = options->line_termination; - - if (!line_termination) - inter_name_termination = 0; + int line_termination = opt->line_termination; + int inter_name_termination = line_termination ? '\t' : '\0'; - path_one = p->one->path; - path_two = p->two->path; - if (line_termination) { - path_one = quote_one(path_one); - path_two = quote_one(path_two); + if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) { + printf(":%06o %06o %s ", p->one->mode, p->two->mode, + diff_unique_abbrev(p->one->sha1, opt->abbrev)); + printf("%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev)); } - - if (p->score) - sprintf(status, "%c%03d", p->status, similarity_index(p)); - else { - status[0] = p->status; - status[1] = 0; - } - switch (p->status) { - case DIFF_STATUS_COPIED: - case DIFF_STATUS_RENAMED: - two_paths = 1; - break; - case DIFF_STATUS_ADDED: - case DIFF_STATUS_DELETED: - two_paths = 0; - break; - default: - two_paths = 0; - break; - } - if (!(options->output_format & DIFF_FORMAT_NAME_STATUS)) { - printf(":%06o %06o %s ", - p->one->mode, p->two->mode, - diff_unique_abbrev(p->one->sha1, abbrev)); - printf("%s ", - diff_unique_abbrev(p->two->sha1, abbrev)); + if (p->score) { + printf("%c%03d%c", p->status, similarity_index(p), + inter_name_termination); + } else { + printf("%c%c", p->status, inter_name_termination); } - printf("%s%c%s", status, inter_name_termination, - two_paths || p->one->mode ? path_one : path_two); - if (two_paths) - printf("%c%s", inter_name_termination, path_two); - putchar(line_termination); - if (path_one != p->one->path) - free((void*)path_one); - if (path_two != p->two->path) - free((void*)path_two); -} -static void diff_flush_name(struct diff_filepair *p, struct diff_options *opt) -{ - char *path = p->two->path; - - if (opt->line_termination) - path = quote_one(p->two->path); - printf("%s%c", path, opt->line_termination); - if (p->two->path != path) - free(path); + if (p->status == DIFF_STATUS_COPIED || p->status == DIFF_STATUS_RENAMED) { + write_name_quoted(p->one->path, stdout, inter_name_termination); + write_name_quoted(p->two->path, stdout, line_termination); + } else { + const char *path = p->one->mode ? p->one->path : p->two->path; + write_name_quoted(path, stdout, line_termination); + } } int diff_unmodified_pair(struct diff_filepair *p) @@ -2567,14 +2493,11 @@ int diff_unmodified_pair(struct diff_filepair *p) * let transformers to produce diff_filepairs any way they want, * and filter and clean them up here before producing the output. */ - struct diff_filespec *one, *two; + struct diff_filespec *one = p->one, *two = p->two; if (DIFF_PAIR_UNMERGED(p)) return 0; /* unmerged is interesting */ - one = p->one; - two = p->two; - /* deletion, addition, mode or type change * and rename are all interesting. */ @@ -2763,32 +2686,27 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt) else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS)) diff_flush_raw(p, opt); else if (fmt & DIFF_FORMAT_NAME) - diff_flush_name(p, opt); + write_name_quoted(p->two->path, stdout, opt->line_termination); } static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs) { - char *name = quote_one(fs->path); if (fs->mode) - printf(" %s mode %06o %s\n", newdelete, fs->mode, name); + printf(" %s mode %06o ", newdelete, fs->mode); else - printf(" %s %s\n", newdelete, name); - free(name); + printf(" %s ", newdelete); + write_name_quoted(fs->path, stdout, '\n'); } static void show_mode_change(struct diff_filepair *p, int show_name) { if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) { + printf(" mode change %06o => %06o%c", p->one->mode, p->two->mode, + show_name ? ' ' : '\n'); if (show_name) { - char *name = quote_one(p->two->path); - printf(" mode change %06o => %06o %s\n", - p->one->mode, p->two->mode, name); - free(name); + write_name_quoted(p->two->path, stdout, '\n'); } - else - printf(" mode change %06o => %06o\n", - p->one->mode, p->two->mode); } } @@ -2818,12 +2736,11 @@ static void diff_summary(struct diff_filepair *p) break; default: if (p->score) { - char *name = quote_one(p->two->path); - printf(" rewrite %s (%d%%)\n", name, - similarity_index(p)); - free(name); - show_mode_change(p, 0); - } else show_mode_change(p, 1); + puts(" rewrite "); + write_name_quoted(p->two->path, stdout, ' '); + printf("(%d%%)\n", similarity_index(p)); + } + show_mode_change(p, !p->score); break; } } @@ -2837,14 +2754,14 @@ struct patch_id_t { static int remove_space(char *line, int len) { int i; - char *dst = line; - unsigned char c; + char *dst = line; + unsigned char c; - for (i = 0; i < len; i++) - if (!isspace((c = line[i]))) - *dst++ = c; + for (i = 0; i < len; i++) + if (!isspace((c = line[i]))) + *dst++ = c; - return dst - line; + return dst - line; } static void patch_id_consume(void *priv, char *line, unsigned long len) diff --git a/quote.c b/quote.c index 7771c9c..b1efe18 100644 --- a/quote.c +++ b/quote.c @@ -157,83 +157,143 @@ char *sq_dequote(char *arg) } } +/* 1 means: quote as octal + * 0 means: quote as octal if (quote_path_fully) + * -1 means: never quote + * c: quote as "\\c" + */ +#define X8(x) x, x, x, x, x, x, x, x +#define X16(x) X8(x), X8(x) +static signed char const sq_lookup[256] = { + /* 0 1 2 3 4 5 6 7 */ + /* 0x00 */ 1, 1, 1, 1, 1, 1, 1, 'a', + /* 0x08 */ 'b', 't', 'n', 'v', 'f', 'r', 1, 1, + /* 0x10 */ X16(1), + /* 0x20 */ -1, -1, '"', -1, -1, -1, -1, -1, + /* 0x28 */ X16(-1), X16(-1), X16(-1), + /* 0x58 */ -1, -1, -1, -1,'\\', -1, -1, -1, + /* 0x60 */ X16(-1), X8(-1), + /* 0x78 */ -1, -1, -1, -1, -1, -1, -1, 1, + /* 0x80 */ /* set to 0 */ +}; + +static inline int sq_must_quote(char c) { + return sq_lookup[(unsigned char)c] + quote_path_fully > 0; +} + +/* returns the longest prefix not needing a quote up to maxlen if positive. + This stops at the first \0 because it's marked as a character needing an + escape */ +static size_t next_quote_pos(const char *s, ssize_t maxlen) +{ + size_t len; + if (maxlen < 0) { + for (len = 0; !sq_must_quote(s[len]); len++); + } else { + for (len = 0; len < maxlen && !sq_must_quote(s[len]); len++); + } + return len; +} + /* * C-style name quoting. * - * Does one of three things: - * - * (1) if outbuf and outfp are both NULL, inspect the input name and - * counts the number of bytes that are needed to hold c_style - * quoted version of name, counting the double quotes around - * it but not terminating NUL, and returns it. However, if name - * does not need c_style quoting, it returns 0. - * - * (2) if outbuf is not NULL, it must point at a buffer large enough - * to hold the c_style quoted version of name, enclosing double - * quotes, and terminating NUL. Fills outbuf with c_style quoted - * version of name enclosed in double-quote pair. Return value - * is undefined. + * (1) if sb and fp are both NULL, inspect the input name and counts the + * number of bytes that are needed to hold c_style quoted version of name, + * counting the double quotes around it but not terminating NUL, and + * returns it. + * However, if name does not need c_style quoting, it returns 0. * - * (3) if outfp is not NULL, outputs c_style quoted version of name, - * but not enclosed in double-quote pair. Return value is undefined. + * (2) if sb or fp are not NULL, it emits the c_style quoted version + * of name, enclosed with double quotes if asked and needed only. + * Return value is the same as in (1). */ - -static int quote_c_style_counted(const char *name, int namelen, - char *outbuf, FILE *outfp, int no_dq) +static size_t quote_c_style_counted(const char *name, ssize_t maxlen, + struct strbuf *sb, FILE *fp, int no_dq) { #undef EMIT -#define EMIT(c) \ - (outbuf ? (*outbuf++ = (c)) : outfp ? fputc(c, outfp) : (count++)) - -#define EMITQ() EMIT('\\') +#define EMIT(c) \ + do { \ + if (sb) strbuf_addch(sb, (c)); \ + if (fp) fputc((c), fp); \ + count++; \ + } while (0) +#define EMITBUF(s, l) \ + do { \ + if (sb) strbuf_add(sb, (s), (l)); \ + if (fp) fwrite((s), (l), 1, fp); \ + count += (l); \ + } while (0) + + size_t len, count = 0; + const char *p = name; - const char *sp; - unsigned char ch; - int count = 0, needquote = 0; + for (;;) { + int ch; - if (!no_dq) - EMIT('"'); - for (sp = name; sp < name + namelen; sp++) { - ch = *sp; - if (!ch) + len = next_quote_pos(p, maxlen); + if (len == maxlen || !p[len]) break; - if ((ch < ' ') || (ch == '"') || (ch == '\\') || - (quote_path_fully && (ch >= 0177))) { - needquote = 1; - switch (ch) { - case '\a': EMITQ(); ch = 'a'; break; - case '\b': EMITQ(); ch = 'b'; break; - case '\f': EMITQ(); ch = 'f'; break; - case '\n': EMITQ(); ch = 'n'; break; - case '\r': EMITQ(); ch = 'r'; break; - case '\t': EMITQ(); ch = 't'; break; - case '\v': EMITQ(); ch = 'v'; break; - - case '\\': /* fallthru */ - case '"': EMITQ(); break; - default: - /* octal */ - EMITQ(); - EMIT(((ch >> 6) & 03) + '0'); - EMIT(((ch >> 3) & 07) + '0'); - ch = (ch & 07) + '0'; - break; - } + + if (!no_dq && p == name) + EMIT('"'); + + EMITBUF(p, len); + EMIT('\\'); + p += len; + ch = (unsigned char)*p++; + if (sq_lookup[ch] >= ' ') { + EMIT(sq_lookup[ch]); + } else { + EMIT(((ch >> 6) & 03) + '0'); + EMIT(((ch >> 3) & 07) + '0'); + EMIT(((ch >> 0) & 07) + '0'); } - EMIT(ch); } + + EMITBUF(p, len); + if (p == name) /* no ending quote needed */ + return 0; + if (!no_dq) EMIT('"'); - if (outbuf) - *outbuf = 0; + return count; +} - return needquote ? count : 0; +size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, int nodq) +{ + return quote_c_style_counted(name, -1, sb, fp, nodq); } -int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq) +void write_name_quoted(const char *name, FILE *fp, int terminator) { - int cnt = strlen(name); - return quote_c_style_counted(name, cnt, outbuf, outfp, no_dq); + if (terminator) { + quote_c_style(name, NULL, fp, 0); + } else { + fputs(name, fp); + } + fputc(terminator, fp); +} + +extern void write_name_quotedpfx(const char *pfx, size_t pfxlen, + const char *name, FILE *fp, int terminator) +{ + int needquote = 0; + + if (terminator) { + needquote = next_quote_pos(pfx, pfxlen) < pfxlen + || name[next_quote_pos(name, -1)]; + } + if (needquote) { + fputc('"', fp); + quote_c_style_counted(pfx, pfxlen, NULL, fp, 1); + quote_c_style(name, NULL, fp, 1); + fputc('"', fp); + } else { + fwrite(pfx, pfxlen, 1, fp); + fputs(name, fp); + } + fputc(terminator, fp); } /* @@ -306,37 +366,6 @@ int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp) return -1; } -void write_name_quoted(const char *prefix, int prefix_len, - const char *name, int quote, FILE *out) -{ - int needquote; - - if (!quote) { - no_quote: - if (prefix_len) - fprintf(out, "%.*s", prefix_len, prefix); - fputs(name, out); - return; - } - - needquote = 0; - if (prefix_len) - needquote = quote_c_style_counted(prefix, prefix_len, - NULL, NULL, 0); - if (!needquote) - needquote = quote_c_style(name, NULL, NULL, 0); - if (needquote) { - fputc('"', out); - if (prefix_len) - quote_c_style_counted(prefix, prefix_len, - NULL, out, 1); - quote_c_style(name, NULL, out, 1); - fputc('"', out); - } - else - goto no_quote; -} - /* quoting as a string literal for other languages */ void perl_quote_print(FILE *stream, const char *src) diff --git a/quote.h b/quote.h index f48a3fc..2769f71 100644 --- a/quote.h +++ b/quote.h @@ -44,11 +44,11 @@ extern int add_to_string(char **ptrp, int *sizep, const char *str, int quote); extern char *sq_dequote(char *); extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp); -extern int quote_c_style(const char *name, char *outbuf, FILE *outfp, - int nodq); +extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq); -extern void write_name_quoted(const char *prefix, int prefix_len, - const char *name, int quote, FILE *out); +extern void write_name_quoted(const char *name, FILE *, int terminator); +extern void write_name_quotedpfx(const char *pfx, size_t pfxlen, + const char *name, FILE *, int terminator); /* quoting as a string literal for other languages */ extern void perl_quote_print(FILE *stream, const char *src); -- cgit v0.10.2-6-g49f6 From 400e58b74e701398bd7e51bd3f102f31cc45ee93 Mon Sep 17 00:00:00 2001 From: Sam Vilain Date: Fri, 21 Sep 2007 14:02:33 +1200 Subject: git-svn: fix test for trunk svn (commit message not needed) The 'svn mv -m "rename to thunk"' was a local operation, therefore not needing a commit message, it was silently ignored. Newer svn clients will instead raise an error. Signed-off-by: Sam Vilain Acked-by: Eric Wong Signed-off-by: Junio C Hamano diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh index d8f9cab..9eab945 100755 --- a/t/t9104-git-svn-follow-parent.sh +++ b/t/t9104-git-svn-follow-parent.sh @@ -19,8 +19,7 @@ test_expect_success 'initialize repo' " poke trunk/readme && svn commit -m 'another commit' && svn up && - svn mv -m 'rename to thunk' trunk thunk && - svn up && + svn mv trunk thunk && echo goodbye >> thunk/readme && poke thunk/readme && svn commit -m 'bye now' && -- cgit v0.10.2-6-g49f6 From d99c74e2913ab098953bd5b422b95f39e2dcfb55 Mon Sep 17 00:00:00 2001 From: Sam Vilain Date: Fri, 21 Sep 2007 14:02:34 +1200 Subject: git-svn: fix test for trunk svn (transaction out of date) Older svn clients did not raise a 'transaction out of date' error here, but trunk does - so 'svn up'. Signed-off-by: Sam Vilain Acked-by: Eric Wong Signed-off-by: Junio C Hamano diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh index 622ea1c..5aac644 100755 --- a/t/t9101-git-svn-props.sh +++ b/t/t9101-git-svn-props.sh @@ -140,6 +140,7 @@ test_expect_success 'test show-ignore' " cd test_wc && mkdir -p deeply/nested/directory && svn add deeply && + svn up && svn propset -R svn:ignore 'no-such-file*' . svn commit -m 'propset svn:ignore' cd .. && -- cgit v0.10.2-6-g49f6 From ffab62681cf420abb87aabf3fd2fc96e000877e4 Mon Sep 17 00:00:00 2001 From: Sam Vilain Date: Fri, 21 Sep 2007 15:27:01 +1200 Subject: git-svn: handle changed svn command-line syntax Previously, if you passed a revision and a path to svn cp, it meant to look back at that revision and select that path. New behaviour is to get the path then go back to the revision (like other commands that accept @REV or -rREV do). The more consistent syntax is not supported by the old tools, so we have to try both in turn. Signed-off-by: Sam Vilain Acked-by: Eric Wong Signed-off-by: Junio C Hamano diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh index 9eab945..7ba7630 100755 --- a/t/t9104-git-svn-follow-parent.sh +++ b/t/t9104-git-svn-follow-parent.sh @@ -51,8 +51,10 @@ test_expect_success 'init and fetch from one svn-remote' " " test_expect_success 'follow deleted parent' " - svn cp -m 'resurrecting trunk as junk' \ - -r2 $svnrepo/trunk $svnrepo/junk && + (svn cp -m 'resurrecting trunk as junk' \ + $svnrepo/trunk@2 $svnrepo/junk || + svn cp -m 'resurrecting trunk as junk' \ + -r2 $svnrepo/trunk $svnrepo/junk) && git config --add svn-remote.svn.fetch \ junk:refs/remotes/svn/junk && git-svn fetch -i svn/thunk && -- cgit v0.10.2-6-g49f6 From 7a33bcbe802080f3a926e93d66b65ff7c5e8c5ed Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 20 Sep 2007 00:42:13 +0200 Subject: sq_quote_argv and add_to_string rework with strbuf's. * sq_quote_buf is made public, and works on a strbuf. * sq_quote_argv also works on a strbuf. * make sq_quote_argv take a "maxlen" argument to check the buffer won't grow too big. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/connect.c b/connect.c index 1653a0e..06d279e 100644 --- a/connect.c +++ b/connect.c @@ -577,16 +577,13 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags) if (pid < 0) die("unable to fork"); if (!pid) { - char command[MAX_CMD_LEN]; - char *posn = command; - int size = MAX_CMD_LEN; - int of = 0; + struct strbuf cmd; - of |= add_to_string(&posn, &size, prog, 0); - of |= add_to_string(&posn, &size, " ", 0); - of |= add_to_string(&posn, &size, path, 1); - - if (of) + strbuf_init(&cmd, MAX_CMD_LEN); + strbuf_addstr(&cmd, prog); + strbuf_addch(&cmd, ' '); + sq_quote_buf(&cmd, path); + if (cmd.len >= MAX_CMD_LEN) die("command line too long"); dup2(pipefd[1][0], 0); @@ -606,10 +603,10 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags) ssh_basename++; if (!port) - execlp(ssh, ssh_basename, host, command, NULL); + execlp(ssh, ssh_basename, host, cmd.buf, NULL); else execlp(ssh, ssh_basename, "-p", port, host, - command, NULL); + cmd.buf, NULL); } else { unsetenv(ALTERNATE_DB_ENVIRONMENT); @@ -618,7 +615,7 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags) unsetenv(GIT_WORK_TREE_ENVIRONMENT); unsetenv(GRAFT_ENVIRONMENT); unsetenv(INDEX_ENVIRONMENT); - execlp("sh", "sh", "-c", command, NULL); + execlp("sh", "sh", "-c", cmd.buf, NULL); } die("exec failed"); } diff --git a/git.c b/git.c index 56ae8cc..9eaca1d 100644 --- a/git.c +++ b/git.c @@ -187,19 +187,13 @@ static int handle_alias(int *argcp, const char ***argv) if (alias_string) { if (alias_string[0] == '!') { if (*argcp > 1) { - int i, sz = PATH_MAX; - char *s = xmalloc(sz), *new_alias = s; + struct strbuf buf; - add_to_string(&s, &sz, alias_string, 0); + strbuf_init(&buf, PATH_MAX); + strbuf_addstr(&buf, alias_string); + sq_quote_argv(&buf, (*argv) + 1, *argcp - 1, PATH_MAX); free(alias_string); - alias_string = new_alias; - for (i = 1; i < *argcp && - !add_to_string(&s, &sz, " ", 0) && - !add_to_string(&s, &sz, (*argv)[i], 1) - ; i++) - ; /* do nothing */ - if (!sz) - die("Too many or long arguments"); + alias_string = buf.buf; } trace_printf("trace: alias to shell cmd: %s => %s\n", alias_command, alias_string + 1); diff --git a/quote.c b/quote.c index b1efe18..800fd88 100644 --- a/quote.c +++ b/quote.c @@ -12,37 +12,31 @@ * a'b ==> a'\''b ==> 'a'\''b' * a!b ==> a'\!'b ==> 'a'\!'b' */ -#undef EMIT -#define EMIT(x) do { if (++len < n) *bp++ = (x); } while(0) - static inline int need_bs_quote(char c) { return (c == '\'' || c == '!'); } -static size_t sq_quote_buf(char *dst, size_t n, const char *src) +void sq_quote_buf(struct strbuf *dst, const char *src) { - char c; - char *bp = dst; - size_t len = 0; - - EMIT('\''); - while ((c = *src++)) { - if (need_bs_quote(c)) { - EMIT('\''); - EMIT('\\'); - EMIT(c); - EMIT('\''); - } else { - EMIT(c); + char *to_free = NULL; + + if (dst->buf == src) + to_free = strbuf_detach(dst); + + strbuf_addch(dst, '\''); + while (*src) { + size_t len = strcspn(src, "'\\"); + strbuf_add(dst, src, len); + src += len; + while (need_bs_quote(*src)) { + strbuf_addstr(dst, "'\\"); + strbuf_addch(dst, *src++); + strbuf_addch(dst, '\''); } } - EMIT('\''); - - if ( n ) - *bp = 0; - - return len; + strbuf_addch(dst, '\''); + free(to_free); } void sq_quote_print(FILE *stream, const char *src) @@ -62,11 +56,10 @@ void sq_quote_print(FILE *stream, const char *src) fputc('\'', stream); } -char *sq_quote_argv(const char** argv, int count) +void sq_quote_argv(struct strbuf *dst, const char** argv, int count, + size_t maxlen) { - char *buf, *to; int i; - size_t len = 0; /* Count argv if needed. */ if (count < 0) { @@ -74,53 +67,14 @@ char *sq_quote_argv(const char** argv, int count) ; /* just counting */ } - /* Special case: no argv. */ - if (!count) - return xcalloc(1,1); - - /* Get destination buffer length. */ - for (i = 0; i < count; i++) - len += sq_quote_buf(NULL, 0, argv[i]) + 1; - - /* Alloc destination buffer. */ - to = buf = xmalloc(len + 1); - /* Copy into destination buffer. */ + strbuf_grow(dst, 32 * count); for (i = 0; i < count; ++i) { - *to++ = ' '; - to += sq_quote_buf(to, len, argv[i]); + strbuf_addch(dst, ' '); + sq_quote_buf(dst, argv[i]); + if (maxlen && dst->len > maxlen) + die("Too many or long arguments"); } - - return buf; -} - -/* - * Append a string to a string buffer, with or without shell quoting. - * Return true if the buffer overflowed. - */ -int add_to_string(char **ptrp, int *sizep, const char *str, int quote) -{ - char *p = *ptrp; - int size = *sizep; - int oc; - int err = 0; - - if (quote) - oc = sq_quote_buf(p, size, str); - else { - oc = strlen(str); - memcpy(p, str, (size <= oc) ? size - 1 : oc); - } - - if (size <= oc) { - err = 1; - oc = size - 1; - } - - *ptrp += oc; - **ptrp = '\0'; - *sizep -= oc; - return err; } char *sq_dequote(char *arg) diff --git a/quote.h b/quote.h index 2769f71..4287990 100644 --- a/quote.h +++ b/quote.h @@ -29,13 +29,10 @@ */ extern void sq_quote_print(FILE *stream, const char *src); -extern char *sq_quote_argv(const char** argv, int count); -/* - * Append a string to a string buffer, with or without shell quoting. - * Return true if the buffer overflowed. - */ -extern int add_to_string(char **ptrp, int *sizep, const char *str, int quote); +extern void sq_quote_buf(struct strbuf *, const char *src); +extern void sq_quote_argv(struct strbuf *, const char **argv, int count, + size_t maxlen); /* This unwraps what sq_quote() produces in place, but returns * NULL if the input does not look like what sq_quote would have diff --git a/rsh.c b/rsh.c index 5754a23..016d72e 100644 --- a/rsh.c +++ b/rsh.c @@ -10,12 +10,9 @@ int setup_connection(int *fd_in, int *fd_out, const char *remote_prog, char *host; char *path; int sv[2]; - char command[COMMAND_SIZE]; - char *posn; - int sizen; - int of; int i; pid_t pid; + struct strbuf cmd; if (!strcmp(url, "-")) { *fd_in = 0; @@ -36,24 +33,23 @@ int setup_connection(int *fd_in, int *fd_out, const char *remote_prog, if (!path) { return error("Bad URL: %s", url); } + /* $GIT_RSH "env GIT_DIR= " */ - sizen = COMMAND_SIZE; - posn = command; - of = 0; - of |= add_to_string(&posn, &sizen, "env ", 0); - of |= add_to_string(&posn, &sizen, GIT_DIR_ENVIRONMENT "=", 0); - of |= add_to_string(&posn, &sizen, path, 1); - of |= add_to_string(&posn, &sizen, " ", 0); - of |= add_to_string(&posn, &sizen, remote_prog, 1); + strbuf_init(&cmd, COMMAND_SIZE); + strbuf_addstr(&cmd, "env "); + strbuf_addstr(&cmd, GIT_DIR_ENVIRONMENT "="); + sq_quote_buf(&cmd, path); + strbuf_addch(&cmd, ' '); + sq_quote_buf(&cmd, remote_prog); - for ( i = 0 ; i < rmt_argc ; i++ ) { - of |= add_to_string(&posn, &sizen, " ", 0); - of |= add_to_string(&posn, &sizen, rmt_argv[i], 1); + for (i = 0 ; i < rmt_argc ; i++) { + strbuf_addch(&cmd, ' '); + sq_quote_buf(&cmd, rmt_argv[i]); } - of |= add_to_string(&posn, &sizen, " -", 0); + strbuf_addstr(&cmd, " -"); - if ( of ) + if (cmd.len >= COMMAND_SIZE) return error("Command line too long"); if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv)) @@ -74,7 +70,7 @@ int setup_connection(int *fd_in, int *fd_out, const char *remote_prog, close(sv[1]); dup2(sv[0], 0); dup2(sv[0], 1); - execlp(ssh, ssh_basename, host, command, NULL); + execlp(ssh, ssh_basename, host, cmd.buf, NULL); } close(sv[0]); *fd_in = sv[1]; diff --git a/trace.c b/trace.c index 91548a5..69fa05e 100644 --- a/trace.c +++ b/trace.c @@ -64,7 +64,7 @@ static const char err_msg[] = "Could not trace into fd given by " void trace_printf(const char *fmt, ...) { - char buf[8192]; + struct strbuf buf; va_list ap; int fd, len, need_close = 0; @@ -72,12 +72,22 @@ void trace_printf(const char *fmt, ...) if (!fd) return; + strbuf_init(&buf, 0); va_start(ap, fmt); - len = vsnprintf(buf, sizeof(buf), fmt, ap); + len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap); va_end(ap); - if (len >= sizeof(buf)) - die("unreasonnable trace length"); - write_or_whine_pipe(fd, buf, len, err_msg); + if (len >= strbuf_avail(&buf)) { + strbuf_grow(&buf, len - strbuf_avail(&buf) + 128); + va_start(ap, fmt); + len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap); + va_end(ap); + if (len >= strbuf_avail(&buf)) + die("broken vsnprintf"); + } + strbuf_setlen(&buf, len); + + write_or_whine_pipe(fd, buf.buf, buf.len, err_msg); + strbuf_release(&buf); if (need_close) close(fd); @@ -85,31 +95,32 @@ void trace_printf(const char *fmt, ...) void trace_argv_printf(const char **argv, int count, const char *fmt, ...) { - char buf[8192]; + struct strbuf buf; va_list ap; - char *argv_str; - size_t argv_len; int fd, len, need_close = 0; fd = get_trace_fd(&need_close); if (!fd) return; + strbuf_init(&buf, 0); va_start(ap, fmt); - len = vsnprintf(buf, sizeof(buf), fmt, ap); + len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap); va_end(ap); - if (len >= sizeof(buf)) - die("unreasonnable trace length"); - - /* Get the argv string. */ - argv_str = sq_quote_argv(argv, count); - argv_len = strlen(argv_str); - - write_or_whine_pipe(fd, buf, len, err_msg); - write_or_whine_pipe(fd, argv_str, argv_len, err_msg); - write_or_whine_pipe(fd, "\n", 1, err_msg); + if (len >= strbuf_avail(&buf)) { + strbuf_grow(&buf, len - strbuf_avail(&buf) + 128); + va_start(ap, fmt); + len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap); + va_end(ap); + if (len >= strbuf_avail(&buf)) + die("broken vsnprintf"); + } + strbuf_setlen(&buf, len); - free(argv_str); + sq_quote_argv(&buf, argv, count, 0); + strbuf_addch(&buf, '\n'); + write_or_whine_pipe(fd, buf.buf, buf.len, err_msg); + strbuf_release(&buf); if (need_close) close(fd); -- cgit v0.10.2-6-g49f6 From 8fceacee676ba9595a4dfdbe7b9726781bfd3ef9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 21 Sep 2007 12:52:30 -0700 Subject: Documentation/git-lost-found.txt: drop unnecessarily duplicated name. I only did this back when I wanted to make sure git-log and gitk work properly with non Occidental characters. There is really no reason to keep it around. Signed-off-by: Junio C Hamano diff --git a/Documentation/git-lost-found.txt b/Documentation/git-lost-found.txt index e48607f..bc73911 100644 --- a/Documentation/git-lost-found.txt +++ b/Documentation/git-lost-found.txt @@ -65,7 +65,7 @@ $ git rev-parse not-lost-anymore Author ------ -Written by Junio C Hamano 濱野 純 +Written by Junio C Hamano Documentation -------------- -- cgit v0.10.2-6-g49f6 From 1e6ab5de4fc4ae2902a09f18fe43249c4d012a35 Mon Sep 17 00:00:00 2001 From: Matt Kraai Date: Fri, 21 Sep 2007 06:43:44 -0700 Subject: Move the paragraph specifying where the .idx and .pack files should be Signed-off-by: Matt Kraai Signed-off-by: Junio C Hamano diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index f8a0be3..d18259d 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -25,16 +25,16 @@ is efficient to access. The packed archive format (.pack) is designed to be unpackable without having anything else, but for random access, accompanied with the pack index file (.idx). +Placing both in the pack/ subdirectory of $GIT_OBJECT_DIRECTORY (or +any of the directories on $GIT_ALTERNATE_OBJECT_DIRECTORIES) +enables git to read from such an archive. + 'git-unpack-objects' command can read the packed archive and expand the objects contained in the pack into "one-file one-object" format; this is typically done by the smart-pull commands when a pack is created on-the-fly for efficient network transport by their peers. -Placing both in the pack/ subdirectory of $GIT_OBJECT_DIRECTORY (or -any of the directories on $GIT_ALTERNATE_OBJECT_DIRECTORIES) -enables git to read from such an archive. - In a packed archive, an object is either stored as a compressed whole, or as a difference from some other object. The latter is often called a delta. -- cgit v0.10.2-6-g49f6 From 9c2d28c74e374174de01901238159b63a96a04ba Mon Sep 17 00:00:00 2001 From: Matt Kraai Date: Fri, 21 Sep 2007 07:37:18 -0700 Subject: Conjugate "search" correctly in the git-prune-packed man page. Signed-off-by: Matt Kraai Signed-off-by: Junio C Hamano diff --git a/Documentation/git-prune-packed.txt b/Documentation/git-prune-packed.txt index 3800edb..9f85f38 100644 --- a/Documentation/git-prune-packed.txt +++ b/Documentation/git-prune-packed.txt @@ -13,7 +13,7 @@ SYNOPSIS DESCRIPTION ----------- -This program search the `$GIT_OBJECT_DIR` for all objects that currently +This program searches the `$GIT_OBJECT_DIR` for all objects that currently exist in a pack file as well as the independent object directories. All such extra objects are removed. -- cgit v0.10.2-6-g49f6 From b9fc6ea9efdc988d851666d45d80076839d9c225 Mon Sep 17 00:00:00 2001 From: David Brown Date: Wed, 19 Sep 2007 13:12:48 -0700 Subject: Detect exec bit in more cases. git-p4 was missing the execute bit setting if the file had other attribute bits set. Acked-By: Simon Hausmann diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 55778c5..65c57ac 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -63,6 +63,14 @@ def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) +def isP4Exec(kind): + """Determine if a Perforce 'kind' should have execute permission + + 'p4 help filetypes' gives a list of the types. If it starts with 'x', + or x follows one of a few letters. Otherwise, if there is an 'x' after + a plus sign, it is also executable""" + return (re.search(r"(^[cku]?x)|\+.*x", kind) != None) + def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): cmd = "p4 -G %s" % cmd if verbose: @@ -916,7 +924,7 @@ class P4Sync(Command): data = file['data'] mode = "644" - if file["type"].startswith("x"): + if isP4Exec(file["type"]): mode = "755" elif file["type"] == "symlink": mode = "120000" -- cgit v0.10.2-6-g49f6 From fc74ecc12cd339c6d8abd1c363733e67eb50945c Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 9 Sep 2007 22:07:02 -0400 Subject: user-manual: don't assume refs are stored under .git/refs The scripts taken from Tony Luck's howto assume all refs can be found under .git/refs, but this is not necessarily true, especially since git-gc runs git-pack-refs. Also add a note warning of this in the chapter that introduces refs, and fix the same incorrect assumption in one other spot. Signed-off-by: J. Bruce Fields diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index ecb2bf9..cf02e14 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -369,6 +369,11 @@ shorthand: The full name is occasionally useful if, for example, there ever exists a tag and a branch with the same name. +(Newly created refs are actually stored in the .git/refs directory, +under the path given by their name. However, for efficiency reasons +they may also be packed together in a single file; see +gitlink:git-pack-refs[1]). + As another useful shortcut, the "HEAD" of a repository can be referred to just using the name of that repository. So, for example, "origin" is usually a shortcut for the HEAD branch in the repository "origin". @@ -2189,9 +2194,9 @@ test|release) git checkout $1 && git pull . origin ;; origin) - before=$(cat .git/refs/remotes/origin/master) + before=$(git rev-parse refs/remotes/origin/master) git fetch origin - after=$(cat .git/refs/remotes/origin/master) + after=$(git rev-parse refs/remotes/origin/master) if [ $before != $after ] then git log $before..$after | git shortlog @@ -2216,11 +2221,10 @@ usage() exit 1 } -if [ ! -f .git/refs/heads/"$1" ] -then +git show-ref -q --verify -- refs/heads/"$1" || { echo "Can't see branch <$1>" 1>&2 usage -fi +} case "$2" in test|release) @@ -2251,7 +2255,7 @@ then git log test..release fi -for branch in `ls .git/refs/heads` +for branch in `git show-ref --heads | sed 's|^.*/||'` do if [ $branch = test -o $branch = release ] then @@ -2946,7 +2950,7 @@ nLE/L9aUXdWeTFPron96DLA= See the gitlink:git-tag[1] command to learn how to create and verify tag objects. (Note that gitlink:git-tag[1] can also be used to create "lightweight tags", which are not tag objects at all, but just simple -references in .git/refs/tags/). +references whose names begin with "refs/tags/"). [[pack-files]] How git stores objects efficiently: pack files -- cgit v0.10.2-6-g49f6 From 38a457baae8202e43b092dbb71dca736f6e45600 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Thu, 20 Sep 2007 02:34:14 +0200 Subject: User Manual: add a chapter for submodules Signed-off-by: Michael Smith Signed-off-by: Miklos Vajna Signed-off-by: J. Bruce Fields diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index cf02e14..a085ca1 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -3159,6 +3159,208 @@ a tree which you are in the process of working on. If you blow the index away entirely, you generally haven't lost any information as long as you have the name of the tree that it described. +[[submodules]] +Submodules +========== + +This tutorial explains how to create and publish a repository with submodules +using the gitlink:git-submodule[1] command. + +Submodules maintain their own identity; the submodule support just stores the +submodule repository location and commit ID, so other developers who clone the +superproject can easily clone all the submodules at the same revision. + +To see how submodule support works, create (for example) four example +repositories that can be used later as a submodule: + +------------------------------------------------- +$ mkdir ~/git +$ cd ~/git +$ for i in a b c d +do + mkdir $i + cd $i + git init + echo "module $i" > $i.txt + git add $i.txt + git commit -m "Initial commit, submodule $i" + cd .. +done +------------------------------------------------- + +Now create the superproject and add all the submodules: + +------------------------------------------------- +$ mkdir super +$ cd super +$ git init +$ for i in a b c d +do + git submodule add ~/git/$i +done +------------------------------------------------- + +NOTE: Do not use local URLs here if you plan to publish your superproject! + +See what files `git submodule` created: + +------------------------------------------------- +$ ls -a +. .. .git .gitmodules a b c d +------------------------------------------------- + +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 adds the submodule's clone path to the `.gitmodules` 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 + committed. + +Commit the superproject: + +------------------------------------------------- +$ git commit -m "Add submodules a, b, c and d." +------------------------------------------------- + +Now clone the superproject: + +------------------------------------------------- +$ cd .. +$ git clone super cloned +$ cd cloned +------------------------------------------------- + +The submodule directories are there, but they're empty: + +------------------------------------------------- +$ ls -a a +. .. +$ git submodule status +-d266b9873ad50488163457f025db7cdd9683d88b a +-e81d457da15309b4fef4249aba9b50187999670d b +-c1536a972b9affea0f16e0680ba87332dc059146 c +-d96249ff5d57de5de093e6baff9e0aafa5276a74 d +------------------------------------------------- + +NOTE: The commit object names shown above would be different for you, but they +should match the HEAD commit object names of your repositories. You can check +it by running `git ls-remote ../a`. + +Pulling down the submodules is a two-step process. First run `git submodule +init` to add the submodule repository URLs to `.git/config`: + +------------------------------------------------- +$ git submodule init +------------------------------------------------- + +Now use `git submodule update` to clone the repositories and check out the +commits specified in the superproject: + +------------------------------------------------- +$ git submodule update +$ cd a +$ ls -a +. .. .git a.txt +------------------------------------------------- + +One major difference between `git submodule update` and `git submodule add` is +that `git submodule update` checks out a specific commit, rather than the tip +of a branch. It's like checking out a tag: the head is detached, so you're not +working on a branch. + +------------------------------------------------- +$ git branch +* (no branch) + master +------------------------------------------------- + +If you want to make a change within a submodule and you have a detached head, +then you should create or checkout a branch, make your changes, publish the +change within the submodule, and then update the superproject to reference the +new commit: + +------------------------------------------------- +$ git checkout master +------------------------------------------------- + +or + +------------------------------------------------- +$ git checkout -b fix-up +------------------------------------------------- + +then + +------------------------------------------------- +$ echo "adding a line again" >> a.txt +$ git commit -a -m "Updated the submodule from within the superproject." +$ git push +$ cd .. +$ git diff +diff --git a/a b/a +index d266b98..261dfac 160000 +--- a/a ++++ b/a +@@ -1 +1 @@ +-Subproject commit d266b9873ad50488163457f025db7cdd9683d88b ++Subproject commit 261dfac35cb99d380eb966e102c1197139f7fa24 +$ git add a +$ git commit -m "Updated submodule a." +$ git push +------------------------------------------------- + +You have to run `git submodule update` after `git pull` if you want to update +submodules, too. + +Pitfalls with submodules +------------------------ + +Always publish the submodule change before publishing the change to the +superproject that references it. If you forget to publish the submodule change, +others won't be able to clone the repository: + +------------------------------------------------- +$ cd ~/git/super/a +$ echo i added another line to this file >> a.txt +$ git commit -a -m "doing it wrong this time" +$ cd .. +$ git add a +$ git commit -m "Updated submodule a again." +$ git push +$ cd ~/git/cloned +$ git pull +$ git submodule update +error: pathspec '261dfac35cb99d380eb966e102c1197139f7fa24' did not match any file(s) known to git. +Did you forget to 'git add'? +Unable to checkout '261dfac35cb99d380eb966e102c1197139f7fa24' in submodule path 'a' +------------------------------------------------- + +You also should not rewind branches in a submodule beyond commits that were +ever recorded in any superproject. + +It's not safe to run `git submodule update` if you've made and committed +changes within a submodule without checking out a branch first. They will be +silently overwritten: + +------------------------------------------------- +$ cat a.txt +module a +$ echo line added from private2 >> a.txt +$ git commit -a -m "line added inside private2" +$ cd .. +$ git submodule update +Submodule path 'a': checked out 'd266b9873ad50488163457f025db7cdd9683d88b' +$ cd a +$ cat a.txt +module a +------------------------------------------------- + +NOTE: The changes are still visible in the submodule's reflog. + +This is not the case if you did not commit your changes. + [[low-level-operations]] Low-level git operations ======================== -- cgit v0.10.2-6-g49f6 From 822f7c7349d61f6075961ce42c1bd1a85cf999e5 Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Sun, 23 Sep 2007 22:42:08 +0200 Subject: Supplant the "while case ... break ;; esac" idiom A lot of shell scripts contained stuff starting with while case "$#" in 0) break ;; esac and similar. I consider breaking out of the condition instead of the body od the loop ugly, and the implied "true" value of the non-matching case is not really obvious to humans at first glance. It happens not to be obvious to some BSD shells, either, but that's because they are not POSIX-compliant. In most cases, this has been replaced by a straight condition using "test". "case" has the advantage of being faster than "test" on vintage shells where "test" is not a builtin. Since none of them is likely to run the git scripts, anyway, the added readability should be worth the change. A few loops have had their termination condition expressed differently. Signed-off-by: David Kastrup Signed-off-by: Junio C Hamano diff --git a/contrib/examples/git-gc.sh b/contrib/examples/git-gc.sh index 2ae235b..1597e9f 100755 --- a/contrib/examples/git-gc.sh +++ b/contrib/examples/git-gc.sh @@ -9,7 +9,7 @@ SUBDIRECTORY_OK=Yes . git-sh-setup no_prune=: -while case $# in 0) break ;; esac +while test $# != 0 do case "$1" in --prune) diff --git a/contrib/examples/git-tag.sh b/contrib/examples/git-tag.sh index 5ee3f50..ae7c531 100755 --- a/contrib/examples/git-tag.sh +++ b/contrib/examples/git-tag.sh @@ -14,7 +14,7 @@ username= list= verify= LINES=0 -while case "$#" in 0) break ;; esac +while test $# != 0 do case "$1" in -a) diff --git a/contrib/examples/git-verify-tag.sh b/contrib/examples/git-verify-tag.sh index 37b0023..0902a5c 100755 --- a/contrib/examples/git-verify-tag.sh +++ b/contrib/examples/git-verify-tag.sh @@ -5,7 +5,7 @@ SUBDIRECTORY_OK='Yes' . git-sh-setup verbose= -while case $# in 0) break;; esac +while test $# != 0 do case "$1" in -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) diff --git a/git-am.sh b/git-am.sh index 6809aa0..b66173c 100755 --- a/git-am.sh +++ b/git-am.sh @@ -109,7 +109,7 @@ dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= resolvemsg= resume= git_apply_opt= -while case "$#" in 0) break;; esac +while test $# != 0 do case "$1" in -d=*|--d=*|--do=*|--dot=*|--dote=*|--dotes=*|--dotest=*) diff --git a/git-clean.sh b/git-clean.sh index a5cfd9f..4491738 100755 --- a/git-clean.sh +++ b/git-clean.sh @@ -26,7 +26,7 @@ rmrf="rm -rf --" rm_refuse="echo Not removing" echo1="echo" -while case "$#" in 0) break ;; esac +while test $# != 0 do case "$1" in -d) diff --git a/git-commit.sh b/git-commit.sh index bb113e8..7a7a2cb 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -89,7 +89,7 @@ force_author= only_include_assumed= untracked_files= templatefile="`git config commit.template`" -while case "$#" in 0) break;; esac +while test $# != 0 do case "$1" in -F|--F|-f|--f|--fi|--fil|--file) diff --git a/git-fetch.sh b/git-fetch.sh index c3a2001..e44af2c 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -27,7 +27,7 @@ shallow_depth= no_progress= test -t 1 || no_progress=--no-progress quiet= -while case "$#" in 0) break ;; esac +while test $# != 0 do case "$1" in -a|--a|--ap|--app|--appe|--appen|--append) diff --git a/git-filter-branch.sh b/git-filter-branch.sh index a4b6577..a12f6c2 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -105,8 +105,9 @@ filter_tag_name= filter_subdir= orig_namespace=refs/original/ force= -while case "$#" in 0) usage;; esac +while : do + test $# = 0 && usage case "$1" in --) shift diff --git a/git-instaweb.sh b/git-instaweb.sh index b79c6b6..f5629e7 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -61,7 +61,7 @@ stop_httpd () { test -f "$fqgitdir/pid" && kill `cat "$fqgitdir/pid"` } -while case "$#" in 0) break ;; esac +while test $# != 0 do case "$1" in --stop|stop) diff --git a/git-ls-remote.sh b/git-ls-remote.sh index b7e5d04..d56cf92 100755 --- a/git-ls-remote.sh +++ b/git-ls-remote.sh @@ -13,7 +13,7 @@ die () { } exec= -while case "$#" in 0) break;; esac +while test $# != 0 do case "$1" in -h|--h|--he|--hea|--head|--heads) diff --git a/git-merge.sh b/git-merge.sh index 3a01db0..cde09d4 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -122,7 +122,7 @@ merge_name () { case "$#" in 0) usage ;; esac have_message= -while case "$#" in 0) break ;; esac +while test $# != 0 do case "$1" in -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\ diff --git a/git-mergetool.sh b/git-mergetool.sh index 47a8055..a0e44f7 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -268,7 +268,7 @@ merge_file () { cleanup_temp_files } -while case $# in 0) break ;; esac +while test $# != 0 do case "$1" in -t|--tool*) diff --git a/git-pull.sh b/git-pull.sh index 5e96d1f..c3f05f5 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -16,7 +16,7 @@ test -z "$(git ls-files -u)" || die "You are in the middle of a conflicted merge." strategy_args= no_summary= no_commit= squash= -while case "$#,$1" in 0) break ;; *,-*) ;; *) break ;; esac +while : do case "$1" in -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\ @@ -46,8 +46,8 @@ do -h|--h|--he|--hel|--help) usage ;; - -*) - # Pass thru anything that is meant for fetch. + *) + # Pass thru anything that may be meant for fetch. break ;; esac diff --git a/git-quiltimport.sh b/git-quiltimport.sh index 9de54d1..74a54d5 100755 --- a/git-quiltimport.sh +++ b/git-quiltimport.sh @@ -5,7 +5,7 @@ SUBDIRECTORY_ON=Yes dry_run="" quilt_author="" -while case "$#" in 0) break;; esac +while test $# != 0 do case "$1" in --au=*|--aut=*|--auth=*|--autho=*|--author=*) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index abc2b1c..2fa53fd 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -317,7 +317,7 @@ do_rest () { done } -while case $# in 0) break ;; esac +while test $# != 0 do case "$1" in --continue) diff --git a/git-rebase.sh b/git-rebase.sh index 3bd66b0..058fcac 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -122,15 +122,14 @@ finish_rb_merge () { is_interactive () { test -f "$dotest"/interactive || - while case $#,"$1" in 0,|*,-i|*,--interactive) break ;; esac - do + while :; do case $#,"$1" in 0,|*,-i|*,--interactive) break ;; esac shift done && test -n "$1" } is_interactive "$@" && exec git-rebase--interactive "$@" -while case "$#" in 0) break ;; esac +while test $# != 0 do case "$1" in --continue) diff --git a/git-repack.sh b/git-repack.sh index 156c5e8..0aae1a3 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -9,7 +9,7 @@ SUBDIRECTORY_OK='Yes' no_update_info= all_into_one= remove_redundant= local= quiet= no_reuse= extra= -while case "$#" in 0) break ;; esac +while test $# != 0 do case "$1" in -n) no_update_info=t ;; diff --git a/git-reset.sh b/git-reset.sh index 1dc606f..bafeb52 100755 --- a/git-reset.sh +++ b/git-reset.sh @@ -11,7 +11,7 @@ require_work_tree update= reset_type=--mixed unset rev -while case $# in 0) break ;; esac +while test $# != 0 do case "$1" in --mixed | --soft | --hard) diff --git a/git-submodule.sh b/git-submodule.sh index 3320998..673aa27 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -251,7 +251,7 @@ modules_list() done } -while case "$#" in 0) break ;; esac +while test $# != 0 do case "$1" in add) -- cgit v0.10.2-6-g49f6 From b019304886c2f5d702988e8f8bd86a1b621183c6 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 21 Sep 2007 18:48:45 -0700 Subject: git-svn: don't attempt to spawn pager if we don't want one Even though config_pager() unset the $pager variable, we were blindly calling exec() on it through run_pager(). Noticed-by: Chris Moore Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano diff --git a/git-svn.perl b/git-svn.perl index f818160..c015ea8 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3576,7 +3576,7 @@ sub config_pager { } sub run_pager { - return unless -t *STDOUT; + return unless -t *STDOUT && defined $pager; pipe my $rfd, my $wfd or return; defined(my $pid = fork) or ::fatal "Can't fork: $!\n"; if (!$pid) { -- cgit v0.10.2-6-g49f6 From a85d1b69172f397e815e1ce02db41e4b82b86162 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 24 Sep 2007 00:51:40 +0200 Subject: Add test-script for git-merge porcelain This test-script excercises the porcelainish aspects of git-merge, and does it thoroughly enough to detect a small bug already noticed by Junio: squashing an octopus generates a faulty .git/SQUASH_MSG. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh new file mode 100755 index 0000000..dec6ea2 --- /dev/null +++ b/t/t7600-merge.sh @@ -0,0 +1,344 @@ +#!/bin/sh +# +# Copyright (c) 2007 Lars Hjemli +# + +test_description='git-merge + +Testing basic merge operations/option parsing.' + +. ./test-lib.sh + +cat >file <file.1 <file.5 <file.9 <result.1 <result.1-5 <result.1-5-9 <msg.1-5 && + echo "Merge commit 'c2'; commit 'c3'" >msg.1-5-9 && + echo "Squashed commit of the following:" >squash.1 && + echo >>squash.1 && + git log --no-merges ^HEAD c1 >>squash.1 && + echo "Squashed commit of the following:" >squash.1-5 && + echo >>squash.1-5 && + git log --no-merges ^HEAD c2 >>squash.1-5 && + echo "Squashed commit of the following:" >squash.1-5-9 && + echo >>squash.1-5-9 && + git log --no-merges ^HEAD c2 c3 >>squash.1-5-9 +} + +verify_diff() { + if ! diff -u "$1" "$2" + then + echo "$3" + false + fi +} + +verify_merge() { + verify_diff "$2" "$1" "[OOPS] bad merge result" && + if test $(git ls-files -u | wc -l) -gt 0 + then + echo "[OOPS] unmerged files" + false + fi && + if ! git diff --exit-code + then + echo "[OOPS] working tree != index" + false + fi && + if test -n "$3" + then + git show -s --pretty=format:%s HEAD >msg.act && + verify_diff "$3" msg.act "[OOPS] bad merge message" + fi +} + +verify_head() { + if test "$1" != "$(git rev-parse HEAD)" + then + echo "[OOPS] HEAD != $1" + false + fi +} + +verify_parents() { + i=1 + while test $# -gt 0 + do + if test "$1" != "$(git rev-parse HEAD^$i)" + then + echo "[OOPS] HEAD^$i != $1" + return 1 + fi + i=$(expr $i + 1) + shift + done +} + +verify_mergeheads() { + i=1 + if ! test -f .git/MERGE_HEAD + then + echo "[OOPS] MERGE_HEAD is missing" + false + fi && + while test $# -gt 0 + do + head=$(head -n $i .git/MERGE_HEAD | tail -n 1) + if test "$1" != "$head" + then + echo "[OOPS] MERGE_HEAD $i != $1" + return 1 + fi + i=$(expr $i + 1) + shift + done +} + +verify_no_mergehead() { + if test -f .git/MERGE_HEAD + then + echo "[OOPS] MERGE_HEAD exists" + false + fi +} + + +test_expect_success 'setup' ' + git add file && + test_tick && + git commit -m "commit 0" && + git tag c0 && + c0=$(git rev-parse HEAD) && + cp file.1 file && + git add file && + test_tick && + git commit -m "commit 1" && + git tag c1 && + c1=$(git rev-parse HEAD) && + git reset --hard "$c0" && + cp file.5 file && + git add file && + test_tick && + git commit -m "commit 2" && + git tag c2 && + c2=$(git rev-parse HEAD) && + git reset --hard "$c0" && + cp file.9 file && + git add file && + test_tick && + git commit -m "commit 3" && + git tag c3 && + c3=$(git rev-parse HEAD) + git reset --hard "$c0" && + create_merge_msgs +' + +test_debug 'gitk --all' + +test_expect_success 'test option parsing' ' + if git merge -$ c1 + then + echo "[OOPS] -$ accepted" + false + fi && + if git merge --no-such c1 + then + echo "[OOPS] --no-such accepted" + false + fi && + if git merge -s foobar c1 + then + echo "[OOPS] -s foobar accepted" + false + fi && + if git merge -s=foobar c1 + then + echo "[OOPS] -s=foobar accepted" + false + fi && + if git merge -m + then + echo "[OOPS] missing commit msg accepted" + false + fi && + if git merge + then + echo "[OOPS] missing commit references accepted" + false + fi +' + +test_expect_success 'merge c0 with c1' ' + git reset --hard c0 && + git merge c1 && + verify_merge file result.1 && + verify_head "$c1" +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2' ' + git reset --hard c1 && + test_tick && + git merge c2 && + verify_merge file result.1-5 msg.1-5 && + verify_parents $c1 $c2 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2 and c3' ' + git reset --hard c1 && + test_tick && + git merge c2 c3 && + verify_merge file result.1-5-9 msg.1-5-9 && + verify_parents $c1 $c2 $c3 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with c1 (no-commit)' ' + git reset --hard c0 && + git merge --no-commit c1 && + verify_merge file result.1 && + verify_head $c1 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2 (no-commit)' ' + git reset --hard c1 && + git merge --no-commit c2 && + verify_merge file result.1-5 && + verify_head $c1 && + verify_mergeheads $c2 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2 and c3 (no-commit)' ' + git reset --hard c1 && + git merge --no-commit c2 c3 && + verify_merge file result.1-5-9 && + verify_head $c1 && + verify_mergeheads $c2 $c3 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with c1 (squash)' ' + git reset --hard c0 && + git merge --squash c1 && + verify_merge file result.1 && + verify_head $c0 && + verify_no_mergehead && + verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message" +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2 (squash)' ' + git reset --hard c1 && + git merge --squash c2 && + verify_merge file result.1-5 && + verify_head $c1 && + verify_no_mergehead && + verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message" +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2 and c3 (squash)' ' + git reset --hard c1 && + git merge --squash c2 c3 && + verify_merge file result.1-5-9 && + verify_head $c1 && + verify_no_mergehead && + verify_diff squash.1-5-9 .git/SQUASH_MSG "[OOPS] bad squash message" +' + +test_debug 'gitk --all' + +test_done -- cgit v0.10.2-6-g49f6 From 2ae4fd7695abcf2ab36f3fe984cc4ca44559635f Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 24 Sep 2007 00:51:41 +0200 Subject: git-merge: fix faulty SQUASH_MSG Only the first 'remote' head is currently specified as an argument to 'git log' when generating a SQUSH_MSG, which makes the generated message fail to mention every commit involved in the merge. This fixes the problem. Noticed-by: Junio C Hamano Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/git-merge.sh b/git-merge.sh index cde09d4..919e6be 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -59,7 +59,7 @@ finish_up_to_date () { squash_message () { echo Squashed commit of the following: echo - git log --no-merges ^"$head" $remote + git log --no-merges ^"$head" $remoteheads } finish () { -- cgit v0.10.2-6-g49f6 From d38eb710d92864b0b1f7cd36f17e273e3d8c735c Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 24 Sep 2007 00:51:42 +0200 Subject: git-merge: refactor option parsing Move the option parsing into a separate function as preparation for reuse by the next commit. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/git-merge.sh b/git-merge.sh index 919e6be..49185eb 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -119,11 +119,7 @@ merge_name () { fi } -case "$#" in 0) usage ;; esac - -have_message= -while test $# != 0 -do +parse_option () { case "$1" in -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\ --no-summa|--no-summar|--no-summary) @@ -166,9 +162,21 @@ do have_message=t ;; -*) usage ;; - *) break ;; + *) return 1 ;; esac shift + args_left=$# +} + +test $# != 0 || usage + +have_message= +while parse_option "$@" +do + while test $args_left -lt $# + do + shift + done done if test -z "$show_diffstat"; then -- cgit v0.10.2-6-g49f6 From aec7b362ad07e1a2c58051c8db653dabffee8960 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 24 Sep 2007 00:51:43 +0200 Subject: git-merge: add support for branch..mergeoptions This enables per branch configuration of merge options. Currently, the most useful options to specify per branch are --squash, --summary/--no-summary and possibly --strategy, but all options are supported. Note: Options containing whitespace will _not_ be handled correctly. Luckily, the only option which can include whitespace is --message and it doesn't make much sense to give that option a default value. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/Documentation/config.txt b/Documentation/config.txt index 015910f..d3c25f3 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -337,6 +337,12 @@ branch..merge:: branch..merge to the desired branch, and use the special setting `.` (a period) for branch..remote. +branch..mergeoptions:: + Sets default options for merging into branch . The syntax and + supported options are equal to that of gitlink:git-merge[1], but + option values containing whitespace characters are currently not + supported. + clean.requireForce:: A boolean to make git-clean do nothing unless given -f or -n. Defaults to false. diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index 144bc16..b1771a1 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -58,6 +58,10 @@ merge.verbosity:: above outputs debugging information. The default is level 2. Can be overriden by 'GIT_MERGE_VERBOSITY' environment variable. +branch..mergeoptions:: + Sets default options for merging into branch . The syntax and + supported options are equal to that of git-merge, but option values + containing whitespace characters are currently not supported. HOW MERGE WORKS --------------- diff --git a/git-merge.sh b/git-merge.sh index 49185eb..a35b151 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -168,9 +168,30 @@ parse_option () { args_left=$# } +parse_config () { + while test $# -gt 0 + do + parse_option "$@" || usage + while test $args_left -lt $# + do + shift + done + done +} + test $# != 0 || usage have_message= + +if branch=$(git-symbolic-ref -q HEAD) +then + mergeopts=$(git config "branch.${branch#refs/heads/}.mergeoptions") + if test -n "$mergeopts" + then + parse_config $mergeopts + fi +fi + while parse_option "$@" do while test $args_left -lt $# diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index dec6ea2..110974c 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -341,4 +341,58 @@ test_expect_success 'merge c1 with c2 and c3 (squash)' ' test_debug 'gitk --all' +test_expect_success 'merge c1 with c2 (no-commit in config)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "--no-commit" && + git merge c2 && + verify_merge file result.1-5 && + verify_head $c1 && + verify_mergeheads $c2 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2 (squash in config)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "--squash" && + git merge c2 && + verify_merge file result.1-5 && + verify_head $c1 && + verify_no_mergehead && + verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message" +' + +test_debug 'gitk --all' + +test_expect_success 'override config option -n' ' + git reset --hard c1 && + git config branch.master.mergeoptions "-n" && + test_tick && + git merge --summary c2 >diffstat.txt && + verify_merge file result.1-5 msg.1-5 && + verify_parents $c1 $c2 && + if ! grep -e "^ file | \+2 +-$" diffstat.txt + then + echo "[OOPS] diffstat was not generated" + fi +' + +test_debug 'gitk --all' + +test_expect_success 'override config option --summary' ' + git reset --hard c1 && + git config branch.master.mergeoptions "--summary" && + test_tick && + git merge -n c2 >diffstat.txt && + verify_merge file result.1-5 msg.1-5 && + verify_parents $c1 $c2 && + if grep -e "^ file | \+2 +-$" diffstat.txt + then + echo "[OOPS] diffstat was generated" + false + fi +' + +test_debug 'gitk --all' + test_done -- cgit v0.10.2-6-g49f6 From d08af0ad745869a4fe36bc8df4f9804edfb74eb9 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 24 Sep 2007 00:51:44 +0200 Subject: git-merge: add support for --commit and --no-squash These options can be used to override --no-commit and --squash, which is needed since --no-commit and --squash now can be specified as default merge options in $GIT_DIR/config. The change also introduces slightly different behavior for --no-commit: when specified, it explicitly overrides --squash. Earlier, 'git merge --squash --no-commit' would result in a squashed merge (i.e. no $GIT_DIR/MERGE_HEAD was created) but with this patch the command will behave as if --squash hadn't been specified. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index d64c259..0464a34 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -10,6 +10,10 @@ not autocommit, to give the user a chance to inspect and further tweak the merge result before committing. +--commit:: + Perform the merge and commit the result. This option can + be used to override --no-commit. + --squash:: Produce the working tree and index state as if a real merge happened, but do not actually make a commit or @@ -19,6 +23,10 @@ top of the current branch whose effect is the same as merging another branch (or more in case of an octopus). +--no-squash:: + Perform the merge and commit the result. This option can + be used to override --squash. + -s , \--strategy=:: Use the given merge strategy; can be supplied more than once to specify them in the order they should be tried. diff --git a/git-merge.sh b/git-merge.sh index a35b151..a0fc606 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano # -USAGE='[-n] [--summary] [--no-commit] [--squash] [-s ] [-m=] +' +USAGE='[-n] [--summary] [--[no-]commit] [--[no-]squash] [-s ] [-m=] +' SUBDIRECTORY_OK=Yes . git-sh-setup @@ -128,8 +128,12 @@ parse_option () { show_diffstat=t ;; --sq|--squ|--squa|--squas|--squash) squash=t no_commit=t ;; + --no-sq|--no-squ|--no-squa|--no-squas|--no-squash) + squash= no_commit= ;; + --c|--co|--com|--comm|--commi|--commit) + squash= no_commit= ;; --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit) - no_commit=t ;; + squash= no_commit=t ;; -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\ --strateg=*|--strategy=*|\ -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy) diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 110974c..b0ef488 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -395,4 +395,26 @@ test_expect_success 'override config option --summary' ' test_debug 'gitk --all' +test_expect_success 'merge c1 with c2 (override --no-commit)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "--no-commit" && + test_tick && + git merge --commit c2 && + verify_merge file result.1-5 msg.1-5 && + verify_parents $c1 $c2 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c2 (override --squash)' ' + git reset --hard c1 && + git config branch.master.mergeoptions "--squash" && + test_tick && + git merge --no-squash c2 && + verify_merge file result.1-5 msg.1-5 && + verify_parents $c1 $c2 +' + +test_debug 'gitk --all' + test_done -- cgit v0.10.2-6-g49f6 From d66424c4ac661c69640765260235452499d80378 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 24 Sep 2007 00:51:45 +0200 Subject: git-merge: add --ff and --no-ff options These new options can be used to control the policy for fast-forward merges: --ff allows it (this is the default) while --no-ff will create a merge commit. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index 0464a34..9f1fc82 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -27,6 +27,15 @@ Perform the merge and commit the result. This option can be used to override --squash. +--no-ff:: + Generate a merge commit even if the merge resolved as a + fast-forward. + +--ff:: + Do not generate a merge commit if the merge resolved as + a fast-forward, only update the branch pointer. This is + the default behavior of git-merge. + -s , \--strategy=:: Use the given merge strategy; can be supplied more than once to specify them in the order they should be tried. diff --git a/git-merge.sh b/git-merge.sh index a0fc606..ce66524 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano # -USAGE='[-n] [--summary] [--[no-]commit] [--[no-]squash] [-s ] [-m=] +' +USAGE='[-n] [--summary] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s ] [-m=] +' SUBDIRECTORY_OK=Yes . git-sh-setup @@ -127,13 +127,17 @@ parse_option () { --summary) show_diffstat=t ;; --sq|--squ|--squa|--squas|--squash) - squash=t no_commit=t ;; + allow_fast_forward=t squash=t no_commit=t ;; --no-sq|--no-squ|--no-squa|--no-squas|--no-squash) - squash= no_commit= ;; + allow_fast_forward=t squash= no_commit= ;; --c|--co|--com|--comm|--commi|--commit) - squash= no_commit= ;; + allow_fast_forward=t squash= no_commit= ;; --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit) - squash= no_commit=t ;; + allow_fast_forward=t squash= no_commit=t ;; + --ff) + allow_fast_forward=t squash= no_commit= ;; + --no-ff) + allow_fast_forward=false squash= no_commit= ;; -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\ --strateg=*|--strategy=*|\ -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy) @@ -477,7 +481,13 @@ done # auto resolved the merge cleanly. if test '' != "$result_tree" then - parents=$(git show-branch --independent "$head" "$@" | sed -e 's/^/-p /') + if test "$allow_fast_forward" = "t" + then + parents=$(git show-branch --independent "$head" "$@") + else + parents=$(git rev-parse "$head" "$@") + fi + parents=$(echo "$parents" | sed -e 's/^/-p /') result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit finish "$result_commit" "Merge made by $wt_strategy." dropsave diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index b0ef488..6424c6e 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -417,4 +417,24 @@ test_expect_success 'merge c1 with c2 (override --squash)' ' test_debug 'gitk --all' +test_expect_success 'merge c0 with c1 (no-ff)' ' + git reset --hard c0 && + test_tick && + git merge --no-ff c1 && + verify_merge file result.1 && + verify_parents $c0 $c1 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c0 with c1 (ff overrides no-ff)' ' + git reset --hard c0 && + git config branch.master.mergeoptions "--no-ff" && + git merge --ff c1 && + verify_merge file result.1 && + verify_head $c1 +' + +test_debug 'gitk --all' + test_done -- cgit v0.10.2-6-g49f6 From 683b56791b4e8a29ff9bc98c7faff69d7854d845 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 23 Sep 2007 22:29:12 -0700 Subject: git-remote rm: add tests and minor fix-ups This fixes "git remote rm" which always exited with a failure, corrects indentation, and adds tests. Signed-off-by: Junio C Hamano diff --git a/git-remote.perl b/git-remote.perl index f513a8a..b7c1e01 100755 --- a/git-remote.perl +++ b/git-remote.perl @@ -317,7 +317,7 @@ sub update_remote { } sub rm_remote { - my ($name) = @_; + my ($name) = @_; if (!exists $remote->{$name}) { print STDERR "No such remote $name\n"; return; @@ -336,7 +336,7 @@ sub rm_remote { }; - my @refs = $git->command('for-each-ref', + my @refs = $git->command('for-each-ref', '--format=%(refname) %(objectname)', "refs/remotes/$name"); for (@refs) { ($ref, $object) = split; @@ -453,11 +453,9 @@ elsif ($ARGV[0] eq 'add') { elsif ($ARGV[0] eq 'rm') { if (@ARGV <= 1) { print STDERR "Usage: git remote rm \n"; + exit(1); } - else { - rm_remote($ARGV[1]); - } - exit(1); + rm_remote($ARGV[1]); } else { print STDERR "Usage: git remote\n"; diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh new file mode 100755 index 0000000..636aec2 --- /dev/null +++ b/t/t5505-remote.sh @@ -0,0 +1,100 @@ +#!/bin/sh + +test_description='git remote porcelain-ish' + +. ./test-lib.sh + +GIT_CONFIG=.git/config +export GIT_CONFIG + +setup_repository () { + mkdir "$1" && ( + cd "$1" && + git init && + >file && + git add file && + git commit -m "Initial" && + git checkout -b side && + >elif && + git add elif && + git commit -m "Second" && + git checkout master + ) +} + +tokens_match () { + echo "$1" | tr ' ' '\012' | sort | sed -e '/^$/d' >expect && + echo "$2" | tr ' ' '\012' | sort | sed -e '/^$/d' >actual && + diff -u expect actual +} + +check_remote_track () { + actual=$(git remote show "$1" | sed -n -e '$p') && + shift && + tokens_match "$*" "$actual" +} + +check_tracking_branch () { + f="" && + r=$(git for-each-ref "--format=%(refname)" | + sed -ne "s|^refs/remotes/$1/||p") && + shift && + tokens_match "$*" "$r" +} + +test_expect_success setup ' + + setup_repository one && + setup_repository two && + ( + cd two && git branch another + ) && + git clone one test + +' + +test_expect_success 'remote information for the origin' ' +( + cd test && + tokens_match origin "$(git remote)" && + check_remote_track origin master side && + check_tracking_branch origin HEAD master side +) +' + +test_expect_success 'add another remote' ' +( + cd test && + git remote add -f second ../two && + tokens_match "origin second" "$(git remote)" && + check_remote_track origin master side && + check_remote_track second master side another && + check_tracking_branch second master side another && + git for-each-ref "--format=%(refname)" refs/remotes | + sed -e "/^refs\/remotes\/origin\//d" \ + -e "/^refs\/remotes\/second\//d" >actual && + >expect && + diff -u expect actual +) +' + +test_expect_success 'remove remote' ' +( + cd test && + git remote rm second +) +' + +test_expect_success 'remove remote' ' +( + cd test && + tokens_match origin "$(git remote)" && + check_remote_track origin master side && + git for-each-ref "--format=%(refname)" refs/remotes | + sed -e "/^refs\/remotes\/origin\//d" >actual && + >expect && + diff -u expect actual +) +' + +test_done -- cgit v0.10.2-6-g49f6 From 711fa74266ebfaf58ee0cd79988748fbaa752538 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 8 Sep 2007 21:49:11 +0200 Subject: gitweb: Remove parse_from_to_diffinfo code from git_patchset_body In commit 90921740bd00029708370673fdc537522aa48e6f "gitweb: Split git_patchset_body into separate subroutines" a part of git_patchset_body code was separated into parse_from_to_diffinfo subroutine. But instead of replacing the separated code by the call to mentioned subroutine, the call to subroutine was placed before the separated code. This patch removes parse_from_to_diffinfo code from git_patchset_body subroutine. Signed-off-by: Jakub Narebski diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b2bae1b..c18339f 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3191,44 +3191,10 @@ sub git_patchset_body { } } until (!defined $to_name || $to_name eq $diffinfo->{'to_file'} || $patch_idx > $#$difftree); + # modifies %from, %to hashes parse_from_to_diffinfo($diffinfo, \%from, \%to, @hash_parents); - if ($diffinfo->{'nparents'}) { - # combined diff - $from{'file'} = []; - $from{'href'} = []; - fill_from_file_info($diffinfo, @hash_parents) - unless exists $diffinfo->{'from_file'}; - for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) { - $from{'file'}[$i] = $diffinfo->{'from_file'}[$i] || $diffinfo->{'to_file'}; - if ($diffinfo->{'status'}[$i] ne "A") { # not new (added) file - $from{'href'}[$i] = href(action=>"blob", - hash_base=>$hash_parents[$i], - hash=>$diffinfo->{'from_id'}[$i], - file_name=>$from{'file'}[$i]); - } else { - $from{'href'}[$i] = undef; - } - } - } else { - $from{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'}; - if ($diffinfo->{'status'} ne "A") { # not new (added) file - $from{'href'} = href(action=>"blob", hash_base=>$hash_parent, - hash=>$diffinfo->{'from_id'}, - file_name=>$from{'file'}); - } else { - delete $from{'href'}; - } - } - $to{'file'} = $diffinfo->{'to_file'} || $diffinfo->{'file'}; - if (!is_deleted($diffinfo)) { # file exists in result - $to{'href'} = href(action=>"blob", hash_base=>$hash, - hash=>$diffinfo->{'to_id'}, - file_name=>$to{'file'}); - } else { - delete $to{'href'}; - } # this is first patch for raw difftree line with $patch_idx index # we index @$difftree array from 0, but number patches from 1 print "
\n"; -- cgit v0.10.2-6-g49f6 From 3ef408aefe937fe76559e9b41602cddc0ac8af8c Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 8 Sep 2007 21:54:28 +0200 Subject: gitweb: No difftree output for trivial merge In 'commitdiff' view, for the merge commit, there is an extra header for the difftree table, with links to commitdiffs to individual parents. Do not show such header when there is nothing to show, for trivial merges. This means that for trivial merge you have to go to 'commit' view to get links to diffs to each parent. Signed-off-by: Jakub Narebski diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index c18339f..3064298 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2809,7 +2809,7 @@ sub git_difftree_body { "diff_tree\">\n"; # header only for combined diff in 'commitdiff' view - my $has_header = @parents > 1 && $action eq 'commitdiff'; + my $has_header = @$difftree && @parents > 1 && $action eq 'commitdiff'; if ($has_header) { # table header print "\n" . -- cgit v0.10.2-6-g49f6 From bc12651b08fe295ac5647208f9b3f946145cc9b1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 24 Sep 2007 01:12:16 -0700 Subject: Start RelNotes for 1.5.4 Signed-off-by: Junio C Hamano diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt index 1df66af..ceee857 100644 --- a/Documentation/RelNotes-1.5.4.txt +++ b/Documentation/RelNotes-1.5.4.txt @@ -4,7 +4,22 @@ GIT v1.5.4 Release Notes Updates since v1.5.3 -------------------- + * git-reset is now built-in. + * git-send-email can optionally talk over ssmtp and use SMTP-AUTH. + + * git-rebase learned --whitespace option. + + * git-remote knows --mirror mode. + + * git-merge can call the "post-merge" hook. + + * git-pack-objects can optionally run deltification with multiple threads. + + * git-archive can optionally substitute keywords in files marked with + export-subst attribute. + + * Various Perforce importer updates. Fixes since v1.5.3 ------------------ @@ -12,3 +27,9 @@ Fixes since v1.5.3 All of the fixes in v1.5.3 maintenance series are included in this release, unless otherwise noted. +-- +exec >/var/tmp/1 +O=v1.5.3.2-99-ge4b2890 +echo O=`git describe refs/heads/master` +git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint + -- cgit v0.10.2-6-g49f6 From 5d17d765ca754f3e9d87110394a0a397ea7ac2cf Mon Sep 17 00:00:00 2001 From: Stefan Sperling Date: Mon, 24 Sep 2007 12:57:40 +0200 Subject: Fix pool handling in git-svnimport to avoid memory leaks. - Create an explicit one-and-only root pool. - Closely follow examples in SVN::Core man page. Before calling a subversion function, create a subpool of our root pool and make it the new default pool. - Create a subpool for looping over svn revisions and clear this subpool (i.e. it mark for reuse, don't decallocate it) at the start of the loop instead of allocating new memory with each iteration. See http://marc.info/?l=git&m=118554191513822&w=2 for a detailed explanation of the issue. Signed-off-by: Stefan Sperling Signed-off-by: Junio C Hamano diff --git a/git-svnimport.perl b/git-svnimport.perl index aa5b3b2..ea8c1b2 100755 --- a/git-svnimport.perl +++ b/git-svnimport.perl @@ -54,6 +54,7 @@ my $branch_name = $opt_b || "branches"; my $project_name = $opt_P || ""; $project_name = "/" . $project_name if ($project_name); my $repack_after = $opt_R || 1000; +my $root_pool = SVN::Pool->new_default; @ARGV == 1 or @ARGV == 2 or usage(); @@ -132,7 +133,7 @@ sub conn { my $auth = SVN::Core::auth_open ([SVN::Client::get_simple_provider, SVN::Client::get_ssl_server_trust_file_provider, SVN::Client::get_username_provider]); - my $s = SVN::Ra->new(url => $repo, auth => $auth); + my $s = SVN::Ra->new(url => $repo, auth => $auth, pool => $root_pool); die "SVN connection to $repo: $!\n" unless defined $s; $self->{'svn'} = $s; $self->{'repo'} = $repo; @@ -147,11 +148,10 @@ sub file { print "... $rev $path ...\n" if $opt_v; my (undef, $properties); - my $pool = SVN::Pool->new(); $path =~ s#^/*##; + my $subpool = SVN::Pool::new_default_sub; eval { (undef, $properties) - = $self->{'svn'}->get_file($path,$rev,$fh,$pool); }; - $pool->clear; + = $self->{'svn'}->get_file($path,$rev,$fh); }; if($@) { return undef if $@ =~ /Attempted to get checksum/; die $@; @@ -185,6 +185,7 @@ sub ignore { print "... $rev $path ...\n" if $opt_v; $path =~ s#^/*##; + my $subpool = SVN::Pool::new_default_sub; my (undef,undef,$properties) = $self->{'svn'}->get_dir($path,$rev,undef); if (exists $properties->{'svn:ignore'}) { @@ -202,6 +203,7 @@ sub ignore { sub dir_list { my($self,$path,$rev) = @_; $path =~ s#^/*##; + my $subpool = SVN::Pool::new_default_sub; my ($dirents,undef,$properties) = $self->{'svn'}->get_dir($path,$rev,undef); return $dirents; @@ -358,10 +360,9 @@ open BRANCHES,">>", "$git_dir/svn2git"; sub node_kind($$) { my ($svnpath, $revision) = @_; - my $pool=SVN::Pool->new; $svnpath =~ s#^/*##; - my $kind = $svn->{'svn'}->check_path($svnpath,$revision,$pool); - $pool->clear; + my $subpool = SVN::Pool::new_default_sub; + my $kind = $svn->{'svn'}->check_path($svnpath,$revision); return $kind; } @@ -889,7 +890,7 @@ sub commit_all { # Recursive use of the SVN connection does not work local $svn = $svn2; - my ($changed_paths, $revision, $author, $date, $message, $pool) = @_; + my ($changed_paths, $revision, $author, $date, $message) = @_; my %p; while(my($path,$action) = each %$changed_paths) { $p{$path} = [ $action->action,$action->copyfrom_path, $action->copyfrom_rev, $path ]; @@ -925,14 +926,14 @@ print "Processing from $current_rev to $opt_l ...\n" if $opt_v; my $from_rev; my $to_rev = $current_rev - 1; +my $subpool = SVN::Pool::new_default_sub; while ($to_rev < $opt_l) { + $subpool->clear; $from_rev = $to_rev + 1; $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; - my $pool=SVN::Pool->new; - $svn->{'svn'}->get_log("/",$from_rev,$to_rev,0,1,1,\&commit_all,$pool); - $pool->clear; + $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 v0.10.2-6-g49f6 From 85d81a757e495fe74d8a403f813d3748e832c112 Mon Sep 17 00:00:00 2001 From: Glenn Rempe Date: Mon, 24 Sep 2007 13:33:38 -0700 Subject: Fixed minor typo in t/t9001-send-email.sh test command line. The git-send-email command line in the test was missing a single hyphen. Signed-off-by: Glenn Rempe Signed-off-by: Junio C Hamano diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index e9ea33c..83f9470 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -30,7 +30,7 @@ test_expect_success 'Extract patches' ' ' test_expect_success 'Send patches' ' - git send-email -from="Example " --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors + git send-email --from="Example " --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors ' cat >expected <<\EOF -- cgit v0.10.2-6-g49f6 From e883932f7dc83ea69a990cba656464d5315f3300 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Tue, 25 Sep 2007 08:42:16 +0200 Subject: unexpected Make output (e.g. from --debug) causes build failure Without this, the extra output produced e.g., by "make --debug" would go into $INSTLIBDIR and then cause the sed command to fail. Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index dace211..42ba1d0 100644 --- a/Makefile +++ b/Makefile @@ -783,7 +783,7 @@ perl/perl.mak: GIT-CFLAGS $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl $(QUIET_GEN)$(RM) $@ $@+ && \ - INSTLIBDIR=`$(MAKE) -C perl -s --no-print-directory instlibdir` && \ + INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \ sed -e '1{' \ -e ' s|#!.*perl|#!$(PERL_PATH_SQ)|' \ -e ' h' \ -- cgit v0.10.2-6-g49f6 From d1637a07f684acd80007723f94c4da9649d85525 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Tue, 25 Sep 2007 08:48:59 +0200 Subject: Do not over-quote the -f envelopesender value. Without this, the value passed to sendmail would have an extra set of single quotes. At least exim's sendmail emulation would object to that: exim: bad -f address "'list-addr@example.org'": malformed address: ' \ may not follow 'list-addr@example.org error: hooks/post-receive exited with error code 1 Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index c589a39..1f88099 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -571,6 +571,15 @@ generate_delete_general_email() echo $LOGEND } +send_mail() +{ + if [ -n "$envelopesender" ]; then + /usr/sbin/sendmail -t -f "$envelopesender" + else + /usr/sbin/sendmail -t + fi +} + # ---------------------------- main() # --- Constants @@ -607,13 +616,8 @@ if [ -n "$1" -a -n "$2" -a -n "$3" ]; then # resend an email; they could redirect the output to sendmail themselves PAGER= generate_email $2 $3 $1 else - if [ -n "$envelopesender" ]; then - envelopesender="-f '$envelopesender'" - fi - while read oldrev newrev refname do - generate_email $oldrev $newrev $refname | - /usr/sbin/sendmail -t $envelopesender + generate_email $oldrev $newrev $refname | send_mail done fi -- cgit v0.10.2-6-g49f6 From f31a522a2d64a5c7d83047e9234e1722ef63985e Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Sun, 23 Sep 2007 22:19:42 -0400 Subject: git-submodule - allow a relative path as the subproject url This allows a subproject's location to be specified and stored as relative to the parent project's location (e.g., ./foo, or ../foo). This url is stored in .gitmodules as given. It is resolved into an absolute url by appending it to the parent project's url when the information is written to .git/config (i.e., during submodule add for the originator, and submodule init for a downstream recipient). This allows cloning of the project to work "as expected" if the project is hosted on a different server than when the subprojects were added. Signed-off-by: Mark Levedahl Signed-off-by: Junio C Hamano diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 2c48936..335e973 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -21,6 +21,9 @@ add:: repository is cloned at the specified path, added to the changeset and registered in .gitmodules. If no path is specified, the path is deduced from the repository specification. + If the repository url begins with ./ or ../, it is stored as + given but resolved as a relative path from the main project's + url when cloning. status:: Show the status of the submodules. This will print the SHA-1 of the diff --git a/git-submodule.sh b/git-submodule.sh index 673aa27..727b1d3 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -39,6 +39,32 @@ get_repo_base() { ) 2>/dev/null } +# Resolve relative url by appending to parent's url +resolve_relative_url () +{ + branch="$(git symbolic-ref HEAD 2>/dev/null)" + remote="$(git config branch.${branch#refs/heads/}.remote)" + remote="${remote:-origin}" + remoteurl="$(git config remote.$remote.url)" || + die "remote ($remote) does not have a url in .git/config" + url="$1" + while test -n "$url" + do + case "$url" in + ../*) + url="${url#../}" + remoteurl="${remoteurl%/*}" + ;; + ./*) + url="${url#./}" + ;; + *) + break;; + esac + done + echo "$remoteurl/$url" +} + # # Map submodule path to submodule name # @@ -103,11 +129,19 @@ module_add() usage fi - # Turn the source into an absolute path if - # it is local - if base=$(get_repo_base "$repo"); then - repo="$base" - fi + case "$repo" in + ./*|../*) + # dereference source url relative to parent's url + realrepo="$(resolve_relative_url $repo)" ;; + *) + # Turn the source into an absolute path if + # it is local + if base=$(get_repo_base "$repo"); then + repo="$base" + realrepo=$repo + fi + ;; + esac # Guess path from repo if not specified or strip trailing slashes if test -z "$path"; then @@ -122,7 +156,7 @@ module_add() git ls-files --error-unmatch "$path" > /dev/null 2>&1 && die "'$path' already exists in the index" - module_clone "$path" "$repo" || exit + module_clone "$path" "$realrepo" || exit (unset GIT_DIR && cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) || die "Unable to checkout submodule '$path'" git add "$path" || @@ -153,6 +187,13 @@ modules_init() test -z "$url" && die "No url found for submodule path '$path' in .gitmodules" + # Possibly a url relative to parent + case "$url" in + ./*|../*) + url="$(resolve_relative_url "$url")" + ;; + esac + git config submodule."$name".url "$url" || die "Failed to register url for submodule path '$path'" -- cgit v0.10.2-6-g49f6 From 6dd14366d9478fdb3b459e1b39e384deb5f38f9f Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Tue, 25 Sep 2007 08:44:38 -0400 Subject: user-manual: Explain what submodules are good for. Rework the introduction to the Submodules section to explain why someone would use them, and fix up submodule references from the tree-object and todo sections. Signed-off-by: Michael Smith Acked-by: J. Bruce Fields Signed-off-by: Junio C Hamano diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index a085ca1..c7fdf25 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -2856,8 +2856,7 @@ between two related tree objects, since it can ignore any entries with identical object names. (Note: in the presence of submodules, trees may also have commits as -entries. See gitlink:git-submodule[1] and gitlink:gitmodules.txt[1] -for partial documentation.) +entries. See <> for documentation.) Note that the files all have mode 644 or 755: git actually only pays attention to the executable bit. @@ -3163,12 +3162,45 @@ information as long as you have the name of the tree that it described. Submodules ========== -This tutorial explains how to create and publish a repository with submodules -using the gitlink:git-submodule[1] command. - -Submodules maintain their own identity; the submodule support just stores the -submodule repository location and commit ID, so other developers who clone the -superproject can easily clone all the submodules at the same revision. +Large projects are often composed of smaller, self-contained modules. For +example, an embedded Linux distribution's source tree would include every +piece of software in the distribution with some local modifications; a movie +player might need to build against a specific, known-working version of a +decompression library; several independent programs might all share the same +build scripts. + +With centralized revision control systems this is often accomplished by +including every module in one single repository. Developers can check out +all modules or only the modules they need to work with. They can even modify +files across several modules in a single commit while moving things around +or updating APIs and translations. + +Git does not allow partial checkouts, so duplicating this approach in Git +would force developers to keep a local copy of modules they are not +interested in touching. Commits in an enormous checkout would be slower +than you'd expect as Git would have to scan every directory for changes. +If modules have a lot of local history, clones would take forever. + +On the plus side, distributed revision control systems can much better +integrate with external sources. In a centralized model, a single arbitrary +snapshot of the external project is exported from its own revision control +and then imported into the local revision control on a vendor branch. All +the history is hidden. With distributed revision control you can clone the +entire external history and much more easily follow development and re-merge +local changes. + +Git's submodule support allows a repository to contain, as a subdirectory, a +checkout of an external project. Submodules maintain their own identity; +the submodule support just stores the submodule repository location and +commit ID, so other developers who clone the containing project +("superproject") can easily clone all the submodules at the same revision. +Partial checkouts of the superproject are possible: you can tell Git to +clone none, some or all of the submodules. + +The gitlink:git-submodule[1] command is available since Git 1.5.3. Users +with Git 1.5.2 can look up the submodule commits in the repository and +manually check them out; earlier versions won't recognize the submodules at +all. To see how submodule support works, create (for example) four example repositories that can be used later as a submodule: @@ -3213,8 +3245,8 @@ 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 adds the submodule's clone path to the `.gitmodules` file and adds this - file to the index, ready to be committed. +- It adds the submodule's clone path to the gitlink: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 committed. @@ -4277,5 +4309,3 @@ Write a chapter on using plumbing and writing scripts. Alternates, clone -reference, etc. git unpack-objects -r for recovery - -submodules -- cgit v0.10.2-6-g49f6 From 2ecb5ea2ad375017eedf73bd0130fa9ca33010d2 Mon Sep 17 00:00:00 2001 From: Matt Kraai Date: Tue, 25 Sep 2007 07:03:46 -0700 Subject: Move convert-objects to contrib. convert-objects was needed to convert from an old-style repository, which hashed the compressed contents and used a different date format. Such repositories are presumably no longer common and, if such conversions are necessary, should be done by writing a frontend for git-fast-import. Linus, the original author, is OK with moving it to contrib. Signed-off-by: Matt Kraai Signed-off-by: Junio C Hamano diff --git a/.gitignore b/.gitignore index 63c918c..e0b91be 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,6 @@ git-clone git-commit git-commit-tree git-config -git-convert-objects git-count-objects git-cvsexportcommit git-cvsimport diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl index 4ee76ea..1061fd8 100755 --- a/Documentation/cmd-list.perl +++ b/Documentation/cmd-list.perl @@ -94,7 +94,6 @@ git-clone mainporcelain git-commit mainporcelain git-commit-tree plumbingmanipulators git-config ancillarymanipulators -git-convert-objects ancillarymanipulators git-count-objects ancillaryinterrogators git-cvsexportcommit foreignscminterface git-cvsimport foreignscminterface diff --git a/Documentation/git-convert-objects.txt b/Documentation/git-convert-objects.txt deleted file mode 100644 index 9718abf..0000000 --- a/Documentation/git-convert-objects.txt +++ /dev/null @@ -1,28 +0,0 @@ -git-convert-objects(1) -====================== - -NAME ----- -git-convert-objects - Converts old-style git repository - - -SYNOPSIS --------- -'git-convert-objects' - -DESCRIPTION ------------ -Converts old-style git repository to the latest format - - -Author ------- -Written by Linus Torvalds - -Documentation --------------- -Documentation by David Greaves, Junio C Hamano and the git-list . - -GIT ---- -Part of the gitlink:git[7] suite diff --git a/Makefile b/Makefile index 97ce687..8db4dbe 100644 --- a/Makefile +++ b/Makefile @@ -233,7 +233,7 @@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ - git-convert-objects$X git-fetch-pack$X \ + git-fetch-pack$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \ git-fast-import$X \ git-daemon$X \ diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index cad842a..e760930 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -299,7 +299,6 @@ __git_commands () check-attr) : plumbing;; check-ref-format) : plumbing;; commit-tree) : plumbing;; - convert-objects) : plumbing;; cvsexportcommit) : export;; cvsimport) : import;; cvsserver) : daemon;; diff --git a/contrib/convert-objects/convert-objects.c b/contrib/convert-objects/convert-objects.c new file mode 100644 index 0000000..90e7900 --- /dev/null +++ b/contrib/convert-objects/convert-objects.c @@ -0,0 +1,329 @@ +#include "cache.h" +#include "blob.h" +#include "commit.h" +#include "tree.h" + +struct entry { + unsigned char old_sha1[20]; + unsigned char new_sha1[20]; + int converted; +}; + +#define MAXOBJECTS (1000000) + +static struct entry *convert[MAXOBJECTS]; +static int nr_convert; + +static struct entry * convert_entry(unsigned char *sha1); + +static struct entry *insert_new(unsigned char *sha1, int pos) +{ + struct entry *new = xcalloc(1, sizeof(struct entry)); + hashcpy(new->old_sha1, sha1); + memmove(convert + pos + 1, convert + pos, (nr_convert - pos) * sizeof(struct entry *)); + convert[pos] = new; + nr_convert++; + if (nr_convert == MAXOBJECTS) + die("you're kidding me - hit maximum object limit"); + return new; +} + +static struct entry *lookup_entry(unsigned char *sha1) +{ + int low = 0, high = nr_convert; + + while (low < high) { + int next = (low + high) / 2; + struct entry *n = convert[next]; + int cmp = hashcmp(sha1, n->old_sha1); + if (!cmp) + return n; + if (cmp < 0) { + high = next; + continue; + } + low = next+1; + } + return insert_new(sha1, low); +} + +static void convert_binary_sha1(void *buffer) +{ + struct entry *entry = convert_entry(buffer); + hashcpy(buffer, entry->new_sha1); +} + +static void convert_ascii_sha1(void *buffer) +{ + unsigned char sha1[20]; + struct entry *entry; + + if (get_sha1_hex(buffer, sha1)) + die("expected sha1, got '%s'", (char*) buffer); + entry = convert_entry(sha1); + memcpy(buffer, sha1_to_hex(entry->new_sha1), 40); +} + +static unsigned int convert_mode(unsigned int mode) +{ + unsigned int newmode; + + newmode = mode & S_IFMT; + if (S_ISREG(mode)) + newmode |= (mode & 0100) ? 0755 : 0644; + return newmode; +} + +static int write_subdirectory(void *buffer, unsigned long size, const char *base, int baselen, unsigned char *result_sha1) +{ + char *new = xmalloc(size); + unsigned long newlen = 0; + unsigned long used; + + used = 0; + while (size) { + int len = 21 + strlen(buffer); + char *path = strchr(buffer, ' '); + unsigned char *sha1; + unsigned int mode; + char *slash, *origpath; + + if (!path || strtoul_ui(buffer, 8, &mode)) + die("bad tree conversion"); + mode = convert_mode(mode); + path++; + if (memcmp(path, base, baselen)) + break; + origpath = path; + path += baselen; + slash = strchr(path, '/'); + if (!slash) { + newlen += sprintf(new + newlen, "%o %s", mode, path); + new[newlen++] = '\0'; + hashcpy((unsigned char*)new + newlen, (unsigned char *) buffer + len - 20); + newlen += 20; + + used += len; + size -= len; + buffer = (char *) buffer + len; + continue; + } + + newlen += sprintf(new + newlen, "%o %.*s", S_IFDIR, (int)(slash - path), path); + new[newlen++] = 0; + sha1 = (unsigned char *)(new + newlen); + newlen += 20; + + len = write_subdirectory(buffer, size, origpath, slash-origpath+1, sha1); + + used += len; + size -= len; + buffer = (char *) buffer + len; + } + + write_sha1_file(new, newlen, tree_type, result_sha1); + free(new); + return used; +} + +static void convert_tree(void *buffer, unsigned long size, unsigned char *result_sha1) +{ + void *orig_buffer = buffer; + unsigned long orig_size = size; + + while (size) { + size_t len = 1+strlen(buffer); + + convert_binary_sha1((char *) buffer + len); + + len += 20; + if (len > size) + die("corrupt tree object"); + size -= len; + buffer = (char *) buffer + len; + } + + write_subdirectory(orig_buffer, orig_size, "", 0, result_sha1); +} + +static unsigned long parse_oldstyle_date(const char *buf) +{ + char c, *p; + char buffer[100]; + struct tm tm; + const char *formats[] = { + "%c", + "%a %b %d %T", + "%Z", + "%Y", + " %Y", + NULL + }; + /* We only ever did two timezones in the bad old format .. */ + const char *timezones[] = { + "PDT", "PST", "CEST", NULL + }; + const char **fmt = formats; + + p = buffer; + while (isspace(c = *buf)) + buf++; + while ((c = *buf++) != '\n') + *p++ = c; + *p++ = 0; + buf = buffer; + memset(&tm, 0, sizeof(tm)); + do { + const char *next = strptime(buf, *fmt, &tm); + if (next) { + if (!*next) + return mktime(&tm); + buf = next; + } else { + const char **p = timezones; + while (isspace(*buf)) + buf++; + while (*p) { + if (!memcmp(buf, *p, strlen(*p))) { + buf += strlen(*p); + break; + } + p++; + } + } + fmt++; + } while (*buf && *fmt); + printf("left: %s\n", buf); + return mktime(&tm); +} + +static int convert_date_line(char *dst, void **buf, unsigned long *sp) +{ + unsigned long size = *sp; + char *line = *buf; + char *next = strchr(line, '\n'); + char *date = strchr(line, '>'); + int len; + + if (!next || !date) + die("missing or bad author/committer line %s", line); + next++; date += 2; + + *buf = next; + *sp = size - (next - line); + + len = date - line; + memcpy(dst, line, len); + dst += len; + + /* Is it already in new format? */ + if (isdigit(*date)) { + int datelen = next - date; + memcpy(dst, date, datelen); + return len + datelen; + } + + /* + * Hacky hacky: one of the sparse old-style commits does not have + * any date at all, but we can fake it by using the committer date. + */ + if (*date == '\n' && strchr(next, '>')) + date = strchr(next, '>')+2; + + return len + sprintf(dst, "%lu -0700\n", parse_oldstyle_date(date)); +} + +static void convert_date(void *buffer, unsigned long size, unsigned char *result_sha1) +{ + char *new = xmalloc(size + 100); + unsigned long newlen = 0; + + /* "tree \n" */ + memcpy(new + newlen, buffer, 46); + newlen += 46; + buffer = (char *) buffer + 46; + size -= 46; + + /* "parent \n" */ + while (!memcmp(buffer, "parent ", 7)) { + memcpy(new + newlen, buffer, 48); + newlen += 48; + buffer = (char *) buffer + 48; + size -= 48; + } + + /* "author xyz date" */ + newlen += convert_date_line(new + newlen, &buffer, &size); + /* "committer xyz date" */ + newlen += convert_date_line(new + newlen, &buffer, &size); + + /* Rest */ + memcpy(new + newlen, buffer, size); + newlen += size; + + write_sha1_file(new, newlen, commit_type, result_sha1); + free(new); +} + +static void convert_commit(void *buffer, unsigned long size, unsigned char *result_sha1) +{ + void *orig_buffer = buffer; + unsigned long orig_size = size; + + if (memcmp(buffer, "tree ", 5)) + die("Bad commit '%s'", (char*) buffer); + convert_ascii_sha1((char *) buffer + 5); + buffer = (char *) buffer + 46; /* "tree " + "hex sha1" + "\n" */ + while (!memcmp(buffer, "parent ", 7)) { + convert_ascii_sha1((char *) buffer + 7); + buffer = (char *) buffer + 48; + } + convert_date(orig_buffer, orig_size, result_sha1); +} + +static struct entry * convert_entry(unsigned char *sha1) +{ + struct entry *entry = lookup_entry(sha1); + enum object_type type; + void *buffer, *data; + unsigned long size; + + if (entry->converted) + return entry; + data = read_sha1_file(sha1, &type, &size); + if (!data) + die("unable to read object %s", sha1_to_hex(sha1)); + + buffer = xmalloc(size); + memcpy(buffer, data, size); + + if (type == OBJ_BLOB) { + write_sha1_file(buffer, size, blob_type, entry->new_sha1); + } else if (type == OBJ_TREE) + convert_tree(buffer, size, entry->new_sha1); + else if (type == OBJ_COMMIT) + convert_commit(buffer, size, entry->new_sha1); + else + die("unknown object type %d in %s", type, sha1_to_hex(sha1)); + entry->converted = 1; + free(buffer); + free(data); + return entry; +} + +int main(int argc, char **argv) +{ + unsigned char sha1[20]; + struct entry *entry; + + setup_git_directory(); + + if (argc != 2) + usage("git-convert-objects "); + if (get_sha1(argv[1], sha1)) + die("Not a valid object name %s", argv[1]); + + entry = convert_entry(sha1); + printf("new sha1: %s\n", sha1_to_hex(entry->new_sha1)); + return 0; +} diff --git a/contrib/convert-objects/git-convert-objects.txt b/contrib/convert-objects/git-convert-objects.txt new file mode 100644 index 0000000..9718abf --- /dev/null +++ b/contrib/convert-objects/git-convert-objects.txt @@ -0,0 +1,28 @@ +git-convert-objects(1) +====================== + +NAME +---- +git-convert-objects - Converts old-style git repository + + +SYNOPSIS +-------- +'git-convert-objects' + +DESCRIPTION +----------- +Converts old-style git repository to the latest format + + +Author +------ +Written by Linus Torvalds + +Documentation +-------------- +Documentation by David Greaves, Junio C Hamano and the git-list . + +GIT +--- +Part of the gitlink:git[7] suite diff --git a/convert-objects.c b/convert-objects.c deleted file mode 100644 index 90e7900..0000000 --- a/convert-objects.c +++ /dev/null @@ -1,329 +0,0 @@ -#include "cache.h" -#include "blob.h" -#include "commit.h" -#include "tree.h" - -struct entry { - unsigned char old_sha1[20]; - unsigned char new_sha1[20]; - int converted; -}; - -#define MAXOBJECTS (1000000) - -static struct entry *convert[MAXOBJECTS]; -static int nr_convert; - -static struct entry * convert_entry(unsigned char *sha1); - -static struct entry *insert_new(unsigned char *sha1, int pos) -{ - struct entry *new = xcalloc(1, sizeof(struct entry)); - hashcpy(new->old_sha1, sha1); - memmove(convert + pos + 1, convert + pos, (nr_convert - pos) * sizeof(struct entry *)); - convert[pos] = new; - nr_convert++; - if (nr_convert == MAXOBJECTS) - die("you're kidding me - hit maximum object limit"); - return new; -} - -static struct entry *lookup_entry(unsigned char *sha1) -{ - int low = 0, high = nr_convert; - - while (low < high) { - int next = (low + high) / 2; - struct entry *n = convert[next]; - int cmp = hashcmp(sha1, n->old_sha1); - if (!cmp) - return n; - if (cmp < 0) { - high = next; - continue; - } - low = next+1; - } - return insert_new(sha1, low); -} - -static void convert_binary_sha1(void *buffer) -{ - struct entry *entry = convert_entry(buffer); - hashcpy(buffer, entry->new_sha1); -} - -static void convert_ascii_sha1(void *buffer) -{ - unsigned char sha1[20]; - struct entry *entry; - - if (get_sha1_hex(buffer, sha1)) - die("expected sha1, got '%s'", (char*) buffer); - entry = convert_entry(sha1); - memcpy(buffer, sha1_to_hex(entry->new_sha1), 40); -} - -static unsigned int convert_mode(unsigned int mode) -{ - unsigned int newmode; - - newmode = mode & S_IFMT; - if (S_ISREG(mode)) - newmode |= (mode & 0100) ? 0755 : 0644; - return newmode; -} - -static int write_subdirectory(void *buffer, unsigned long size, const char *base, int baselen, unsigned char *result_sha1) -{ - char *new = xmalloc(size); - unsigned long newlen = 0; - unsigned long used; - - used = 0; - while (size) { - int len = 21 + strlen(buffer); - char *path = strchr(buffer, ' '); - unsigned char *sha1; - unsigned int mode; - char *slash, *origpath; - - if (!path || strtoul_ui(buffer, 8, &mode)) - die("bad tree conversion"); - mode = convert_mode(mode); - path++; - if (memcmp(path, base, baselen)) - break; - origpath = path; - path += baselen; - slash = strchr(path, '/'); - if (!slash) { - newlen += sprintf(new + newlen, "%o %s", mode, path); - new[newlen++] = '\0'; - hashcpy((unsigned char*)new + newlen, (unsigned char *) buffer + len - 20); - newlen += 20; - - used += len; - size -= len; - buffer = (char *) buffer + len; - continue; - } - - newlen += sprintf(new + newlen, "%o %.*s", S_IFDIR, (int)(slash - path), path); - new[newlen++] = 0; - sha1 = (unsigned char *)(new + newlen); - newlen += 20; - - len = write_subdirectory(buffer, size, origpath, slash-origpath+1, sha1); - - used += len; - size -= len; - buffer = (char *) buffer + len; - } - - write_sha1_file(new, newlen, tree_type, result_sha1); - free(new); - return used; -} - -static void convert_tree(void *buffer, unsigned long size, unsigned char *result_sha1) -{ - void *orig_buffer = buffer; - unsigned long orig_size = size; - - while (size) { - size_t len = 1+strlen(buffer); - - convert_binary_sha1((char *) buffer + len); - - len += 20; - if (len > size) - die("corrupt tree object"); - size -= len; - buffer = (char *) buffer + len; - } - - write_subdirectory(orig_buffer, orig_size, "", 0, result_sha1); -} - -static unsigned long parse_oldstyle_date(const char *buf) -{ - char c, *p; - char buffer[100]; - struct tm tm; - const char *formats[] = { - "%c", - "%a %b %d %T", - "%Z", - "%Y", - " %Y", - NULL - }; - /* We only ever did two timezones in the bad old format .. */ - const char *timezones[] = { - "PDT", "PST", "CEST", NULL - }; - const char **fmt = formats; - - p = buffer; - while (isspace(c = *buf)) - buf++; - while ((c = *buf++) != '\n') - *p++ = c; - *p++ = 0; - buf = buffer; - memset(&tm, 0, sizeof(tm)); - do { - const char *next = strptime(buf, *fmt, &tm); - if (next) { - if (!*next) - return mktime(&tm); - buf = next; - } else { - const char **p = timezones; - while (isspace(*buf)) - buf++; - while (*p) { - if (!memcmp(buf, *p, strlen(*p))) { - buf += strlen(*p); - break; - } - p++; - } - } - fmt++; - } while (*buf && *fmt); - printf("left: %s\n", buf); - return mktime(&tm); -} - -static int convert_date_line(char *dst, void **buf, unsigned long *sp) -{ - unsigned long size = *sp; - char *line = *buf; - char *next = strchr(line, '\n'); - char *date = strchr(line, '>'); - int len; - - if (!next || !date) - die("missing or bad author/committer line %s", line); - next++; date += 2; - - *buf = next; - *sp = size - (next - line); - - len = date - line; - memcpy(dst, line, len); - dst += len; - - /* Is it already in new format? */ - if (isdigit(*date)) { - int datelen = next - date; - memcpy(dst, date, datelen); - return len + datelen; - } - - /* - * Hacky hacky: one of the sparse old-style commits does not have - * any date at all, but we can fake it by using the committer date. - */ - if (*date == '\n' && strchr(next, '>')) - date = strchr(next, '>')+2; - - return len + sprintf(dst, "%lu -0700\n", parse_oldstyle_date(date)); -} - -static void convert_date(void *buffer, unsigned long size, unsigned char *result_sha1) -{ - char *new = xmalloc(size + 100); - unsigned long newlen = 0; - - /* "tree \n" */ - memcpy(new + newlen, buffer, 46); - newlen += 46; - buffer = (char *) buffer + 46; - size -= 46; - - /* "parent \n" */ - while (!memcmp(buffer, "parent ", 7)) { - memcpy(new + newlen, buffer, 48); - newlen += 48; - buffer = (char *) buffer + 48; - size -= 48; - } - - /* "author xyz date" */ - newlen += convert_date_line(new + newlen, &buffer, &size); - /* "committer xyz date" */ - newlen += convert_date_line(new + newlen, &buffer, &size); - - /* Rest */ - memcpy(new + newlen, buffer, size); - newlen += size; - - write_sha1_file(new, newlen, commit_type, result_sha1); - free(new); -} - -static void convert_commit(void *buffer, unsigned long size, unsigned char *result_sha1) -{ - void *orig_buffer = buffer; - unsigned long orig_size = size; - - if (memcmp(buffer, "tree ", 5)) - die("Bad commit '%s'", (char*) buffer); - convert_ascii_sha1((char *) buffer + 5); - buffer = (char *) buffer + 46; /* "tree " + "hex sha1" + "\n" */ - while (!memcmp(buffer, "parent ", 7)) { - convert_ascii_sha1((char *) buffer + 7); - buffer = (char *) buffer + 48; - } - convert_date(orig_buffer, orig_size, result_sha1); -} - -static struct entry * convert_entry(unsigned char *sha1) -{ - struct entry *entry = lookup_entry(sha1); - enum object_type type; - void *buffer, *data; - unsigned long size; - - if (entry->converted) - return entry; - data = read_sha1_file(sha1, &type, &size); - if (!data) - die("unable to read object %s", sha1_to_hex(sha1)); - - buffer = xmalloc(size); - memcpy(buffer, data, size); - - if (type == OBJ_BLOB) { - write_sha1_file(buffer, size, blob_type, entry->new_sha1); - } else if (type == OBJ_TREE) - convert_tree(buffer, size, entry->new_sha1); - else if (type == OBJ_COMMIT) - convert_commit(buffer, size, entry->new_sha1); - else - die("unknown object type %d in %s", type, sha1_to_hex(sha1)); - entry->converted = 1; - free(buffer); - free(data); - return entry; -} - -int main(int argc, char **argv) -{ - unsigned char sha1[20]; - struct entry *entry; - - setup_git_directory(); - - if (argc != 2) - usage("git-convert-objects "); - if (get_sha1(argv[1], sha1)) - die("Not a valid object name %s", argv[1]); - - entry = convert_entry(sha1); - printf("new sha1: %s\n", sha1_to_hex(entry->new_sha1)); - return 0; -} -- cgit v0.10.2-6-g49f6 From df3a02f6125f7ac82b6e81e3e32cd7ca3c7905ee Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Tue, 25 Sep 2007 08:36:38 +0200 Subject: Make merge-recursive honor diff.renamelimit It might be a sign of source code management gone bad, but when two branches has diverged almost beyond recognition and time has come for the branches to merge, the user is going to need all the help his tool can give him. Honoring diff.renamelimit has great potential as a painkiller in such situations. The painkiller effect could have been achieved by e.g. 'merge.renamelimit', but the flexibility gained by a separate option is questionable: our user would probably expect git to detect renames equally good when merging as when diffing (I known I did). Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/merge-recursive.c b/merge-recursive.c index 19d5f3b..97dcf9b 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -96,6 +96,7 @@ static struct path_list current_directory_set = {NULL, 0, 0, 1}; static int call_depth = 0; static int verbosity = 2; +static int rename_limit = -1; static int buffer_output = 1; static struct output_buffer *output_list, *output_end; @@ -372,6 +373,7 @@ static struct path_list *get_renames(struct tree *tree, diff_setup(&opts); opts.recursive = 1; opts.detect_rename = DIFF_DETECT_RENAME; + opts.rename_limit = rename_limit; opts.output_format = DIFF_FORMAT_NO_OUTPUT; if (diff_setup_done(&opts) < 0) die("diff setup failed"); @@ -1693,6 +1695,10 @@ static int merge_config(const char *var, const char *value) verbosity = git_config_int(var, value); return 0; } + if (!strcasecmp(var, "diff.renamelimit")) { + rename_limit = git_config_int(var, value); + return 0; + } return git_default_config(var, value); } -- cgit v0.10.2-6-g49f6 From 2dbbc45730cd85e311a1bff38a07148a423a4f1e Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 25 Sep 2007 15:05:28 +0200 Subject: gitattributes.txt: Remove a duplicated paragraph about 'ident' and 'crlf' interaction. The order in which 'ident' and 'crlf' are carried out is documented a few paragraphs later again, after 'filter' was introduced. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 46f9d59..676f694 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -145,17 +145,6 @@ sign `$` upon checkout. Any byte sequence that begins with with `$Id$` upon check-in. -Interaction between checkin/checkout attributes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In the check-in codepath, the worktree file is first converted -with `ident` (if specified), and then with `crlf` (again, if -specified and applicable). - -In the check-out codepath, the blob content is first converted -with `crlf`, and then `ident`. - - `filter` ^^^^^^^^ -- cgit v0.10.2-6-g49f6 From 4d84aff356d9c6d86a8d621ee577283ed70e145e Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 25 Sep 2007 15:05:29 +0200 Subject: gitattributes.txt: Be more to the point in the filter driver description. The description was meant to emphasizes that the project should remain usable even if the filter driver was not used. This makes it more explicit and removes the "here is rope to hang yourself" paraphrase. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 676f694..dd51aa1 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -164,11 +164,10 @@ but makes the filter a no-op passthru. The content filtering is done to massage the content into a shape that is more convenient for the platform, filesystem, and the user to use. The keyword here is "more convenient" and not -"turning something unusable into usable". In other words, it is -"hanging yourself because we gave you a long rope" if your -project uses filtering mechanism in such a way that it makes -your project unusable unless the checkout is done with a -specific filter in effect. +"turning something unusable into usable". In other words, the +intent is that if someone unsets the filter driver definition, +or does not have the appropriate filter program, the project +should still be usable. Interaction between checkin/checkout attributes -- cgit v0.10.2-6-g49f6 From be6ff208d8248c3f282df14fdf3c08db57e92007 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 25 Sep 2007 16:42:36 +0100 Subject: rebase -i: commit when continuing after "edit" When doing an "edit" on a commit, editing and git-adding some files, "git rebase -i" complained about a missing "author-script". The idea was that the user would call "git commit --amend" herself. But we can be nice and do that for the user. Noticed by Dmitry Potapov. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 2fa53fd..56e6f7f 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -77,15 +77,16 @@ mark_action_done () { } make_patch () { - parent_sha1=$(git rev-parse --verify "$1"^ 2> /dev/null) + parent_sha1=$(git rev-parse --verify "$1"^) || + die "Cannot get patch for $1^" git diff "$parent_sha1".."$1" > "$DOTEST"/patch + test -f "$DOTEST"/message || + git cat-file commit "$1" | sed "1,/^$/d" > "$DOTEST"/message + test -f "$DOTEST"/author-script || + get_author_ident_from_commit "$1" > "$DOTEST"/author-script } die_with_patch () { - test -f "$DOTEST"/message || - git cat-file commit $sha1 | sed "1,/^$/d" > "$DOTEST"/message - test -f "$DOTEST"/author-script || - get_author_ident_from_commit $sha1 > "$DOTEST"/author-script make_patch "$1" die "$2" } @@ -214,6 +215,7 @@ peek_next_command () { do_next () { test -f "$DOTEST"/message && rm "$DOTEST"/message test -f "$DOTEST"/author-script && rm "$DOTEST"/author-script + test -f "$DOTEST"/amend && rm "$DOTEST"/amend read command sha1 rest < "$TODO" case "$command" in \#|'') @@ -233,6 +235,7 @@ do_next () { pick_one $sha1 || die_with_patch $sha1 "Could not apply $sha1... $rest" make_patch $sha1 + : > "$DOTEST"/amend warn warn "You can amend the commit now, with" warn @@ -330,7 +333,9 @@ do git update-index --refresh && git diff-files --quiet && ! git diff-index --cached --quiet HEAD && - . "$DOTEST"/author-script && + . "$DOTEST"/author-script && { + test ! -f "$DOTEST"/amend || git reset --soft HEAD^ + } && export GIT_AUTHOR_NAME GIT_AUTHOR_NAME GIT_AUTHOR_DATE && git commit -F "$DOTEST"/message -e diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 718c9c1..1af73a4 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -80,7 +80,7 @@ cat "$1".tmp action=pick for line in $FAKE_LINES; do case $line in - squash) + squash|edit) action="$line";; *) echo sed -n "${line}s/^pick/$action/p" @@ -297,4 +297,16 @@ test_expect_success 'ignore patch if in upstream' ' test $HEAD = $(git rev-parse HEAD^) ' +test_expect_success '--continue tries to commit, even for "edit"' ' + parent=$(git rev-parse HEAD^) && + test_tick && + FAKE_LINES="edit 1" git rebase -i HEAD^ && + echo edited > file7 && + git add file7 && + FAKE_COMMIT_MESSAGE="chouette!" git rebase --continue && + test edited = $(git show HEAD:file7) && + git show HEAD | grep chouette && + test $parent = $(git rev-parse HEAD^) +' + test_done -- cgit v0.10.2-6-g49f6 From 376ccb8cbb453343998e734d8a1ce79f57a4e092 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 25 Sep 2007 16:42:51 +0100 Subject: rebase -i: style fixes and minor cleanups This patch indents ";;" consistently with the rest of git's shell scripts, and makes sure that ";;" are before each "esac". It introduces a helper function "has_action", to make it easier to read the intentions of the code. Errors from "git rev-parse --verify" are no longer ignored. Spaces are quoted using single quotes instead of a backslash, for readability. A "test $preserve=f" (missing spaces) was fixed; hashes are no longer written to "$DOTEST"/rewritten/ unnecessarily. We used to quote the message for a squash, only to have "echo" unquote it. Now we use "printf" and do not need to quote to start with. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 56e6f7f..cab5e93 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -41,9 +41,10 @@ output () { test $status != 0 && cat "$DOTEST"/output return $status - ;; + ;; *) "$@" + ;; esac } @@ -63,6 +64,7 @@ comment_for_reflog () { ''|rebase*) GIT_REFLOG_ACTION="rebase -i ($1)" export GIT_REFLOG_ACTION + ;; esac } @@ -96,13 +98,18 @@ die_abort () { die "$1" } +has_action () { + grep -vqe '^$' -e '^#' "$1" +} + pick_one () { no_ff= case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1" test -d "$REWRITTEN" && pick_one_preserving_merges "$@" && return - parent_sha1=$(git rev-parse --verify $sha1^ 2>/dev/null) + parent_sha1=$(git rev-parse --verify $sha1^) || + die "Could not get the parent of $sha1" current_sha1=$(git rev-parse --verify HEAD) if test $no_ff$current_sha1 = $parent_sha1; then output git reset --hard $sha1 @@ -130,7 +137,7 @@ pick_one_preserving_merges () { fast_forward=t preserve=t new_parents= - for p in $(git rev-list --parents -1 $sha1 | cut -d\ -f2-) + for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-) do if test -f "$REWRITTEN"/$p then @@ -142,41 +149,43 @@ pick_one_preserving_merges () { ;; # do nothing; that parent is already there *) new_parents="$new_parents $new_p" + ;; esac fi done case $fast_forward in t) output warn "Fast forward to $sha1" - test $preserve=f && echo $sha1 > "$REWRITTEN"/$sha1 + test $preserve = f || echo $sha1 > "$REWRITTEN"/$sha1 ;; f) test "a$1" = a-n && die "Refusing to squash a merge: $sha1" - first_parent=$(expr "$new_parents" : " \([^ ]*\)") + first_parent=$(expr "$new_parents" : ' \([^ ]*\)') # detach HEAD to current parent output git checkout $first_parent 2> /dev/null || die "Cannot move HEAD to $first_parent" echo $sha1 > "$DOTEST"/current-commit case "$new_parents" in - \ *\ *) + ' '*' '*) # redo merge author_script=$(get_author_ident_from_commit $sha1) eval "$author_script" - msg="$(git cat-file commit $sha1 | \ - sed -e '1,/^$/d' -e "s/[\"\\]/\\\\&/g")" + msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')" # NEEDSWORK: give rerere a chance if ! output git merge $STRATEGY -m "$msg" $new_parents then - echo "$msg" > "$GIT_DIR"/MERGE_MSG + printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG die Error redoing merge $sha1 fi ;; *) output git cherry-pick $STRATEGY "$@" || die_with_patch $sha1 "Could not pick $sha1" + ;; esac + ;; esac } @@ -213,12 +222,11 @@ peek_next_command () { } do_next () { - test -f "$DOTEST"/message && rm "$DOTEST"/message - test -f "$DOTEST"/author-script && rm "$DOTEST"/author-script - test -f "$DOTEST"/amend && rm "$DOTEST"/amend + rm -f "$DOTEST"/message "$DOTEST"/author-script \ + "$DOTEST"/amend || exit read command sha1 rest < "$TODO" case "$command" in - \#|'') + '#'*|'') mark_action_done ;; pick) @@ -246,7 +254,7 @@ do_next () { squash) comment_for_reflog squash - test -z "$(grep -ve '^$' -e '^#' < $DONE)" && + has_action "$DONE" || die "Cannot 'squash' without a previous commit" mark_action_done @@ -256,11 +264,12 @@ do_next () { EDIT_COMMIT= USE_OUTPUT=output cp "$MSG" "$SQUASH_MSG" - ;; + ;; *) EDIT_COMMIT=-e USE_OUTPUT= - test -f "$SQUASH_MSG" && rm "$SQUASH_MSG" + rm -f "$SQUASH_MSG" || exit + ;; esac failed=f @@ -280,11 +289,13 @@ do_next () { warn warn "Could not apply $sha1... $rest" die_with_patch $sha1 "" + ;; esac ;; *) warn "Unknown command: $command $sha1 $rest" die_with_patch $sha1 "Please fix this in the file $TODO." + ;; esac test -s "$TODO" && return @@ -473,17 +484,18 @@ EOF $UPSTREAM...$HEAD | \ sed -n "s/^>/pick /p" >> "$TODO" - test -z "$(grep -ve '^$' -e '^#' < $TODO)" && + has_action "$TODO" || die_abort "Nothing to do" cp "$TODO" "$TODO".backup git_editor "$TODO" || die "Could not execute editor" - test -z "$(grep -ve '^$' -e '^#' < $TODO)" && + has_action "$TODO" || die_abort "Nothing to do" output git checkout $ONTO && do_rest + ;; esac shift done -- cgit v0.10.2-6-g49f6 From dad4e32c4658f12a6eaa2decead5d77678911d95 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 25 Sep 2007 16:43:04 +0100 Subject: rebase -i: Fix numbers in progress report Instead of counting all lines in done and todo, we now count the actions before outputting "$Rebasing ($count/$total)". Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index cab5e93..efa83f6 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -72,8 +72,8 @@ mark_action_done () { sed -e 1q < "$TODO" >> "$DONE" sed -e 1d < "$TODO" >> "$TODO".new mv -f "$TODO".new "$TODO" - count=$(($(wc -l < "$DONE"))) - total=$(($count+$(wc -l < "$TODO"))) + count=$(($(grep -ve '^$' -e '^#' < "$DONE" | wc -l))) + total=$(($count+$(grep -ve '^$' -e '^#' < "$TODO" | wc -l))) printf "Rebasing (%d/%d)\r" $count $total test -z "$VERBOSE" || echo } -- cgit v0.10.2-6-g49f6 From ae830ed7f50377e64b774ce87441ff4538f04ad7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 25 Sep 2007 16:43:44 +0100 Subject: rebase -i: avoid exporting GIT_AUTHOR_* variables It is somewhat unsafe to export the GIT_AUTHOR_* variables, since a later call to git-commit or git-merge could pick them up inadvertently. So avoid the export, using a recipe provided by Johannes Sixt. Incidentally, this fixes authorship of merges with "rebase --preserve -i". Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index efa83f6..d751984 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -174,7 +174,11 @@ pick_one_preserving_merges () { eval "$author_script" msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')" # NEEDSWORK: give rerere a chance - if ! output git merge $STRATEGY -m "$msg" $new_parents + if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \ + GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \ + GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \ + output git merge $STRATEGY -m "$msg" \ + $new_parents then printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG die Error redoing merge $sha1 @@ -281,7 +285,9 @@ do_next () { f) # This is like --amend, but with a different message eval "$author_script" - export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE + GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \ + GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \ + GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \ $USE_OUTPUT git commit -F "$MSG" $EDIT_COMMIT ;; t) -- cgit v0.10.2-6-g49f6 From b06743925c3022ffbbd459431531709df13bbf38 Mon Sep 17 00:00:00 2001 From: Shawn Bohrer Date: Tue, 25 Sep 2007 23:12:46 -0500 Subject: Fix spelling of overridden in documentation Signed-off-by: Shawn Bohrer Signed-off-by: Junio C Hamano diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index 144bc16..eae49c4 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -56,7 +56,7 @@ merge.verbosity:: message if conflicts were detected. Level 1 outputs only conflicts, 2 outputs conflicts and file changes. Level 5 and above outputs debugging information. The default is level 2. - Can be overriden by 'GIT_MERGE_VERBOSITY' environment variable. + Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable. HOW MERGE WORKS diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt index 9c83095..e8b8581 100644 --- a/Documentation/gitignore.txt +++ b/Documentation/gitignore.txt @@ -26,7 +26,7 @@ precedence, the last matching pattern decides the outcome): * Patterns read from a `.gitignore` file in the same directory as the path, or in any parent directory, with patterns in the - higher level files (up to the root) being overriden by those in + higher level files (up to the root) being overridden by those in lower level files down to the directory containing the file. These patterns match relative to the location of the `.gitignore` file. A project normally includes such -- cgit v0.10.2-6-g49f6 From bce92405aebf217a4738bab0df89516910818e68 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 25 Sep 2007 22:02:28 -0700 Subject: core-tutorial: correct URL The tinyurl is incorrect -- it attempts to go to groups.osdl.org, which is gone. Either use the full URL (in patch) or create a new tinyurl for this URL. Is the web page (where I first saw this problem) generated from this txt file? http://www.kernel.org/pub/software/scm/git/docs/core-tutorial.html If not, it needs to be updated also. Signed-off-by: Randy Dunlap Signed-off-by: Junio C Hamano diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt index 4b4fd9a..6b2590d 100644 --- a/Documentation/core-tutorial.txt +++ b/Documentation/core-tutorial.txt @@ -1459,7 +1459,8 @@ Although git is a truly distributed system, it is often convenient to organize your project with an informal hierarchy of developers. Linux kernel development is run this way. There is a nice illustration (page 17, "Merges to Mainline") in -link:http://tinyurl.com/a2jdg[Randy Dunlap's presentation]. +link:http://www.xenotime.net/linux/mentor/linux-mentoring-2006.pdf +[Randy Dunlap's presentation]. It should be stressed that this hierarchy is purely *informal*. There is nothing fundamental in git that enforces the "chain of -- cgit v0.10.2-6-g49f6 From 5166810b1e16b22e342f2181a3535e70c6e7a119 Mon Sep 17 00:00:00 2001 From: Matt Kraai Date: Tue, 25 Sep 2007 18:30:13 -0700 Subject: rebase -i: create .dotest-merge after validating options. Creating .dotest-merge before validating the options prevents both --continue and --interactive from working if the options are invalid, so only create it after validating the options. [jc: however, just moving the creation of DOTEST breaks output] Signed-off-by: Matt Kraai Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index d751984..268a629 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -36,10 +36,9 @@ warn () { output () { case "$VERBOSE" in '') - "$@" > "$DOTEST"/output 2>&1 + output=$("$@" 2>&1 ) status=$? - test $status != 0 && - cat "$DOTEST"/output + test $status != 0 && printf "%s\n" "$output" return $status ;; *) @@ -428,7 +427,6 @@ do require_clean_work_tree - mkdir "$DOTEST" || die "Could not create temporary $DOTEST" if test ! -z "$2" then output git show-ref --verify --quiet "refs/heads/$2" || @@ -440,6 +438,8 @@ do HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?" UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" + mkdir "$DOTEST" || die "Could not create temporary $DOTEST" + test -z "$ONTO" && ONTO=$UPSTREAM : > "$DOTEST"/interactive || die "Could not mark as interactive" -- cgit v0.10.2-6-g49f6 From a5a3878ba7a0da5ee4331f6064d6b50aadbb10ff Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 25 Sep 2007 15:29:42 -0400 Subject: diffcore-rename: cache file deltas We find rename candidates by computing a fingerprint hash of each file, and then comparing those fingerprints. There are inherently O(n^2) comparisons, so it pays in CPU time to hoist the (rather expensive) computation of the fingerprint out of that loop (or to cache it once we have computed it once). Previously, we didn't keep the filespec information around because then we had the potential to consume a great deal of memory. However, instead of keeping all of the filespec data, we can instead just keep the fingerprint. This patch implements and uses diff_free_filespec_data_large to accomplish that goal. We also have to change estimate_similarity not to needlessly repopulate the filespec data when we already have the hash. Practical tests showed 4.5x speedup for a 10% memory usage increase. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano diff --git a/diff.c b/diff.c index 0ee9ea1..35e3c61 100644 --- a/diff.c +++ b/diff.c @@ -1675,7 +1675,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) return 0; } -void diff_free_filespec_data(struct diff_filespec *s) +void diff_free_filespec_data_large(struct diff_filespec *s) { if (s->should_free) free(s->data); @@ -1686,6 +1686,11 @@ void diff_free_filespec_data(struct diff_filespec *s) s->should_free = s->should_munmap = 0; s->data = NULL; } +} + +void diff_free_filespec_data(struct diff_filespec *s) +{ + diff_free_filespec_data_large(s); free(s->cnt_data); s->cnt_data = NULL; } diff --git a/diffcore-rename.c b/diffcore-rename.c index 41b35c3..4fc2000 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -184,7 +184,8 @@ static int estimate_similarity(struct diff_filespec *src, if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE) return 0; - if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0)) + if ((!src->cnt_data && diff_populate_filespec(src, 0)) + || (!dst->cnt_data && diff_populate_filespec(dst, 0))) return 0; /* error but caught downstream */ @@ -377,10 +378,10 @@ void diffcore_rename(struct diff_options *options) m->score = estimate_similarity(one, two, minimum_score); m->name_score = basename_same(one, two); - diff_free_filespec_data(one); + diff_free_filespec_data_large(one); } /* We do not need the text anymore */ - diff_free_filespec_data(two); + diff_free_filespec_data_large(two); dst_cnt++; } /* cost matrix sorted by most to least similar pair */ diff --git a/diffcore.h b/diffcore.h index eef17c4..4bf175b 100644 --- a/diffcore.h +++ b/diffcore.h @@ -48,6 +48,7 @@ extern void fill_filespec(struct diff_filespec *, const unsigned char *, extern int diff_populate_filespec(struct diff_filespec *, int); extern void diff_free_filespec_data(struct diff_filespec *); +extern void diff_free_filespec_data_large(struct diff_filespec *); extern int diff_filespec_is_binary(struct diff_filespec *); struct diff_filepair { -- cgit v0.10.2-6-g49f6 From 55246aac6717e86c14f31391ac903ed810d1a9a0 Mon Sep 17 00:00:00 2001 From: Michal Vitecek Date: Tue, 25 Sep 2007 16:38:46 +0200 Subject: Don't use "" for placeholders and suppress printing of empty user formats. This changes the interporate() to replace entries with NULL values by the empty string, and uses it to interpolate missing fields in custom format output used in git-log and friends. It is most useful to avoid output from %b format for a commit log message that lack any body text. Signed-off-by: Junio C Hamano diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 3894633..0b74eb3 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -85,7 +85,8 @@ static void show_commit(struct commit *commit) pretty_print_commit(revs.commit_format, commit, ~0, &buf, &buflen, revs.abbrev, NULL, NULL, revs.date_mode); - printf("%s%c", buf, hdr_termination); + if (*buf) + printf("%s%c", buf, hdr_termination); free(buf); } maybe_flush_or_die(stdout, "stdout"); diff --git a/commit.c b/commit.c index 99f65ce..c9a1818 100644 --- a/commit.c +++ b/commit.c @@ -917,9 +917,6 @@ long format_commit_message(const struct commit *commit, const void *format, } if (msg[i]) table[IBODY].value = xstrdup(msg + i); - for (i = 0; i < ARRAY_SIZE(table); i++) - if (!table[i].value) - interp_set_entry(table, i, ""); do { char *buf = *buf_p; diff --git a/interpolate.c b/interpolate.c index 0082677..2f727cd 100644 --- a/interpolate.c +++ b/interpolate.c @@ -76,8 +76,12 @@ unsigned long interpolate(char *result, unsigned long reslen, /* Check for valid interpolation. */ if (i < ninterps) { value = interps[i].value; - valuelen = strlen(value); + if (!value) { + src += namelen; + continue; + } + valuelen = strlen(value); if (newlen + valuelen + 1 < reslen) { /* Substitute. */ strncpy(dest, value, valuelen); diff --git a/log-tree.c b/log-tree.c index a642371..79e3dee 100644 --- a/log-tree.c +++ b/log-tree.c @@ -298,7 +298,8 @@ void show_log(struct rev_info *opt, const char *sep) if (opt->show_log_size) printf("log size %i\n", len); - printf("%s%s%s", msgbuf, extra, sep); + if (*msgbuf) + printf("%s%s%s", msgbuf, extra, sep); free(msgbuf); } diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh index ad6d0b8..1e4541a 100755 --- a/t/t6006-rev-list-format.sh +++ b/t/t6006-rev-list-format.sh @@ -79,9 +79,7 @@ EOF test_format encoding %e <<'EOF' commit 131a310eb913d107dd3c09a65d1651175898735d - commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 - EOF test_format subject %s <<'EOF' @@ -93,9 +91,7 @@ EOF test_format body %b <<'EOF' commit 131a310eb913d107dd3c09a65d1651175898735d - commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 - EOF test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<'EOF' @@ -121,9 +117,7 @@ test_format complex-encoding %e <<'EOF' commit f58db70b055c5718631e5c61528b28b12090cdea iso8859-1 commit 131a310eb913d107dd3c09a65d1651175898735d - commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 - EOF test_format complex-subject %s <<'EOF' @@ -142,9 +136,7 @@ and it will be encoded in iso8859-1. We should therefore include an iso8859 character: ¡bueno! commit 131a310eb913d107dd3c09a65d1651175898735d - commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 - EOF test_done diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index f11ada8..abbf54b 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -81,7 +81,7 @@ test_expect_success 'explicit commit message should override template' ' git add foo && GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" \ -m "command line msg" && - commit_msg_is "command line msg" + commit_msg_is "command line msg" ' test_expect_success 'commit message from file should override template' ' @@ -90,7 +90,7 @@ test_expect_success 'commit message from file should override template' ' echo "standard input msg" | GIT_EDITOR=../t7500/add-content git commit \ --template "$TEMPLATE" --file - && - commit_msg_is "standard input msg" + commit_msg_is "standard input msg" ' test_done -- cgit v0.10.2-6-g49f6 From 73697a0b572f7f216d8355d83bf69e9b42e9a077 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 25 Sep 2007 16:43:15 +0100 Subject: rebase -i: work on a detached HEAD Earlier, rebase -i refused to rebase a detached HEAD. Now it no longer does. Incidentally, this fixes "git gc --auto" shadowing the true exit status. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 8e6e943..823291d 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -317,17 +317,20 @@ do_next () { else NEWHEAD=$(git rev-parse HEAD) fi && - message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" && - git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD && - git symbolic-ref HEAD $HEADNAME && { + case $HEADNAME in + refs/*) + message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" && + git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD && + git symbolic-ref HEAD $HEADNAME + ;; + esac && { test ! -f "$DOTEST"/verbose || git diff --stat $(cat "$DOTEST"/head)..HEAD } && rm -rf "$DOTEST" && + git gc --auto && warn "Successfully rebased and updated $HEADNAME." - git gc --auto - exit } @@ -367,7 +370,11 @@ do HEADNAME=$(cat "$DOTEST"/head-name) HEAD=$(cat "$DOTEST"/head) - git symbolic-ref HEAD $HEADNAME && + case $HEADNAME in + refs/*) + git symbolic-ref HEAD $HEADNAME + ;; + esac && output git reset --hard $HEAD && rm -rf "$DOTEST" exit @@ -445,8 +452,8 @@ do test -z "$ONTO" && ONTO=$UPSTREAM : > "$DOTEST"/interactive || die "Could not mark as interactive" - git symbolic-ref HEAD > "$DOTEST"/head-name || - die "Could not get HEAD" + git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null || + echo "detached HEAD" > "$DOTEST"/head-name echo $HEAD > "$DOTEST"/head echo $UPSTREAM > "$DOTEST"/upstream diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 1af73a4..f2214dd 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -309,4 +309,12 @@ test_expect_success '--continue tries to commit, even for "edit"' ' test $parent = $(git rev-parse HEAD^) ' +test_expect_success 'rebase a detached HEAD' ' + grandparent=$(git rev-parse HEAD~2) && + git checkout $(git rev-parse HEAD) && + test_tick && + FAKE_LINES="2 1" git rebase -i HEAD~2 && + test $grandparent = $(git rev-parse HEAD~2) +' + test_done -- cgit v0.10.2-6-g49f6 From a8f3e2219c237661a30b54fe23d58e055f0b548c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Sep 2007 02:26:06 -0700 Subject: strbuf_setlen(): do not barf on setting length of an empty buffer to 0 strbuf_setlen() expect to be able to NUL terminate the buffer, but a completely empty strbuf could have an empty buffer with 0 allocation; both the assert() and the assignment for NUL termination would fail. Signed-off-by: Junio C Hamano diff --git a/strbuf.h b/strbuf.h index 567e2d1..3b19de3 100644 --- a/strbuf.h +++ b/strbuf.h @@ -65,14 +65,17 @@ static inline void strbuf_swap(struct strbuf *a, struct strbuf *b) { static inline size_t strbuf_avail(struct strbuf *sb) { return sb->alloc ? sb->alloc - sb->len - 1 : 0; } + +extern void strbuf_grow(struct strbuf *, size_t); + static inline void strbuf_setlen(struct strbuf *sb, size_t len) { - assert (len < sb->alloc); + if (!sb->alloc) + strbuf_grow(sb, 0); + assert(len < sb->alloc); sb->len = len; sb->buf[len] = '\0'; } -extern void strbuf_grow(struct strbuf *, size_t); - /*----- content related -----*/ extern void strbuf_rtrim(struct strbuf *); -- cgit v0.10.2-6-g49f6 From 45f66f64636350b67eaf6832b0c424592be6ddda Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 24 Sep 2007 11:25:03 +0200 Subject: Add strbuf_cmp. Signed-off-by: Junio C Hamano diff --git a/strbuf.c b/strbuf.c index dcb725d..d5e92ee 100644 --- a/strbuf.c +++ b/strbuf.c @@ -50,6 +50,18 @@ void strbuf_rtrim(struct strbuf *sb) sb->buf[sb->len] = '\0'; } +int strbuf_cmp(struct strbuf *a, struct strbuf *b) +{ + int cmp; + if (a->len < b->len) { + cmp = memcmp(a->buf, b->buf, a->len); + return cmp ? cmp : -1; + } else { + cmp = memcmp(a->buf, b->buf, b->len); + return cmp ? cmp : a->len != b->len; + } +} + void strbuf_splice(struct strbuf *sb, size_t pos, size_t len, const void *data, size_t dlen) { diff --git a/strbuf.h b/strbuf.h index 3b19de3..fd68389 100644 --- a/strbuf.h +++ b/strbuf.h @@ -78,6 +78,7 @@ static inline void strbuf_setlen(struct strbuf *sb, size_t len) { /*----- content related -----*/ extern void strbuf_rtrim(struct strbuf *); +extern int strbuf_cmp(struct strbuf *, struct strbuf *); /*----- add data in your buffer -----*/ static inline void strbuf_addch(struct strbuf *sb, int c) { -- cgit v0.10.2-6-g49f6 From 8289b6209552a57c255561a2585d0edbe96d62d3 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 24 Sep 2007 11:25:04 +0200 Subject: Make builtin-rerere use of strbuf nicer and more efficient. memory is now reused across hunks. Signed-off-by: Junio C Hamano diff --git a/builtin-rerere.c b/builtin-rerere.c index 58288f6..d331772 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -72,13 +72,9 @@ static int handle_file(const char *path, SHA_CTX ctx; char buf[1024]; int hunk = 0, hunk_no = 0; - struct strbuf minus, plus; - struct strbuf *one = &minus, *two = + + struct strbuf one, two; FILE *f = fopen(path, "r"); - FILE *out; - - strbuf_init(&minus, 0); - strbuf_init(&plus, 0); + FILE *out = NULL; if (!f) return error("Could not open %s", path); @@ -89,51 +85,48 @@ static int handle_file(const char *path, fclose(f); return error("Could not write %s", output); } - } else - out = NULL; + } if (sha1) SHA1_Init(&ctx); + strbuf_init(&one, 0); + strbuf_init(&two, 0); while (fgets(buf, sizeof(buf), f)) { if (!prefixcmp(buf, "<<<<<<< ")) hunk = 1; else if (!prefixcmp(buf, "=======")) hunk = 2; else if (!prefixcmp(buf, ">>>>>>> ")) { - int one_is_longer = (one->len > two->len); - int common_len = one_is_longer ? two->len : one->len; - int cmp = memcmp(one->buf, two->buf, common_len); + int cmp = strbuf_cmp(&one, &two); hunk_no++; hunk = 0; - if ((cmp > 0) || ((cmp == 0) && one_is_longer)) { - struct strbuf *swap = one; - one = two; - two = swap; + if (cmp > 0) { + strbuf_swap(&one, &two); } if (out) { fputs("<<<<<<<\n", out); - fwrite(one->buf, one->len, 1, out); + fwrite(one.buf, one.len, 1, out); fputs("=======\n", out); - fwrite(two->buf, two->len, 1, out); + fwrite(two.buf, two.len, 1, out); fputs(">>>>>>>\n", out); } if (sha1) { - SHA1_Update(&ctx, one->buf, one->len); - SHA1_Update(&ctx, "\0", 1); - SHA1_Update(&ctx, two->buf, two->len); - SHA1_Update(&ctx, "\0", 1); + SHA1_Update(&ctx, one.buf, one.len + 1); + SHA1_Update(&ctx, two.buf, two.len + 1); } - strbuf_release(one); - strbuf_release(two); + strbuf_reset(&one); + strbuf_reset(&two); } else if (hunk == 1) - strbuf_addstr(one, buf); + strbuf_addstr(&one, buf); else if (hunk == 2) - strbuf_addstr(two, buf); + strbuf_addstr(&two, buf); else if (out) fputs(buf, out); } + strbuf_release(&one); + strbuf_release(&two); fclose(f); if (out) -- cgit v0.10.2-6-g49f6 From 1dffb8fa8056860e769f3a0c6e232d436f5a5c17 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Tue, 25 Sep 2007 10:22:44 +0200 Subject: Small cache_tree_write refactor. This function cannot fail, make it void. Also make write_one act on a const char* instead of a char*. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/cache-tree.c b/cache-tree.c index 5471844..50b3526 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -369,10 +369,8 @@ int cache_tree_update(struct cache_tree *it, return 0; } -static void write_one(struct cache_tree *it, - char *path, - int pathlen, - struct strbuf *buffer) +static void write_one(struct strbuf *buffer, struct cache_tree *it, + const char *path, int pathlen) { int i; @@ -407,20 +405,13 @@ static void write_one(struct cache_tree *it, prev->name, prev->namelen) <= 0) die("fatal - unsorted cache subtree"); } - write_one(down->cache_tree, down->name, down->namelen, buffer); + write_one(buffer, down->cache_tree, down->name, down->namelen); } } -void *cache_tree_write(struct cache_tree *root, unsigned long *size_p) +void cache_tree_write(struct strbuf *sb, struct cache_tree *root) { - char path[PATH_MAX]; - struct strbuf buffer; - - path[0] = 0; - strbuf_init(&buffer, 0); - write_one(root, path, 0, &buffer); - *size_p = buffer.len; - return strbuf_detach(&buffer); + write_one(sb, root, "", 0); } static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) diff --git a/cache-tree.h b/cache-tree.h index 119407e..8243228 100644 --- a/cache-tree.h +++ b/cache-tree.h @@ -22,7 +22,7 @@ void cache_tree_free(struct cache_tree **); void cache_tree_invalidate_path(struct cache_tree *, const char *); struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const char *); -void *cache_tree_write(struct cache_tree *root, unsigned long *size_p); +void cache_tree_write(struct strbuf *, struct cache_tree *root); struct cache_tree *cache_tree_read(const char *buffer, unsigned long size); int cache_tree_fully_valid(struct cache_tree *); diff --git a/read-cache.c b/read-cache.c index 2e40a34..56202d1 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1136,7 +1136,7 @@ int write_index(struct index_state *istate, int newfd) { SHA_CTX c; struct cache_header hdr; - int i, removed; + int i, err, removed; struct cache_entry **cache = istate->cache; int entries = istate->cache_nr; @@ -1165,16 +1165,15 @@ int write_index(struct index_state *istate, int newfd) /* Write extension data here */ if (istate->cache_tree) { - unsigned long sz; - void *data = cache_tree_write(istate->cache_tree, &sz); - if (data && - !write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) && - !ce_write(&c, newfd, data, sz)) - free(data); - else { - free(data); + struct strbuf sb; + + strbuf_init(&sb, 0); + cache_tree_write(&sb, istate->cache_tree); + err = write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sb.len) < 0 + || ce_write(&c, newfd, sb.buf, sb.len) < 0; + strbuf_release(&sb); + if (err) return -1; - } } return ce_flush(&c, newfd); } -- cgit v0.10.2-6-g49f6 From 44b2476a1278c87d6985993b5e6fd45f1e5f08f2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 25 Sep 2007 17:27:54 -0700 Subject: send-email --smtp-server-port: allow overriding the default port You can use --smtp-server-port option to specify a port different from the default (typically, SMTP servers listen to smtp port 25 and ssmtp port 465). Users should be aware that sending auth info over non-ssl connections may be unsafe or just may not work at all depending on SMTP server config. Signed-off-by: Glenn Rempe Signed-off-by: Junio C Hamano diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 1ec61af..3727776 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -91,6 +91,11 @@ The --cc option must be repeated for each user you want on the cc list. `/usr/lib/sendmail` if such program is available, or `localhost` otherwise. +--smtp-server-port:: + Specifies a port different from the default port (SMTP + servers typically listen to smtp port 25 and ssmtp port + 465). + --smtp-user, --smtp-pass:: Username and password for SMTP-AUTH. Defaults are the values of the configuration values 'sendemail.smtpuser' and diff --git a/git-send-email.perl b/git-send-email.perl index 4031e86..62e1429 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -77,7 +77,10 @@ Options: the default section. --smtp-server If set, specifies the outgoing SMTP server to use. - Defaults to localhost. + Defaults to localhost. Port number can be specified here with + hostname:port format or by using --smtp-server-port option. + + --smtp-server-port Specify a port on the outgoing SMTP server to connect to. --smtp-user The username for SMTP-AUTH. @@ -172,8 +175,8 @@ my ($quiet, $dry_run) = (0, 0); # Variables with corresponding config settings my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd); -my ($smtp_server, $smtp_authuser, $smtp_authpass, $smtp_ssl); -my ($identity, $aliasfiletype, @alias_files); +my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_authpass, $smtp_ssl); +my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts); my %config_bool_settings = ( "thread" => [\$thread, 1], @@ -185,6 +188,7 @@ my %config_bool_settings = ( my %config_settings = ( "smtpserver" => \$smtp_server, + "smtpserverport" => \$smtp_server_port, "smtpuser" => \$smtp_authuser, "smtppass" => \$smtp_authpass, "cccmd" => \$cc_cmd, @@ -204,6 +208,7 @@ my $rc = GetOptions("sender|from=s" => \$sender, "bcc=s" => \@bcclist, "chain-reply-to!" => \$chain_reply_to, "smtp-server=s" => \$smtp_server, + "smtp-server-port=s" => \$smtp_server_port, "smtp-user=s" => \$smtp_authuser, "smtp-pass=s" => \$smtp_authpass, "smtp-ssl!" => \$smtp_ssl, @@ -602,16 +607,30 @@ X-Mailer: git-send-email $gitversion print $sm "$header\n$message"; close $sm or die $?; } else { + + if (!defined $smtp_server) { + die "The required SMTP server is not properly defined." + } + if ($smtp_ssl) { + $smtp_server_port ||= 465; # ssmtp require Net::SMTP::SSL; - $smtp ||= Net::SMTP::SSL->new( $smtp_server, Port => 465 ); + $smtp ||= Net::SMTP::SSL->new($smtp_server, Port => $smtp_server_port); } else { require Net::SMTP; - $smtp ||= Net::SMTP->new( $smtp_server ); + $smtp ||= Net::SMTP->new((defined $smtp_server_port) + ? "$smtp_server:$smtp_server_port" + : $smtp_server); + } + + if (!$smtp) { + die "Unable to initialize SMTP properly. Is there something wrong with your config?"; + } + + if ((defined $smtp_authuser) && (defined $smtp_authpass)) { + $smtp->auth( $smtp_authuser, $smtp_authpass ) or die $smtp->message; } - $smtp->auth( $smtp_authuser, $smtp_authpass ) - or die $smtp->message if (defined $smtp_authuser); $smtp->mail( $raw_from ) or die $smtp->message; $smtp->to( @recipients ) or die $smtp->message; $smtp->data or die $smtp->message; -- cgit v0.10.2-6-g49f6 From 102c2338da0b0954a04742f5cbe307fa6b49f225 Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Tue, 11 Sep 2007 05:17:28 +0200 Subject: Move make_cache_entry() from merge-recursive.c into read-cache.c The function make_cache_entry() is too useful to be hidden away in merge-recursive. So move it to libgit.a (exposing it via cache.h). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index 70abbd5..fc195bc 100644 --- a/cache.h +++ b/cache.h @@ -264,6 +264,7 @@ extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int reall extern int remove_index_entry_at(struct index_state *, int pos); extern int remove_file_from_index(struct index_state *, const char *path); extern int add_file_to_index(struct index_state *, const char *path, int verbose); +extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh); extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, int); extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int); diff --git a/merge-recursive.c b/merge-recursive.c index 16f6a0f..19d5f3b 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -171,30 +171,6 @@ static void output_commit_title(struct commit *commit) } } -static struct cache_entry *make_cache_entry(unsigned int mode, - const unsigned char *sha1, const char *path, int stage, int refresh) -{ - int size, len; - struct cache_entry *ce; - - if (!verify_path(path)) - return NULL; - - len = strlen(path); - size = cache_entry_size(len); - ce = xcalloc(1, size); - - hashcpy(ce->sha1, sha1); - memcpy(ce->name, path, len); - ce->ce_flags = create_ce_flags(len, stage); - ce->ce_mode = create_ce_mode(mode); - - if (refresh) - return refresh_cache_entry(ce, 0); - - return ce; -} - static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh, int options) { diff --git a/read-cache.c b/read-cache.c index 8b1c94e..536f4d0 100644 --- a/read-cache.c +++ b/read-cache.c @@ -434,6 +434,31 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose) return 0; } +struct cache_entry *make_cache_entry(unsigned int mode, + const unsigned char *sha1, const char *path, int stage, + int refresh) +{ + int size, len; + struct cache_entry *ce; + + if (!verify_path(path)) + return NULL; + + len = strlen(path); + size = cache_entry_size(len); + ce = xcalloc(1, size); + + hashcpy(ce->sha1, sha1); + memcpy(ce->name, path, len); + ce->ce_flags = create_ce_flags(len, stage); + ce->ce_mode = create_ce_mode(mode); + + if (refresh) + return refresh_cache_entry(ce, 0); + + return ce; +} + int ce_same_name(struct cache_entry *a, struct cache_entry *b) { int len = ce_namelen(a); -- cgit v0.10.2-6-g49f6 From 26b28007689d27a921ea90e5a29fc8eb74b0d297 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 17 Sep 2007 23:34:06 +0100 Subject: apply: get rid of --index-info in favor of --build-fake-ancestor git-am used "git apply -z --index-info" to find the original versions of the files touched by the diff, to be able to do an inexpensive three-way merge. This operation makes only sense in a repository, since the index information in the diff refers to blobs, which have to be present in the current repository. Therefore, teach "git apply" a mode to write out the result as an index file to begin with, obviating the need for scripts to do it themselves. The sole user for --index-info is "git am" is converted to use --build-fake-ancestor in this patch. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt index 4c7e3a2..c1c54bf 100644 --- a/Documentation/git-apply.txt +++ b/Documentation/git-apply.txt @@ -10,7 +10,7 @@ SYNOPSIS -------- [verse] 'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] - [--apply] [--no-add] [--index-info] [-R | --reverse] + [--apply] [--no-add] [--build-fake-ancestor ] [-R | --reverse] [--allow-binary-replacement | --binary] [--reject] [-z] [-pNUM] [-CNUM] [--inaccurate-eof] [--cached] [--whitespace=] @@ -63,12 +63,15 @@ OPTIONS cached data, apply the patch, and store the result in the index, without using the working tree. This implies '--index'. ---index-info:: +--build-fake-ancestor :: Newer git-diff output has embedded 'index information' for each blob to help identify the original version that the patch applies to. When this flag is given, and if - the original version of the blob is available locally, - outputs information about them to the standard output. + the original versions of the blobs is available locally, + builds a temporary index containing those blobs. ++ +When a pure mode change is encountered (which has no index information), +the information is read from the current index instead. -R, --reverse:: Apply the patch in reverse. diff --git a/builtin-apply.c b/builtin-apply.c index bd96977..5cc90e6 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -41,7 +41,7 @@ static int apply_in_reverse; static int apply_with_reject; static int apply_verbosely; static int no_add; -static int show_index_info; +static const char *fake_ancestor; static int line_termination = '\n'; static unsigned long p_context = ULONG_MAX; static const char apply_usage[] = @@ -2248,9 +2248,12 @@ static int get_current_sha1(const char *path, unsigned char *sha1) return 0; } -static void show_index_list(struct patch *list) +/* Build an index that contains the just the files needed for a 3way merge */ +static void build_fake_ancestor(struct patch *list, const char *filename) { struct patch *patch; + struct index_state result = { 0 }; + int fd; /* Once we start supporting the reverse patch, it may be * worth showing the new sha1 prefix, but until then... @@ -2258,11 +2261,12 @@ static void show_index_list(struct patch *list) for (patch = list; patch; patch = patch->next) { const unsigned char *sha1_ptr; unsigned char sha1[20]; + struct cache_entry *ce; const char *name; name = patch->old_name ? patch->old_name : patch->new_name; if (0 < patch->is_new) - sha1_ptr = null_sha1; + continue; else if (get_sha1(patch->old_sha1_prefix, sha1)) /* git diff has no index line for mode/type changes */ if (!patch->lines_added && !patch->lines_deleted) { @@ -2277,13 +2281,16 @@ static void show_index_list(struct patch *list) else sha1_ptr = sha1; - printf("%06o %s ",patch->old_mode, sha1_to_hex(sha1_ptr)); - if (line_termination && quote_c_style(name, NULL, NULL, 0)) - quote_c_style(name, NULL, stdout, 0); - else - fputs(name, stdout); - putchar(line_termination); + ce = make_cache_entry(patch->old_mode, sha1_ptr, name, 0, 0); + if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD)) + die ("Could not add %s to temporary index", name); } + + fd = open(filename, O_WRONLY | O_CREAT, 0666); + if (fd < 0 || write_index(&result, fd) || close(fd)) + die ("Could not write temporary index to %s", filename); + + discard_index(&result); } static void stat_patch_list(struct patch *patch) @@ -2805,8 +2812,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) if (apply && write_out_results(list, skipped_patch)) exit(1); - if (show_index_info) - show_index_list(list); + if (fake_ancestor) + build_fake_ancestor(list, fake_ancestor); if (diffstat) stat_patch_list(list); @@ -2914,9 +2921,11 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) apply = 1; continue; } - if (!strcmp(arg, "--index-info")) { + if (!strcmp(arg, "--build-fake-ancestor")) { apply = 0; - show_index_info = 1; + if (++i >= argc) + die ("need a filename"); + fake_ancestor = argv[i]; continue; } if (!strcmp(arg, "-z")) { diff --git a/git-am.sh b/git-am.sh index b66173c..32c46d7 100755 --- a/git-am.sh +++ b/git-am.sh @@ -62,10 +62,8 @@ fall_back_3way () { mkdir "$dotest/patch-merge-tmp-dir" # First see if the patch records the index info that we can use. - git apply -z --index-info "$dotest/patch" \ - >"$dotest/patch-merge-index-info" && - GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \ - git update-index -z --index-info <"$dotest/patch-merge-index-info" && + git apply --build-fake-ancestor "$dotest/patch-merge-tmp-index" \ + "$dotest/patch" && GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \ git write-tree >"$dotest/patch-merge-base+" || cannot_fallback "Repository lacks necessary blobs to fall back on 3-way merge." -- cgit v0.10.2-6-g49f6 From b4833a2c62578bdbfd300e296702214cb1b9a601 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Sep 2007 23:34:01 -0700 Subject: rerere: Fix use of an empty strbuf.buf The code incorrectly assumed that strbuf.buf is always an allocated piece of memory that has NUL at offset strbuf.len. That assumption does not hold for a freshly initialized empty strbuf. Signed-off-by: Junio C Hamano diff --git a/builtin-rerere.c b/builtin-rerere.c index d331772..b820674 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -113,8 +113,10 @@ static int handle_file(const char *path, fputs(">>>>>>>\n", out); } if (sha1) { - SHA1_Update(&ctx, one.buf, one.len + 1); - SHA1_Update(&ctx, two.buf, two.len + 1); + SHA1_Update(&ctx, one.buf ? one.buf : "", + one.len + 1); + SHA1_Update(&ctx, two.buf ? two.buf : "", + two.len + 1); } strbuf_reset(&one); strbuf_reset(&two); -- cgit v0.10.2-6-g49f6 From a9390b9fcefb18c4ccdb521086a051bc9112e03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 17 Sep 2007 20:06:46 -0400 Subject: Add strbuf_read_file(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano diff --git a/builtin-tag.c b/builtin-tag.c index 82ebda1..fcbf9bb 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -22,7 +22,6 @@ static void launch_editor(const char *path, struct strbuf *buffer) const char *editor, *terminal; struct child_process child; const char *args[3]; - int fd; editor = getenv("GIT_EDITOR"); if (!editor && editor_program) @@ -52,13 +51,9 @@ static void launch_editor(const char *path, struct strbuf *buffer) if (run_command(&child)) die("There was a problem with the editor %s.", editor); - fd = open(path, O_RDONLY); - if (fd < 0) - die("could not open '%s': %s", path, strerror(errno)); - if (strbuf_read(buffer, fd, 0) < 0) { - die("could not read message file '%s': %s", path, strerror(errno)); - } - close(fd); + if (strbuf_read_file(buffer, path) < 0) + die("could not read message file '%s': %s", + path, strerror(errno)); } struct tag_filter { diff --git a/strbuf.c b/strbuf.c index d5e92ee..d1e338b 100644 --- a/strbuf.c +++ b/strbuf.c @@ -177,3 +177,18 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term) sb->buf[sb->len] = '\0'; return 0; } + +int strbuf_read_file(struct strbuf *sb, const char *path) +{ + int fd, len; + + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + len = strbuf_read(sb, fd, 0); + close(fd); + if (len < 0) + return -1; + + return len; +} diff --git a/strbuf.h b/strbuf.h index fd68389..d4d9e56 100644 --- a/strbuf.h +++ b/strbuf.h @@ -108,6 +108,7 @@ extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); extern size_t strbuf_fread(struct strbuf *, size_t, FILE *); /* XXX: if read fails, any partial read is undone */ extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint); +extern int strbuf_read_file(struct strbuf *sb, const char *path); extern int strbuf_getline(struct strbuf *, FILE *, int); -- cgit v0.10.2-6-g49f6 From 6d69b6f6ac27ab6f71a10da34b813ca25fd2a358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 17 Sep 2007 20:06:45 -0400 Subject: Clean up stripspace a bit, use strbuf even more. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano diff --git a/builtin-stripspace.c b/builtin-stripspace.c index 1ce2847..c0b2130 100644 --- a/builtin-stripspace.c +++ b/builtin-stripspace.c @@ -8,17 +8,13 @@ */ static size_t cleanup(char *line, size_t len) { - if (len) { - if (line[len - 1] == '\n') - len--; - - while (len) { - unsigned char c = line[len - 1]; - if (!isspace(c)) - break; - len--; - } + while (len) { + unsigned char c = line[len - 1]; + if (!isspace(c)) + break; + len--; } + return len; } @@ -34,42 +30,42 @@ static size_t cleanup(char *line, size_t len) * If the input has only empty lines and spaces, * no output will be produced. * - * If last line has a newline at the end, it will be removed. + * If last line does not have a newline at the end, one is added. * * Enable skip_comments to skip every line starting with "#". */ -size_t stripspace(char *buffer, size_t length, int skip_comments) +void stripspace(struct strbuf *sb, int skip_comments) { - int empties = -1; + int empties = 0; size_t i, j, len, newlen; char *eol; - for (i = j = 0; i < length; i += len, j += newlen) { - eol = memchr(buffer + i, '\n', length - i); - len = eol ? eol - (buffer + i) + 1 : length - i; + /* We may have to add a newline. */ + strbuf_grow(sb, 1); - if (skip_comments && len && buffer[i] == '#') { + for (i = j = 0; i < sb->len; i += len, j += newlen) { + eol = memchr(sb->buf + i, '\n', sb->len - i); + len = eol ? eol - (sb->buf + i) + 1 : sb->len - i; + + if (skip_comments && len && sb->buf[i] == '#') { newlen = 0; continue; } - newlen = cleanup(buffer + i, len); + newlen = cleanup(sb->buf + i, len); /* Not just an empty line? */ if (newlen) { - if (empties != -1) - buffer[j++] = '\n'; - if (empties > 0) - buffer[j++] = '\n'; + if (empties > 0 && j > 0) + sb->buf[j++] = '\n'; empties = 0; - memmove(buffer + j, buffer + i, newlen); - continue; + memmove(sb->buf + j, sb->buf + i, newlen); + sb->buf[newlen + j++] = '\n'; + } else { + empties++; } - if (empties < 0) - continue; - empties++; } - return j; + strbuf_setlen(sb, j); } int cmd_stripspace(int argc, const char **argv, const char *prefix) @@ -85,9 +81,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix) if (strbuf_read(&buf, 0, 1024) < 0) die("could not read the input"); - strbuf_setlen(&buf, stripspace(buf.buf, buf.len, strip_comments)); - if (buf.len) - strbuf_addch(&buf, '\n'); + stripspace(&buf, strip_comments); write_or_die(1, buf.buf, buf.len); strbuf_release(&buf); diff --git a/builtin-tag.c b/builtin-tag.c index fcbf9bb..6132cac 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -291,14 +291,11 @@ static void create_tag(const unsigned char *object, const char *tag, free(path); } - strbuf_setlen(buf, stripspace(buf->buf, buf->len, 1)); + stripspace(buf, 1); if (!message && !buf->len) die("no tag message?"); - /* insert the header and add the '\n' if needed: */ - if (buf->len) - strbuf_addch(buf, '\n'); strbuf_insert(buf, 0, header_buf, header_len); if (sign && do_sign(buf) < 0) diff --git a/builtin.h b/builtin.h index 03ee7bf..d6f2c76 100644 --- a/builtin.h +++ b/builtin.h @@ -7,7 +7,6 @@ extern const char git_version_string[]; extern const char git_usage_string[]; extern void help_unknown_cmd(const char *cmd); -extern size_t stripspace(char *buffer, size_t length, int skip_comments); extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); extern void prune_packed_objects(int); diff --git a/strbuf.h b/strbuf.h index d4d9e56..5657e3d 100644 --- a/strbuf.h +++ b/strbuf.h @@ -112,4 +112,6 @@ extern int strbuf_read_file(struct strbuf *sb, const char *path); extern int strbuf_getline(struct strbuf *, FILE *, int); +extern void stripspace(struct strbuf *buf, int skip_comments); + #endif /* STRBUF_H */ -- cgit v0.10.2-6-g49f6 From 9f569fe58f2e9a6547ae75971ebf3910be3d5872 Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Thu, 27 Sep 2007 13:30:59 -0700 Subject: quiltimport: Skip non-existent patches When quiltimport encounters a non-existent patch in the series file, just skip to the next patch. This matches the behavior of quilt. Signed-off-by: Dan Nicholson Signed-off-by: Junio C Hamano diff --git a/git-quiltimport.sh b/git-quiltimport.sh index 74a54d5..880c81d 100755 --- a/git-quiltimport.sh +++ b/git-quiltimport.sh @@ -71,6 +71,10 @@ commit=$(git rev-parse HEAD) mkdir $tmp_dir || exit 2 for patch_name in $(grep -v '^#' < "$QUILT_PATCHES/series" ); do + if ! [ -f "$QUILT_PATCHES/$patch_name" ] ; then + echo "$patch_name doesn't exist. Skipping." + continue + fi echo $patch_name git mailinfo "$tmp_msg" "$tmp_patch" \ <"$QUILT_PATCHES/$patch_name" >"$tmp_info" || exit 3 -- cgit v0.10.2-6-g49f6 From 769f39861b73a374f116fcb913e5ff276fe92751 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 27 Sep 2007 14:41:23 -0700 Subject: Mergetool generating blank files (1.5.3) When mergetool is run from a subdirectory, "ls-files -u" nicely limits the output to conflicted files in that directory, but we need to give the full path to cat-file plumbing to grab the contents of stages. Signed-off-by: Junio C Hamano Signed-off-by: "Theodore Ts'o" diff --git a/git-mergetool.sh b/git-mergetool.sh index a0e44f7..e00682a 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -12,6 +12,7 @@ USAGE='[--tool=tool] [file to merge] ...' SUBDIRECTORY_OK=Yes . git-sh-setup require_work_tree +prefix=$(git rev-parse --show-prefix) # Returns true if the mode reflects a symlink is_symlink () { @@ -162,9 +163,9 @@ merge_file () { local_mode=`git ls-files -u -- "$path" | awk '{if ($3==2) print $1;}'` remote_mode=`git ls-files -u -- "$path" | awk '{if ($3==3) print $1;}'` - base_present && git cat-file blob ":1:$path" > "$BASE" 2>/dev/null - local_present && git cat-file blob ":2:$path" > "$LOCAL" 2>/dev/null - remote_present && git cat-file blob ":3:$path" > "$REMOTE" 2>/dev/null + base_present && git cat-file blob ":1:$prefix$path" >"$BASE" 2>/dev/null + local_present && git cat-file blob ":2:$prefix$path" >"$LOCAL" 2>/dev/null + remote_present && git cat-file blob ":3:$prefix$path" >"$REMOTE" 2>/dev/null if test -z "$local_mode" -o -z "$remote_mode"; then echo "Deleted merge conflict for '$path':" -- cgit v0.10.2-6-g49f6 From f6e0e559340af6e300b63da061fa05ff07e3d6f6 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 28 Sep 2007 21:23:22 -0400 Subject: mergetool: fix emerge when running in a subdirectory Only pass the basename of the output filename when to emerge, since emerge interprets non-absolute pathnames relative to the containing directory of the output buffer. Thanks to Kelvie Wong for pointing this out. Signed-off-by: "Theodore Ts'o" diff --git a/git-mergetool.sh b/git-mergetool.sh index e00682a..a92019a 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -252,9 +252,9 @@ merge_file () { ;; emerge) if base_present ; then - emacs -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$path" + emacs -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")" else - emacs -f emerge-files-command "$LOCAL" "$REMOTE" "$path" + emacs -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$path")" fi status=$? save_backup -- cgit v0.10.2-6-g49f6 From 208fe3ac7e1fd5be173cae1db4fa776390bc4e7d Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 28 Sep 2007 22:26:05 -0400 Subject: mergetool: Fix typo in options passed to kdiff3 Fix missing double hyphens in "-L1" and "-L2" Signed-off-by: "Theodore Ts'o" diff --git a/git-mergetool.sh b/git-mergetool.sh index a92019a..9f4f313 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -192,10 +192,10 @@ merge_file () { case "$merge_tool" in kdiff3) if base_present ; then - (kdiff3 --auto --L1 "$path (Base)" -L2 "$path (Local)" --L3 "$path (Remote)" \ + (kdiff3 --auto --L1 "$path (Base)" --L2 "$path (Local)" --L3 "$path (Remote)" \ -o "$path" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1) else - (kdiff3 --auto -L1 "$path (Local)" --L2 "$path (Remote)" \ + (kdiff3 --auto --L1 "$path (Local)" --L2 "$path (Remote)" \ -o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1) fi status=$? -- cgit v0.10.2-6-g49f6 From 690b61f5f13db05aa4ad0efc422bd01aef3c1367 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 27 Sep 2007 12:51:18 +0200 Subject: double free in builtin-update-index.c path_name is either ptr that should not be freed, or a pointer to a strbuf buffer that is deallocated when exiting the loop. Don't do that ! Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/builtin-update-index.c b/builtin-update-index.c index c76879e..e1a938d 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -377,8 +377,6 @@ static void read_index_info(int line_termination) die("git-update-index: unable to update %s", path_name); } - if (path_name != ptr) - free(path_name); continue; bad_line: -- cgit v0.10.2-6-g49f6 From b315c5c08139c0d3c1e4867a305334e29da01d07 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 27 Sep 2007 12:58:23 +0200 Subject: strbuf change: be sure ->buf is never ever NULL. For that purpose, the ->buf is always initialized with a char * buf living in the strbuf module. It is made a char * so that we can sloppily accept things that perform: sb->buf[0] = '\0', and because you can't pass "" as an initializer for ->buf without making gcc unhappy for very good reasons. strbuf_init/_detach/_grow have been fixed to trust ->alloc and not ->buf anymore. as a consequence strbuf_detach is _mandatory_ to detach a buffer, copying ->buf isn't an option anymore, if ->buf is going to escape from the scope, and eventually be free'd. API changes: * strbuf_setlen now always works, so just make strbuf_reset a convenience macro. * strbuf_detatch takes a size_t* optional argument (meaning it can be NULL) to copy the buffer's len, as it was needed for this refactor to make the code more readable, and working like the callers. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/builtin-apply.c b/builtin-apply.c index 01c9d60..740623e 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -178,14 +178,13 @@ static void say_patch_name(FILE *output, const char *pre, struct patch *patch, c #define CHUNKSIZE (8192) #define SLOP (16) -static void *read_patch_file(int fd, unsigned long *sizep) +static void *read_patch_file(int fd, size_t *sizep) { struct strbuf buf; strbuf_init(&buf, 0); if (strbuf_read(&buf, fd, 0) < 0) die("git-apply: read returned %s", strerror(errno)); - *sizep = buf.len; /* * Make sure that we have some slop in the buffer @@ -194,7 +193,7 @@ static void *read_patch_file(int fd, unsigned long *sizep) */ strbuf_grow(&buf, SLOP); memset(buf.buf + buf.len, 0, SLOP); - return strbuf_detach(&buf); + return strbuf_detach(&buf, sizep); } static unsigned long linelen(const char *buffer, unsigned long size) @@ -253,7 +252,7 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) */ strbuf_remove(&name, 0, cp - name.buf); free(def); - return name.buf; + return strbuf_detach(&name, NULL); } } strbuf_release(&name); @@ -607,7 +606,7 @@ static char *git_header_name(char *line, int llen) if (strcmp(cp + 1, first.buf)) goto free_and_fail1; strbuf_release(&sp); - return first.buf; + return strbuf_detach(&first, NULL); } /* unquoted second */ @@ -618,7 +617,7 @@ static char *git_header_name(char *line, int llen) if (line + llen - cp != first.len + 1 || memcmp(first.buf, cp, first.len)) goto free_and_fail1; - return first.buf; + return strbuf_detach(&first, NULL); free_and_fail1: strbuf_release(&first); @@ -655,7 +654,7 @@ static char *git_header_name(char *line, int llen) isspace(name[len])) { /* Good */ strbuf_remove(&sp, 0, np - sp.buf); - return sp.buf; + return strbuf_detach(&sp, NULL); } free_and_fail2: @@ -1968,8 +1967,7 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * if (apply_fragments(&buf, patch) < 0) return -1; /* note with --reject this succeeds. */ - patch->result = buf.buf; - patch->resultsize = buf.len; + patch->result = strbuf_detach(&buf, &patch->resultsize); if (0 < patch->is_delete && patch->resultsize) return error("removal patch leaves file contents"); diff --git a/builtin-archive.c b/builtin-archive.c index 843a9e3..04385de 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -89,7 +89,7 @@ static void format_subst(const struct commit *commit, struct strbuf fmt; if (src == buf->buf) - to_free = strbuf_detach(buf); + to_free = strbuf_detach(buf, NULL); strbuf_init(&fmt, 0); for (;;) { const char *b, *c; @@ -153,8 +153,7 @@ void *sha1_file_to_archive(const char *path, const unsigned char *sha1, strbuf_attach(&buf, buffer, *sizep, *sizep + 1); convert_to_working_tree(path, buf.buf, buf.len, &buf); convert_to_archive(path, buf.buf, buf.len, &buf, commit); - *sizep = buf.len; - buffer = buf.buf; + buffer = strbuf_detach(&buf, sizep); } return buffer; diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index 349b59c..1e43d79 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -10,7 +10,7 @@ static char *get_stdin(void) if (strbuf_read(&buf, 0, 1024) < 0) { die("error reading standard input: %s", strerror(errno)); } - return strbuf_detach(&buf); + return strbuf_detach(&buf, NULL); } static void show_new(enum object_type type, unsigned char *sha1_new) diff --git a/commit.c b/commit.c index 55b08ec..62cc74d 100644 --- a/commit.c +++ b/commit.c @@ -663,7 +663,7 @@ static char *replace_encoding_header(char *buf, const char *encoding) len - strlen("encoding \n"), encoding, strlen(encoding)); } - return tmp.buf; + return strbuf_detach(&tmp, NULL); } static char *logmsg_reencode(const struct commit *commit, diff --git a/convert.c b/convert.c index 79c9df2..0d5e909 100644 --- a/convert.c +++ b/convert.c @@ -168,7 +168,7 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len, /* are we "faking" in place editing ? */ if (src == buf->buf) - to_free = strbuf_detach(buf); + to_free = strbuf_detach(buf, NULL); strbuf_grow(buf, len + stats.lf - stats.crlf); for (;;) { @@ -464,7 +464,7 @@ static int ident_to_worktree(const char *path, const char *src, size_t len, /* are we "faking" in place editing ? */ if (src == buf->buf) - to_free = strbuf_detach(buf); + to_free = strbuf_detach(buf, NULL); hash_sha1_file(src, len, "blob", sha1); strbuf_grow(buf, len + cnt * 43); diff --git a/diff.c b/diff.c index fb6d077..ab57519 100644 --- a/diff.c +++ b/diff.c @@ -197,7 +197,7 @@ static char *quote_two(const char *one, const char *two) strbuf_addstr(&res, one); strbuf_addstr(&res, two); } - return res.buf; + return strbuf_detach(&res, NULL); } static const char *external_diff(void) @@ -662,7 +662,7 @@ static char *pprint_rename(const char *a, const char *b) quote_c_style(a, &name, NULL, 0); strbuf_addstr(&name, " => "); quote_c_style(b, &name, NULL, 0); - return name.buf; + return strbuf_detach(&name, NULL); } /* Find common prefix */ @@ -710,7 +710,7 @@ static char *pprint_rename(const char *a, const char *b) strbuf_addch(&name, '}'); strbuf_add(&name, a + len_a - sfx_length, sfx_length); } - return name.buf; + return strbuf_detach(&name, NULL); } struct diffstat_t { @@ -827,7 +827,7 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) strbuf_init(&buf, 0); if (quote_c_style(file->name, &buf, NULL, 0)) { free(file->name); - file->name = buf.buf; + file->name = strbuf_detach(&buf, NULL); } else { strbuf_release(&buf); } @@ -1519,8 +1519,7 @@ static int populate_from_stdin(struct diff_filespec *s) strerror(errno)); s->should_munmap = 0; - s->size = buf.len; - s->data = strbuf_detach(&buf); + s->data = strbuf_detach(&buf, &s->size); s->should_free = 1; return 0; } @@ -1612,8 +1611,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) if (convert_to_git(s->path, s->data, s->size, &buf)) { munmap(s->data, s->size); s->should_munmap = 0; - s->data = buf.buf; - s->size = buf.len; + s->data = strbuf_detach(&buf, &s->size); s->should_free = 1; } } diff --git a/entry.c b/entry.c index 4a8c73b..98f5f6d 100644 --- a/entry.c +++ b/entry.c @@ -120,8 +120,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout strbuf_init(&buf, 0); if (convert_to_working_tree(ce->name, new, size, &buf)) { free(new); - new = buf.buf; - size = buf.len; + new = strbuf_detach(&buf, &size); } if (to_tempfile) { diff --git a/fast-import.c b/fast-import.c index a870a44..e9c80be 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1562,7 +1562,7 @@ static int read_next_command(void) } else { struct recent_command *rc; - strbuf_detach(&command_buf); + strbuf_detach(&command_buf, NULL); stdin_eof = strbuf_getline(&command_buf, stdin, '\n'); if (stdin_eof) return EOF; diff --git a/imap-send.c b/imap-send.c index e95cdde..a429a76 100644 --- a/imap-send.c +++ b/imap-send.c @@ -1180,7 +1180,7 @@ read_message( FILE *f, msg_data_t *msg ) } while (!feof(f)); msg->len = buf.len; - msg->data = strbuf_detach(&buf); + msg->data = strbuf_detach(&buf, NULL); return msg->len; } diff --git a/quote.c b/quote.c index 800fd88..482be05 100644 --- a/quote.c +++ b/quote.c @@ -22,7 +22,7 @@ void sq_quote_buf(struct strbuf *dst, const char *src) char *to_free = NULL; if (dst->buf == src) - to_free = strbuf_detach(dst); + to_free = strbuf_detach(dst, NULL); strbuf_addch(dst, '\''); while (*src) { diff --git a/sha1_file.c b/sha1_file.c index 385c5d8..753742a 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2340,8 +2340,7 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, strbuf_init(&nbuf, 0); if (convert_to_git(path, buf, size, &nbuf)) { munmap(buf, size); - size = nbuf.len; - buf = nbuf.buf; + buf = strbuf_detach(&nbuf, &size); re_allocated = 1; } } diff --git a/strbuf.c b/strbuf.c index d1e338b..0e431da 100644 --- a/strbuf.c +++ b/strbuf.c @@ -1,27 +1,33 @@ #include "cache.h" +/* + * Used as the default ->buf value, so that people can always assume + * buf is non NULL and ->buf is NUL terminated even for a freshly + * initialized strbuf. + */ +char strbuf_slopbuf[1]; + void strbuf_init(struct strbuf *sb, size_t hint) { - memset(sb, 0, sizeof(*sb)); + sb->alloc = sb->len = 0; + sb->buf = strbuf_slopbuf; if (hint) strbuf_grow(sb, hint); } void strbuf_release(struct strbuf *sb) { - free(sb->buf); - memset(sb, 0, sizeof(*sb)); -} - -void strbuf_reset(struct strbuf *sb) -{ - if (sb->len) - strbuf_setlen(sb, 0); + if (sb->alloc) { + free(sb->buf); + strbuf_init(sb, 0); + } } -char *strbuf_detach(struct strbuf *sb) +char *strbuf_detach(struct strbuf *sb, size_t *sz) { - char *res = sb->buf; + char *res = sb->alloc ? sb->buf : NULL; + if (sz) + *sz = sb->len; strbuf_init(sb, 0); return res; } @@ -40,6 +46,8 @@ void strbuf_grow(struct strbuf *sb, size_t extra) { if (sb->len + extra + 1 <= sb->len) die("you want to use way too much memory"); + if (!sb->alloc) + sb->buf = NULL; ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); } diff --git a/strbuf.h b/strbuf.h index 5657e3d..6deec78 100644 --- a/strbuf.h +++ b/strbuf.h @@ -10,8 +10,7 @@ * 1. the ->buf member is always malloc-ed, hence strbuf's can be used to * build complex strings/buffers whose final size isn't easily known. * - * It is legal to copy the ->buf pointer away. Though if you want to reuse - * the strbuf after that, setting ->buf to NULL isn't legal. + * It is NOT legal to copy the ->buf pointer away. * `strbuf_detach' is the operation that detachs a buffer from its shell * while keeping the shell valid wrt its invariants. * @@ -41,19 +40,19 @@ #include +extern char strbuf_slopbuf[]; struct strbuf { size_t alloc; size_t len; char *buf; }; -#define STRBUF_INIT { 0, 0, NULL } +#define STRBUF_INIT { 0, 0, strbuf_slopbuf } /*----- strbuf life cycle -----*/ extern void strbuf_init(struct strbuf *, size_t); extern void strbuf_release(struct strbuf *); -extern void strbuf_reset(struct strbuf *); -extern char *strbuf_detach(struct strbuf *); +extern char *strbuf_detach(struct strbuf *, size_t *); extern void strbuf_attach(struct strbuf *, void *, size_t, size_t); static inline void strbuf_swap(struct strbuf *a, struct strbuf *b) { struct strbuf tmp = *a; @@ -75,6 +74,7 @@ static inline void strbuf_setlen(struct strbuf *sb, size_t len) { sb->len = len; sb->buf[len] = '\0'; } +#define strbuf_reset(sb) strbuf_setlen(sb, 0) /*----- content related -----*/ extern void strbuf_rtrim(struct strbuf *); -- cgit v0.10.2-6-g49f6 From ee8245b54a2e4ea8838d28a7bcce2e07ef887a2c Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Thu, 27 Sep 2007 01:34:59 +0200 Subject: git-bundle: fix commandline examples in the manpage Multiple commands were displayed in one line, making the manpage hard to read. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt index 5051e2b..0cc6511 100644 --- a/Documentation/git-bundle.txt +++ b/Documentation/git-bundle.txt @@ -103,14 +103,20 @@ We set a tag in R1 (lastR2bundle) after the previous such transport, and move it afterwards to help build the bundle. in R1 on A: + +------------ $ git-bundle create mybundle master ^lastR2bundle $ git tag -f lastR2bundle master +------------ (move mybundle from A to B by some mechanism) in R2 on B: + +------------ $ git-bundle verify mybundle $ git-fetch mybundle refspec +------------ where refspec is refInBundle:localRef @@ -124,9 +130,11 @@ Also, with something like this in your config: You can first sneakernet the bundle file to ~/tmp/file.bdl and then these commands: +------------ $ git ls-remote bundle $ git fetch bundle $ git pull bundle +------------ would treat it as if it is talking with a remote side over the network. -- cgit v0.10.2-6-g49f6 From b7bb760d5ed4881422673d32f869d140221d3564 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 29 Sep 2007 09:50:39 -0700 Subject: Fix revision log diff setup, avoid unnecessary diff generation We used to incorrectly start calculating diffs whenever any argument but '-z' was recognized by the diff options parsing. That was bogus, since not all arguments result in diffs being needed, so we just waste a lot of time and effort on calculating diffs that don't matter. This actually also fixes another bug in "git log". Try this: git log -C and notice how it prints an extra empty line in between log entries, even though it never prints the actual diff (because we didn't ask for any diff format, so the diff machinery never prints anything). With this patch, that bogus empty line is gone, because "revs->diff" is never set. So this isn't just a "wasted time and effort" issue, it's also a slight semantic fix. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/revision.c b/revision.c index 33d092c..6584713 100644 --- a/revision.c +++ b/revision.c @@ -1209,8 +1209,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i); if (opts > 0) { - if (strcmp(argv[i], "-z")) - revs->diff = 1; i += opts - 1; continue; } @@ -1254,6 +1252,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch add_pending_object_with_mode(revs, object, def, mode); } + /* Did the user ask for any diff output? Run the diff! */ + if (revs->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) + revs->diff = 1; + + /* Pickaxe needs diffs */ + if (revs->diffopt.pickaxe) + revs->diff = 1; + if (revs->topo_order) revs->limited = 1; -- cgit v0.10.2-6-g49f6 From 552ce11006e39bd07efd79946f180df47aa35b4e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 29 Sep 2007 16:07:46 -0700 Subject: GIT 1.5.3.3 Signed-off-by: Junio C Hamano diff --git a/Documentation/RelNotes-1.5.3.3.txt b/Documentation/RelNotes-1.5.3.3.txt new file mode 100644 index 0000000..e91bd84 --- /dev/null +++ b/Documentation/RelNotes-1.5.3.3.txt @@ -0,0 +1,37 @@ +GIT v1.5.3.3 Release Notes +========================== + +Fixes since v1.5.3.2 +-------------------- + + * git-quiltimport did not like it when a patch described in the + series file does not exist. + + * p4 importer missed executable bit in some cases. + + * The default shell on some FreeBSD did not execute the + argument parsing code correctly and made git unusable. + + * git-svn incorrectly spawned pager even when the user user + explicitly asked not to. + + * sample post-receive hook overquoted the envelope sender + value. + + * git-am got confused when the patch contained a change that is + only about type and not contents. + + * git-mergetool did not show our and their version of the + conflicted file when started from a subdirectory of the + project. + + * git-mergetool did not pass correct options when invoking diff3. + + * git-log sometimes invoked underlying "diff" machinery + unnecessarily. + +-- +exec >/var/tmp/1 +O=v1.5.3.2-29-gb7bb760 +echo O=`git describe refs/heads/maint` +git shortlog --no-merges $O..refs/heads/maint diff --git a/RelNotes b/RelNotes index 599a855..44b610e 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.5.3.2.txt \ No newline at end of file +Documentation/RelNotes-1.5.3.3.txt \ No newline at end of file -- cgit v0.10.2-6-g49f6 From 856665f827c31ace3f19e672f8911f7f15f2a0e2 Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Fri, 28 Sep 2007 15:17:31 +0100 Subject: parse_date_format(): convert a format name to an enum date_mode Factor out the code to parse --date= parameter to revision walkers into a separate function, parse_date_format(). This function is passed a string and converts it to an enum date_format: - "relative" => DATE_RELATIVE - "iso8601" or "iso" => DATE_ISO8601 - "rfc2822" => DATE_RFC2822 - "short" => DATE_SHORT - "local" => DATE_LOCAL - "default" => DATE_NORMAL In the event that none of these strings is found, the function die()s. Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index 8246500..5587f7e 100644 --- a/cache.h +++ b/cache.h @@ -432,6 +432,7 @@ const char *show_date(unsigned long time, int timezone, enum date_mode mode); int parse_date(const char *date, char *buf, int bufsize); void datestamp(char *buf, int bufsize); unsigned long approxidate(const char *); +enum date_mode parse_date_format(const char *format); extern const char *git_author_info(int); extern const char *git_committer_info(int); diff --git a/date.c b/date.c index 93bef6e..8f70500 100644 --- a/date.c +++ b/date.c @@ -584,6 +584,26 @@ int parse_date(const char *date, char *result, int maxlen) return date_string(then, offset, result, maxlen); } +enum date_mode parse_date_format(const char *format) +{ + if (!strcmp(format, "relative")) + return DATE_RELATIVE; + else if (!strcmp(format, "iso8601") || + !strcmp(format, "iso")) + return DATE_ISO8601; + else if (!strcmp(format, "rfc2822") || + !strcmp(format, "rfc")) + return DATE_RFC2822; + else if (!strcmp(format, "short")) + return DATE_SHORT; + else if (!strcmp(format, "local")) + return DATE_LOCAL; + else if (!strcmp(format, "default")) + return DATE_NORMAL; + else + die("unknown date format %s", format); +} + void datestamp(char *buf, int bufsize) { time_t now; diff --git a/revision.c b/revision.c index 6584713..5d294be 100644 --- a/revision.c +++ b/revision.c @@ -1134,22 +1134,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch continue; } if (!strncmp(arg, "--date=", 7)) { - if (!strcmp(arg + 7, "relative")) - revs->date_mode = DATE_RELATIVE; - else if (!strcmp(arg + 7, "iso8601") || - !strcmp(arg + 7, "iso")) - revs->date_mode = DATE_ISO8601; - else if (!strcmp(arg + 7, "rfc2822") || - !strcmp(arg + 7, "rfc")) - revs->date_mode = DATE_RFC2822; - else if (!strcmp(arg + 7, "short")) - revs->date_mode = DATE_SHORT; - else if (!strcmp(arg + 7, "local")) - revs->date_mode = DATE_LOCAL; - else if (!strcmp(arg + 7, "default")) - revs->date_mode = DATE_NORMAL; - else - die("unknown date format %s", arg); + revs->date_mode = parse_date_format(arg + 7); continue; } if (!strcmp(arg, "--log-size")) { -- cgit v0.10.2-6-g49f6 From b64265ca8da17d30561febf62a13990a2dc96d2f Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Fri, 28 Sep 2007 15:17:39 +0100 Subject: Make for-each-ref allow atom names like ":" In anticipation of supplying a per-field date format specifier, this patch makes parse_atom() in builtin-for-each-ref.c allow atoms that have a valid atom name (as determined by the valid_atom[] table) followed by a colon, followed by an arbitrary string. The arbitrary string is where the format for the atom will be specified. Note, if different formats are specified for the same atom, multiple entries will be made in the used_atoms table to allow them to be distinguished by the grab_XXXX() functions. Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index 0afa1c5..74a7337 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -106,7 +106,16 @@ static int parse_atom(const char *atom, const char *ep) /* Is the atom a valid one? */ for (i = 0; i < ARRAY_SIZE(valid_atom); i++) { int len = strlen(valid_atom[i].name); - if (len == ep - sp && !memcmp(valid_atom[i].name, sp, len)) + /* + * If the atom name has a colon, strip it and everything after + * it off - it specifies the format for this entry, and + * shouldn't be used for checking against the valid_atom + * table. + */ + const char *formatp = strchr(sp, ':'); + if (!formatp) + formatp = ep; + if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len)) break; } -- cgit v0.10.2-6-g49f6 From d392e712a844e6ce029aa901902e08a3da82f89d Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Fri, 28 Sep 2007 15:17:45 +0100 Subject: Make for-each-ref's grab_date() support per-atom formatting grab_date() gets an extra parameter - atomname; this extra parameter is checked to see if it has a ":" extra component in it, and if so that "" string is passed to parse_date_format() to produce an enum date_mode value which is then further passed to show_date(). In short it allows the user of git-for-each-ref to do things like this: $ git-for-each-ref --format='%(taggerdate:default)' refs/tags/v1.5.2 Sun May 20 00:30:42 2007 -0700 $ git-for-each-ref --format='%(taggerdate:relative)' refs/tags/v1.5.2 4 months ago $ git-for-each-ref --format='%(taggerdate:short)' refs/tags/v1.5.2 2007-05-20 $ git-for-each-ref --format='%(taggerdate:local)' refs/tags/v1.5.2 Sun May 20 08:30:42 2007 $ git-for-each-ref --format='%(taggerdate:iso8601)' refs/tags/v1.5.2 2007-05-20 00:30:42 -0700 $ git-for-each-ref --format='%(taggerdate:rfc2822)' refs/tags/v1.5.2 Sun, 20 May 2007 00:30:42 -0700 The default, when no ":" is specified is ":default", leaving the existing behaviour unchanged. Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 6df8e85..f1f90cc 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -100,6 +100,11 @@ In any case, a field name that refers to a field inapplicable to the object referred by the ref does not cause an error. It returns an empty string instead. +As a special case for the date-type fields, you may specify a format for +the date by adding one of `:default`, `:relative`, `:short`, `:local`, +`:iso8601` or `:rfc2822` to the end of the fieldname; e.g. +`%(taggerdate:relative)`. + EXAMPLES -------- diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index 74a7337..c904ac3 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -356,12 +356,26 @@ static const char *copy_email(const char *buf) return line; } -static void grab_date(const char *buf, struct atom_value *v) +static void grab_date(const char *buf, struct atom_value *v, const char *atomname) { const char *eoemail = strstr(buf, "> "); char *zone; unsigned long timestamp; long tz; + enum date_mode date_mode = DATE_NORMAL; + const char *formatp; + + /* + * We got here because atomname ends in "date" or "date"; + * it's not possible that is not ":" because + * parse_atom() wouldn't have allowed it, so we can assume that no + * ":" means no format is specified, and use the default. + */ + formatp = strchr(atomname, ':'); + if (formatp != NULL) { + formatp++; + date_mode = parse_date_format(formatp); + } if (!eoemail) goto bad; @@ -371,7 +385,7 @@ static void grab_date(const char *buf, struct atom_value *v) tz = strtol(zone, NULL, 10); if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE) goto bad; - v->s = xstrdup(show_date(timestamp, tz, 0)); + v->s = xstrdup(show_date(timestamp, tz, date_mode)); v->ul = timestamp; return; bad: @@ -398,7 +412,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru if (name[wholen] != 0 && strcmp(name + wholen, "name") && strcmp(name + wholen, "email") && - strcmp(name + wholen, "date")) + prefixcmp(name + wholen, "date")) continue; if (!wholine) wholine = find_wholine(who, wholen, buf, sz); @@ -410,8 +424,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru v->s = copy_name(wholine); else if (!strcmp(name + wholen, "email")) v->s = copy_email(wholine); - else if (!strcmp(name + wholen, "date")) - grab_date(wholine, v); + else if (!prefixcmp(name + wholen, "date")) + grab_date(wholine, v, name); } /* For a tag or a commit object, if "creator" or "creatordate" is @@ -431,8 +445,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru if (deref) name++; - if (!strcmp(name, "creatordate")) - grab_date(wholine, v); + if (!prefixcmp(name, "creatordate")) + grab_date(wholine, v, name); else if (!strcmp(name, "creator")) v->s = copy_line(wholine); } -- cgit v0.10.2-6-g49f6 From 1abbe475ff17349839f72a024cf665b8ec86473f Mon Sep 17 00:00:00 2001 From: Josh England Date: Wed, 26 Sep 2007 15:31:01 -0600 Subject: post-checkout hook, tests, and docs Updated post-checkout hook to take a flag specifying whether the checkout is a branch checkout or a file checkout (from the index). Signed-off-by: Josh England Signed-off-by: Junio C Hamano diff --git a/Documentation/hooks.txt b/Documentation/hooks.txt index 58b9547..f110162 100644 --- a/Documentation/hooks.txt +++ b/Documentation/hooks.txt @@ -87,6 +87,20 @@ parameter, and is invoked after a commit is made. This hook is meant primarily for notification, and cannot affect the outcome of `git-commit`. +post-checkout +----------- + +This hook is invoked when a `git-checkout` is run after having updated the +worktree. The hook is given three parameters: the ref of the previous HEAD, +the ref of the new HEAD (which may or may not have changed), and a flag +indicating whether the checkout was a branch checkout (changing branches, +flag=1) or a file checkout (retrieving a file from the index, flag=0). +This hook cannot affect the outcome of `git-checkout`. + +This hook can be used to perform repository validity checks, auto-display +differences from the previous HEAD if different, or set working dir metadata +properties. + post-merge ----------- diff --git a/git-checkout.sh b/git-checkout.sh index 17f4392..8993920 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -137,6 +137,13 @@ Did you intend to checkout '$@' which can not be resolved as commit?" git ls-files --error-unmatch -- "$@" >/dev/null || exit git ls-files -- "$@" | git checkout-index -f -u --stdin + + # Run a post-checkout hook -- the HEAD does not change so the + # current HEAD is passed in for both args + if test -x "$GIT_DIR"/hooks/post-checkout; then + "$GIT_DIR"/hooks/post-checkout $old $old 0 + fi + exit $? else # Make sure we did not fall back on $arg^{tree} codepath @@ -284,3 +291,8 @@ if [ "$?" -eq 0 ]; then else exit 1 fi + +# Run a post-checkout hook +if test -x "$GIT_DIR"/hooks/post-checkout; then + "$GIT_DIR"/hooks/post-checkout $old $new 1 +fi diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh new file mode 100755 index 0000000..487abf3 --- /dev/null +++ b/t/t5403-post-checkout-hook.sh @@ -0,0 +1,74 @@ +#!/bin/sh +# +# Copyright (c) 2006 Josh England +# + +test_description='Test the post-checkout hook.' +. ./test-lib.sh + +test_expect_success setup ' + echo Data for commit0. >a && + echo Data for commit0. >b && + git update-index --add a && + git update-index --add b && + tree0=$(git write-tree) && + commit0=$(echo setup | git commit-tree $tree0) && + git update-ref refs/heads/master $commit0 && + git-clone ./. clone1 && + git-clone ./. clone2 && + GIT_DIR=clone2/.git git branch -a new2 && + echo Data for commit1. >clone2/b && + GIT_DIR=clone2/.git git add clone2/b && + GIT_DIR=clone2/.git git commit -m new2 +' + +for clone in 1 2; do + cat >clone${clone}/.git/hooks/post-checkout <<'EOF' +#!/bin/sh +echo $@ > $GIT_DIR/post-checkout.args +EOF + chmod u+x clone${clone}/.git/hooks/post-checkout +done + +test_expect_success 'post-checkout runs as expected ' ' + GIT_DIR=clone1/.git git checkout master && + test -e clone1/.git/post-checkout.args +' + +test_expect_success 'post-checkout receives the right arguments with HEAD unchanged ' ' + old=$(awk "{print \$1}" clone1/.git/post-checkout.args) && + new=$(awk "{print \$2}" clone1/.git/post-checkout.args) && + flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) && + test $old = $new -a $flag == 1 +' + +test_expect_success 'post-checkout runs as expected ' ' + GIT_DIR=clone1/.git git checkout master && + test -e clone1/.git/post-checkout.args +' + +test_expect_success 'post-checkout args are correct with git checkout -b ' ' + GIT_DIR=clone1/.git git checkout -b new1 && + old=$(awk "{print \$1}" clone1/.git/post-checkout.args) && + new=$(awk "{print \$2}" clone1/.git/post-checkout.args) && + flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) && + test $old = $new -a $flag == 1 +' + +test_expect_success 'post-checkout receives the right args with HEAD changed ' ' + GIT_DIR=clone2/.git git checkout new2 && + old=$(awk "{print \$1}" clone2/.git/post-checkout.args) && + new=$(awk "{print \$2}" clone2/.git/post-checkout.args) && + flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) && + test $old != $new -a $flag == 1 +' + +test_expect_success 'post-checkout receives the right args when not switching branches ' ' + GIT_DIR=clone2/.git git checkout master b && + old=$(awk "{print \$1}" clone2/.git/post-checkout.args) && + new=$(awk "{print \$2}" clone2/.git/post-checkout.args) && + flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) && + test $old == $new -a $flag == 0 +' + +test_done -- cgit v0.10.2-6-g49f6 From 387e7e19d7eb5444be8da8e99ed7491989dc1cbb Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 27 Sep 2007 15:25:55 +0200 Subject: strbuf_read_file enhancement, and use it. * make strbuf_read_file take a size hint (works like strbuf_read) * use it in a couple of places. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/builtin-apply.c b/builtin-apply.c index 740623e..fec96a8 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -1425,8 +1425,6 @@ static void show_stats(struct patch *patch) static int read_old_data(struct stat *st, const char *path, struct strbuf *buf) { - int fd; - switch (st->st_mode & S_IFMT) { case S_IFLNK: strbuf_grow(buf, st->st_size); @@ -1435,14 +1433,8 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf) strbuf_setlen(buf, st->st_size); return 0; case S_IFREG: - fd = open(path, O_RDONLY); - if (fd < 0) - return error("unable to open %s", path); - if (strbuf_read(buf, fd, st->st_size) < 0) { - close(fd); - return -1; - } - close(fd); + if (strbuf_read_file(buf, path, st->st_size) != st->st_size) + return error("unable to open or read %s", path); convert_to_git(path, buf->buf, buf->len, buf); return 0; default: diff --git a/builtin-blame.c b/builtin-blame.c index 16c0ca8..e3112a2 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -2002,7 +2002,6 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con unsigned char head_sha1[20]; struct strbuf buf; const char *ident; - int fd; time_t now; int size, len; struct cache_entry *ce; @@ -2041,11 +2040,8 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con mode = canon_mode(st.st_mode); switch (st.st_mode & S_IFMT) { case S_IFREG: - fd = open(read_from, O_RDONLY); - if (fd < 0) - die("cannot open %s", read_from); - if (strbuf_read(&buf, fd, 0) != xsize_t(st.st_size)) - die("cannot read %s", read_from); + if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size) + die("cannot open or read %s", read_from); break; case S_IFLNK: if (readlink(read_from, buf.buf, buf.alloc) != fin_size) diff --git a/builtin-tag.c b/builtin-tag.c index 6132cac..66e5a58 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -51,7 +51,7 @@ static void launch_editor(const char *path, struct strbuf *buffer) if (run_command(&child)) die("There was a problem with the editor %s.", editor); - if (strbuf_read_file(buffer, path) < 0) + if (strbuf_read_file(buffer, path, 0) < 0) die("could not read message file '%s': %s", path, strerror(errno)); } @@ -356,8 +356,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "-F")) { - int fd; - annotate = 1; i++; if (i == argc) @@ -365,17 +363,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (message) die("only one -F or -m option is allowed."); - if (!strcmp(argv[i], "-")) - fd = 0; - else { - fd = open(argv[i], O_RDONLY); - if (fd < 0) - die("could not open '%s': %s", + if (!strcmp(argv[i], "-")) { + if (strbuf_read(&buf, 0, 1024) < 0) + die("cannot read %s", argv[i]); + } else { + if (strbuf_read_file(&buf, argv[i], 1024) < 0) + die("could not open or read '%s': %s", argv[i], strerror(errno)); } - if (strbuf_read(&buf, fd, 1024) < 0) { - die("cannot read %s", argv[i]); - } message = 1; continue; } diff --git a/strbuf.c b/strbuf.c index 0e431da..f4201e1 100644 --- a/strbuf.c +++ b/strbuf.c @@ -186,14 +186,14 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term) return 0; } -int strbuf_read_file(struct strbuf *sb, const char *path) +int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint) { int fd, len; fd = open(path, O_RDONLY); if (fd < 0) return -1; - len = strbuf_read(sb, fd, 0); + len = strbuf_read(sb, fd, hint); close(fd); if (len < 0) return -1; diff --git a/strbuf.h b/strbuf.h index 6deec78..9b9e861 100644 --- a/strbuf.h +++ b/strbuf.h @@ -108,7 +108,7 @@ extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); extern size_t strbuf_fread(struct strbuf *, size_t, FILE *); /* XXX: if read fails, any partial read is undone */ extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint); -extern int strbuf_read_file(struct strbuf *sb, const char *path); +extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint); extern int strbuf_getline(struct strbuf *, FILE *, int); -- cgit v0.10.2-6-g49f6 From 9a76adebd6dfe144c3643e7e877fff2b41be5c8e Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 27 Sep 2007 13:33:19 +0200 Subject: Make read_patch_file work on a strbuf. So that we don't need to use strbuf_detach. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano diff --git a/builtin-apply.c b/builtin-apply.c index fec96a8..4113d66 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -178,12 +178,9 @@ static void say_patch_name(FILE *output, const char *pre, struct patch *patch, c #define CHUNKSIZE (8192) #define SLOP (16) -static void *read_patch_file(int fd, size_t *sizep) +static void read_patch_file(struct strbuf *sb, int fd) { - struct strbuf buf; - - strbuf_init(&buf, 0); - if (strbuf_read(&buf, fd, 0) < 0) + if (strbuf_read(sb, fd, 0) < 0) die("git-apply: read returned %s", strerror(errno)); /* @@ -191,9 +188,8 @@ static void *read_patch_file(int fd, size_t *sizep) * so that we can do speculative "memcmp" etc, and * see to it that it is NUL-filled. */ - strbuf_grow(&buf, SLOP); - memset(buf.buf + buf.len, 0, SLOP); - return strbuf_detach(&buf, sizep); + strbuf_grow(sb, SLOP); + memset(sb->buf + sb->len, 0, SLOP); } static unsigned long linelen(const char *buffer, unsigned long size) @@ -2633,22 +2629,22 @@ static void prefix_patches(struct patch *p) static int apply_patch(int fd, const char *filename, int inaccurate_eof) { - unsigned long offset, size; - char *buffer = read_patch_file(fd, &size); + size_t offset; + struct strbuf buf; struct patch *list = NULL, **listp = &list; int skipped_patch = 0; + strbuf_init(&buf, 0); patch_input_file = filename; - if (!buffer) - return -1; + read_patch_file(&buf, fd); offset = 0; - while (size > 0) { + while (offset < buf.len) { struct patch *patch; int nr; patch = xcalloc(1, sizeof(*patch)); patch->inaccurate_eof = inaccurate_eof; - nr = parse_chunk(buffer + offset, size, patch); + nr = parse_chunk(buf.buf + offset, buf.len, patch); if (nr < 0) break; if (apply_in_reverse) @@ -2666,7 +2662,6 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) skipped_patch++; } offset += nr; - size -= nr; } if (whitespace_error && (new_whitespace == error_on_whitespace)) @@ -2701,7 +2696,7 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) if (summary) summary_patch_list(list); - free(buffer); + strbuf_release(&buf); return 0; } -- cgit v0.10.2-6-g49f6 From c95c02486c4dd82f8910a8de1cb7c8f9db899995 Mon Sep 17 00:00:00 2001 From: Jean-Luc Herren Date: Wed, 26 Sep 2007 15:56:19 +0200 Subject: git-add--interactive: Allow Ctrl-D to exit Hitting Ctrl-D (EOF) is a common way to exit shell-like tools. When in a sub-menu it will still behave as if an empty line had been entered, carrying out the action on the selected items and returning to the previous menu. Signed-off-by: Jean-Luc Herren Signed-off-by: Junio C Hamano diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 7921cde..f9e9f02 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -213,7 +213,11 @@ sub list_and_choose { print ">> "; } my $line = ; - last if (!$line); + if (!$line) { + print "\n"; + $opts->{ON_EOF}->() if $opts->{ON_EOF}; + last; + } chomp $line; my $donesomething = 0; for my $choice (split(/[\s,]+/, $line)) { @@ -791,6 +795,7 @@ sub main_loop { SINGLETON => 1, LIST_FLAT => 4, HEADER => '*** Commands ***', + ON_EOF => \&quit_cmd, IMMEDIATE => 1 }, @cmd); if ($it) { eval { -- cgit v0.10.2-6-g49f6 From 6a6eb3d09fcbfae41978fb5223ae03e2d103968b Mon Sep 17 00:00:00 2001 From: Jean-Luc Herren Date: Wed, 26 Sep 2007 16:05:01 +0200 Subject: git-add--interactive: Improve behavior on bogus input 1) Previously, any menu would cause a perl error when entered '0', which is never a valid option. 2) Entering a bogus choice (like 998 or 4-2) surprisingly caused the same behavior as if the user had just hit 'enter', which means to carry out the selected action on the selected items. Entering such bogus input is now a no-op and the sub-menu doesn't exit. Signed-off-by: Jean-Luc Herren Signed-off-by: Junio C Hamano diff --git a/git-add--interactive.perl b/git-add--interactive.perl index f9e9f02..be68814 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -219,7 +219,7 @@ sub list_and_choose { last; } chomp $line; - my $donesomething = 0; + last if $line eq ''; for my $choice (split(/[\s,]+/, $line)) { my $choose = 1; my ($bottom, $top); @@ -251,12 +251,11 @@ sub list_and_choose { next TOPLOOP; } for ($i = $bottom-1; $i <= $top-1; $i++) { - next if (@stuff <= $i); + next if (@stuff <= $i || $i < 0); $chosen[$i] = $choose; - $donesomething++; } } - last if (!$donesomething || $opts->{IMMEDIATE}); + last if ($opts->{IMMEDIATE}); } for ($i = 0; $i < @stuff; $i++) { if ($chosen[$i]) { -- cgit v0.10.2-6-g49f6 From f8babc4dabebebd9e95537df6da0408c1c178615 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 29 Sep 2007 03:32:11 +0100 Subject: rebase -i: support single-letter abbreviations for the actions When you do many rebases, you can get annoyed by having to type out the actions "edit" or "squash" in total. This commit helps that, by allowing you to enter "e" instead of "edit", "p" instead of "pick", or "s" instead of "squash". Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 268a629..8de5b79 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -232,14 +232,14 @@ do_next () { '#'*|'') mark_action_done ;; - pick) + pick|p) comment_for_reflog pick mark_action_done pick_one $sha1 || die_with_patch $sha1 "Could not apply $sha1... $rest" ;; - edit) + edit|e) comment_for_reflog edit mark_action_done @@ -254,7 +254,7 @@ do_next () { warn exit 0 ;; - squash) + squash|s) comment_for_reflog squash has_action "$DONE" || @@ -263,7 +263,7 @@ do_next () { mark_action_done make_squash_message $sha1 > "$MSG" case "$(peek_next_command)" in - squash) + squash|s) EDIT_COMMIT= USE_OUTPUT=output cp "$MSG" "$SQUASH_MSG" -- cgit v0.10.2-6-g49f6 From 81ab1cb43a872fc527b26388bc7e781c816d723b Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 30 Sep 2007 00:34:23 +0100 Subject: rebase -i: squash should retain the authorship of the _first_ commit It was determined on the mailing list, that it makes more sense for a "squash" to keep the author of the first commit as the author for the result of the squash. Make it so. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 61b1810..dfb8a0d 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -293,7 +293,7 @@ rebasing. If you want to fold two or more commits into one, replace the command "pick" with "squash" for the second and subsequent commit. If the commits had different authors, it will attribute the squashed commit to -the author of the last commit. +the author of the first commit. In both cases, or when a "pick" does not succeed (because of merge errors), the loop will stop to let you fix things, and you can continue diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 2fa53fd..653393d 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -261,9 +261,9 @@ do_next () { esac failed=f + author_script=$(get_author_ident_from_commit HEAD) output git reset --soft HEAD^ pick_one -n $sha1 || failed=t - author_script=$(get_author_ident_from_commit $sha1) echo "$author_script" > "$DOTEST"/author-script case $failed in f) diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 718c9c1..6c92d61 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -180,7 +180,7 @@ test_expect_success 'squash' ' ' test_expect_success 'retain authorship when squashing' ' - git show HEAD | grep "^Author: Nitfol" + git show HEAD | grep "^Author: Twerp Snog" ' test_expect_success 'preserve merges with -p' ' -- cgit v0.10.2-6-g49f6 From f4bb20cc99fe18ba0e7dd421f1d95a05c1cbbc93 Mon Sep 17 00:00:00 2001 From: Jari Aalto Date: Sat, 29 Sep 2007 23:29:43 -0700 Subject: git-remote: exit with non-zero status after detecting errors. Some subcommands of "git-remote" detected and issued error messages but did not signal that to the calling process with exit status. Signed-off-by: Jari Aalto Signed-off-by: Junio C Hamano diff --git a/git-remote.perl b/git-remote.perl index 01cf480..8e2dc4d 100755 --- a/git-remote.perl +++ b/git-remote.perl @@ -218,7 +218,7 @@ sub prune_remote { my ($name, $ls_remote) = @_; if (!exists $remote->{$name}) { print STDERR "No such remote $name\n"; - return; + return 1; } my $info = $remote->{$name}; update_ls_remote($ls_remote, $info); @@ -229,13 +229,14 @@ sub prune_remote { my @v = $git->command(qw(rev-parse --verify), "$prefix/$to_prune"); $git->command(qw(update-ref -d), "$prefix/$to_prune", $v[0]); } + return 0; } sub show_remote { my ($name, $ls_remote) = @_; if (!exists $remote->{$name}) { print STDERR "No such remote $name\n"; - return; + return 1; } my $info = $remote->{$name}; update_ls_remote($ls_remote, $info); @@ -265,6 +266,7 @@ sub show_remote { print " Local branch(es) pushed with 'git push'\n"; print " @pushed\n"; } + return 0; } sub add_remote { @@ -351,9 +353,11 @@ elsif ($ARGV[0] eq 'show') { print STDERR "Usage: git remote show \n"; exit(1); } + my $status = 0; for (; $i < @ARGV; $i++) { - show_remote($ARGV[$i], $ls_remote); + $status |= show_remote($ARGV[$i], $ls_remote); } + exit($status); } elsif ($ARGV[0] eq 'update') { if (@ARGV <= 1) { @@ -379,9 +383,11 @@ elsif ($ARGV[0] eq 'prune') { print STDERR "Usage: git remote prune \n"; exit(1); } + my $status = 0; for (; $i < @ARGV; $i++) { - prune_remote($ARGV[$i], $ls_remote); + $status |= prune_remote($ARGV[$i], $ls_remote); } + exit($status); } elsif ($ARGV[0] eq 'add') { my %opts = (); -- cgit v0.10.2-6-g49f6 From 6982ccecaf32ebf8adfcb66b4225da3b46255621 Mon Sep 17 00:00:00 2001 From: Jari Aalto Date: Sat, 29 Sep 2007 23:34:19 -0700 Subject: git-remote: exit with non-zero status after detecting error in "rm". Exit with non-zero status when "git remote rm" was told to remove a non-existing remote. Signed-off-by: Jari Aalto Signed-off-by: Junio C Hamano diff --git a/git-remote.perl b/git-remote.perl index 79941e4..9ca3e7e 100755 --- a/git-remote.perl +++ b/git-remote.perl @@ -322,7 +322,7 @@ sub rm_remote { my ($name) = @_; if (!exists $remote->{$name}) { print STDERR "No such remote $name\n"; - return; + return 1; } $git->command('config', '--remove-section', "remote.$name"); @@ -337,13 +337,13 @@ sub rm_remote { } }; - my @refs = $git->command('for-each-ref', '--format=%(refname) %(objectname)', "refs/remotes/$name"); for (@refs) { ($ref, $object) = split; $git->command(qw(update-ref -d), $ref, $object); } + return 0; } sub add_usage { @@ -461,7 +461,7 @@ elsif ($ARGV[0] eq 'rm') { print STDERR "Usage: git remote rm \n"; exit(1); } - rm_remote($ARGV[1]); + exit(rm_remote($ARGV[1])); } else { print STDERR "Usage: git remote\n"; -- cgit v0.10.2-6-g49f6 From fe50eef59b049588b7dc9b5e02707084f0516b7f Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sat, 29 Sep 2007 19:39:54 -0400 Subject: Fix adding a submodule with a remote url Without this, a non-path URL gets lost before the clone. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano diff --git a/git-submodule.sh b/git-submodule.sh index 727b1d3..4aaaaab 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -138,8 +138,8 @@ module_add() # it is local if base=$(get_repo_base "$repo"); then repo="$base" - realrepo=$repo fi + realrepo=$repo ;; esac -- cgit v0.10.2-6-g49f6 From b9b7bab4b6190ef879bb72c7fabf879fd9ca852f Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 29 Sep 2007 11:58:08 +0200 Subject: git.el: Preserve file marks when doing a full refresh. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 2d77fd4..7b4a0d3 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -494,24 +494,31 @@ and returns the process output as a string." (setf (git-fileinfo->orig-name info) nil) (setf (git-fileinfo->needs-refresh info) t)))) -(defun git-set-filenames-state (status files state) - "Set the state of a list of named files." +(defun git-status-filenames-map (status func files &rest args) + "Apply FUNC to the status files names in the FILES list." (when files (setq files (sort files #'string-lessp)) (let ((file (pop files)) (node (ewoc-nth status 0))) (while (and file node) (let ((info (ewoc-data node))) - (cond ((string-lessp (git-fileinfo->name info) file) - (setq node (ewoc-next status node))) - ((string-equal (git-fileinfo->name info) file) - (unless (eq (git-fileinfo->state info) state) - (setf (git-fileinfo->state info) state) - (setf (git-fileinfo->rename-state info) nil) - (setf (git-fileinfo->orig-name info) nil) - (setf (git-fileinfo->needs-refresh info) t)) - (setq file (pop files))) - (t (setq file (pop files))))))) + (if (string-lessp (git-fileinfo->name info) file) + (setq node (ewoc-next status node)) + (if (string-equal (git-fileinfo->name info) file) + (apply func info args)) + (setq file (pop files)))))))) + +(defun git-set-filenames-state (status files state) + "Set the state of a list of named files." + (when files + (git-status-filenames-map status + (lambda (info state) + (unless (eq (git-fileinfo->state info) state) + (setf (git-fileinfo->state info) state) + (setf (git-fileinfo->rename-state info) nil) + (setf (git-fileinfo->orig-name info) nil) + (setf (git-fileinfo->needs-refresh info) t))) + files state) (unless state ;; delete files whose state has been set to nil (ewoc-filter status (lambda (info) (git-fileinfo->state info)))))) @@ -1197,11 +1204,20 @@ Return the list of files that haven't been handled." (interactive) (let* ((status git-status) (pos (ewoc-locate status)) + (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info))))) (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) (unless status (error "Not in git-status buffer.")) (git-run-command nil nil "update-index" "--refresh") (git-clear-status status) (git-update-status-files nil) + ; restore file marks + (when marked-files + (git-status-filenames-map status + (lambda (info) + (setf (git-fileinfo->marked info) t) + (setf (git-fileinfo->needs-refresh info) t)) + marked-files) + (git-refresh-files)) ; move point to the current file name if any (let ((node (and cur-name (git-find-status-file status cur-name)))) (when node (ewoc-goto-node status node))))) -- cgit v0.10.2-6-g49f6 From 9f5599b9829615379353f676369018c47296e1a1 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 29 Sep 2007 11:58:39 +0200 Subject: git.el: Do not print a status message on every git command. Instead print a single message around sequences of commands that can potentially take some time. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 7b4a0d3..ec2e699 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -220,22 +220,15 @@ and returns the process output as a string." (message "Running git %s...done" (car args)) buffer)) -(defun git-run-command (buffer env &rest args) - (message "Running git %s..." (car args)) - (apply #'git-call-process-env buffer env args) - (message "Running git %s...done" (car args))) - (defun git-run-command-region (buffer start end env &rest args) "Run a git command with specified buffer region as input." - (message "Running git %s..." (car args)) (unless (eq 0 (if env (git-run-process-region buffer start end "env" (append (git-get-env-strings env) (list "git") args)) (git-run-process-region buffer start end "git" args))) - (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))) - (message "Running git %s...done" (car args))) + (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))) (defun git-run-hook (hook env &rest args) "Run a git hook and display its output if any." @@ -312,6 +305,13 @@ and returns the process output as a string." "\"") name)) +(defun git-success-message (text files) + "Print a success message after having handled FILES." + (let ((n (length files))) + (if (equal n 1) + (message "%s %s" text (car files)) + (message "%s %d files" text n)))) + (defun git-get-top-dir (dir) "Retrieve the top-level directory of a git tree." (let ((cdup (with-output-to-string @@ -338,7 +338,7 @@ and returns the process output as a string." (sort-lines nil (point-min) (point-max)) (save-buffer)) (when created - (git-run-command nil nil "update-index" "--add" "--" (file-relative-name ignore-name))) + (git-call-process-env nil nil "update-index" "--add" "--" (file-relative-name ignore-name))) (git-update-status-files (list (file-relative-name ignore-name)) 'unknown))) ; propertize definition for XEmacs, stolen from erc-compat @@ -606,7 +606,7 @@ and returns the process output as a string." Return the list of files that haven't been handled." (let (infolist) (with-temp-buffer - (apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files) + (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) (while (re-search-forward ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" @@ -639,7 +639,7 @@ Return the list of files that haven't been handled." Return the list of files that haven't been handled." (let (infolist) (with-temp-buffer - (apply #'git-run-command t nil "ls-files" "-z" (append options (list "--") files)) + (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files)) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (let ((name (match-string 1))) @@ -651,7 +651,7 @@ Return the list of files that haven't been handled." (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." (with-temp-buffer - (apply #'git-run-command t nil "ls-files" "-z" "-u" "--" files) + (apply #'git-call-process-env t nil "ls-files" "-z" "-u" "--" files) (goto-char (point-min)) (let (unmerged-files) (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) @@ -754,11 +754,11 @@ Return the list of files that haven't been handled." ('deleted (push info deleted)) ('modified (push info modified)))) (when added - (apply #'git-run-command nil env "update-index" "--add" "--" (git-get-filenames added))) + (apply #'git-call-process-env nil env "update-index" "--add" "--" (git-get-filenames added))) (when deleted - (apply #'git-run-command nil env "update-index" "--remove" "--" (git-get-filenames deleted))) + (apply #'git-call-process-env nil env "update-index" "--remove" "--" (git-get-filenames deleted))) (when modified - (apply #'git-run-command nil env "update-index" "--" (git-get-filenames modified))))) + (apply #'git-call-process-env nil env "update-index" "--" (git-get-filenames modified))))) (defun git-run-pre-commit-hook () "Run the pre-commit hook if any." @@ -790,6 +790,7 @@ Return the list of files that haven't been handled." head-tree (git-rev-parse "HEAD^{tree}"))) (if files (progn + (message "Running git commit...") (git-read-tree head-tree index-file) (git-update-index nil files) ;update both the default index (git-update-index index-file files) ;and the temporary one @@ -801,7 +802,7 @@ Return the list of files that haven't been handled." (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) (git-set-files-state files 'uptodate) - (git-run-command nil nil "rerere") + (git-call-process-env nil nil "rerere") (git-refresh-files) (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) @@ -912,8 +913,9 @@ Return the list of files that haven't been handled." (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored)))) (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) - (apply #'git-run-command nil nil "update-index" "--add" "--" files) - (git-update-status-files files 'uptodate))) + (apply #'git-call-process-env nil nil "update-index" "--add" "--" files) + (git-update-status-files files 'uptodate) + (git-success-message "Added" files))) (defun git-ignore-file () "Add marked file(s) to the ignore list." @@ -922,7 +924,8 @@ Return the list of files that haven't been handled." (unless files (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files)) (dolist (f files) (git-append-to-ignore f)) - (git-update-status-files files 'ignored))) + (git-update-status-files files 'ignored) + (git-success-message "Ignored" files))) (defun git-remove-file () "Remove the marked file(s)." @@ -935,8 +938,9 @@ Return the list of files that haven't been handled." (progn (dolist (name files) (when (file-exists-p name) (delete-file name))) - (apply #'git-run-command nil nil "update-index" "--remove" "--" files) - (git-update-status-files files nil)) + (apply #'git-call-process-env nil nil "update-index" "--remove" "--" files) + (git-update-status-files files nil) + (git-success-message "Removed" files)) (message "Aborting")))) (defun git-revert-file () @@ -954,18 +958,20 @@ Return the list of files that haven't been handled." ('unmerged (push (git-fileinfo->name info) modified)) ('modified (push (git-fileinfo->name info) modified)))) (when added - (apply #'git-run-command nil nil "update-index" "--force-remove" "--" added)) + (apply #'git-call-process-env nil nil "update-index" "--force-remove" "--" added)) (when modified - (apply #'git-run-command nil nil "checkout" "HEAD" modified)) - (git-update-status-files (append added modified) 'uptodate)))) + (apply #'git-call-process-env nil nil "checkout" "HEAD" modified)) + (git-update-status-files (append added modified) 'uptodate) + (git-success-message "Reverted" files)))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." (interactive) (let ((files (git-get-filenames (git-marked-files-state 'unmerged)))) (when files - (apply #'git-run-command nil nil "update-index" "--" files) - (git-update-status-files files 'uptodate)))) + (apply #'git-call-process-env nil nil "update-index" "--" files) + (git-update-status-files files 'uptodate) + (git-success-message "Resolved" files)))) (defun git-remove-handled () "Remove handled files from the status list." @@ -992,9 +998,11 @@ Return the list of files that haven't been handled." (interactive) (if (setq git-show-ignored (not git-show-ignored)) (progn + (message "Inserting ignored files...") (git-run-ls-files-with-excludes git-status nil 'ignored "-o" "-i") (git-refresh-files) - (git-refresh-ewoc-hf git-status)) + (git-refresh-ewoc-hf git-status) + (message "Inserting ignored files...done")) (git-remove-handled))) (defun git-toggle-show-unknown () @@ -1002,9 +1010,11 @@ Return the list of files that haven't been handled." (interactive) (if (setq git-show-unknown (not git-show-unknown)) (progn + (message "Inserting unknown files...") (git-run-ls-files-with-excludes git-status nil 'unknown "-o") (git-refresh-files) - (git-refresh-ewoc-hf git-status)) + (git-refresh-ewoc-hf git-status) + (message "Inserting unknown files...done")) (git-remove-handled))) (defun git-setup-diff-buffer (buffer) @@ -1207,7 +1217,8 @@ Return the list of files that haven't been handled." (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info))))) (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) (unless status (error "Not in git-status buffer.")) - (git-run-command nil nil "update-index" "--refresh") + (message "Refreshing git status...") + (git-call-process-env nil nil "update-index" "--refresh") (git-clear-status status) (git-update-status-files nil) ; restore file marks @@ -1219,6 +1230,7 @@ Return the list of files that haven't been handled." marked-files) (git-refresh-files)) ; move point to the current file name if any + (message "Refreshing git status...done") (let ((node (and cur-name (git-find-status-file status cur-name)))) (when node (ewoc-goto-node status node))))) -- cgit v0.10.2-6-g49f6 From 0365d885ad17031de27440fec3553675d02aa4c3 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 29 Sep 2007 11:59:07 +0200 Subject: git.el: Update a file status in the git buffer upon save. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index ec2e699..c2a1c3d 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -36,7 +36,6 @@ ;; TODO ;; - portability to XEmacs ;; - better handling of subprocess errors -;; - hook into file save (after-save-hook) ;; - diff against other branch ;; - renaming files from the status buffer ;; - creating tags @@ -1352,9 +1351,24 @@ Commands: (cd dir) (git-status-mode) (git-refresh-status) - (goto-char (point-min))) + (goto-char (point-min)) + (add-hook 'after-save-hook 'git-update-saved-file)) (message "%s is not a git working tree." dir))) +(defun git-update-saved-file () + "Update the corresponding git-status buffer when a file is saved. +Meant to be used in `after-save-hook'." + (let* ((file (expand-file-name buffer-file-name)) + (dir (condition-case nil (git-get-top-dir (file-name-directory file)))) + (buffer (and dir (git-find-status-buffer dir)))) + (when buffer + (with-current-buffer buffer + (let ((filename (file-relative-name file dir))) + ; skip files located inside the .git directory + (unless (string-match "^\\.git/" filename) + (git-call-process-env nil nil "add" "--refresh" "--" filename) + (git-update-status-files (list filename) 'uptodate))))))) + (defun git-help () "Display help for Git mode." (interactive) -- cgit v0.10.2-6-g49f6 From 72dc52bfe6c56e37f290dd2c428d82686d6647df Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 29 Sep 2007 11:59:32 +0200 Subject: git.el: Reset the permission flags when changing a file state. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index c2a1c3d..4286d16 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -484,14 +484,15 @@ and returns the process output as a string." "Remove everything from the status list." (ewoc-filter status (lambda (info) nil))) -(defun git-set-files-state (files state) - "Set the state of a list of files." - (dolist (info files) - (unless (eq (git-fileinfo->state info) state) - (setf (git-fileinfo->state info) state) - (setf (git-fileinfo->rename-state info) nil) - (setf (git-fileinfo->orig-name info) nil) - (setf (git-fileinfo->needs-refresh info) t)))) +(defun git-set-fileinfo-state (info state) + "Set the state of a file info." + (unless (eq (git-fileinfo->state info) state) + (setf (git-fileinfo->state info) state + (git-fileinfo->old-perm info) 0 + (git-fileinfo->new-perm info) 0 + (git-fileinfo->rename-state info) nil + (git-fileinfo->orig-name info) nil + (git-fileinfo->needs-refresh info) t))) (defun git-status-filenames-map (status func files &rest args) "Apply FUNC to the status files names in the FILES list." @@ -510,14 +511,7 @@ and returns the process output as a string." (defun git-set-filenames-state (status files state) "Set the state of a list of named files." (when files - (git-status-filenames-map status - (lambda (info state) - (unless (eq (git-fileinfo->state info) state) - (setf (git-fileinfo->state info) state) - (setf (git-fileinfo->rename-state info) nil) - (setf (git-fileinfo->orig-name info) nil) - (setf (git-fileinfo->needs-refresh info) t))) - files state) + (git-status-filenames-map status #'git-set-fileinfo-state files state) (unless state ;; delete files whose state has been set to nil (ewoc-filter status (lambda (info) (git-fileinfo->state info)))))) @@ -800,7 +794,7 @@ Return the list of files that haven't been handled." (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) - (git-set-files-state files 'uptodate) + (dolist (info files) (git-set-fileinfo-state info 'uptodate)) (git-call-process-env nil nil "rerere") (git-refresh-files) (git-refresh-ewoc-hf git-status) -- cgit v0.10.2-6-g49f6 From 326df26d3349ee2f4d63b737ba318d6e6fafccd9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 30 Sep 2007 17:32:25 -0700 Subject: Update stale documentation link in the k.org site Signed-off-by: Junio C Hamano diff --git a/Documentation/git.txt b/Documentation/git.txt index cb59639..abce801 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -46,6 +46,7 @@ Documentation for older releases are available here: * link:v1.5.3/git.html[documentation for release 1.5.3] * release notes for + link:RelNotes-1.5.3.3.txt[1.5.3.3], link:RelNotes-1.5.3.2.txt[1.5.3.2], link:RelNotes-1.5.3.1.txt[1.5.3.1]. -- cgit v0.10.2-6-g49f6 From e6dc8d60fbd2c84900a26545c5d360b0e202d95b Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Fri, 28 Sep 2007 15:24:26 +0100 Subject: post-receive-hook: Remove the From field from the generated email header so that the pusher's name is used Using the name of the committer of the revision at the tip of the updated ref is not sensible. That information is available in the email itself should it be wanted, and by supplying a "From", we were effectively hiding the person who performed the push - which is useful information in itself. Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 1f88099..cbbd02f 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -177,7 +177,6 @@ generate_email_header() # --- Email (all stdout will be the email) # Generate header cat <<-EOF - From: $committer To: $recipients Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe X-Git-Refname: $refname -- cgit v0.10.2-6-g49f6 From 0bdcac5666170547dc7882cc1d39b0481206074b Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Mon, 1 Oct 2007 00:30:27 +0200 Subject: git stash: document apply's --index switch Signed-off-by: Junio C Hamano diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 05f40cf..5723bb0 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -63,7 +63,7 @@ show []:: it will accept any format known to `git-diff` (e.g., `git-stash show -p stash@\{1}` to view the second most recent stash in patch form). -apply []:: +apply [--index] []:: Restore the changes recorded in the stash on top of the current working tree state. When no `` is given, applies the latest @@ -71,6 +71,11 @@ apply []:: + This operation can fail with conflicts; you need to resolve them by hand in the working tree. ++ +If the `--index` option is used, then tries to reinstate not only the working +tree's changes, but also the index's ones. However, this can fail, when you +have conflicts (which are stored in the index, where you therefore can no +longer apply the changes as they were originally). clear:: Remove all the stashed states. Note that those states will then -- cgit v0.10.2-6-g49f6 From 5946d4ba8970f00bb62b2c9e8714264831034043 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 1 Oct 2007 02:07:47 -0700 Subject: Whip post 1.5.3.3 maintenance series into shape. Signed-off-by: Junio C Hamano diff --git a/Documentation/RelNotes-1.5.3.3.txt b/Documentation/RelNotes-1.5.3.3.txt index e91bd84..2a7bfdd 100644 --- a/Documentation/RelNotes-1.5.3.3.txt +++ b/Documentation/RelNotes-1.5.3.3.txt @@ -29,9 +29,3 @@ Fixes since v1.5.3.2 * git-log sometimes invoked underlying "diff" machinery unnecessarily. - --- -exec >/var/tmp/1 -O=v1.5.3.2-29-gb7bb760 -echo O=`git describe refs/heads/maint` -git shortlog --no-merges $O..refs/heads/maint diff --git a/Documentation/RelNotes-1.5.3.4.txt b/Documentation/RelNotes-1.5.3.4.txt new file mode 100644 index 0000000..47ba287 --- /dev/null +++ b/Documentation/RelNotes-1.5.3.4.txt @@ -0,0 +1,28 @@ +GIT v1.5.3.4 Release Notes +========================== + +Fixes since v1.5.3.3 +-------------------- + + * Sample 'post-receive-hook' incorrectly sent out push + notification e-mails marked as "From: " the committer of the + commit that happened to be at the tip of the branch that was + pushed, not from the person who pushed. + + * git-remote did not exit non-zero status upon error. + + * "git-add -i" did not respond very well to EOF from tty nor + bogus input. + + * "git rebase -i" squash subcommand incorrectly made the + author of later commit the author of resulting commit, + instead of taking from the first one in the squashed series. + + * "git stash apply --index" was not documented. + + +-- +exec >/var/tmp/1 +O=v1.5.3.3-6-g0bdcac5 +echo O=`git describe refs/heads/maint` +git shortlog --no-merges $O..refs/heads/maint diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 7fbde23..6de899a 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.3.2.GIT +DEF_VER=v1.5.3.3.GIT LF=' ' diff --git a/RelNotes b/RelNotes index 44b610e..9829b43 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.5.3.3.txt \ No newline at end of file +Documentation/RelNotes-1.5.3.4.txt \ No newline at end of file -- cgit v0.10.2-6-g49f6 From 63a1f810a6f976a307aaaabdce21082f6d7f20aa Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Tue, 2 Oct 2007 08:14:37 +0200 Subject: fixed link in documentation of diff-options Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 228ccaf..b1f528a 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -179,8 +179,8 @@ --ext-diff:: Allow an external diff helper to be executed. If you set an - external diff driver with gitlink:gitattributes(5), you need - to use this option with gitlink:git-log(1) and friends. + external diff driver with gitlink:gitattributes[5], you need + to use this option with gitlink:git-log[1] and friends. --no-ext-diff:: Disallow external diff drivers. -- cgit v0.10.2-6-g49f6 From c8f203255bc7a782e3a54e73d5801009bb7f0319 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 Oct 2007 11:47:58 -0700 Subject: git-commit: initialize TMP_INDEX just to be sure. We rely on TMP_INDEX variable to decide if we are doing a partial commit, as it is only set in the partial commit codepath. But the variable is never initialized. A stray environment variable from outside could ruin the day. Signed-off-by: Junio C Hamano diff --git a/git-commit.sh b/git-commit.sh index 7a7a2cb..ab43217 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -25,6 +25,7 @@ refuse_partial () { exit 1 } +TMP_INDEX= THIS_INDEX="$GIT_DIR/index" NEXT_INDEX="$GIT_DIR/next-index$$" rm -f "$NEXT_INDEX" -- cgit v0.10.2-6-g49f6 From 070f6918d148f6bac8f5cc829c53fd28be018697 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 Oct 2007 14:31:37 -0700 Subject: dateformat: parse %(xxdate) %(yydate:format) correctly Andy Parkins noticed that parsing of the above would not correctly notice that xxdate does not have any format specifier. Signed-off-by: Junio C Hamano diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index c904ac3..00afe89 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -113,7 +113,7 @@ static int parse_atom(const char *atom, const char *ep) * table. */ const char *formatp = strchr(sp, ':'); - if (!formatp) + if (!formatp || ep < formatp) formatp = ep; if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len)) break; -- cgit v0.10.2-6-g49f6 From 9e1a2acfb2198e29c90b59eef84eb0370ef9f03f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 Oct 2007 15:09:41 -0700 Subject: for-each-ref: fix %(numparent) and %(parent) The string value of %(numparent) was not returned correctly. Also %(parent) misbehaved for the root commits (returned garbage) and merge commits (returned first parent, followed by a space). Signed-off-by: Junio C Hamano diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index 0afa1c5..29f70aa 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -43,7 +43,7 @@ static struct { { "objectsize", FIELD_ULONG }, { "objectname" }, { "tree" }, - { "parent" }, /* NEEDSWORK: how to address 2nd and later parents? */ + { "parent" }, { "numparent", FIELD_ULONG }, { "object" }, { "type" }, @@ -262,24 +262,26 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object } if (!strcmp(name, "numparent")) { char *s = xmalloc(40); + v->ul = num_parents(commit); sprintf(s, "%lu", v->ul); v->s = s; - v->ul = num_parents(commit); } else if (!strcmp(name, "parent")) { int num = num_parents(commit); int i; struct commit_list *parents; - char *s = xmalloc(42 * num); + char *s = xmalloc(41 * num + 1); v->s = s; for (i = 0, parents = commit->parents; parents; - parents = parents->next, i = i + 42) { + parents = parents->next, i = i + 41) { struct commit *parent = parents->item; strcpy(s+i, sha1_to_hex(parent->object.sha1)); if (parents->next) s[i+40] = ' '; } + if (!i) + *s = '\0'; } } } -- cgit v0.10.2-6-g49f6 From 6ea9c7eb2713d55f9af7522e878e6912972bb889 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 2 Oct 2007 21:14:30 +0100 Subject: Fix typo in config.txt There was an 'l' (ell) instead of a '1' (one) in one of the gitlinks. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/Documentation/config.txt b/Documentation/config.txt index 866e053..7ee97df 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -579,7 +579,7 @@ merge.summary:: merge.tool:: Controls which merge resolution program is used by - gitlink:git-mergetool[l]. Valid values are: "kdiff3", "tkdiff", + gitlink:git-mergetool[1]. Valid values are: "kdiff3", "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and "opendiff". merge.verbosity:: -- cgit v0.10.2-6-g49f6 From ac150747d7cc62d53daf9f7c128a0fa88a399f44 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Tue, 2 Oct 2007 18:32:32 -0500 Subject: Say when --track is useful in the git-checkout docs. The documentation used to say what the option does, but it didn't mention a use case. Signed-off-by: Federico Mena Quintero Signed-off-by: Junio C Hamano diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 734928b..2e58481 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -50,7 +50,9 @@ OPTIONS --track:: When -b is given and a branch is created off a remote branch, set up configuration so that git-pull will automatically - retrieve data from the remote branch. Set the + retrieve data from the remote branch. Use this if you always + pull from the same remote branch into the new branch, or if you + don't want to use "git pull " explicitly. Set the branch.autosetupmerge configuration variable to true if you want git-checkout and git-branch to always behave as if '--track' were given. -- cgit v0.10.2-6-g49f6 From 84d176cef65ad23e11643d463c69ad313b728eda Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Tue, 2 Oct 2007 18:33:30 -0500 Subject: Add documentation for --track and --no-track to the git-branch docs. Signed-off-by: Federico Mena Quintero Signed-off-by: Junio C Hamano diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 33bc31b..c839961 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -91,6 +91,21 @@ OPTIONS --no-abbrev:: Display the full sha1s in output listing rather than abbreviating them. +--track:: + Set up configuration so that git-pull will automatically + retrieve data from the remote branch. Use this if you always + pull from the same remote branch into the new branch, or if you + don't want to use "git pull " explicitly. Set the + branch.autosetupmerge configuration variable to true if you + want git-checkout and git-branch to always behave as if + '--track' were given. + +--no-track:: + When -b is given and a branch is created off a remote branch, + set up configuration so that git-pull will not retrieve data + from the remote branch, ignoring the branch.autosetupmerge + configuration variable. + :: The name of the branch to create or delete. The new branch name must pass all checks defined by -- cgit v0.10.2-6-g49f6 From 46749204e97290a0bf20e988d0356f0daba99347 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Tue, 2 Oct 2007 18:34:32 -0500 Subject: Note that git-branch will not automatically checkout the new branch Signed-off-by: Federico Mena Quintero Signed-off-by: Junio C Hamano diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index c839961..b7285bc 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -26,6 +26,10 @@ It will start out with a head equal to the one given as . If no is given, the branch will be created with a head equal to that of the currently checked out branch. +Note that this will create the new branch, but it will not switch the +working tree to it; use "git checkout " to switch to the +new branch. + When a local branch is started off a remote branch, git can setup the branch so that gitlink:git-pull[1] will appropriately merge from that remote branch. If this behavior is desired, it is possible to make it -- cgit v0.10.2-6-g49f6 From 8fc293cb1e639c9ea555a8e95e3a7801fc1d1be8 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Tue, 2 Oct 2007 18:36:30 -0500 Subject: Make git-pull complain and give advice when there is nothing to merge with Signed-off-by: Federico Mena Quintero Signed-off-by: Junio C Hamano diff --git a/git-pull.sh b/git-pull.sh index c3f05f5..74bfc16 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -97,10 +97,24 @@ case "$merge_head" in esac curr_branch=${curr_branch#refs/heads/} - echo >&2 "Warning: No merge candidate found because value of config option - \"branch.${curr_branch}.merge\" does not match any remote branch fetched." - echo >&2 "No changes." - exit 0 + echo >&2 "You asked me to pull without telling me which branch you" + echo >&2 "want to merge with, and 'branch.${curr_branch}.merge' in" + echo >&2 "your configuration file does not tell me either. Please" + echo >&2 "name which branch you want to merge on the command line and" + echo >&2 "try again (e.g. 'git pull ')." + echo >&2 "See git-pull(1) for details on the refspec." + echo >&2 + echo >&2 "If you often merge with the same branch, you may want to" + echo >&2 "configure the following variables in your configuration" + echo >&2 "file:" + echo >&2 + echo >&2 " branch.${curr_branch}.remote = " + echo >&2 " branch.${curr_branch}.merge = " + echo >&2 " remote..url = " + echo >&2 " remote..fetch = " + echo >&2 + echo >&2 "See git-config(1) for details." + exit 1 ;; ?*' '?*) if test -z "$orig_head" -- cgit v0.10.2-6-g49f6 From 2ff5e18a930ddaf03c77a60e52648e7b8b20fc8d Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Wed, 3 Oct 2007 01:42:29 +0200 Subject: Mention 'cpio' dependency in INSTALL Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano diff --git a/INSTALL b/INSTALL index 289b046..f1eb404 100644 --- a/INSTALL +++ b/INSTALL @@ -79,6 +79,9 @@ Issues of note: - "perl" and POSIX-compliant shells are needed to use most of the barebone Porcelainish scripts. + - "cpio" is used by git-merge for saving and restoring the index, + and by git-clone when doing a local (possibly hardlinked) clone. + - Some platform specific issues are dealt with Makefile rules, but depending on your specific installation, you may not have all the libraries/tools needed, or you may have -- cgit v0.10.2-6-g49f6 From eede7b7d110e2c354235d7a3f6c8f1644b5120e5 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 25 Sep 2007 15:29:42 -0400 Subject: diffcore-rename: cache file deltas We find rename candidates by computing a fingerprint hash of each file, and then comparing those fingerprints. There are inherently O(n^2) comparisons, so it pays in CPU time to hoist the (rather expensive) computation of the fingerprint out of that loop (or to cache it once we have computed it once). Previously, we didn't keep the filespec information around because then we had the potential to consume a great deal of memory. However, instead of keeping all of the filespec data, we can instead just keep the fingerprint. This patch implements and uses diff_free_filespec_data_large to accomplish that goal. We also have to change estimate_similarity not to needlessly repopulate the filespec data when we already have the hash. Practical tests showed 4.5x speedup for a 10% memory usage increase. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano diff --git a/diff.c b/diff.c index 0ee9ea1..35e3c61 100644 --- a/diff.c +++ b/diff.c @@ -1675,7 +1675,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) return 0; } -void diff_free_filespec_data(struct diff_filespec *s) +void diff_free_filespec_data_large(struct diff_filespec *s) { if (s->should_free) free(s->data); @@ -1686,6 +1686,11 @@ void diff_free_filespec_data(struct diff_filespec *s) s->should_free = s->should_munmap = 0; s->data = NULL; } +} + +void diff_free_filespec_data(struct diff_filespec *s) +{ + diff_free_filespec_data_large(s); free(s->cnt_data); s->cnt_data = NULL; } diff --git a/diffcore-rename.c b/diffcore-rename.c index 41b35c3..4fc2000 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -184,7 +184,8 @@ static int estimate_similarity(struct diff_filespec *src, if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE) return 0; - if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0)) + if ((!src->cnt_data && diff_populate_filespec(src, 0)) + || (!dst->cnt_data && diff_populate_filespec(dst, 0))) return 0; /* error but caught downstream */ @@ -377,10 +378,10 @@ void diffcore_rename(struct diff_options *options) m->score = estimate_similarity(one, two, minimum_score); m->name_score = basename_same(one, two); - diff_free_filespec_data(one); + diff_free_filespec_data_large(one); } /* We do not need the text anymore */ - diff_free_filespec_data(two); + diff_free_filespec_data_large(two); dst_cnt++; } /* cost matrix sorted by most to least similar pair */ diff --git a/diffcore.h b/diffcore.h index eef17c4..4bf175b 100644 --- a/diffcore.h +++ b/diffcore.h @@ -48,6 +48,7 @@ extern void fill_filespec(struct diff_filespec *, const unsigned char *, extern int diff_populate_filespec(struct diff_filespec *, int); extern void diff_free_filespec_data(struct diff_filespec *); +extern void diff_free_filespec_data_large(struct diff_filespec *); extern int diff_filespec_is_binary(struct diff_filespec *); struct diff_filepair { -- cgit v0.10.2-6-g49f6 From 8ae92e63895000ff9b12046325ae381f3c17d414 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 Oct 2007 21:01:03 -0700 Subject: rename diff_free_filespec_data_large() to diff_free_filespec_blob() Signed-off-by: Junio C Hamano diff --git a/diff.c b/diff.c index 35e3c61..71b340c 100644 --- a/diff.c +++ b/diff.c @@ -1675,7 +1675,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) return 0; } -void diff_free_filespec_data_large(struct diff_filespec *s) +void diff_free_filespec_blob(struct diff_filespec *s) { if (s->should_free) free(s->data); @@ -1690,7 +1690,7 @@ void diff_free_filespec_data_large(struct diff_filespec *s) void diff_free_filespec_data(struct diff_filespec *s) { - diff_free_filespec_data_large(s); + diff_free_filespec_blob(s); free(s->cnt_data); s->cnt_data = NULL; } diff --git a/diffcore-rename.c b/diffcore-rename.c index 4fc2000..142e537 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -378,10 +378,10 @@ void diffcore_rename(struct diff_options *options) m->score = estimate_similarity(one, two, minimum_score); m->name_score = basename_same(one, two); - diff_free_filespec_data_large(one); + diff_free_filespec_blob(one); } /* We do not need the text anymore */ - diff_free_filespec_data_large(two); + diff_free_filespec_blob(two); dst_cnt++; } /* cost matrix sorted by most to least similar pair */ diff --git a/diffcore.h b/diffcore.h index 4bf175b..eb618b1 100644 --- a/diffcore.h +++ b/diffcore.h @@ -48,7 +48,7 @@ extern void fill_filespec(struct diff_filespec *, const unsigned char *, extern int diff_populate_filespec(struct diff_filespec *, int); extern void diff_free_filespec_data(struct diff_filespec *); -extern void diff_free_filespec_data_large(struct diff_filespec *); +extern void diff_free_filespec_blob(struct diff_filespec *); extern int diff_filespec_is_binary(struct diff_filespec *); struct diff_filepair { -- cgit v0.10.2-6-g49f6 From 96e24abc9f14c83abd1e269e1d5bc1c9e50d3fca Mon Sep 17 00:00:00 2001 From: Robert Schiele Date: Wed, 3 Oct 2007 03:49:34 +0200 Subject: the ar tool is called gar on some systems Some systems that have only installed the GNU toolchain (prefixed with "g") do not provide "ar" but only "gar". Make configure find this tool as well. Signed-off-by: Robert Schiele Signed-off-by: Junio C Hamano diff --git a/configure.ac b/configure.ac index 84fd7f1..ed7cc89 100644 --- a/configure.ac +++ b/configure.ac @@ -104,7 +104,7 @@ AC_MSG_NOTICE([CHECKS for programs]) # AC_PROG_CC([cc gcc]) #AC_PROG_INSTALL # needs install-sh or install.sh in sources -AC_CHECK_TOOL(AR, ar, :) +AC_CHECK_TOOLS(AR, [gar ar], :) AC_CHECK_PROGS(TAR, [gtar tar]) # TCLTK_PATH will be set to some value if we want Tcl/Tk # or will be empty otherwise. -- cgit v0.10.2-6-g49f6 From 95af39fcb2d84c8ef2844a9d890e3c67a2e0e1ec Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 2 Oct 2007 22:44:15 -0700 Subject: Must not modify the_index.cache as it may be passed to realloc at some point. The index cache is not static, growing as new entries are added. If entries are added after prune_cache is called, cache will no longer point at the base of the allocation, and realloc will not be happy. I verified that this was the only place in the current source which modified any index_state.cache elements aside from the alloc/realloc calls in read-cache by changing the type of the element to 'struct cache_entry ** const cache' and recompiling. A more efficient patch would create a separate 'cache_base' value to track the allocation and then fix things up when reallocation was necessary, instead of the brute-force memmove used here. Signed-off-by: Junio C Hamano diff --git a/builtin-ls-files.c b/builtin-ls-files.c index 6c1db86..171d449 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -280,7 +280,8 @@ static void prune_cache(const char *prefix) if (pos < 0) pos = -pos-1; - active_cache += pos; + memmove(active_cache, active_cache + pos, + (active_nr - pos) * sizeof(struct cache_entry *)); active_nr -= pos; first = 0; last = active_nr; -- cgit v0.10.2-6-g49f6 From 54e1abce90ed44d0674772a735ac387ce3e264f2 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Wed, 3 Oct 2007 00:03:53 -0700 Subject: Add test case for ls-files --with-tree This tests basic functionality and also exercises a bug noticed by Keith Packard, (prune_cache followed by add_index_entry can trigger an attempt to realloc a pointer into the middle of an allocated buffer). Signed-off-by: Junio C Hamano diff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh new file mode 100755 index 0000000..68eb266 --- /dev/null +++ b/t/t3060-ls-files-with-tree.sh @@ -0,0 +1,71 @@ +#!/bin/sh +# +# Copyright (c) 2007 Carl D. Worth +# + +test_description='git ls-files test (--with-tree). + +This test runs git ls-files --with-tree and in particular in +a scenario known to trigger a crash with some versions of git. +' +. ./test-lib.sh + +test_expect_success setup ' + + # The bug we are exercising requires a fair number of entries + # in a sub-directory so that add_index_entry will trigger a + # realloc. + + echo file >expected && + mkdir sub && + bad= && + for n in 0 1 2 3 4 5 + do + for m in 0 1 2 3 4 5 6 7 8 9 + do + num=00$n$m && + >sub/file-$num && + echo file-$num >>expected || { + bad=t + break + } + done && test -z "$bad" || { + bad=t + break + } + done && test -z "$bad" && + git add . && + git commit -m "add a bunch of files" && + + # We remove them all so that we will have something to add + # back with --with-tree and so that we will definitely be + # under the realloc size to trigger the bug. + rm -rf sub && + git commit -a -m "remove them all" && + + # The bug also requires some entry before our directory so that + # prune_path will modify the_index.cache + + mkdir a_directory_that_sorts_before_sub && + >a_directory_that_sorts_before_sub/file && + mkdir sub && + >sub/file && + git add . +' + +# We have to run from a sub-directory to trigger prune_path +# Then we finally get to run our --with-tree test +cd sub + +test_expect_success 'git -ls-files --with-tree should succeed from subdir' ' + + git ls-files --with-tree=HEAD~1 >../output + +' + +cd .. +test_expect_success \ + 'git -ls-files --with-tree should add entries from named tree.' \ + 'diff -u expected output' + +test_done -- cgit v0.10.2-6-g49f6 From 96b2d4fa927c5055adc5b1d08f10a5d7352e2989 Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Tue, 2 Oct 2007 12:02:57 +0100 Subject: Add a test script for for-each-ref, including test of date formatting Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh new file mode 100644 index 0000000..d0809eb --- /dev/null +++ b/t/t6300-for-each-ref.sh @@ -0,0 +1,151 @@ +#!/bin/sh +# +# Copyright (c) 2007 Andy Parkins +# + +test_description='for-each-ref test' + +. ./test-lib.sh + +# Mon Jul 3 15:18:43 2006 +0000 +datestamp=1151939923 +setdate_and_increment () { + GIT_COMMITTER_DATE="$datestamp +0200" + datestamp=$(expr "$datestamp" + 1) + GIT_AUTHOR_DATE="$datestamp +0200" + datestamp=$(expr "$datestamp" + 1) + export GIT_COMMITTER_DATE GIT_AUTHOR_DATE +} + +test_expect_success 'Create sample commit with known timestamp' ' + setdate_and_increment && + echo "Using $datestamp" > one && + git add one && + git commit -m "Initial" && + setdate_and_increment && + git tag -a -m "Tagging at $datestamp" testtag +' + +test_expect_success 'Check atom names are valid' ' + bad= + for token in \ + refname objecttype objectsize objectname tree parent \ + numparent object type author authorname authoremail \ + authordate committer committername committeremail \ + committerdate tag tagger taggername taggeremail \ + taggerdate creator creatordate subject body contents + do + git for-each-ref --format="$token=%($token)" refs/heads || { + bad=$token + break + } + done + test -z "$bad" +' + +test_expect_failure 'Check invalid atoms names are errors' ' + git-for-each-ref --format="%(INVALID)" refs/heads +' + +test_expect_success 'Check format specifiers are ignored in naming date atoms' ' + git-for-each-ref --format="%(authordate)" refs/heads && + git-for-each-ref --format="%(authordate:default) %(authordate)" refs/heads && + git-for-each-ref --format="%(authordate) %(authordate:default)" refs/heads && + git-for-each-ref --format="%(authordate:default) %(authordate:default)" refs/heads +' + +test_expect_success 'Check valid format specifiers for date fields' ' + git-for-each-ref --format="%(authordate:default)" refs/heads && + git-for-each-ref --format="%(authordate:relative)" refs/heads && + git-for-each-ref --format="%(authordate:short)" refs/heads && + git-for-each-ref --format="%(authordate:local)" refs/heads && + git-for-each-ref --format="%(authordate:iso8601)" refs/heads && + git-for-each-ref --format="%(authordate:rfc2822)" refs/heads +' + +test_expect_failure 'Check invalid format specifiers are errors' ' + git-for-each-ref --format="%(authordate:INVALID)" refs/heads +' + +cat >expected <<\EOF +'refs/heads/master' 'Mon Jul 3 17:18:43 2006 +0200' 'Mon Jul 3 17:18:44 2006 +0200' +'refs/tags/testtag' 'Mon Jul 3 17:18:45 2006 +0200' +EOF + +test_expect_success 'Check unformatted date fields output' ' + (git for-each-ref --shell --format="%(refname) %(committerdate) %(authordate)" refs/heads && + git for-each-ref --shell --format="%(refname) %(taggerdate)" refs/tags) >actual && + git diff expected actual +' + +test_expect_success 'Check format "default" formatted date fields output' ' + f=default && + (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && + git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && + git diff expected actual +' + +# Don't know how to do relative check because I can't know when this script +# is going to be run and can't fake the current time to git, and hence can't +# provide expected output. Instead, I'll just make sure that "relative" +# doesn't exit in error +# +#cat >expected <<\EOF +# +#EOF +# +test_expect_success 'Check format "relative" date fields output' ' + f=relative && + (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && + git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual +' + +cat >expected <<\EOF +'refs/heads/master' '2006-07-03' '2006-07-03' +'refs/tags/testtag' '2006-07-03' +EOF + +test_expect_success 'Check format "short" date fields output' ' + f=short && + (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && + git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && + git diff expected actual +' + +cat >expected <<\EOF +'refs/heads/master' 'Mon Jul 3 15:18:43 2006' 'Mon Jul 3 15:18:44 2006' +'refs/tags/testtag' 'Mon Jul 3 15:18:45 2006' +EOF + +test_expect_success 'Check format "local" date fields output' ' + f=local && + (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && + git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && + git diff expected actual +' + +cat >expected <<\EOF +'refs/heads/master' '2006-07-03 17:18:43 +0200' '2006-07-03 17:18:44 +0200' +'refs/tags/testtag' '2006-07-03 17:18:45 +0200' +EOF + +test_expect_success 'Check format "iso8601" date fields output' ' + f=iso8601 && + (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && + git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && + git diff expected actual +' + +cat >expected <<\EOF +'refs/heads/master' 'Mon, 3 Jul 2006 17:18:43 +0200' 'Mon, 3 Jul 2006 17:18:44 +0200' +'refs/tags/testtag' 'Mon, 3 Jul 2006 17:18:45 +0200' +EOF + +test_expect_success 'Check format "rfc2822" date fields output' ' + f=rfc2822 && + (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && + git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && + git diff expected actual +' + +test_done -- cgit v0.10.2-6-g49f6 From 4c75136f7697f76b31641db775163f5c75906ee2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 3 Oct 2007 02:33:11 -0700 Subject: GIT 1.5.3.4 Signed-off-by: Junio C Hamano diff --git a/Documentation/RelNotes-1.5.3.4.txt b/Documentation/RelNotes-1.5.3.4.txt index 47ba287..b04b3a4 100644 --- a/Documentation/RelNotes-1.5.3.4.txt +++ b/Documentation/RelNotes-1.5.3.4.txt @@ -4,25 +4,32 @@ GIT v1.5.3.4 Release Notes Fixes since v1.5.3.3 -------------------- - * Sample 'post-receive-hook' incorrectly sent out push + * Change to "git-ls-files" in v1.5.3.3 that was introduced to support + partial commit of removal better had a segfaulting bug, which was + diagnosed and fixed by Keith and Carl. + + * Performance improvements for rename detection has been backported + from the 'master' branch. + + * "git-for-each-ref --format='%(numparent)'" was not working + correctly at all, and --format='%(parent)' was not working for + merge commits. + + * Sample "post-receive-hook" incorrectly sent out push notification e-mails marked as "From: " the committer of the commit that happened to be at the tip of the branch that was pushed, not from the person who pushed. - * git-remote did not exit non-zero status upon error. + * "git-remote" did not exit non-zero status upon error. * "git-add -i" did not respond very well to EOF from tty nor bogus input. - * "git rebase -i" squash subcommand incorrectly made the + * "git-rebase -i" squash subcommand incorrectly made the author of later commit the author of resulting commit, instead of taking from the first one in the squashed series. - * "git stash apply --index" was not documented. - + * "git-stash apply --index" was not documented. --- -exec >/var/tmp/1 -O=v1.5.3.3-6-g0bdcac5 -echo O=`git describe refs/heads/maint` -git shortlog --no-merges $O..refs/heads/maint + * autoconfiguration learned that "ar" command is found as "gas" on + some systems. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 6de899a..223c4f5 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.3.3.GIT +DEF_VER=v1.5.3.4.GIT LF=' ' -- cgit v0.10.2-6-g49f6 From f94bf44041f621bad3c99e02476540f4a120a90e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 3 Oct 2007 17:42:52 -0700 Subject: builtin-apply: fix conversion error in strbuf series Signed-off-by: Junio C Hamano diff --git a/builtin-apply.c b/builtin-apply.c index 047a60d..05c6bc3 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2651,7 +2651,7 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) patch = xcalloc(1, sizeof(*patch)); patch->inaccurate_eof = inaccurate_eof; - nr = parse_chunk(buf.buf + offset, buf.len, patch); + nr = parse_chunk(buf.buf + offset, buf.len - offset, patch); if (nr < 0) break; if (apply_in_reverse) -- cgit v0.10.2-6-g49f6 From 9ff74e95da5605778bdc7567ae63d70c96e6bed4 Mon Sep 17 00:00:00 2001 From: Steven Walter Date: Fri, 28 Sep 2007 13:24:19 -0400 Subject: Don't checkout the full tree if avoidable In most cases of branching, the tree is copied unmodified from the trunk to the branch. When that is done, we can simply start with the parent's index and apply the changes on the branch as usual. [ew: rewritten from Steven's original to use SVN::Client instead of the command-line svn client. Since SVN::Client connects separately, we'll share our authentication providers array between our usages of SVN::Client and SVN::Ra, too. Bypassing the high-level SVN::Client library can avoid this, but the code will be much more complex. Regardless, any implementation of this seems to require restarting a connection to the remote server. Also of note is that SVN 1.4 and later allows a more efficient diff_summary to be done instead of a full diff, but since this code is only to support SVN < 1.4.4, we'll ignore it for now.] Signed-off-by: Steven Walter Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano diff --git a/git-svn.perl b/git-svn.perl index 484b057..777e436 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1847,6 +1847,16 @@ sub find_parent_branch { $gs->ra->gs_do_switch($r0, $rev, $gs, $self->full_url, $ed) or die "SVN connection failed somewhere...\n"; + } elsif ($self->ra->trees_match($new_url, $r0, + $self->full_url, $rev)) { + print STDERR "Trees match:\n", + " $new_url\@$r0\n", + " ${\$self->full_url}\@$rev\n", + "Following parent with no changes\n"; + $self->tmp_index_do(sub { + command_noisy('read-tree', $parent); + }); + $self->{last_commit} = $parent; } else { print STDERR "Following parent with do_update\n"; $ed = SVN::Git::Fetcher->new($self); @@ -3027,28 +3037,32 @@ BEGIN { } } +sub _auth_providers () { + [ + SVN::Client::get_simple_provider(), + SVN::Client::get_ssl_server_trust_file_provider(), + SVN::Client::get_simple_prompt_provider( + \&Git::SVN::Prompt::simple, 2), + SVN::Client::get_ssl_client_cert_file_provider(), + SVN::Client::get_ssl_client_cert_prompt_provider( + \&Git::SVN::Prompt::ssl_client_cert, 2), + SVN::Client::get_ssl_client_cert_pw_prompt_provider( + \&Git::SVN::Prompt::ssl_client_cert_pw, 2), + SVN::Client::get_username_provider(), + SVN::Client::get_ssl_server_trust_prompt_provider( + \&Git::SVN::Prompt::ssl_server_trust), + SVN::Client::get_username_prompt_provider( + \&Git::SVN::Prompt::username, 2) + ] +} + sub new { my ($class, $url) = @_; $url =~ s!/+$!!; return $RA if ($RA && $RA->{url} eq $url); SVN::_Core::svn_config_ensure($config_dir, undef); - my ($baton, $callbacks) = SVN::Core::auth_open_helper([ - SVN::Client::get_simple_provider(), - SVN::Client::get_ssl_server_trust_file_provider(), - SVN::Client::get_simple_prompt_provider( - \&Git::SVN::Prompt::simple, 2), - SVN::Client::get_ssl_client_cert_file_provider(), - SVN::Client::get_ssl_client_cert_prompt_provider( - \&Git::SVN::Prompt::ssl_client_cert, 2), - SVN::Client::get_ssl_client_cert_pw_prompt_provider( - \&Git::SVN::Prompt::ssl_client_cert_pw, 2), - SVN::Client::get_username_provider(), - SVN::Client::get_ssl_server_trust_prompt_provider( - \&Git::SVN::Prompt::ssl_server_trust), - SVN::Client::get_username_prompt_provider( - \&Git::SVN::Prompt::username, 2), - ]); + my ($baton, $callbacks) = SVN::Core::auth_open_helper(_auth_providers); my $config = SVN::Core::config_get_config($config_dir); $RA = undef; my $self = SVN::Ra->new(url => $url, auth => $baton, @@ -3112,6 +3126,24 @@ sub get_log { $ret; } +sub trees_match { + my ($self, $url1, $rev1, $url2, $rev2) = @_; + my $ctx = SVN::Client->new(auth => _auth_providers); + my $out = IO::File->new_tmpfile; + + # older SVN (1.1.x) doesn't take $pool as the last parameter for + # $ctx->diff(), so we'll create a default one + my $pool = SVN::Pool->new_default_sub; + + $ra_invalid = 1; # this will open a new SVN::Ra connection to $url1 + $ctx->diff([], $url1, $rev1, $url2, $rev2, 1, 1, 0, $out, $out); + $out->flush; + my $ret = (($out->stat)[7] == 0); + close $out or croak $!; + + $ret; +} + sub get_commit_editor { my ($self, $log, $cb, $pool) = @_; my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : (); -- cgit v0.10.2-6-g49f6 From 58ba4f6ac828606fc206cfd5cec412a6974c91a1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 4 Oct 2007 01:35:01 -0700 Subject: Update state documentation link for 1.5.3.4 Signed-off-by: Junio C Hamano diff --git a/Documentation/git.txt b/Documentation/git.txt index abce801..1c0ee07 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -46,6 +46,7 @@ Documentation for older releases are available here: * link:v1.5.3/git.html[documentation for release 1.5.3] * release notes for + link:RelNotes-1.5.3.4.txt[1.5.3.4], link:RelNotes-1.5.3.3.txt[1.5.3.3], link:RelNotes-1.5.3.2.txt[1.5.3.2], link:RelNotes-1.5.3.1.txt[1.5.3.1]. -- cgit v0.10.2-6-g49f6 From 99516e35d096f41e7133cacde8fbed8ee9a3ecd0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 8 Oct 2007 13:42:41 -0700 Subject: Fix embarrassing "git log --follow" bug It turns out that I completely broke "git log --follow" with my recent patch to revision.c ("Fix revision log diff setup, avoid unnecessary diff generation", commit b7bb760d5ed4881422673d32f869d140221d3564). Why? Because --follow obviously requires the diff machinery to function, exactly the same way pickaxe does. So everybody is away right now, but considering that nobody even noticed this bug, I don't think it matters. But for the record, here's the trivial one-liner fix (well, two, since I also fixed the comment). Because of the nature of the bug, if you ask for patches when following (which is one of the things I normally do), the bug is hidden, because then the request for diff output will automatically also enable the diffs themselves. So while "git log --follow " didn't work, adding a "-p" magically made it work again even without this fix. Signed-off-by: Linus Torvalds Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce diff --git a/revision.c b/revision.c index 6584713..48756b5 100644 --- a/revision.c +++ b/revision.c @@ -1256,8 +1256,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch if (revs->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) revs->diff = 1; - /* Pickaxe needs diffs */ - if (revs->diffopt.pickaxe) + /* Pickaxe and rename following needs diffs */ + if (revs->diffopt.pickaxe || revs->diffopt.follow_renames) revs->diff = 1; if (revs->topo_order) -- cgit v0.10.2-6-g49f6 From 304b5af64f9b5a6b5e9455e2dcab381c568452b6 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 9 Oct 2007 09:35:22 -0700 Subject: Clean up "git log" format with DIFF_FORMAT_NO_OUTPUT This fixes an unnecessary empty line that we add to the log message when we generate diffs, but don't actually end up printing any due to having DIFF_FORMAT_NO_OUTPUT set. This can happen with pickaxe or with rename following. The reason is that we normally add an empty line between the commit and the diff, but we do that even for the case where we've then suppressed the actual printing of the diff. This also updates a couple of tests that assumed the extraneous empty line would exist at the end of output. Signed-off-by: Linus Torvalds Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce diff --git a/log-tree.c b/log-tree.c index a642371..b509c0c 100644 --- a/log-tree.c +++ b/log-tree.c @@ -321,7 +321,8 @@ int log_tree_diff_flush(struct rev_info *opt) * output for readability. */ show_log(opt, opt->diffopt.msg_sep); - if (opt->verbose_header && + if ((opt->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) && + opt->verbose_header && opt->commit_format != CMIT_FMT_ONELINE) { int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH; if ((pch & opt->diffopt.output_format) == pch) diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh index fcbabe8..94b1c24 100755 --- a/t/t3900-i18n-commit.sh +++ b/t/t3900-i18n-commit.sh @@ -8,7 +8,7 @@ test_description='commit and log output encodings' . ./test-lib.sh compare_with () { - git show -s $1 | sed -e '1,/^$/d' -e 's/^ //' -e '$d' >current && + git show -s $1 | sed -e '1,/^$/d' -e 's/^ //' >current && git diff current "$2" } diff --git a/t/t4013/diff.log_-SF_master b/t/t4013/diff.log_-SF_master index 6162ed2..c1599f2 100644 --- a/t/t4013/diff.log_-SF_master +++ b/t/t4013/diff.log_-SF_master @@ -4,5 +4,4 @@ Author: A U Thor Date: Mon Jun 26 00:02:00 2006 +0000 Third - $ -- cgit v0.10.2-6-g49f6 From 2f27f8509edfe55f267ce9207dc42c12d4809a84 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Thu, 4 Oct 2007 15:32:53 +0200 Subject: fix t5403-post-checkout-hook.sh: built-in test in dash does not have "==" Signed-off-by: Alex Riesen Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh index 487abf3..823239a 100755 --- a/t/t5403-post-checkout-hook.sh +++ b/t/t5403-post-checkout-hook.sh @@ -39,7 +39,7 @@ test_expect_success 'post-checkout receives the right arguments with HEAD unchan old=$(awk "{print \$1}" clone1/.git/post-checkout.args) && new=$(awk "{print \$2}" clone1/.git/post-checkout.args) && flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) && - test $old = $new -a $flag == 1 + test $old = $new -a $flag = 1 ' test_expect_success 'post-checkout runs as expected ' ' @@ -52,7 +52,7 @@ test_expect_success 'post-checkout args are correct with git checkout -b ' ' old=$(awk "{print \$1}" clone1/.git/post-checkout.args) && new=$(awk "{print \$2}" clone1/.git/post-checkout.args) && flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) && - test $old = $new -a $flag == 1 + test $old = $new -a $flag = 1 ' test_expect_success 'post-checkout receives the right args with HEAD changed ' ' @@ -60,7 +60,7 @@ test_expect_success 'post-checkout receives the right args with HEAD changed ' ' old=$(awk "{print \$1}" clone2/.git/post-checkout.args) && new=$(awk "{print \$2}" clone2/.git/post-checkout.args) && flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) && - test $old != $new -a $flag == 1 + test $old != $new -a $flag = 1 ' test_expect_success 'post-checkout receives the right args when not switching branches ' ' @@ -68,7 +68,7 @@ test_expect_success 'post-checkout receives the right args when not switching br old=$(awk "{print \$1}" clone2/.git/post-checkout.args) && new=$(awk "{print \$2}" clone2/.git/post-checkout.args) && flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) && - test $old == $new -a $flag == 0 + test $old = $new -a $flag = 0 ' test_done -- cgit v0.10.2-6-g49f6 From edb0e04e819c7cde709d10e03f398d12692259f7 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Wed, 10 Oct 2007 19:59:19 -0500 Subject: git-gc: by default use safer "-A" option to repack when not --prune'ing This makes use of repack's new "-A" option which does not drop packed unreachable objects. This makes git-gc safe to call at any time, particularly when a repository is referenced as an alternate by another repository. git-gc --prune will use the "-a" option to repack instead of "-A", so that packed unreachable objects will be removed. Signed-off-by: Brandon Casey Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce diff --git a/builtin-gc.c b/builtin-gc.c index 23ad2b6..6323e0d 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -26,7 +26,7 @@ static int gc_auto_pack_limit = 20; #define MAX_ADD 10 static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL}; static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL}; -static const char *argv_repack[MAX_ADD] = {"repack", "-a", "-d", "-l", NULL}; +static const char *argv_repack[MAX_ADD] = {"repack", "-d", "-l", NULL}; static const char *argv_prune[] = {"prune", NULL}; static const char *argv_rerere[] = {"rerere", "gc", NULL}; @@ -211,6 +211,16 @@ int cmd_gc(int argc, const char **argv, const char *prefix) prune = 0; if (!need_to_gc()) return 0; + } else { + /* + * Use safer (for shared repos) "-A" option to + * repack when not pruning. Auto-gc makes its + * own decision. + */ + if (prune) + append_option(argv_repack, "-a", MAX_ADD); + else + append_option(argv_repack, "-A", MAX_ADD); } if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD)) -- cgit v0.10.2-6-g49f6 From 729f50453cb54e136567b89ba6aecab60f0257c6 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Wed, 10 Oct 2007 20:00:27 -0500 Subject: git-gc --auto: simplify "repack" command line building Since "-a" is removed from the base repack command line, we can simplify how we add additional options to this command line when using --auto. Signed-off-by: Brandon Casey Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce diff --git a/builtin-gc.c b/builtin-gc.c index 6323e0d..956c32d 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -143,8 +143,6 @@ static int too_many_packs(void) static int need_to_gc(void) { - int ac = 0; - /* * Setting gc.auto and gc.autopacklimit to 0 or negative can * disable the automatic gc. @@ -158,14 +156,10 @@ static int need_to_gc(void) * we run "repack -A -d -l". Otherwise we tell the caller * there is no need. */ - argv_repack[ac++] = "repack"; if (too_many_packs()) - argv_repack[ac++] = "-A"; + append_option(argv_repack, "-A", MAX_ADD); else if (!too_many_loose_objects()) return 0; - argv_repack[ac++] = "-d"; - argv_repack[ac++] = "-l"; - argv_repack[ac++] = NULL; return 1; } -- cgit v0.10.2-6-g49f6 From a72c874e43e89a879fc4a8998ecd6e5a56667929 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Fri, 5 Oct 2007 22:16:44 +0200 Subject: git-config: don't silently ignore options after --list Error out if someone gives options after --list since that is not a valid syntax. Signed-off-by: Frank Lichtenheld Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce diff --git a/builtin-config.c b/builtin-config.c index 0a605e0..cb7e9e9 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -172,8 +172,11 @@ int cmd_config(int argc, const char **argv, const char *prefix) type = T_INT; else if (!strcmp(argv[1], "--bool")) type = T_BOOL; - else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) + else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) { + if (argc != 2) + usage(git_config_set_usage); return git_config(show_all_config); + } else if (!strcmp(argv[1], "--global")) { char *home = getenv("HOME"); if (home) { -- cgit v0.10.2-6-g49f6 From 7288ed8ebdab54e5aa05400bd840e1348ffdc270 Mon Sep 17 00:00:00 2001 From: Jean-Luc Herren Date: Tue, 9 Oct 2007 21:29:26 +0200 Subject: git add -i: Fix parsing of abbreviated hunk headers The unified diff format allows one-line ranges to be abbreviated by omiting the size. The hunk header "@@ -10,1 +10,1 @@" can be expressed as "@@ -10 +10 @@", but this wasn't properly parsed in all cases. Such abbreviated hunk headers are generated when a one-line change (add, remove or modify) appears without context; for example because the file is a one-liner itself or because GIT_DIFF_OPTS was set to '-u0'. If the user then runs 'git add -i' and enters the 'patch' command for that file, perl complains about undefined variables. Signed-off-by: Jean-Luc Herren Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce diff --git a/git-add--interactive.perl b/git-add--interactive.perl index be68814..15b3f5b 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -360,7 +360,9 @@ sub hunk_splittable { sub parse_hunk_header { my ($line) = @_; my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = - $line =~ /^@@ -(\d+)(?:,(\d+)) \+(\d+)(?:,(\d+)) @@/; + $line =~ /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/; + $o_cnt = 1 unless defined $o_cnt; + $n_cnt = 1 unless defined $n_cnt; return ($o_ofs, $o_cnt, $n_ofs, $n_cnt); } @@ -705,9 +707,6 @@ sub patch_update_cmd { parse_hunk_header($text->[0]); if (!$_->{USE}) { - if (!defined $o_cnt) { $o_cnt = 1; } - if (!defined $n_cnt) { $n_cnt = 1; } - # We would have added ($n_cnt - $o_cnt) lines # to the postimage if we were to use this hunk, # but we didn't. So the line number that the next @@ -719,10 +718,10 @@ sub patch_update_cmd { if ($n_lofs) { $n_ofs += $n_lofs; $text->[0] = ("@@ -$o_ofs" . - ((defined $o_cnt) + (($o_cnt != 1) ? ",$o_cnt" : '') . " +$n_ofs" . - ((defined $n_cnt) + (($n_cnt != 1) ? ",$n_cnt" : '') . " @@\n"); } -- cgit v0.10.2-6-g49f6 From 7b40a4552a0fbdc6ee9a22ce10408d79ca1599af Mon Sep 17 00:00:00 2001 From: Jean-Luc Herren Date: Tue, 9 Oct 2007 21:34:17 +0200 Subject: git add -i: Remove unused variables Signed-off-by: Jean-Luc Herren Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 15b3f5b..ac598f8 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -374,9 +374,8 @@ sub split_hunk { # it can be split, but we would need to take care of # overlaps later. - my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = parse_hunk_header($text->[0]); + my ($o_ofs, undef, $n_ofs) = parse_hunk_header($text->[0]); my $hunk_start = 1; - my $next_hunk_start; OUTER: while (1) { @@ -443,8 +442,8 @@ sub split_hunk { for my $hunk (@split) { $o_ofs = $hunk->{OLD}; $n_ofs = $hunk->{NEW}; - $o_cnt = $hunk->{OCNT}; - $n_cnt = $hunk->{NCNT}; + my $o_cnt = $hunk->{OCNT}; + my $n_cnt = $hunk->{NCNT}; my $head = ("@@ -$o_ofs" . (($o_cnt != 1) ? ",$o_cnt" : '') . @@ -459,7 +458,7 @@ sub split_hunk { sub find_last_o_ctx { my ($it) = @_; my $text = $it->{TEXT}; - my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = parse_hunk_header($text->[0]); + my ($o_ofs, $o_cnt) = parse_hunk_header($text->[0]); my $i = @{$text}; my $last_o_ctx = $o_ofs + $o_cnt; while (0 < --$i) { @@ -531,8 +530,7 @@ sub coalesce_overlapping_hunks { for (grep { $_->{USE} } @in) { my $text = $_->{TEXT}; - my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = - parse_hunk_header($text->[0]); + my ($o_ofs) = parse_hunk_header($text->[0]); if (defined $last_o_ctx && $o_ofs <= $last_o_ctx) { merge_hunk($out[-1], $_); @@ -699,7 +697,7 @@ sub patch_update_cmd { @hunk = coalesce_overlapping_hunks(@hunk); - my ($o_lofs, $n_lofs) = (0, 0); + my $n_lofs = 0; my @result = (); for (@hunk) { my $text = $_->{TEXT}; @@ -806,8 +804,6 @@ sub main_loop { } } -my @z; - refresh(); status_cmd(); main_loop(); -- cgit v0.10.2-6-g49f6 From 60fcc2e6ce255da1baa92905c10456981d260fa0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 10 Oct 2007 23:14:35 +0100 Subject: clear_commit_marks(): avoid deep recursion Before this patch, clear_commit_marks() recursed for each parent. This could be potentially very expensive in terms of stack space. Probably the only reason that this did not lead to problems is the fact that we typically call clear_commit_marks() after marking a relatively small set of commits. Use (sort of) a tail recursion instead: first recurse on the parents other than the first one, and then continue the loop with the first parent. Noticed by Shawn Pearce. Signed-off-by: Johannes Schindelin Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce diff --git a/commit.c b/commit.c index dc5a064..1fbdd2d 100644 --- a/commit.c +++ b/commit.c @@ -441,17 +441,22 @@ struct commit *pop_most_recent_commit(struct commit_list **list, void clear_commit_marks(struct commit *commit, unsigned int mark) { - struct commit_list *parents; + while (commit) { + struct commit_list *parents; - commit->object.flags &= ~mark; - parents = commit->parents; - while (parents) { - struct commit *parent = parents->item; + if (!(mark & commit->object.flags)) + return; - /* Have we already cleared this? */ - if (mark & parent->object.flags) - clear_commit_marks(parent, mark); - parents = parents->next; + commit->object.flags &= ~mark; + + parents = commit->parents; + if (!parents) + return; + + while ((parents = parents->next)) + clear_commit_marks(parents->item, mark); + + commit = commit->parents->item; } } -- cgit v0.10.2-6-g49f6 From 1ae14a6b52f8f0506780cd361933f717163ae19b Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Fri, 12 Oct 2007 11:32:51 +0000 Subject: git-config: handle --file option with relative pathname properly When calling git-config not from the top level directory of a repository, it changes directory before trying to open the config file specified through the --file option, which then fails if the config file was specified by a relative pathname. This patch adjusts the pathname to the config file if applicable. The problem was noticed by Joey Hess, reported through http://bugs.debian.org/445208 Signed-off-by: Gerrit Pape Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce diff --git a/builtin-config.c b/builtin-config.c index cb7e9e9..d98b6c2 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -165,7 +165,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) { int nongit = 0; char* value; - setup_git_directory_gently(&nongit); + const char *file = setup_git_directory_gently(&nongit); while (1 < argc) { if (!strcmp(argv[1], "--int")) @@ -192,7 +192,12 @@ int cmd_config(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) { if (argc < 3) usage(git_config_set_usage); - setenv(CONFIG_ENVIRONMENT, argv[2], 1); + if (!is_absolute_path(argv[2]) && file) + file = prefix_filename(file, strlen(file), + argv[2]); + else + file = argv[2]; + setenv(CONFIG_ENVIRONMENT, file, 1); argc--; argv++; } -- cgit v0.10.2-6-g49f6 From a2a9150bf06bdab419016003fa1bc04905d0f4fb Mon Sep 17 00:00:00 2001 From: Kristof Provost Date: Sat, 6 Oct 2007 16:24:42 +0200 Subject: makefile: Add a cscope target The current makefile supports ctags but not cscope. Some people prefer cscope (I do), so this patch adds a cscope target. I've also added cscope* to the .gitignore file. For some reason tags and TAGS weren't in there either so I've added them too. Signed-off-by: Kristof Provost Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce diff --git a/.gitignore b/.gitignore index e0b91be..62afef2 100644 --- a/.gitignore +++ b/.gitignore @@ -171,3 +171,6 @@ config.status config.mak.autogen config.mak.append configure +tags +TAGS +cscope* diff --git a/Makefile b/Makefile index 8db4dbe..55fbcb7 100644 --- a/Makefile +++ b/Makefile @@ -947,6 +947,10 @@ tags: $(RM) tags $(FIND) . -name '*.[hcS]' -print | xargs ctags -a +cscope: + $(RM) cscope* + $(FIND) . -name '*.[hcS]' -print | xargs cscope -b + ### Detect prefix changes TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\ $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ) @@ -1093,7 +1097,7 @@ clean: $(LIB_FILE) $(XDIFF_LIB) $(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X $(RM) $(TEST_PROGRAMS) - $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags + $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope* $(RM) -r autom4te.cache $(RM) configure config.log config.mak.autogen config.mak.append config.status config.cache $(RM) -r $(GIT_TARNAME) .doc-tmp-dir @@ -1111,7 +1115,7 @@ endif $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS .PHONY: all install clean strip -.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags .FORCE-GIT-CFLAGS +.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags cscope .FORCE-GIT-CFLAGS ### Check documentation # -- cgit v0.10.2-6-g49f6 From a5d410153736fc5320ad7322e25a6ccf56efcf10 Mon Sep 17 00:00:00 2001 From: Michele Ballabio Date: Thu, 4 Oct 2007 14:26:54 +0200 Subject: git-reflog: document --verbose Signed-off-by: Michele Ballabio Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt index 5180f68..5c7316c 100644 --- a/Documentation/git-reflog.txt +++ b/Documentation/git-reflog.txt @@ -16,7 +16,7 @@ The command takes various subcommands, and different options depending on the subcommand: [verse] -git reflog expire [--dry-run] [--stale-fix] +git reflog expire [--dry-run] [--stale-fix] [--verbose] [--expire=