Code

Merge branch 'master' into cc/trace
authorJunio C Hamano <junkio@cox.net>
Sun, 3 Sep 2006 00:10:16 +0000 (17:10 -0700)
committerJunio C Hamano <junkio@cox.net>
Sun, 3 Sep 2006 00:10:16 +0000 (17:10 -0700)
* 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:
Documentation/Makefile
Documentation/git-rev-list.txt
Documentation/git.txt
blame.c
builtin-apply.c
builtin-fmt-merge-msg.c
builtin-grep.c
builtin-name-rev.c
builtin-prune.c
builtin-push.c
builtin-repo-config.c
builtin-rev-list.c
builtin-rm.c
builtin-show-branch.c
builtin-symbolic-ref.c
builtin-tar-tree.c
builtin-upload-tar.c
builtin-zip-tree.c
cache.h
config.c
connect.c
diff.c
environment.c
fetch.c
fsck-objects.c
git-compat-util.h
git.c
gitk
gitweb/gitweb.perl
help.c
http-fetch.c
http-push.c
imap-send.c
merge-file.c
merge-recursive.c
merge-tree.c
mktag.c
path-list.c
refs.c
send-pack.c
server-info.c
sha1_file.c
sha1_name.c
t/test-lib.sh
trace.c

index 0d9ffb4ad9475de730ab2ee240d8b528b44017e8..c00f5f62b741150e92f38e6394bfa6f111b82b6e 100644 (file)
@@ -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
index a446a6b5a27067d25c1d32d97ccbbd9e59e3b754..3c4c2fbfb9b2b9a0873d35850d503c2de199f556 100644 (file)
@@ -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
-
index a9c87e38dae69b0799328917727867342eb5d22c..76b41c8e34d4c9c5a155dd9ff1bbbc81984dcafc 100644 (file)
@@ -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
 -----------------------
 
diff --git a/blame.c b/blame.c
index 8968046b0045633e6046c3126978819e9910a5f2..8cfd5d94c777770a2019e058f1140ecd5a5c9aa2 100644 (file)
--- 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;
index 1a1deaf78c61547a53bf0c3f7f1e7f37464610c5..872c8005a27c0085d47680a8e638b8cf6f3d70a3 100644 (file)
@@ -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);
index 76d22b47ba2906ecb59f0d8e279cf55a80bf8c86..c407c033e7d4f43818d62691688911de71a5cf11 100644 (file)
@@ -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);
index 8213ce240232a1dc8a0a498972323a33e8fcb7a0..6430f6d79ed58afa810a2643a13700be6579aee5 100644 (file)
@@ -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);
index d44e782c999eeaf59ec661047eb95544a29dc072..52886b69b068eca899bea574c5d9790395eeb006 100644 (file)
@@ -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;
 }
index fc885ce55bbb3a44367b8cec1ea66b1918c78f15..6228c7907b183fb686c9f4cc54347c3dc16f3ec4 100644 (file)
@@ -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;
index ada8338cc1d0fbf389cd346b5134595afaaa2e66..c43f2566d519ddb238ca70065ba901881dce372b 100644 (file)
@@ -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;
 }
index 6560cf1c2dd04a1ef047767a69a68bac794d7187..9cf12d32e5bc0be8034b16551336afee1e7d3a00 100644 (file)
@@ -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;
index 402af8e1b5516143c5f4e2303abf05d2812c5a1c..8437454fbe4e3237aaa6aaecefe54caefb621f1b 100644 (file)
@@ -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;
index 593d86744c41a1f28258b3adb6e6cd556a64b61f..33d04bd015e43965a1bc44bb281908298f152f6c 100644 (file)
@@ -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;
index d7de18ec0b0ace327616f2bde6ab5cdefad8dd04..578c9fafd022ce8a8676d348120c2fc7b2b1c5dd 100644 (file)
@@ -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;
        }
index b4ec6f28ed8903ec160ce0747fd1916f69bbacdc..1d3a5e229ae1a16211671e7591a7544af98721f8 100644 (file)
@@ -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);
index 61a413590d74d0fb167d715aa754df1a8794c085..fa666f78c5b5e44617495abb2716eded8407626c 100644 (file)
@@ -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;
index 7b401bbb779ea0cc8bca3720eab612af2b50a4c3..06a945a4b16b1b604847527fbac3aca3c7fa3a73 100644 (file)
@@ -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)
index a5b834d360ad20223b52cc3ef2eb5848f5bbdc72..1c1f6830c1a8b9303eec3b1c11cfb1805d3049e4 100644 (file)
@@ -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/cache.h b/cache.h
index f532b465151be418044f3927347589270083a25d..195908fc34445d6c0368e1973955a6e3a058eb65 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -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);
index d9f2b787b94f2506ff7842c1b7b06bd15342ba6f..e8f0caf7cf674ff44b23e0182420988e9672cc69 100644 (file)
--- 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
@@ -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",
index e501ccce259e7ac1a4e92fecc713950e82d3b71a..06ef387649e0645b934f3294df8b843aa328393f 100644 (file)
--- 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 70699fd8c48754215934a8ed45db336b9db68918..9dcbda3117a5b225aa5ee0f077b03cf8e859228b 100644 (file)
--- 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;
 }
 
