diff options
author | Junio C Hamano <junkio@cox.net> | 2006-09-02 17:10:16 -0700 |
---|---|---|
committer | Junio C Hamano <junkio@cox.net> | 2006-09-02 17:10:16 -0700 |
commit | 1bbb2cff62a7c9ff136d17723b30951159e6291a (patch) | |
tree | 61f46f0a8b9659b95b6096a8d6c3bb92fadf01ba | |
parent | 7cf67205ca68a157c6ffdb4e5a4ff231217c0871 (diff) | |
parent | 6ce4e61f1be690681f6494eb5ca26540c2316f81 (diff) | |
download | git-1bbb2cff62a7c9ff136d17723b30951159e6291a.tar.gz |
Merge branch 'master' into cc/trace
* master:
Trace into a file or an open fd and refactor tracing code.
Replace uses of strdup with xstrdup.
consolidate two copies of new style object header parsing code.
Documentation: Fix howto/revert-branch-rebase.html generation
fmt-merge-msg: fix off-by-one bug
git-rev-list(1): group options; reformat; document more options
Constness tightening for move/link_temp_to_file()
gitweb: Fix git_blame
Include config.mak.autogen in the doc Makefile
Use xmalloc instead of malloc
git(7): move gitk(1) to the list of porcelain commands
gitk: Fix some bugs in the new cherry-picking code
gitk: Improve responsiveness while reading and layout out the graph
gitk: Update preceding/following tag info when creating a tag
gitk: Add a menu item for cherry-picking commits
gitk: Fix a couple of buglets in the branch head menu items
gitk: Add a context menu for heads
gitk: Add a row context-menu item for creating a new branch
gitk: Recompute ancestor/descendent heads/tags when rereading refs
gitk: Minor cleanups
45 files changed, 972 insertions, 312 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile index 0d9ffb4ad9..c00f5f62b7 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -33,6 +33,8 @@ man7dir=$(mandir)/man7 INSTALL?=install +-include ../config.mak.autogen + # # Please note that there is a minor bug in asciidoc. # The version after 6.0.3 _will_ include the patch found here: @@ -105,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 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 <commit1>..<commit2> can be used as a -short-hand for {caret}<commit1> <commit2>. +A special notation "'<commit1>'..'<commit2>'" can be used as a +short-hand for "{caret}'<commit1>' '<commit2>'". For example, either of +the following may be used interchangeably: -Another special notation is <commit1>...<commit2> 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 "'<commit1>'...'<commit2>'" 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[='<format>']:: + + Pretty print the contents of the commit logs in a given format, + where '<format>' 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:: + + 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':: ---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. - ---max-count:: Limit the number of commits output. ---max-age=timestamp, --min-age=timestamp:: - Limit the commits output to specified time range. +--since='date', --after='date':: + + Show commits more recent than a specific date. + +--until='date', --before='date':: ---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. + Show commits older than a specific date. + +--max-age='timestamp', --min-age='timestamp':: + + Limit the commits output to specified time range. --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 <commit>. ---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 '<commit>'. --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 <torvalds@osdl.org> Documentation -------------- -Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. +Documentation by David Greaves, Junio C Hamano, Jonas Fonseca +and the git-list <git@vger.kernel.org>. GIT --- Part of the gitlink:git[7] suite - diff --git a/Documentation/git.txt b/Documentation/git.txt index a9c87e38da..76b41c8e34 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -303,6 +303,9 @@ gitlink:git-format-patch[1]:: gitlink:git-grep[1]:: Print lines matching a pattern. +gitlink:gitk[1]:: + The git repository browser. + gitlink:git-log[1]:: Shows commit logs. @@ -483,13 +486,6 @@ gitlink:git-stripspace[1]:: Filter out empty lines. -Commands not yet documented ---------------------------- - -gitlink:gitk[1]:: - The gitk repository browser. - - Configuration Mechanism ----------------------- @@ -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 76d22b47ba..c407c033e7 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -111,43 +111,43 @@ 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; } if (!strcmp(".", src) || !strcmp(src, origin)) { int len = strlen(origin); if (origin[0] == '\'' && origin[len - 1] == '\'') { - char *new_origin = malloc(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); + origin = xstrdup(origin); } else { - char *new_origin = malloc(strlen(origin) + strlen(src) + 5); + char *new_origin = xmalloc(strlen(origin) + strlen(src) + 5); sprintf(new_origin, "%s of %s", origin, src); origin = new_origin; } @@ -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; @@ -214,11 +214,11 @@ static void shortlog(const char *name, unsigned char *sha1, if (eol) { int len = eol - bol; - oneline = malloc(len + 1); + oneline = xmalloc(len + 1); 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 6560cf1c2d..9cf12d32e5 100644 --- a/builtin-repo-config.c +++ b/builtin-repo-config.c @@ -72,19 +72,19 @@ 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) *tl = tolower(*tl); if (use_key_regexp) { - key_regexp = (regex_t*)malloc(sizeof(regex_t)); + key_regexp = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(key_regexp, key, REG_EXTENDED)) { fprintf(stderr, "Invalid key pattern: %s\n", key_); goto free_strings; @@ -97,7 +97,7 @@ static int get_value(const char* key_, const char* regex_) regex_++; } - regexp = (regex_t*)malloc(sizeof(regex_t)); + regexp = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(regexp, regex_, REG_EXTENDED)) { fprintf(stderr, "Invalid pattern: %s\n", regex_); goto free_strings; 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=<repo> */ - 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: @@ -257,7 +257,7 @@ extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned l extern int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, size_t bufsize, size_t *bufposn); extern int write_sha1_to_fd(int fd, const unsigned char *sha1); -extern int move_temp_to_file(const char *tmpfile, char *filename); +extern int move_temp_to_file(const char *tmpfile, const char *filename); extern int has_sha1_pack(const unsigned char *sha1); extern int has_sha1_file(const unsigned char *sha1); @@ -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 @@ -565,7 +565,7 @@ int git_config_set_multivar(const char* key, const char* value, /* * Validate the key and while at it, lower case it for matching. */ - store.key = (char*)malloc(strlen(key)+1); + store.key = xmalloc(strlen(key) + 1); dot = 0; for (i = 0; key[i]; i++) { unsigned char c = key[i]; @@ -633,7 +633,7 @@ int git_config_set_multivar(const char* key, const char* value, } else store.do_not_match = 0; - store.value_regex = (regex_t*)malloc(sizeof(regex_t)); + store.value_regex = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(store.value_regex, value_regex, REG_EXTENDED)) { fprintf(stderr, "Invalid pattern: %s\n", @@ -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 @@ -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) @@ -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); @@ -29,7 +29,7 @@ static void prepend_to_path(const char *dir, int len) path_len = len + strlen(old_path) + 1; - path = malloc(path_len + 1); + path = xmalloc(path_len + 1); memcpy(path, dir, len); path[len] = ':'; @@ -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; } @@ -2,7 +2,7 @@ # Tcl ignores the next line -*- tcl -*- \ exec wish "$0" -- "$@" -# Copyright (C) 2005 Paul Mackerras. All rights reserved. +# Copyright (C) 2005-2006 Paul Mackerras. All rights reserved. # This program is free software; it may be used, copied, modified # and distributed under the terms of the GNU General Public Licence, # either version 2, or (at your option) any later version. @@ -17,13 +17,12 @@ proc gitdir {} { } proc start_rev_list {view} { - global startmsecs nextupdate ncmupdate + global startmsecs nextupdate global commfd leftover tclencoding datemode global viewargs viewfiles commitidx set startmsecs [clock clicks -milliseconds] set nextupdate [expr {$startmsecs + 100}] - set ncmupdate 1 set commitidx($view) 0 set args $viewargs($view) if {$viewfiles($view) ne {}} { @@ -79,7 +78,7 @@ proc getcommitlines {fd view} { global parentlist childlist children curview hlview global vparentlist vchildlist vdisporder vcmitlisted - set stuff [read $fd] + set stuff [read $fd 500000] if {$stuff == {}} { if {![eof $fd]} return global viewname @@ -185,7 +184,7 @@ proc getcommitlines {fd view} { } if {$gotsome} { if {$view == $curview} { - layoutmore + while {[layoutmore $nextupdate]} doupdate } elseif {[info exists hlview] && $view == $hlview} { vhighlightmore } @@ -196,20 +195,13 @@ proc getcommitlines {fd view} { } proc doupdate {} { - global commfd nextupdate numcommits ncmupdate + global commfd nextupdate numcommits foreach v [array names commfd] { fileevent $commfd($v) readable {} } update set nextupdate [expr {[clock clicks -milliseconds] + 100}] - if {$numcommits < 100} { - set ncmupdate [expr {$numcommits + 1}] - } elseif {$numcommits < 10000} { - set ncmupdate [expr {$numcommits + 10}] - } else { - set ncmupdate [expr {$numcommits + 100}] - } foreach v [array names commfd] { set fd $commfd($v) fileevent $fd readable [list getcommitlines $fd $v] @@ -341,13 +333,13 @@ proc readrefs {} { set tag {} catch { set commit [exec git rev-parse "$id^0"] - if {"$commit" != "$id"} { + if {$commit != $id} { set tagids($name) $commit lappend idtags($commit) $name } } catch { - set tagcontents($name) [exec git cat-file tag "$id"] + set tagcontents($name) [exec git cat-file tag $id] } } elseif { $type == "heads" } { set headids($name) $id @@ -384,6 +376,23 @@ proc error_popup msg { show_error $w $w $msg } +proc confirm_popup msg { + global confirm_ok + set confirm_ok 0 + set w .confirm + toplevel $w + wm transient $w . + message $w.m -text $msg -justify center -aspect 400 + pack $w.m -side top -fill x -padx 20 -pady 20 + button $w.ok -text OK -command "set confirm_ok 1; destroy $w" + pack $w.ok -side left -fill x + button $w.cancel -text Cancel -command "destroy $w" + pack $w.cancel -side right -fill x + bind $w <Visibility> "grab $w; focus $w" + tkwait window $w + return $confirm_ok +} + proc makewindow {} { global canv canv2 canv3 linespc charspc ctext cflist global textfont mainfont uifont @@ -394,6 +403,7 @@ proc makewindow {} { global highlight_files gdttype global searchstring sstring global bgcolor fgcolor bglist fglist diffcolors + global headctxmenu menu .bar .bar add cascade -label "File" -menu .bar.file @@ -711,6 +721,16 @@ proc makewindow {} { $rowctxmenu add command -label "Make patch" -command mkpatch $rowctxmenu add command -label "Create tag" -command mktag $rowctxmenu add command -label "Write commit to file" -command writecommit + $rowctxmenu add command -label "Create new branch" -command mkbranch + $rowctxmenu add command -label "Cherry-pick this commit" \ + -command cherrypick + + set headctxmenu .headctxmenu + menu $headctxmenu -tearoff 0 + $headctxmenu add command -label "Check out this branch" \ + -command cobranch + $headctxmenu add command -label "Remove this branch" \ + -command rmbranch } # mouse-2 makes all windows scan vertically, but only the one @@ -1669,7 +1689,7 @@ proc showview {n} { show_status "Reading commits..." } if {[info exists commfd($n)]} { - layoutmore + layoutmore {} } else { finishcommits } @@ -2350,20 +2370,38 @@ proc visiblerows {} { return [list $r0 $r1] } -proc layoutmore {} { +proc layoutmore {tmax} { global rowlaidout rowoptim commitidx numcommits optim_delay global uparrowlen curview - set row $rowlaidout - set rowlaidout [layoutrows $row $commitidx($curview) 0] - set orow [expr {$rowlaidout - $uparrowlen - 1}] - if {$orow > $rowoptim} { - optimize_rows $rowoptim 0 $orow - set rowoptim $orow - } - set canshow [expr {$rowoptim - $optim_delay}] - if {$canshow > $numcommits} { - showstuff $canshow + while {1} { + if {$rowoptim - $optim_delay > $numcommits} { + showstuff [expr {$rowoptim - $optim_delay}] + } elseif {$rowlaidout - $uparrowlen - 1 > $rowoptim} { + set nr [expr {$rowlaidout - $uparrowlen - 1 - $rowoptim}] + if {$nr > 100} { + set nr 100 + } + optimize_rows $rowoptim 0 [expr {$rowoptim + $nr}] + incr rowoptim $nr + } elseif {$commitidx($curview) > $rowlaidout} { + set nr [expr {$commitidx($curview) - $rowlaidout}] + # may need to increase this threshold if uparrowlen or + # mingaplen are increased... + if {$nr > 150} { + set nr 150 + } + set row $rowlaidout + set rowlaidout [layoutrows $row [expr {$row + $nr}] 0] + if {$rowlaidout == $row} { + return 0 + } + } else { + return 0 + } + if {$tmax ne {} && [clock clicks -milliseconds] >= $tmax} { + return 1 + } } } @@ -3236,6 +3274,8 @@ proc drawtags {id x xt y1} { -font $font -tags [list tag.$id text]] if {$ntags >= 0} { $canv bind $t <1> [list showtag $tag 1] + } elseif {$nheads >= 0} { + $canv bind $t <Button-3> [list headmenu %X %Y $id $tag] } } return $xt @@ -3263,8 +3303,7 @@ proc show_status {msg} { proc finishcommits {} { global commitidx phase curview - global canv mainfont ctext maincursor textcursor - global findinprogress pending_select + global pending_select if {$commitidx($curview) > 0} { drawrest @@ -3275,6 +3314,108 @@ proc finishcommits {} { catch {unset pending_select} } +# Insert a new commit as the child of the commit on row $row. +# The new commit will be displayed on row $row and the commits +# on that row and below will move down one row. +proc insertrow {row newcmit} { + global displayorder parentlist childlist commitlisted + global commitrow curview rowidlist rowoffsets numcommits + global rowrangelist idrowranges rowlaidout rowoptim numcommits + global linesegends selectedline + + if {$row >= $numcommits} { + puts "oops, inserting new row $row but only have $numcommits rows" + return + } + set p [lindex $displayorder $row] + set displayorder [linsert $displayorder $row $newcmit] + set parentlist [linsert $parentlist $row $p] + set kids [lindex $childlist $row] + lappend kids $newcmit + lset childlist $row $kids + set childlist [linsert $childlist $row {}] + set commitlisted [linsert $commitlisted $row 1] + set l [llength $displayorder] + for {set r $row} {$r < $l} {incr r} { + set id [lindex $displayorder $r] + set commitrow($curview,$id) $r + } + + set idlist [lindex $rowidlist $row] + set offs [lindex $rowoffsets $row] + set newoffs {} + foreach x $idlist { + if {$x eq {} || ($x eq $p && [llength $kids] == 1)} { + lappend newoffs {} + } else { + lappend newoffs 0 + } + } + if {[llength $kids] == 1} { + set col [lsearch -exact $idlist $p] + lset idlist $col $newcmit + } else { + set col [llength $idlist] + lappend idlist $newcmit + lappend offs {} + lset rowoffsets $row $offs + } + set rowidlist [linsert $rowidlist $row $idlist] + set rowoffsets [linsert $rowoffsets [expr {$row+1}] $newoffs] + + set rowrangelist [linsert $rowrangelist $row {}] + set l [llength $rowrangelist] + for {set r 0} {$r < $l} {incr r} { + set ranges [lindex $rowrangelist $r] + if {$ranges ne {} && [lindex $ranges end] >= $row} { + set newranges {} + foreach x $ranges { + if {$x >= $row} { + lappend newranges [expr {$x + 1}] + } else { + lappend newranges $x + } + } + lset rowrangelist $r $newranges + } + } + if {[llength $kids] > 1} { + set rp1 [expr {$row + 1}] + set ranges [lindex $rowrangelist $rp1] + if {$ranges eq {}} { + set ranges [list $row $rp1] + } elseif {[lindex $ranges end-1] == $rp1} { + lset ranges end-1 $row + } + lset rowrangelist $rp1 $ranges + } + foreach id [array names idrowranges] { + set ranges $idrowranges($id) + if {$ranges ne {} && [lindex $ranges end] >= $row} { + set newranges {} + foreach x $ranges { + if {$x >= $row} { + lappend newranges [expr {$x + 1}] + } else { + lappend newranges $x + } + } + set idrowranges($id) $newranges + } + } + + set linesegends [linsert $linesegends $row {}] + + incr rowlaidout + incr rowoptim + incr numcommits + + if {[info exists selectedline] && $selectedline >= $row} { + incr selectedline + } + redisplay +} + # Don't change the text pane cursor if it is currently the hand cursor, # showing that we are over a sha1 ID link. proc settextcursor {c} { @@ -3307,9 +3448,7 @@ proc notbusy {what} { } proc drawrest {} { - global numcommits global startmsecs - global canvy0 numcommits linespc global rowlaidout commitidx curview global pending_select @@ -3323,6 +3462,7 @@ proc drawrest {} { } set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}] + #global numcommits #puts "overall $drawmsecs ms for $numcommits commits" } @@ -3603,27 +3743,20 @@ proc viewnextline {dir} { # add a list of tag or branch names at position pos # returns the number of names inserted -proc appendrefs {pos l var} { - global ctext commitrow linknum curview idtags $var +proc appendrefs {pos tags var} { + global ctext commitrow linknum curview $var if {[catch {$ctext index $pos}]} { return 0 } - set tags {} - foreach id $l { - foreach tag [set $var\($id\)] { - lappend tags [concat $tag $id] - } - } - set tags [lsort -index 1 $tags] + set tags [lsort $tags] set sep {} foreach tag $tags { - set name [lindex $tag 0] - set id [lindex $tag 1] + set id [set $var\($tag\)] set lk link$linknum incr linknum $ctext insert $pos $sep - $ctext insert $pos $name $lk + $ctext insert $pos $tag $lk $ctext tag conf $lk -foreground blue if {[info exists commitrow($curview,$id)]} { $ctext tag bind $lk <1> \ @@ -3637,6 +3770,18 @@ proc appendrefs {pos l var} { return [llength $tags] } +proc taglist {ids} { + global idtags + + set tags {} + foreach id $ids { + foreach tag $idtags($id) { + lappend tags $tag + } + } + return $tags +} + # called when we have finished computing the nearby tags proc dispneartags {} { global selectedline currentid ctext anc_tags desc_tags showneartags @@ -3646,15 +3791,15 @@ proc dispneartags {} { set id $currentid $ctext conf -state normal if {[info exists desc_heads($id)]} { - if {[appendrefs branch $desc_heads($id) idheads] > 1} { + if {[appendrefs branch $desc_heads($id) headids] > 1} { $ctext insert "branch -2c" "es" } } if {[info exists anc_tags($id)]} { - appendrefs follows $anc_tags($id) idtags + appendrefs follows [taglist $anc_tags($id)] tagids } if {[info exists desc_tags($id)]} { - appendrefs precedes $desc_tags($id) idtags + appendrefs precedes [taglist $desc_tags($id)] tagids } $ctext conf -state disabled } @@ -3787,7 +3932,7 @@ proc selectline {l isnew} { $ctext mark set branch "end -1c" $ctext mark gravity branch left if {[info exists desc_heads($id)]} { - if {[appendrefs branch $desc_heads($id) idheads] > 1} { + if {[appendrefs branch $desc_heads($id) headids] > 1} { # turn "Branch" into "Branches" $ctext insert "branch -2c" "es" } @@ -3796,13 +3941,13 @@ proc selectline {l isnew} { $ctext mark set follows "end -1c" $ctext mark gravity follows left if {[info exists anc_tags($id)]} { - appendrefs follows $anc_tags($id) idtags + appendrefs follows [taglist $anc_tags($id)] tagids } $ctext insert end "\nPrecedes: " $ctext mark set precedes "end -1c" $ctext mark gravity precedes left if {[info exists desc_tags($id)]} { - appendrefs precedes $desc_tags($id) idtags + appendrefs precedes [taglist $desc_tags($id)] tagids } $ctext insert end "\n" } @@ -4463,6 +4608,7 @@ proc redisplay {} { drawvisible if {[info exists selectedline]} { selectline $selectedline 0 + allcanvs yview moveto [lindex $span 0] } } @@ -4930,6 +5076,7 @@ proc domktag {} { set tagids($tag) $id lappend idtags($id) $tag redrawtags $id + addedtag $id } proc redrawtags {id} { @@ -5020,10 +5167,164 @@ proc wrcomcan {} { unset wrcomtop } +proc mkbranch {} { + global rowmenuid mkbrtop + + set top .makebranch + catch {destroy $top} + toplevel $top + label $top.title -text "Create new branch" + grid $top.title - -pady 10 + label $top.id -text "ID:" + entry $top.sha1 -width 40 -relief flat + $top.sha1 insert 0 $rowmenuid + $top.sha1 conf -state readonly + grid $top.id $top.sha1 -sticky w + label $top.nlab -text "Name:" + entry $top.name -width 40 + grid $top.nlab $top.name -sticky w + frame $top.buts + button $top.buts.go -text "Create" -command [list mkbrgo $top] + button $top.buts.can -text "Cancel" -command "catch {destroy $top}" + grid $top.buts.go $top.buts.can + grid columnconfigure $top.buts 0 -weight 1 -uniform a + grid columnconfigure $top.buts 1 -weight 1 -uniform a + grid $top.buts - -pady 10 -sticky ew + focus $top.name +} + +proc mkbrgo {top} { + global headids idheads + + set name [$top.name get] + set id [$top.sha1 get] + if {$name eq {}} { + error_popup "Please specify a name for the new branch" + return + } + catch {destroy $top} + nowbusy newbranch + update + if {[catch { + exec git branch $name $id + } err]} { + notbusy newbranch + error_popup $err + } else { + addedhead $id $name + # XXX should update list of heads displayed for selected commit + notbusy newbranch + redrawtags $id + } +} + +proc cherrypick {} { + global rowmenuid curview commitrow + global mainhead desc_heads anc_tags desc_tags allparents allchildren + + if {[info exists desc_heads($rowmenuid)] + && [lsearch -exact $desc_heads($rowmenuid) $mainhead] >= 0} { + set ok [confirm_popup "Commit [string range $rowmenuid 0 7] is already\ + included in branch $mainhead -- really re-apply it?"] + if {!$ok} return + } + nowbusy cherrypick + update + set oldhead [exec git rev-parse HEAD] + # Unfortunately git-cherry-pick writes stuff to stderr even when + # no error occurs, and exec takes that as an indication of error... + if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} { + notbusy cherrypick + error_popup $err + return + } + set newhead [exec git rev-parse HEAD] + if {$newhead eq $oldhead} { + notbusy cherrypick + error_popup "No changes committed" + return + } + set allparents($newhead) $oldhead + lappend allchildren($oldhead) $newhead + set desc_heads($newhead) $mainhead + if {[info exists anc_tags($oldhead)]} { + set anc_tags($newhead) $anc_tags($oldhead) + } + set desc_tags($newhead) {} + if {[info exists commitrow($curview,$oldhead)]} { + insertrow $commitrow($curview,$oldhead) $newhead + if {$mainhead ne {}} { + movedhead $newhead $mainhead + } + redrawtags $oldhead + redrawtags $newhead + } + notbusy cherrypick +} + +# context menu for a head +proc headmenu {x y id head} { + global headmenuid headmenuhead headctxmenu + + set headmenuid $id + set headmenuhead $head + tk_popup $headctxmenu $x $y +} + +proc cobranch {} { + global headmenuid headmenuhead mainhead headids + + # check the tree is clean first?? + set oldmainhead $mainhead + nowbusy checkout + update + if {[catch { + exec git checkout $headmenuhead + } err]} { + notbusy checkout + error_popup $err + } else { + notbusy checkout + set mainhead $headmenuhead + if {[info exists headids($oldmainhead)]} { + redrawtags $headids($oldmainhead) + } + redrawtags $headmenuid + } +} + +proc rmbranch {} { + global desc_heads headmenuid headmenuhead mainhead + global headids idheads + + set head $headmenuhead + set id $headmenuid + if {$head eq $mainhead} { + error_popup "Cannot delete the currently checked-out branch" + return + } + if {$desc_heads($id) eq $head} { + # the stuff on this branch isn't on any other branch + if {![confirm_popup "The commits on branch $head aren't on any other\ + branch.\nReally delete branch $head?"]} return + } + nowbusy rmbranch + update + if {[catch {exec git branch -D $head} err]} { + notbusy rmbranch + error_popup $err + return + } + removedhead $id $head + redrawtags $id + notbusy rmbranch +} + # Stuff for finding nearby tags proc getallcommits {} { - global allcstart allcommits allcfd + global allcstart allcommits allcfd allids + set allids {} set fd [open [concat | git rev-list --all --topo-order --parents] r] set allcfd $fd fconfigure $fd -blocking 0 @@ -5107,10 +5408,52 @@ proc combine_atags {l1 l2} { return $res } +proc forward_pass {id children} { + global idtags desc_tags idheads desc_heads alldtags tagisdesc + + set dtags {} + set dheads {} + foreach child $children { + if {[info exists idtags($child)]} { + set ctags [list $child] + } else { + set ctags $desc_tags($child) + } + if {$dtags eq {}} { + set dtags $ctags + } elseif {$ctags ne $dtags} { + set dtags [combine_dtags $dtags $ctags] + } + set cheads $desc_heads($child) + if {$dheads eq {}} { + set dheads $cheads + } elseif {$cheads ne $dheads} { + set dheads [lsort -unique [concat $dheads $cheads]] + } + } + set desc_tags($id) $dtags + if {[info exists idtags($id)]} { + set adt $dtags + foreach tag $dtags { + set adt [concat $adt $alldtags($tag)] + } + set adt [lsort -unique $adt] + set alldtags($id) $adt + foreach tag $adt { + set tagisdesc($id,$tag) -1 + set tagisdesc($tag,$id) 1 + } + } + if {[info exists idheads($id)]} { + set dheads [concat $dheads $idheads($id)] + } + set desc_heads($id) $dheads +} + proc getallclines {fd} { global allparents allchildren allcommits allcstart - global desc_tags anc_tags idtags alldtags tagisdesc allids - global desc_heads idheads + global desc_tags anc_tags idtags tagisdesc allids + global idheads travindex while {[gets $fd line] >= 0} { set id [lindex $line 0] @@ -5125,43 +5468,7 @@ proc getallclines {fd} { } # compute nearest tagged descendents as we go # also compute descendent heads - set dtags {} - set dheads {} - foreach child $allchildren($id) { - if {[info exists idtags($child)]} { - set ctags [list $child] - } else { - set ctags $desc_tags($child) - } - if {$dtags eq {}} { - set dtags $ctags - } elseif {$ctags ne $dtags} { - set dtags [combine_dtags $dtags $ctags] - } - set cheads $desc_heads($child) - if {$dheads eq {}} { - set dheads $cheads - } elseif {$cheads ne $dheads} { - set dheads [lsort -unique [concat $dheads $cheads]] - } - } - set desc_tags($id) $dtags - if {[info exists idtags($id)]} { - set adt $dtags - foreach tag $dtags { - set adt [concat $adt $alldtags($tag)] - } - set adt [lsort -unique $adt] - set alldtags($id) $adt - foreach tag $adt { - set tagisdesc($id,$tag) -1 - set tagisdesc($tag,$id) 1 - } - } - if {[info exists idheads($id)]} { - lappend dheads $id - } - set desc_heads($id) $dheads + forward_pass $id $allchildren($id) if {[clock clicks -milliseconds] - $allcstart >= 50} { fileevent $fd readable {} after idle restartgetall $fd @@ -5169,7 +5476,9 @@ proc getallclines {fd} { } } if {[eof $fd]} { - after idle restartatags [llength $allids] + set travindex [llength $allids] + set allcommits "traversing" + after idle restartatags if {[catch {close $fd} err]} { error_popup "Error reading full commit graph: $err.\n\ Results may be incomplete." @@ -5178,10 +5487,11 @@ proc getallclines {fd} { } # walk backward through the tree and compute nearest tagged ancestors -proc restartatags {i} { - global allids allparents idtags anc_tags t0 +proc restartatags {} { + global allids allparents idtags anc_tags travindex set t0 [clock clicks -milliseconds] + set i $travindex while {[incr i -1] >= 0} { set id [lindex $allids $i] set atags {} @@ -5199,17 +5509,195 @@ proc restartatags {i} { } set anc_tags($id) $atags if {[clock clicks -milliseconds] - $t0 >= 50} { - after idle restartatags $i + set travindex $i + after idle restartatags return } } set allcommits "done" + set travindex 0 notbusy allcommits dispneartags } +# update the desc_tags and anc_tags arrays for a new tag just added +proc addedtag {id} { + global desc_tags anc_tags allparents allchildren allcommits + global idtags tagisdesc alldtags + + if {![info exists desc_tags($id)]} return + set adt $desc_tags($id) + foreach t $desc_tags($id) { + set adt [concat $adt $alldtags($t)] + } + set adt [lsort -unique $adt] + set alldtags($id) $adt + foreach t $adt { + set tagisdesc($id,$t) -1 + set tagisdesc($t,$id) 1 + } + if {[info exists anc_tags($id)]} { + set todo $anc_tags($id) + while {$todo ne {}} { + set do [lindex $todo 0] + set todo [lrange $todo 1 end] + if {[info exists tagisdesc($id,$do)]} continue + set tagisdesc($do,$id) -1 + set tagisdesc($id,$do) 1 + if {[info exists anc_tags($do)]} { + set todo [concat $todo $anc_tags($do)] + } + } + } + + set lastold $desc_tags($id) + set lastnew [list $id] + set nup 0 + set nch 0 + set todo $allparents($id) + while {$todo ne {}} { + set do [lindex $todo 0] + set todo [lrange $todo 1 end] + if {![info exists desc_tags($do)]} continue + if {$desc_tags($do) ne $lastold} { + set lastold $desc_tags($do) + set lastnew [combine_dtags $lastold [list $id]] + incr nch + } + if {$lastold eq $lastnew} continue + set desc_tags($do) $lastnew + incr nup + if {![info exists idtags($do)]} { + set todo [concat $todo $allparents($do)] + } + } + + if {![info exists anc_tags($id)]} return + set lastold $anc_tags($id) + set lastnew [list $id] + set nup 0 + set nch 0 + set todo $allchildren($id) + while {$todo ne {}} { + set do [lindex $todo 0] + set todo [lrange $todo 1 end] + if {![info exists anc_tags($do)]} continue + if {$anc_tags($do) ne $lastold} { + set lastold $anc_tags($do) + set lastnew [combine_atags $lastold [list $id]] + incr nch + } + if {$lastold eq $lastnew} continue + set anc_tags($do) $lastnew + incr nup + if {![info exists idtags($do)]} { + set todo [concat $todo $allchildren($do)] + } + } +} + +# update the desc_heads array for a new head just added +proc addedhead {hid head} { + global desc_heads allparents headids idheads + + set headids($head) $hid + lappend idheads($hid) $head + + set todo [list $hid] + while {$todo ne {}} { + set do [lindex $todo 0] + set todo [lrange $todo 1 end] + if {![info exists desc_heads($do)] || + [lsearch -exact $desc_heads($do) $head] >= 0} continue + set oldheads $desc_heads($do) + lappend desc_heads($do) $head + set heads $desc_heads($do) + while {1} { + set p $allparents($do) + if {[llength $p] != 1 || ![info exists desc_heads($p)] || + $desc_heads($p) ne $oldheads} break + set do $p + set desc_heads($do) $heads + } + set todo [concat $todo $p] + } +} + +# update the desc_heads array for a head just removed +proc removedhead {hid head} { + global desc_heads allparents headids idheads + + unset headids($head) + if {$idheads($hid) eq $head} { + unset idheads($hid) + } else { + set i [lsearch -exact $idheads($hid) $head] + if {$i >= 0} { + set idheads($hid) [lreplace $idheads($hid) $i $i] + } + } + + set todo [list $hid] + while {$todo ne {}} { + set do [lindex $todo 0] + set todo [lrange $todo 1 end] + if {![info exists desc_heads($do)]} continue + set i [lsearch -exact $desc_heads($do) $head] + if {$i < 0} continue + set oldheads $desc_heads($do) + set heads [lreplace $desc_heads($do) $i $i] + while {1} { + set desc_heads($do) $heads + set p $allparents($do) + if {[llength $p] != 1 || ![info exists desc_heads($p)] || + $desc_heads($p) ne $oldheads} break + set do $p + } + set todo [concat $todo $p] + } +} + +# update things for a head moved to a child of its previous location +proc movedhead {id name} { + global headids idheads + + set oldid $headids($name) + set headids($name) $id + if {$idheads($oldid) eq $name} { + unset idheads($oldid) + } else { + set i [lsearch -exact $idheads($oldid) $name] + if {$i >= 0} { + set idheads($oldid) [lreplace $idheads($oldid) $i $i] + } + } + lappend idheads($id) $name +} + +proc changedrefs {} { + global desc_heads desc_tags anc_tags allcommits allids + global allchildren allparents idtags travindex + + if {![info exists allcommits]} return + catch {unset desc_heads} + catch {unset desc_tags} + catch {unset anc_tags} + catch {unset alldtags} + catch {unset tagisdesc} + foreach id $allids { + forward_pass $id $allchildren($id) + } + if {$allcommits ne "reading"} { + set travindex [llength $allids] + if {$allcommits ne "traversing"} { + set allcommits "traversing" + after idle restartatags + } + } +} + proc rereadrefs {} { - global idtags idheads idotherrefs + global idtags idheads idotherrefs mainhead set refids [concat [array names idtags] \ [array names idheads] [array names idotherrefs]] @@ -5218,12 +5706,16 @@ proc rereadrefs {} { set ref($id) [listrefs $id] } } + set oldmainhead $mainhead readrefs + changedrefs set refids [lsort -unique [concat $refids [array names idtags] \ [array names idheads] [array names idotherrefs]]] foreach id $refids { set v [listrefs $id] - if {![info exists ref($id)] || $ref($id) != $v} { + if {![info exists ref($id)] || $ref($id) != $v || + ($id eq $oldmainhead && $id ne $mainhead) || + ($id eq $mainhead && $id ne $oldmainhead)} { redrawtags $id } } diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 0984e85623..57ffa25070 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2251,7 +2251,8 @@ sub git_blame2 { my $fd; my $ftype; - if (!gitweb_check_feature('blame')) { + my ($have_blame) = gitweb_check_feature('blame'); + if (!$have_blame) { die_error('403 Permission denied', "Permission denied"); } die_error('404 Not Found', "File name not defined") if (!$file_name); @@ -2320,7 +2321,8 @@ HTML sub git_blame { my $fd; - if (!gitweb_check_feature('blame')) { + my ($have_blame) = gitweb_check_feature('blame'); + if (!$have_blame) { die_error('403 Permission denied', "Permission denied"); } die_error('404 Not Found', "File name not defined") if (!$file_name); @@ -2494,7 +2496,7 @@ sub git_blob { die_error(undef, "No file name defined"); } } - my $have_blame = gitweb_check_feature('blame'); + my ($have_blame) = gitweb_check_feature('blame'); open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash or die_error(undef, "Couldn't cat $file_name, $hash"); my $mimetype = blob_mimetype($fd, $file_name); @@ -2570,7 +2572,7 @@ sub git_tree { my $ref = format_ref_marker($refs, $hash_base); git_header_html(); my $base = ""; - my $have_blame = gitweb_check_feature('blame'); + my ($have_blame) = gitweb_check_feature('blame'); if (defined $hash_base && (my %co = parse_commit($hash_base))) { git_print_page_nav('tree','', $hash_base); git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base); @@ -184,7 +184,7 @@ static void show_man_page(const char *git_cmd) page = git_cmd; else { int page_len = strlen(git_cmd) + 4; - char *p = malloc(page_len + 1); + char *p = xmalloc(page_len + 1); strcpy(p, "git-"); strcpy(p + 4, git_cmd); p[page_len] = 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 6a52dbdca4..362e474374 100644 --- a/imap-send.c +++ b/imap-send.c @@ -1007,7 +1007,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 ); @@ -1263,7 +1263,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 )) @@ -1273,16 +1273,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 39a1eae894..611cd95cf5 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -283,7 +283,7 @@ static int save_files_dirs(const unsigned char *sha1, unsigned int mode, int stage) { int len = strlen(path); - char *newpath = malloc(baselen + len + 1); + char *newpath = xmalloc(baselen + len + 1); memcpy(newpath, base, baselen); memcpy(newpath + baselen, path, len); newpath[baselen + len] = '\0'; @@ -455,7 +455,7 @@ static int remove_path(const char *name) if (ret) return ret; len = strlen(name); - dirs = malloc(len+1); + dirs = xmalloc(len+1); memcpy(dirs, name, len); dirs[len] = '\0'; while ((slash = strrchr(name, '/'))) { @@ -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; @@ -572,7 +572,7 @@ void update_file_flags(const unsigned char *sha, flush_buffer(fd, buf, size); close(fd); } else if (S_ISLNK(mode)) { - char *lnk = malloc(size + 1); + char *lnk = xmalloc(size + 1); memcpy(lnk, buf, size); lnk[size] = '\0'; mkdir_p(path, 0777); @@ -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 <base>... -- <head> <remote> ...\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; @@ -119,7 +119,7 @@ static int verify_tag(char *buffer, unsigned long size) int main(int argc, char **argv) { unsigned long size = 4096; - char *buffer = malloc(size); + char *buffer = xmalloc(size); unsigned char result_sha1[20]; if (argc != 1) 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++; @@ -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/send-pack.c b/send-pack.c index fd79a61923..ac4501d341 100644 --- a/send-pack.c +++ b/send-pack.c @@ -53,7 +53,7 @@ static void exec_rev_list(struct ref *refs) if (900 < i) die("git-rev-list environment overflow"); if (!is_zero_sha1(ref->new_sha1)) { - char *buf = malloc(100); + char *buf = xmalloc(100); args[i++] = buf; snprintf(buf, 50, "%s", sha1_to_hex(ref->new_sha1)); buf += 50; @@ -75,7 +75,7 @@ static void exec_rev_list(struct ref *refs) if (is_zero_sha1(ref->new_sha1) && !is_zero_sha1(ref->old_sha1) && has_sha1_file(ref->old_sha1)) { - char *buf = malloc(42); + char *buf = xmalloc(42); args[i++] = buf; snprintf(buf, 42, "^%s", sha1_to_hex(ref->old_sha1)); } 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 46272b5916..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 @@ -711,17 +711,39 @@ int legacy_loose_object(unsigned char *map) return 0; } -static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz) +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 int bits; unsigned long size; - static const char *typename[8] = { - NULL, /* OBJ_EXT */ - "commit", "tree", "blob", "tag", - NULL, NULL, NULL + 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 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, @@ -1348,7 +1353,7 @@ char *write_sha1_file_prepare(void *buf, * * Returns the errno on failure, 0 on success. */ -static int link_temp_to_file(const char *tmpfile, char *filename) +static int link_temp_to_file(const char *tmpfile, const char *filename) { int ret; char *dir; @@ -1381,7 +1386,7 @@ static int link_temp_to_file(const char *tmpfile, char *filename) /* * Move the just written object into its final resting place */ -int move_temp_to_file(const char *tmpfile, char *filename) +int move_temp_to_file(const char *tmpfile, const char *filename) { int ret = link_temp_to_file(tmpfile, filename); @@ -1756,7 +1761,7 @@ int read_pipe(int fd, char** return_buf, unsigned long* return_size) int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) { unsigned long size = 4096; - char *buf = malloc(size); + char *buf = xmalloc(size); int ret; unsigned char hdr[50]; int hdrlen; 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; } diff --git a/t/test-lib.sh b/t/test-lib.sh index b0d7990a66..e2629339d4 100755 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -34,6 +34,15 @@ 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... @@ -51,7 +51,7 @@ int nfvasprintf(char **str, const char *fmt, va_list va) } /* Get a trace file descriptor from GIT_TRACE env variable. */ -static int get_trace_fd() +static int get_trace_fd(int *need_close) { char *trace = getenv("GIT_TRACE"); @@ -61,9 +61,25 @@ static int get_trace_fd() 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; } @@ -74,7 +90,8 @@ void trace_printf(const char *format, ...) { char *trace_str; va_list rest; - int fd = get_trace_fd(); + int need_close = 0; + int fd = get_trace_fd(&need_close); if (!fd) return; @@ -86,6 +103,9 @@ void trace_printf(const char *format, ...) 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, ...) @@ -93,7 +113,8 @@ 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 fd = get_trace_fd(); + int need_close = 0; + int fd = get_trace_fd(&need_close); if (!fd) return; @@ -122,4 +143,7 @@ void trace_argv_printf(const char **argv, int count, const char *format, ...) free(argv_str); free(format_str); free(trace_str); + + if (need_close) + close(fd); } |