From 8c02eee29e116355474250a5e9270db6034c60de Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Fri, 1 Sep 2006 00:37:15 +0200 Subject: [PATCH 1/6] git-rev-list(1): group options; reformat; document more options Signed-off-by: Jonas Fonseca Signed-off-by: Junio C Hamano --- Documentation/git-rev-list.txt | 252 ++++++++++++++++++++++++--------- 1 file changed, 187 insertions(+), 65 deletions(-) diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index a446a6b5a2..3c4c2fbfb9 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -27,111 +27,233 @@ SYNOPSIS DESCRIPTION ----------- + Lists commit objects in reverse chronological order starting at the given commit(s), taking ancestry relationship into account. This is useful to produce human-readable log output. -Commits which are stated with a preceding '{caret}' cause listing to stop at -that point. Their parents are implied. "git-rev-list foo bar {caret}baz" thus +Commits which are stated with a preceding '{caret}' cause listing to +stop at that point. Their parents are implied. Thus the following +command: + +----------------------------------------------------------------------- + $ git-rev-list foo bar ^baz +----------------------------------------------------------------------- + means "list all the commits which are included in 'foo' and 'bar', but not in 'baz'". -A special notation .. can be used as a -short-hand for {caret} . +A special notation "''..''" can be used as a +short-hand for "{caret}'' ''". For example, either of +the following may be used interchangeably: -Another special notation is ... which is useful for -merges. The resulting set of commits is the symmetric difference +----------------------------------------------------------------------- + $ git-rev-list origin..HEAD + $ git-rev-list HEAD ^origin +----------------------------------------------------------------------- + +Another special notation is "''...''" which is useful +for merges. The resulting set of commits is the symmetric difference between the two operands. The following two commands are equivalent: ------------- -$ git-rev-list A B --not $(git-merge-base --all A B) -$ git-rev-list A...B ------------- +----------------------------------------------------------------------- + $ git-rev-list A B --not $(git-merge-base --all A B) + $ git-rev-list A...B +----------------------------------------------------------------------- + +gitlink:git-rev-list[1] is a very essential git program, since it +provides the ability to build and traverse commit ancestry graphs. For +this reason, it has a lot of different options that enables it to be +used by commands as different as gitlink:git-bisect[1] and +gitlink:git-repack[1]. OPTIONS ------- ---pretty:: - Print the contents of the commit changesets in human-readable form. + +Commit Formatting +~~~~~~~~~~~~~~~~~ + +Using these options, gitlink:git-rev-list[1] will act similar to the +more specialized family of commit log tools: gitlink:git-log[1], +gitlink:git-show[1], and gitlink:git-whatchanged[1] + +--pretty[='']:: + + Pretty print the contents of the commit logs in a given format, + where '' can be one of 'raw', 'medium', 'short', 'full', + and 'oneline'. When left out the format default to 'medium'. + +--relative-date:: + + Show dates relative to the current time, e.g. "2 hours ago". + Only takes effect for dates shown in human-readable format, such + as when using "--pretty". --header:: - Print the contents of the commit in raw-format; each - record is separated with a NUL character. + + Print the contents of the commit in raw-format; each record is + separated with a NUL character. --parents:: + Print the parents of the commit. ---objects:: - Print the object IDs of any object referenced by the listed commits. - 'git-rev-list --objects foo ^bar' thus means "send me all object IDs - which I need to download if I have the commit object 'bar', but - not 'foo'". +Diff Formatting +~~~~~~~~~~~~~~~ ---objects-edge:: - Similar to `--objects`, but also print the IDs of - excluded commits prefixed with a `-` character. This is - used by `git-pack-objects` to build 'thin' pack, which - records objects in deltified form based on objects - contained in these excluded commits to reduce network - traffic. +Below are listed options that control the formatting of diff output. +Some of them are specific to gitlink:git-rev-list[1], however other diff +options may be given. See gitlink:git-diff-files[1] for more options. ---unpacked:: - Only useful with `--objects`; print the object IDs that - are not in packs. +-c:: ---bisect:: - Limit output to the one commit object which is roughly halfway - between the included and excluded commits. Thus, if 'git-rev-list - --bisect foo {caret}bar {caret}baz' outputs 'midpoint', the output - of 'git-rev-list foo {caret}midpoint' and 'git-rev-list midpoint - {caret}bar {caret}baz' would be of roughly the same length. - Finding the change - which introduces a regression is thus reduced to a binary search: - repeatedly generate and test new 'midpoint's until the commit chain - is of length one. + This flag changes the way a merge commit is displayed. It shows + the differences from each of the parents to the merge result + simultaneously instead of showing pairwise diff between a parent + and the result one at a time. Furthermore, it lists only files + which were modified from all parents. + +--cc:: + + This flag implies the '-c' options and further compresses the + patch output by omitting hunks that show differences from only + one parent, or show the same change from all but one parent for + an Octopus merge. + +-r:: + + Show recursive diffs. + +-t:: + + Show the tree objects in the diff output. This implies '-r'. + +Commit Limiting +~~~~~~~~~~~~~~~ + +Besides specifying a range of commits that should be listed using the +special notations explained in the description, additional commit +limiting may be applied. + +-- + +-n 'number', --max-count='number':: ---max-count:: Limit the number of commits output. ---max-age=timestamp, --min-age=timestamp:: +--since='date', --after='date':: + + Show commits more recent than a specific date. + +--until='date', --before='date':: + + Show commits older than a specific date. + +--max-age='timestamp', --min-age='timestamp':: + Limit the commits output to specified time range. ---sparse:: - When optional paths are given, the command outputs only - the commits that changes at least one of them, and also - ignores merges that do not touch the given paths. This - flag makes the command output all eligible commits - (still subject to count and age limitation), but apply - merge simplification nevertheless. - --remove-empty:: + Stop when a given path disappears from the tree. --no-merges:: + Do not print commits with more than one parent. --not:: - Reverses the meaning of the '{caret}' prefix (or lack - thereof) for all following revision specifiers, up to - the next `--not`. + + Reverses the meaning of the '{caret}' prefix (or lack thereof) + for all following revision specifiers, up to the next '--not'. --all:: - Pretend as if all the refs in `$GIT_DIR/refs/` are - listed on the command line as . ---topo-order:: - By default, the commits are shown in reverse - chronological order. This option makes them appear in - topological order (i.e. descendant commits are shown - before their parents). + Pretend as if all the refs in `$GIT_DIR/refs/` are listed on the + command line as ''. --merge:: + After a failed merge, show refs that touch files having a conflict and don't exist on all heads to merge. ---relative-date:: - Show dates relative to the current time, e.g. "2 hours ago". - Only takes effect for dates shown in human-readable format, - such as when using "--pretty". +--boundary:: + + Output uninteresting commits at the boundary, which are usually + not shown. + +--dense, --sparse:: + +When optional paths are given, the default behaviour ('--dense') is to +only output commits that changes at least one of them, and also ignore +merges that do not touch the given paths. + +Use the '--sparse' flag to makes the command output all eligible commits +(still subject to count and age limitation), but apply merge +simplification nevertheless. + +--bisect:: + +Limit output to the one commit object which is roughly halfway between +the included and excluded commits. Thus, if + +----------------------------------------------------------------------- + $ git-rev-list --bisect foo ^bar ^baz +----------------------------------------------------------------------- + +outputs 'midpoint', the output of the two commands + +----------------------------------------------------------------------- + $ git-rev-list foo ^midpoint + $ git-rev-list midpoint ^bar ^baz +----------------------------------------------------------------------- + +would be of roughly the same length. Finding the change which +introduces a regression is thus reduced to a binary search: repeatedly +generate and test new 'midpoint's until the commit chain is of length +one. + +-- + +Commit Ordering +~~~~~~~~~~~~~~~ + +By default, the commits are shown in reverse chronological order. + +--topo-order:: + + This option makes them appear in topological order (i.e. + descendant commits are shown before their parents). + +--date-order:: + + This option is similar to '--topo-order' in the sense that no + parent comes before all of its children, but otherwise things + are still ordered in the commit timestamp order. + +Object Traversal +~~~~~~~~~~~~~~~~ + +These options are mostly targeted for packing of git repositories. + +--objects:: + + Print the object IDs of any object referenced by the listed + commits. 'git-rev-list --objects foo ^bar' thus means "send me + all object IDs which I need to download if I have the commit + object 'bar', but not 'foo'". + +--objects-edge:: + + Similar to '--objects', but also print the IDs of excluded + commits prefixed with a "-" character. This is used by + gitlink:git-pack-objects[1] to build "thin" pack, which records + objects in deltified form based on objects contained in these + excluded commits to reduce network traffic. + +--unpacked:: + + Only useful with '--objects'; print the object IDs that are not + in packs. Author ------ @@ -139,9 +261,9 @@ Written by Linus Torvalds Documentation -------------- -Documentation by David Greaves, Junio C Hamano and the git-list . +Documentation by David Greaves, Junio C Hamano, Jonas Fonseca +and the git-list . GIT --- Part of the gitlink:git[7] suite - From af04b1271090801b277938836dcb7a39fc059721 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 1 Sep 2006 10:49:29 +0200 Subject: [PATCH 2/6] fmt-merge-msg: fix off-by-one bug Thanks to the recent malloc()->xmalloc() change, and XMALLOC_POISON, this bug was found. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-fmt-merge-msg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index ed59e77e19..432963db90 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -142,7 +142,7 @@ static int handle_line(char *line) if (origin[0] == '\'' && origin[len - 1] == '\'') { char *new_origin = xmalloc(len - 1); memcpy(new_origin, origin + 1, len - 2); - new_origin[len - 1] = 0; + new_origin[len - 2] = 0; origin = new_origin; } else origin = strdup(origin); From 501524e938aee0b9691fe7fb1abf5eb17a23132f Mon Sep 17 00:00:00 2001 From: Sergey Vlasov Date: Fri, 1 Sep 2006 22:42:59 +0400 Subject: [PATCH 3/6] Documentation: Fix howto/revert-branch-rebase.html generation The rule for howto/*.html used "$?", which expands to the list of all newer prerequisites, including asciidoc.conf added by another rule. "$<" should be used instead. Signed-off-by: Sergey Vlasov Signed-off-by: Junio C Hamano --- Documentation/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Makefile b/Documentation/Makefile index ed8b886802..c00f5f62b7 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -107,7 +107,7 @@ WEBDOC_DEST = /pub/software/scm/git/docs $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt rm -f $@+ $@ - sed -e '1,/^$$/d' $? | asciidoc -b xhtml11 - >$@+ + sed -e '1,/^$$/d' $< | asciidoc -b xhtml11 - >$@+ mv $@+ $@ install-webdoc : html From ad1ed5ee896ba5d7f89bc04c7441b1532efb9853 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 1 Sep 2006 15:17:01 -0700 Subject: [PATCH 4/6] consolidate two copies of new style object header parsing code. Also while we are at it, remove redundant typename[] array from unpack_sha1_header. The only reason it is different from the type_names[] array in object.c module is that this code cares about the subset of object types that are valid in a loose object, so prepare a separate array of boolean that tells us which types are valid, and share the name translation with the others. Signed-off-by: Junio C Hamano --- sha1_file.c | 87 ++++++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index ce90e200cb..76f66e6a85 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -711,17 +711,39 @@ int legacy_loose_object(unsigned char *map) return 0; } +static unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep) +{ + unsigned shift; + unsigned char c; + unsigned long size; + unsigned long used = 0; + + c = buf[used++]; + *type = (c >> 4) & 7; + size = c & 15; + shift = 4; + while (c & 0x80) { + if (len <= used) + return 0; + if (sizeof(long) * 8 <= shift) + return 0; + c = buf[used++]; + size += (c & 0x7f) << shift; + shift += 7; + } + *sizep = size; + return used; +} + static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz) { - unsigned char c; - unsigned int bits; - unsigned long size; - static const char *typename[8] = { - NULL, /* OBJ_EXT */ - "commit", "tree", "blob", "tag", - NULL, NULL, NULL + unsigned long size, used; + static const char valid_loose_object_type[8] = { + 0, /* OBJ_EXT */ + 1, 1, 1, 1, /* "commit", "tree", "blob", "tag" */ + 0, /* "delta" and others are invalid in a loose object */ }; - const char *type; + enum object_type type; /* Get the data stream */ memset(stream, 0, sizeof(*stream)); @@ -735,22 +757,11 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon return inflate(stream, 0); } - c = *map++; - mapsize--; - type = typename[(c >> 4) & 7]; - if (!type) + used = unpack_object_header_gently(map, mapsize, &type, &size); + if (!used || !valid_loose_object_type[type]) return -1; - - bits = 4; - size = c & 0xf; - while ((c & 0x80)) { - if (bits >= 8*sizeof(long)) - return -1; - c = *map++; - size += (c & 0x7f) << bits; - bits += 7; - mapsize--; - } + map += used; + mapsize -= used; /* Set up the stream for the rest.. */ stream->next_in = map; @@ -758,7 +769,8 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon inflateInit(stream); /* And generate the fake traditional header */ - stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu", type, size); + stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu", + type_names[type], size); return 0; } @@ -916,25 +928,18 @@ static int packed_delta_info(unsigned char *base_sha1, static unsigned long unpack_object_header(struct packed_git *p, unsigned long offset, enum object_type *type, unsigned long *sizep) { - unsigned shift; - unsigned char c; - unsigned long size; + unsigned long used; - if (offset >= p->pack_size) + if (p->pack_size <= offset) die("object offset outside of pack file"); - c = *((unsigned char *)p->pack_base + offset++); - *type = (c >> 4) & 7; - size = c & 15; - shift = 4; - while (c & 0x80) { - if (offset >= p->pack_size) - die("object offset outside of pack file"); - c = *((unsigned char *)p->pack_base + offset++); - size += (c & 0x7f) << shift; - shift += 7; - } - *sizep = size; - return offset; + + used = unpack_object_header_gently((unsigned char *)p->pack_base + + offset, + p->pack_size - offset, type, sizep); + if (!used) + die("object offset outside of pack file"); + + return offset + used; } int check_reuse_pack_delta(struct packed_git *p, unsigned long offset, From 9befac470b4cfad529032dbcffcb71242ec71f91 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Sat, 2 Sep 2006 00:16:31 -0400 Subject: [PATCH 5/6] Replace uses of strdup with xstrdup. Like xmalloc and xrealloc xstrdup dies with a useful message if the native strdup() implementation returns NULL rather than a valid pointer. I just tried to use xstrdup in new code and found it to be missing. However I expected it to be present as xmalloc and xrealloc are already commonly used throughout the code. [jc: removed the part that deals with last_XXX, which I am finding more and more dubious these days.] Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- blame.c | 2 +- builtin-apply.c | 2 +- builtin-fmt-merge-msg.c | 22 +++++++++++----------- builtin-grep.c | 2 +- builtin-name-rev.c | 2 +- builtin-prune.c | 2 +- builtin-push.c | 10 +++++----- builtin-repo-config.c | 6 +++--- builtin-rev-list.c | 4 ++-- builtin-rm.c | 2 +- builtin-show-branch.c | 6 +++--- builtin-symbolic-ref.c | 4 ++-- builtin-tar-tree.c | 2 +- builtin-upload-tar.c | 2 +- builtin-zip-tree.c | 4 ++-- config.c | 8 ++++---- connect.c | 6 +++--- diff.c | 4 ++-- environment.c | 2 +- fetch.c | 4 ++-- fsck-objects.c | 2 +- git-compat-util.h | 8 ++++++++ git.c | 2 +- http-fetch.c | 2 +- http-push.c | 6 +++--- imap-send.c | 12 ++++++------ merge-file.c | 2 +- merge-recursive.c | 14 +++++++------- merge-tree.c | 4 ++-- path-list.c | 2 +- refs.c | 4 ++-- server-info.c | 2 +- sha1_file.c | 2 +- sha1_name.c | 2 +- 34 files changed, 84 insertions(+), 76 deletions(-) diff --git a/blame.c b/blame.c index 8968046b00..8cfd5d94c7 100644 --- a/blame.c +++ b/blame.c @@ -617,7 +617,7 @@ static void simplify_commit(struct rev_info *revs, struct commit *commit) if (new_name) { struct util_info* putil = get_util(p); if (!putil->pathname) - putil->pathname = strdup(new_name); + putil->pathname = xstrdup(new_name); } else { *pp = parent->next; continue; diff --git a/builtin-apply.c b/builtin-apply.c index 1a1deaf78c..872c8005a2 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2449,7 +2449,7 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) static int git_apply_config(const char *var, const char *value) { if (!strcmp(var, "apply.whitespace")) { - apply_default_whitespace = strdup(value); + apply_default_whitespace = xstrdup(value); return 0; } return git_default_config(var, value); diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index 432963db90..c407c033e7 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -111,29 +111,29 @@ static int handle_line(char *line) i = find_in_list(&srcs, src); if (i < 0) { i = srcs.nr; - append_to_list(&srcs, strdup(src), + append_to_list(&srcs, xstrdup(src), xcalloc(1, sizeof(struct src_data))); } src_data = srcs.payload[i]; if (pulling_head) { - origin = strdup(src); + origin = xstrdup(src); src_data->head_status |= 1; } else if (!strncmp(line, "branch ", 7)) { - origin = strdup(line + 7); + origin = xstrdup(line + 7); append_to_list(&src_data->branch, origin, NULL); src_data->head_status |= 2; } else if (!strncmp(line, "tag ", 4)) { origin = line; - append_to_list(&src_data->tag, strdup(origin + 4), NULL); + append_to_list(&src_data->tag, xstrdup(origin + 4), NULL); src_data->head_status |= 2; } else if (!strncmp(line, "remote branch ", 14)) { - origin = strdup(line + 14); + origin = xstrdup(line + 14); append_to_list(&src_data->r_branch, origin, NULL); src_data->head_status |= 2; } else { - origin = strdup(src); - append_to_list(&src_data->generic, strdup(line), NULL); + origin = xstrdup(src); + append_to_list(&src_data->generic, xstrdup(line), NULL); src_data->head_status |= 2; } @@ -145,7 +145,7 @@ static int handle_line(char *line) new_origin[len - 2] = 0; origin = new_origin; } else - origin = strdup(origin); + origin = xstrdup(origin); } else { char *new_origin = xmalloc(strlen(origin) + strlen(src) + 5); sprintf(new_origin, "%s of %s", origin, src); @@ -203,7 +203,7 @@ static void shortlog(const char *name, unsigned char *sha1, bol = strstr(commit->buffer, "\n\n"); if (!bol) { - append_to_list(&subjects, strdup(sha1_to_hex( + append_to_list(&subjects, xstrdup(sha1_to_hex( commit->object.sha1)), NULL); continue; @@ -218,7 +218,7 @@ static void shortlog(const char *name, unsigned char *sha1, memcpy(oneline, bol, len); oneline[len] = 0; } else - oneline = strdup(bol); + oneline = xstrdup(bol); append_to_list(&subjects, oneline, NULL); } @@ -277,7 +277,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) usage(fmt_merge_msg_usage); /* get current branch */ - head = strdup(git_path("HEAD")); + head = xstrdup(git_path("HEAD")); current_branch = resolve_ref(head, head_sha1, 1); current_branch += strlen(head) - 4; free((char *)head); diff --git a/builtin-grep.c b/builtin-grep.c index 8213ce2402..6430f6d79e 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -1048,7 +1048,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) /* ignore empty line like grep does */ if (!buf[0]) continue; - add_pattern(&opt, strdup(buf), argv[1], ++lno, + add_pattern(&opt, xstrdup(buf), argv[1], ++lno, GREP_PATTERN); } fclose(patterns); diff --git a/builtin-name-rev.c b/builtin-name-rev.c index d44e782c99..52886b69b0 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -100,7 +100,7 @@ static int name_ref(const char *path, const unsigned char *sha1) else if (!strncmp(path, "refs/", 5)) path = path + 5; - name_rev(commit, strdup(path), 0, 0, deref); + name_rev(commit, xstrdup(path), 0, 0, deref); } return 0; } diff --git a/builtin-prune.c b/builtin-prune.c index fc885ce55b..6228c7907b 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -106,7 +106,7 @@ static void process_tree(struct tree *tree, obj->flags |= SEEN; if (parse_tree(tree) < 0) die("bad tree object %s", sha1_to_hex(obj->sha1)); - name = strdup(name); + name = xstrdup(name); add_object(obj, p, path, name); me.up = path; me.elem = name; diff --git a/builtin-push.c b/builtin-push.c index ada8338cc1..c43f2566d5 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -33,7 +33,7 @@ static int expand_one_ref(const char *ref, const unsigned char *sha1) ref += 5; if (!strncmp(ref, "tags/", 5)) - add_refspec(strdup(ref)); + add_refspec(xstrdup(ref)); return 0; } @@ -100,12 +100,12 @@ static int get_remotes_uri(const char *repo, const char *uri[MAX_URI]) if (!is_refspec) { if (n < MAX_URI) - uri[n++] = strdup(s); + uri[n++] = xstrdup(s); else error("more than %d URL's specified, ignoring the rest", MAX_URI); } else if (is_refspec && !has_explicit_refspec) - add_refspec(strdup(s)); + add_refspec(xstrdup(s)); } fclose(f); if (!n) @@ -125,13 +125,13 @@ static int get_remote_config(const char* key, const char* value) !strncmp(key + 7, config_repo, config_repo_len)) { if (!strcmp(key + 7 + config_repo_len, ".url")) { if (config_current_uri < MAX_URI) - config_uri[config_current_uri++] = strdup(value); + config_uri[config_current_uri++] = xstrdup(value); else error("more than %d URL's specified, ignoring the rest", MAX_URI); } else if (config_get_refspecs && !strcmp(key + 7 + config_repo_len, ".push")) - add_refspec(strdup(value)); + add_refspec(xstrdup(value)); } return 0; } diff --git a/builtin-repo-config.c b/builtin-repo-config.c index a1756c8580..9cf12d32e5 100644 --- a/builtin-repo-config.c +++ b/builtin-repo-config.c @@ -72,12 +72,12 @@ static int get_value(const char* key_, const char* regex_) const char *home = getenv("HOME"); local = getenv("GIT_CONFIG_LOCAL"); if (!local) - local = repo_config = strdup(git_path("config")); + local = repo_config = xstrdup(git_path("config")); if (home) - global = strdup(mkpath("%s/.gitconfig", home)); + global = xstrdup(mkpath("%s/.gitconfig", home)); } - key = strdup(key_); + key = xstrdup(key_); for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl) *tl = tolower(*tl); for (tl=key; *tl && *tl != '.'; ++tl) diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 402af8e1b5..8437454fbe 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -109,7 +109,7 @@ static void process_blob(struct blob *blob, if (obj->flags & (UNINTERESTING | SEEN)) return; obj->flags |= SEEN; - name = strdup(name); + name = xstrdup(name); add_object(obj, p, path, name); } @@ -130,7 +130,7 @@ static void process_tree(struct tree *tree, if (parse_tree(tree) < 0) die("bad tree object %s", sha1_to_hex(obj->sha1)); obj->flags |= SEEN; - name = strdup(name); + name = xstrdup(name); add_object(obj, p, path, name); me.up = path; me.elem = name; diff --git a/builtin-rm.c b/builtin-rm.c index 593d86744c..33d04bd015 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -32,7 +32,7 @@ static int remove_file(const char *name) ret = unlink(name); if (!ret && (slash = strrchr(name, '/'))) { - char *n = strdup(name); + char *n = xstrdup(name); do { n[slash - name] = 0; name = n; diff --git a/builtin-show-branch.c b/builtin-show-branch.c index d7de18ec0b..578c9fafd0 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -163,7 +163,7 @@ static void name_commits(struct commit_list *list, en += sprintf(en, "^"); else en += sprintf(en, "^%d", nth); - name_commit(p, strdup(newname), 0); + name_commit(p, xstrdup(newname), 0); i++; name_first_parent_chain(p); } @@ -364,7 +364,7 @@ static int append_ref(const char *refname, const unsigned char *sha1) refname, MAX_REVS); return 0; } - ref_name[ref_name_cnt++] = strdup(refname); + ref_name[ref_name_cnt++] = xstrdup(refname); ref_name[ref_name_cnt] = NULL; return 0; } @@ -521,7 +521,7 @@ static int git_show_branch_config(const char *var, const char *value) default_alloc = default_alloc * 3 / 2 + 20; default_arg = xrealloc(default_arg, sizeof *default_arg * default_alloc); } - default_arg[default_num++] = strdup(value); + default_arg[default_num++] = xstrdup(value); default_arg[default_num] = NULL; return 0; } diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index b4ec6f28ed..1d3a5e229a 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -7,7 +7,7 @@ static const char git_symbolic_ref_usage[] = static void check_symref(const char *HEAD) { unsigned char sha1[20]; - const char *git_HEAD = strdup(git_path("%s", HEAD)); + const char *git_HEAD = xstrdup(git_path("%s", HEAD)); const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 0); if (git_refs_heads_master) { /* we want to strip the .git/ part */ @@ -26,7 +26,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) check_symref(argv[1]); break; case 3: - create_symref(strdup(git_path("%s", argv[1])), argv[2]); + create_symref(xstrdup(git_path("%s", argv[1])), argv[2]); break; default: usage(git_symbolic_ref_usage); diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c index 61a413590d..fa666f78c5 100644 --- a/builtin-tar-tree.c +++ b/builtin-tar-tree.c @@ -351,7 +351,7 @@ static int remote_tar(int argc, const char **argv) usage(tar_tree_usage); /* --remote= */ - url = strdup(argv[1]+9); + url = xstrdup(argv[1]+9); pid = git_connect(fd, url, exec); if (pid < 0) return 1; diff --git a/builtin-upload-tar.c b/builtin-upload-tar.c index 7b401bbb77..06a945a4b1 100644 --- a/builtin-upload-tar.c +++ b/builtin-upload-tar.c @@ -53,7 +53,7 @@ int cmd_upload_tar(int argc, const char **argv, const char *prefix) return nak("expected (optional) base"); if (buf[len-1] == '\n') buf[--len] = 0; - base = strdup(buf + 5); + base = xstrdup(buf + 5); len = packet_read_line(0, buf, sizeof(buf)); } if (len) diff --git a/builtin-zip-tree.c b/builtin-zip-tree.c index a5b834d360..1c1f6830c1 100644 --- a/builtin-zip-tree.c +++ b/builtin-zip-tree.c @@ -311,11 +311,11 @@ int cmd_zip_tree(int argc, const char **argv, const char *prefix) switch (argc) { case 3: - base = strdup(argv[2]); + base = xstrdup(argv[2]); baselen = strlen(base); break; case 2: - base = strdup(""); + base = xstrdup(""); baselen = 0; break; default: diff --git a/config.c b/config.c index c0897cc807..e8f0caf7cf 100644 --- a/config.c +++ b/config.c @@ -350,11 +350,11 @@ int git_config(config_fn_t fn) home = getenv("HOME"); filename = getenv("GIT_CONFIG_LOCAL"); if (!filename) - filename = repo_config = strdup(git_path("config")); + filename = repo_config = xstrdup(git_path("config")); } if (home) { - char *user_config = strdup(mkpath("%s/.gitconfig", home)); + char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); if (!access(user_config, R_OK)) ret = git_config_from_file(fn, user_config); free(user_config); @@ -545,8 +545,8 @@ int git_config_set_multivar(const char* key, const char* value, if (!config_filename) config_filename = git_path("config"); } - config_filename = strdup(config_filename); - lock_file = strdup(mkpath("%s.lock", config_filename)); + config_filename = xstrdup(config_filename); + lock_file = xstrdup(mkpath("%s.lock", config_filename)); /* * Since "key" actually contains the section name and the real diff --git a/connect.c b/connect.c index e501ccce25..06ef387649 100644 --- a/connect.c +++ b/connect.c @@ -69,7 +69,7 @@ struct ref **get_remote_heads(int in, struct ref **list, if (len != name_len + 41) { if (server_capabilities) free(server_capabilities); - server_capabilities = strdup(name + name_len + 1); + server_capabilities = xstrdup(name + name_len + 1); } if (!check_ref(name, name_len, flags)) @@ -661,7 +661,7 @@ int git_connect(int fd[2], char *url, const char *prog) if (path[1] == '~') path++; else { - path = strdup(ptr); + path = xstrdup(ptr); free_path = 1; } @@ -672,7 +672,7 @@ int git_connect(int fd[2], char *url, const char *prog) /* These underlying connection commands die() if they * cannot connect. */ - char *target_host = strdup(host); + char *target_host = xstrdup(host); if (git_use_proxy(host)) git_proxy_connect(fd, host); else diff --git a/diff.c b/diff.c index 70699fd8c4..9dcbda3117 100644 --- a/diff.c +++ b/diff.c @@ -216,7 +216,7 @@ static char *quote_one(const char *str) return NULL; needlen = quote_c_style(str, NULL, NULL, 0); if (!needlen) - return strdup(str); + return xstrdup(str); xp = xmalloc(needlen + 1); quote_c_style(str, xp, NULL, 0); return xp; @@ -658,7 +658,7 @@ static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat, x->is_renamed = 1; } else - x->name = strdup(name_a); + x->name = xstrdup(name_a); return x; } diff --git a/environment.c b/environment.c index 5fae9ac305..84d870ca4e 100644 --- a/environment.c +++ b/environment.c @@ -47,7 +47,7 @@ static void setup_git_env(void) } git_graft_file = getenv(GRAFT_ENVIRONMENT); if (!git_graft_file) - git_graft_file = strdup(git_path("info/grafts")); + git_graft_file = xstrdup(git_path("info/grafts")); } const char *get_git_dir(void) diff --git a/fetch.c b/fetch.c index 7d3812c406..34df8d37d7 100644 --- a/fetch.c +++ b/fetch.c @@ -234,8 +234,8 @@ int pull_targets_stdin(char ***target, const char ***write_ref) *target = xrealloc(*target, targets_alloc * sizeof(**target)); *write_ref = xrealloc(*write_ref, targets_alloc * sizeof(**write_ref)); } - (*target)[targets] = strdup(tg_one); - (*write_ref)[targets] = rf_one ? strdup(rf_one) : NULL; + (*target)[targets] = xstrdup(tg_one); + (*write_ref)[targets] = rf_one ? xstrdup(rf_one) : NULL; targets++; } return targets; diff --git a/fsck-objects.c b/fsck-objects.c index 24286de15d..4d994f3fc8 100644 --- a/fsck-objects.c +++ b/fsck-objects.c @@ -458,7 +458,7 @@ static void fsck_object_dir(const char *path) static int fsck_head_link(void) { unsigned char sha1[20]; - const char *git_HEAD = strdup(git_path("HEAD")); + const char *git_HEAD = xstrdup(git_path("HEAD")); const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 1); int pfxlen = strlen(git_HEAD) - 4; /* strip .../.git/ part */ diff --git a/git-compat-util.h b/git-compat-util.h index 91f2b0d3f0..552b8ec23a 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -84,6 +84,14 @@ extern char *gitstrcasestr(const char *haystack, const char *needle); extern size_t gitstrlcpy(char *, const char *, size_t); #endif +static inline char* xstrdup(const char *str) +{ + char *ret = strdup(str); + if (!ret) + die("Out of memory, strdup failed"); + return ret; +} + static inline void *xmalloc(size_t size) { void *ret = malloc(size); diff --git a/git.c b/git.c index 05871ad42c..9bb21ede8f 100644 --- a/git.c +++ b/git.c @@ -97,7 +97,7 @@ static char *alias_string; static int git_alias_config(const char *var, const char *value) { if (!strncmp(var, "alias.", 6) && !strcmp(var + 6, alias_command)) { - alias_string = strdup(value); + alias_string = xstrdup(value); } return 0; } diff --git a/http-fetch.c b/http-fetch.c index 6806f3678c..fac17607b4 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -787,7 +787,7 @@ static int remote_ls(struct alt_base *repo, const char *path, int flags, ls.flags = flags; ls.repo = repo; - ls.path = strdup(path); + ls.path = xstrdup(path); ls.dentry_name = NULL; ls.dentry_flags = 0; ls.userData = userData; diff --git a/http-push.c b/http-push.c index 7814666d8d..670ff007be 100644 --- a/http-push.c +++ b/http-push.c @@ -1539,7 +1539,7 @@ static void remote_ls(const char *path, int flags, struct remote_ls_ctx ls; ls.flags = flags; - ls.path = strdup(path); + ls.path = xstrdup(path); ls.dentry_name = NULL; ls.dentry_flags = 0; ls.userData = userData; @@ -1738,7 +1738,7 @@ static struct object_list **process_tree(struct tree *tree, die("bad tree object %s", sha1_to_hex(obj->sha1)); obj->flags |= SEEN; - name = strdup(name); + name = xstrdup(name); p = add_one_object(obj, p); me.up = path; me.elem = name; @@ -2467,7 +2467,7 @@ int main(int argc, char **argv) /* Set up revision info for this refspec */ commit_argc = 3; - new_sha1_hex = strdup(sha1_to_hex(ref->new_sha1)); + new_sha1_hex = xstrdup(sha1_to_hex(ref->new_sha1)); old_sha1_hex = NULL; commit_argv[1] = "--objects"; commit_argv[2] = new_sha1_hex; diff --git a/imap-send.c b/imap-send.c index 65c71c602d..8ed0f0aa6f 100644 --- a/imap-send.c +++ b/imap-send.c @@ -1032,7 +1032,7 @@ imap_open_store( imap_server_conf_t *srvc ) * getpass() returns a pointer to a static buffer. make a copy * for long term storage. */ - srvc->pass = strdup( arg ); + srvc->pass = xstrdup( arg ); } if (CAP(NOLOGIN)) { fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host ); @@ -1288,7 +1288,7 @@ git_imap_config(const char *key, const char *val) key += sizeof imap_key - 1; if (!strcmp( "folder", key )) { - imap_folder = strdup( val ); + imap_folder = xstrdup( val ); } else if (!strcmp( "host", key )) { { if (!strncmp( "imap:", val, 5 )) @@ -1298,16 +1298,16 @@ git_imap_config(const char *key, const char *val) } if (!strncmp( "//", val, 2 )) val += 2; - server.host = strdup( val ); + server.host = xstrdup( val ); } else if (!strcmp( "user", key )) - server.user = strdup( val ); + server.user = xstrdup( val ); else if (!strcmp( "pass", key )) - server.pass = strdup( val ); + server.pass = xstrdup( val ); else if (!strcmp( "port", key )) server.port = git_config_int( key, val ); else if (!strcmp( "tunnel", key )) - server.tunnel = strdup( val ); + server.tunnel = xstrdup( val ); return 0; } diff --git a/merge-file.c b/merge-file.c index f32c653825..fc9b148993 100644 --- a/merge-file.c +++ b/merge-file.c @@ -21,7 +21,7 @@ static const char *write_temp_file(mmfile_t *f) fd = mkstemp(filename); if (fd < 0) return NULL; - filename = strdup(filename); + filename = xstrdup(filename); if (f->size != xwrite(fd, f->ptr, f->size)) { rm_temp_file(filename); return NULL; diff --git a/merge-recursive.c b/merge-recursive.c index 48b2763de6..611cd95cf5 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -513,8 +513,8 @@ static char *unique_path(const char *path, const char *branch) static int mkdir_p(const char *path, unsigned long mode) { - /* path points to cache entries, so strdup before messing with it */ - char *buf = strdup(path); + /* path points to cache entries, so xstrdup before messing with it */ + char *buf = xstrdup(path); int result = safe_create_leading_directories(buf); free(buf); return result; @@ -668,9 +668,9 @@ static struct merge_file_info merge_file(struct diff_filespec *o, git_unpack_file(a->sha1, src1); git_unpack_file(b->sha1, src2); - argv[2] = la = strdup(mkpath("%s/%s", branch1, a->path)); - argv[6] = lb = strdup(mkpath("%s/%s", branch2, b->path)); - argv[4] = lo = strdup(mkpath("orig/%s", o->path)); + argv[2] = la = xstrdup(mkpath("%s/%s", branch1, a->path)); + argv[6] = lb = xstrdup(mkpath("%s/%s", branch2, b->path)); + argv[4] = lo = xstrdup(mkpath("orig/%s", o->path)); argv[7] = src1; argv[8] = orig; argv[9] = src2, @@ -1314,9 +1314,9 @@ int main(int argc, char *argv[]) original_index_file = getenv("GIT_INDEX_FILE"); if (!original_index_file) - original_index_file = strdup(git_path("index")); + original_index_file = xstrdup(git_path("index")); - temporary_index_file = strdup(git_path("mrg-rcrsv-tmp-idx")); + temporary_index_file = xstrdup(git_path("mrg-rcrsv-tmp-idx")); if (argc < 4) die("Usage: %s ... -- ...\n", argv[0]); diff --git a/merge-tree.c b/merge-tree.c index c2e9a867ed..60df758c41 100644 --- a/merge-tree.c +++ b/merge-tree.c @@ -177,7 +177,7 @@ static void resolve(const char *base, struct name_entry *branch1, struct name_en if (!branch1) return; - path = strdup(mkpath("%s%s", base, result->path)); + path = xstrdup(mkpath("%s%s", base, result->path)); orig = create_entry(2, branch1->mode, branch1->sha1, path); final = create_entry(0, result->mode, result->sha1, path); @@ -233,7 +233,7 @@ static struct merge_list *link_entry(unsigned stage, const char *base, struct na if (entry) path = entry->path; else - path = strdup(mkpath("%s%s", base, n->path)); + path = xstrdup(mkpath("%s%s", base, n->path)); link = create_entry(stage, n->mode, n->sha1, path); link->link = entry; return link; diff --git a/path-list.c b/path-list.c index b1ee72d1dc..0c332dc7b5 100644 --- a/path-list.c +++ b/path-list.c @@ -45,7 +45,7 @@ static int add_entry(struct path_list *list, const char *path) (list->nr - index) * sizeof(struct path_list_item)); list->items[index].path = list->strdup_paths ? - strdup(path) : (char *)path; + xstrdup(path) : (char *)path; list->items[index].util = NULL; list->nr++; diff --git a/refs.c b/refs.c index aab14fc107..5e653141ce 100644 --- a/refs.c +++ b/refs.c @@ -313,8 +313,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *path, } lock->lk = xcalloc(1, sizeof(struct lock_file)); - lock->ref_file = strdup(path); - lock->log_file = strdup(git_path("logs/%s", lock->ref_file + plen)); + lock->ref_file = xstrdup(path); + lock->log_file = xstrdup(git_path("logs/%s", lock->ref_file + plen)); lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT; if (safe_create_leading_directories(lock->ref_file)) diff --git a/server-info.c b/server-info.c index 7df628f2b2..2fb8f57103 100644 --- a/server-info.c +++ b/server-info.c @@ -23,7 +23,7 @@ static int add_info_ref(const char *path, const unsigned char *sha1) static int update_info_refs(int force) { - char *path0 = strdup(git_path("info/refs")); + char *path0 = xstrdup(git_path("info/refs")); int len = strlen(path0); char *path1 = xmalloc(len + 2); diff --git a/sha1_file.c b/sha1_file.c index 76f66e6a85..4ef98053f8 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -115,7 +115,7 @@ static void fill_sha1_path(char *pathbuf, const unsigned char *sha1) /* * NOTE! This returns a statically allocated buffer, so you have to be - * careful about using it. Do a "strdup()" if you need to save the + * careful about using it. Do a "xstrdup()" if you need to save the * filename. * * Also note that this returns the location for creating. Reading diff --git a/sha1_name.c b/sha1_name.c index 3f6b77ccfa..1fbc443805 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -279,7 +279,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) pathname = resolve_ref(git_path(*p, len, str), this_result, 1); if (pathname) { if (!refs_found++) - real_path = strdup(pathname); + real_path = xstrdup(pathname); if (!warn_ambiguous_refs) break; } From 6ce4e61f1be690681f6494eb5ca26540c2316f81 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 2 Sep 2006 18:23:48 +0200 Subject: [PATCH 6/6] Trace into a file or an open fd and refactor tracing code. If GIT_TRACE is set to an absolute path (starting with a '/' character), we interpret this as a file path and we trace into it. Also if GIT_TRACE is set to an integer value greater than 1 and lower than 10, we interpret this as an open fd value and we trace into it. Note that this behavior is not compatible with the previous one. We also trace whole messages using one write(2) call to make sure messages from processes do net get mixed up in the middle. This patch makes it possible to get trace information when running "make test". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Makefile | 2 +- cache.h | 6 ++ exec_cmd.c | 18 +----- git.c | 25 ++------- imap-send.c | 25 --------- quote.c | 32 +++++++++++ quote.h | 1 + t/test-lib.sh | 10 +++- trace.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++ write_or_die.c | 25 +++++++++ 10 files changed, 229 insertions(+), 64 deletions(-) create mode 100644 trace.c diff --git a/Makefile b/Makefile index 05bd77f967..a639cdfcfb 100644 --- a/Makefile +++ b/Makefile @@ -249,7 +249,7 @@ LIB_OBJS = \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \ - write_or_die.o \ + write_or_die.o trace.o \ alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) BUILTIN_OBJS = \ diff --git a/cache.h b/cache.h index 7257c4c53d..195908fc34 100644 --- a/cache.h +++ b/cache.h @@ -398,6 +398,7 @@ extern char git_commit_encoding[MAX_ENCODING_LENGTH]; extern int copy_fd(int ifd, int ofd); extern void write_or_die(int fd, const void *buf, size_t count); +extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg); /* Finish off pack transfer receiving end */ extern int receive_unpack_pack(int fd[2], const char *me, int quiet, int); @@ -423,4 +424,9 @@ extern struct commit *alloc_commit_node(void); extern struct tag *alloc_tag_node(void); extern void alloc_report(void); +/* trace.c */ +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, ...); + #endif /* CACHE_H */ diff --git a/exec_cmd.c b/exec_cmd.c index e30936d72c..5d6a1247b4 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -97,26 +97,12 @@ int execv_git_cmd(const char **argv) tmp = argv[0]; argv[0] = git_command; - if (getenv("GIT_TRACE")) { - const char **p = argv; - fputs("trace: exec:", stderr); - while (*p) { - fputc(' ', stderr); - sq_quote_print(stderr, *p); - ++p; - } - putc('\n', stderr); - fflush(stderr); - } + trace_argv_printf(argv, -1, "trace: exec:"); /* execve() can only ever return if it fails */ execve(git_command, (char **)argv, environ); - if (getenv("GIT_TRACE")) { - fprintf(stderr, "trace: exec failed: %s\n", - strerror(errno)); - fflush(stderr); - } + trace_printf("trace: exec failed: %s\n", strerror(errno)); argv[0] = tmp; } diff --git a/git.c b/git.c index 9bb21ede8f..1d00111819 100644 --- a/git.c +++ b/git.c @@ -179,17 +179,9 @@ static int handle_alias(int *argcp, const char ***argv) if (!strcmp(alias_command, new_argv[0])) die("recursive alias: %s", alias_command); - if (getenv("GIT_TRACE")) { - int i; - fprintf(stderr, "trace: alias expansion: %s =>", - alias_command); - for (i = 0; i < count; ++i) { - fputc(' ', stderr); - sq_quote_print(stderr, new_argv[i]); - } - fputc('\n', stderr); - fflush(stderr); - } + trace_argv_printf(new_argv, count, + "trace: alias expansion: %s =>", + alias_command); new_argv = xrealloc(new_argv, sizeof(char*) * (count + *argcp + 1)); @@ -292,16 +284,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) prefix = setup_git_directory(); if (p->option & USE_PAGER) setup_pager(); - if (getenv("GIT_TRACE")) { - int j; - fprintf(stderr, "trace: built-in: git"); - for (j = 0; j < argc; ++j) { - fputc(' ', stderr); - sq_quote_print(stderr, argv[j]); - } - putc('\n', stderr); - fflush(stderr); - } + trace_argv_printf(argv, argc, "trace: built-in: git"); exit(p->fn(argc, argv, prefix)); } diff --git a/imap-send.c b/imap-send.c index 8ed0f0aa6f..362e474374 100644 --- a/imap-send.c +++ b/imap-send.c @@ -110,7 +110,6 @@ static char *next_arg( char ** ); static void free_generic_messages( message_t * ); -static int nfvasprintf( char **str, const char *fmt, va_list va ); static int nfsnprintf( char *buf, int blen, const char *fmt, ... ); @@ -371,21 +370,6 @@ free_generic_messages( message_t *msgs ) } } -static int -git_vasprintf( 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 ))) - return -1; - if (len >= (int)sizeof(tmp)) - vsprintf( *strp, fmt, ap ); - else - memcpy( *strp, tmp, len + 1 ); - return len; -} - static int nfsnprintf( char *buf, int blen, const char *fmt, ... ) { @@ -399,15 +383,6 @@ nfsnprintf( char *buf, int blen, const char *fmt, ... ) return ret; } -static int -nfvasprintf( char **str, const char *fmt, va_list va ) -{ - int ret = git_vasprintf( str, fmt, va ); - if (ret < 0) - die( "Fatal: Out of memory\n"); - return ret; -} - static struct { unsigned char i, j, s[256]; } rs; diff --git a/quote.c b/quote.c index e220dcc280..a38786c177 100644 --- a/quote.c +++ b/quote.c @@ -74,6 +74,38 @@ char *sq_quote(const char *src) return buf; } +char *sq_quote_argv(const char** argv, int count) +{ + char *buf, *to; + int i; + size_t len = 0; + + /* Count argv if needed. */ + if (count < 0) { + for (count = 0; argv[count]; 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. */ + for (i = 0; i < count; ++i) { + *to++ = ' '; + to += sq_quote_buf(to, len, argv[i]); + } + + return buf; +} + char *sq_dequote(char *arg) { char *dst = arg; diff --git a/quote.h b/quote.h index fc5481e78a..a6c4611c25 100644 --- a/quote.h +++ b/quote.h @@ -31,6 +31,7 @@ extern char *sq_quote(const char *src); extern void sq_quote_print(FILE *stream, const char *src); extern size_t sq_quote_buf(char *dst, size_t n, const char *src); +extern char *sq_quote_argv(const char** argv, int count); /* 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/t/test-lib.sh b/t/test-lib.sh index 470a909891..e2629339d4 100755 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -28,13 +28,21 @@ unset GIT_DIR unset GIT_EXTERNAL_DIFF unset GIT_INDEX_FILE unset GIT_OBJECT_DIRECTORY -unset GIT_TRACE unset SHA1_FILE_DIRECTORIES unset SHA1_FILE_DIRECTORY export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME export EDITOR VISUAL +case $(echo $GIT_TRACE |tr [A-Z] [a-z]) in + 1|2|true) + echo "* warning: Some tests will not work if GIT_TRACE" \ + "is set as to trace on STDERR ! *" + echo "* warning: Please set GIT_TRACE to something" \ + "other than 1, 2 or true ! *" + ;; +esac + # Each test should start with something like this, after copyright notices: # # test_description='Description of this test... diff --git a/trace.c b/trace.c new file mode 100644 index 0000000000..ce01c34749 --- /dev/null +++ b/trace.c @@ -0,0 +1,149 @@ +/* + * GIT - The information manager from hell + * + * Copyright (C) 2000-2002 Michael R. Elkins + * Copyright (C) 2002-2004 Oswald Buddenhagen + * Copyright (C) 2004 Theodore Y. Ts'o + * Copyright (C) 2006 Mike McCormack + * Copyright (C) 2006 Christian Couder + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "cache.h" +#include "quote.h" + +/* Stolen from "imap-send.c". */ +static int git_vasprintf(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))) + return -1; + if (len >= (int)sizeof(tmp)) + vsprintf(*strp, fmt, ap); + else + memcpy(*strp, tmp, len + 1); + return len; +} + +/* Stolen from "imap-send.c". */ +int nfvasprintf(char **str, const char *fmt, va_list va) +{ + int ret = git_vasprintf(str, fmt, va); + if (ret < 0) + die("Fatal: Out of memory\n"); + return ret; +} + +/* Get a trace file descriptor from GIT_TRACE env variable. */ +static int get_trace_fd(int *need_close) +{ + char *trace = getenv("GIT_TRACE"); + + if (!trace || !strcmp(trace, "0") || !strcasecmp(trace," false")) + return 0; + if (!strcmp(trace, "1") || !strcasecmp(trace, "true")) + return STDERR_FILENO; + if (strlen(trace) == 1 && isdigit(*trace)) + return atoi(trace); + if (*trace == '/') { + int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666); + if (fd == -1) { + fprintf(stderr, + "Could not open '%s' for tracing: %s\n" + "Defaulting to tracing on stderr...\n", + trace, strerror(errno)); + return STDERR_FILENO; + } + *need_close = 1; + return fd; + } + + fprintf(stderr, "What does '%s' for GIT_TRACE means ?\n", trace); + fprintf(stderr, "If you want to trace into a file, " + "then please set GIT_TRACE to an absolute pathname " + "(starting with /).\n"); + fprintf(stderr, "Defaulting to tracing on stderr...\n"); + + return STDERR_FILENO; +} + +static const char err_msg[] = "Could not trace into fd given by " + "GIT_TRACE environment variable"; + +void trace_printf(const char *format, ...) +{ + char *trace_str; + va_list rest; + int need_close = 0; + int fd = get_trace_fd(&need_close); + + if (!fd) + return; + + va_start(rest, format); + nfvasprintf(&trace_str, format, rest); + va_end(rest); + + write_or_whine(fd, trace_str, strlen(trace_str), err_msg); + + free(trace_str); + + if (need_close) + close(fd); +} + +void trace_argv_printf(const char **argv, int count, const char *format, ...) +{ + 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); + + if (!fd) + return; + + /* 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(fd, trace_str, trace_len, err_msg); + + free(argv_str); + free(format_str); + free(trace_str); + + if (need_close) + close(fd); +} diff --git a/write_or_die.c b/write_or_die.c index ab4cb8a69c..bfe4eeb649 100644 --- a/write_or_die.c +++ b/write_or_die.c @@ -18,3 +18,28 @@ void write_or_die(int fd, const void *buf, size_t count) p += written; } } + +int write_or_whine(int fd, const void *buf, size_t count, const char *msg) +{ + const char *p = buf; + ssize_t written; + + while (count > 0) { + written = xwrite(fd, p, count); + if (written == 0) { + fprintf(stderr, "%s: disk full?\n", msg); + return 0; + } + else if (written < 0) { + if (errno == EPIPE) + exit(0); + fprintf(stderr, "%s: write error (%s)\n", + msg, strerror(errno)); + return 0; + } + count -= written; + p += written; + } + + return 1; +}