index 5fae9ac3056dd00beba3b9a13be99f7bb39d584e..84d870ca4eca6e202bcbec388e9a299b887f9a12 100644 (file)
@@ -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 7d3812c40606a6afad796826dd0937a92f71bc7a..34df8d37d7dc92f8b652c160c49f4ace6e44e19c 100644 (file)
--- 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;
index 24286de15dbba4795b75112d3957e8974c3ceafa..4d994f3fc83d71501bbfde5159a869b4f2a38e99 100644 (file)
@@ -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 */
 
index 91f2b0d3f0cf9d8539e7e09adc057dc337caae21..552b8ec23a1a927208f1ed4c48d08b513d8572c5 100644 (file)
@@ -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 0c8cfa8d34ed5520c57dbee1e2f2932d695e752f..1d00111819f69cb4495be582bebee95be73ab214 100644 (file)
--- a/git.c
+++ b/git.c
@@ -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;
 }
diff --git a/gitk b/gitk
index a92ab007b4e22d51bc9b1c6ac2a739a7bbdfcfcc..ebbeac63aaac66c3ffef53c1d66836c11d22ddbf 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -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
        }
     }
index 0984e856235bcfe5e93c74504b605169d1b65daf..57ffa25070984f6ca3310c83062ac8dcd68d30fc 100755 (executable)
@@ -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);
diff --git a/help.c b/help.c
index 9ecdefdb03be7fac8d9283c93f2320f71bd062d6..0824c25226ad7b106e56d1b7c23fad40eed92749 100644 (file)
--- a/help.c
+++ b/help.c
@@ -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;
index 6806f3678c2878d5d2550750394a998b2c5d54db..fac17607b442c5bf1e1d21c27ae9a2b6c473358c 100644 (file)
@@ -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;
index 7814666d8df2e202c547cf05f0768bdc3830873c..670ff007beb8a5ba3043045a0b31399cd108c310 100644 (file)
@@ -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;
index 6a52dbdca4f61cc1bfc288e7766e87a1425e9b3c..362e4743740435dc0e5b08933c0e05c53b7b62af 100644 (file)
@@ -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;
 }
 
index f32c6538250e560039de2d5262def7953915411d..fc9b148993027d4064257d4df8e63a12e1afc5d4 100644 (file)
@@ -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;
index 39a1eae894398cd235a1dfb643c523823670f656..611cd95cf597b0ccf06cf9b4318eb34a86a4c05a 100644 (file)
@@ -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]);
index c2e9a867edb5b843150cd3df47583f49f15314cb..60df758c41174bb8a9cc526660752c80b1988bbf 100644 (file)
@@ -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/mktag.c b/mktag.c
index be23e589fbf504cb165abc3b97bdf5adb7317f68..3448a5dde7cbd795b08db872180f03dcfb66b99a 100644 (file)
--- a/mktag.c
+++ b/mktag.c
@@ -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)
index b1ee72d1dc25085f2114748001d21e3e518e54ee..0c332dc7b556ba894f0452b0172e7dd1e485f929 100644 (file)
@@ -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 aab14fc107353099756403c1bf08a0786e184d98..5e653141ceb8183cfb7922c3ad89fd1a0110647a 100644 (file)
--- 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))
index fd79a61923ddc20af216ccd252e40bf38a70eccb..ac4501d34154301030cf98ff122eb233095417b7 100644 (file)
@@ -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));
                }
index 7df628f2b226f6984d168af0382680b9dae0751a..2fb8f571033918b870cde35979b377c98b9845b5 100644 (file)
@@ -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);
 
index 46272b591645e444396ae683673620594f336a7c..4ef98053f8b5ba9988fd28afc60a1ae2fced93c9 100644 (file)
@@ -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;
index 3f6b77ccfadab14453a76b6654b50b186c9f5b25..1fbc4438059b68f31aa25fb8e3b82b37a4d02819 100644 (file)
@@ -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;
                }
index b0d7990a669cb053e5112bd459e9ecaffbd4c504..e2629339d4b1c9acf8cdf002f8ac59ef736eacf3 100755 (executable)
@@ -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...
diff --git a/trace.c b/trace.c
index 90847c3fd84b4cc065db8ef41d1010991fe4bafc..ce01c3474948ec593c87843309ec767ace57f07e 100644 (file)
--- a/trace.c
+++ b/trace.c
@@ -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);
 }