Merge branch 'tb/crlf-tests'
authorJunio C Hamano <gitster@pobox.com>
Wed, 16 Jul 2014 18:25:45 +0000 (11:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 16 Jul 2014 18:25:45 +0000 (11:25 -0700)
* tb/crlf-tests:
t0027: combinations of core.autocrlf, core.eol and text
t0025: rename the test files

136 files changed:
.gitignore
Documentation/RelNotes/2.0.2.txt [new file with mode: 0644]
Documentation/RelNotes/2.1.0.txt
Documentation/git-rev-parse.txt
Documentation/git-update-index.txt
Documentation/git-verify-commit.txt [new file with mode: 0644]
Documentation/gitrepository-layout.txt
Documentation/technical/index-format.txt
Makefile
advice.c
alias.c
alloc.c
branch.c
builtin.h
builtin/add.c
builtin/apply.c
builtin/blame.c
builtin/branch.c
builtin/checkout-index.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit-tree.c
builtin/commit.c
builtin/diff-tree.c
builtin/fast-export.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/fsck.c
builtin/index-pack.c
builtin/log.c
builtin/merge.c
builtin/mv.c
builtin/name-rev.c
builtin/push.c
builtin/read-tree.c
builtin/receive-pack.c
builtin/remote.c
builtin/reset.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/rm.c
builtin/show-branch.c
builtin/tag.c
builtin/update-index.c
builtin/verify-commit.c [new file with mode: 0644]
cache-tree.c
cache-tree.h
cache.h
column.c
command-list.txt
commit-slab.h
commit.c
commit.h
compat/mingw.c
compat/mingw.h
compat/win32/dirent.c
compat/win32/dirent.h
compat/winansi.c
config.c
config.mak.uname
connect.c
convert.c
credential-cache--daemon.c
credential.c
daemon.c
diff.c
entry.c
environment.c
ewah/ewah_io.c
ewah/ewok.h
fast-import.c
fetch-pack.c
fsck.c
git-compat-util.h
git-rebase--merge.sh
git.c
gpg-interface.c
gpg-interface.h
help.c
http-backend.c
http-push.c
http-walker.c
http.c
imap-send.c
line-log.c
lockfile.c
log-tree.c
match-trees.c
merge-recursive.c
merge.c
notes-cache.c
notes-merge.c
notes-utils.c
notes-utils.h
object.c
parse-options.c
preload-index.c
pretty.c
read-cache.c
refs.c
remote-curl.c
remote.c
rerere.c
resolve-undo.c
revision.c
sequencer.c
sha1_file.c
sha1_name.c
shell.c
split-index.c [new file with mode: 0644]
split-index.h [new file with mode: 0644]
strbuf.c
strbuf.h
symlinks.c
t/lib-httpd/error.sh
t/t1402-check-ref-format.sh
t/t1700-split-index.sh [new file with mode: 0755]
t/t2104-update-index-skip-worktree.sh
t/t3402-rebase-merge.sh
t/t5000-tar-tree.sh
t/t5003-archive-zip.sh
t/t5550-http-fetch-dumb.sh
t/t6006-rev-list-format.sh
t/t7300-clean.sh
t/t7510-signed-commit.sh
t/valgrind/default.supp
test-dump-cache-tree.c
test-dump-split-index.c [new file with mode: 0644]
test-scrap-cache-tree.c
transport-helper.c
transport.c
unpack-trees.c
urlmatch.c
walker.c
wt-status.c
index 42294e59a1fcf5d744199b6a0feafd4ca6e3eda1..81e12c0621d42b495ab540391e1cd8ece18587b7 100644 (file)
 /git-upload-archive
 /git-upload-pack
 /git-var
+/git-verify-commit
 /git-verify-pack
 /git-verify-tag
 /git-web--browse
 /test-date
 /test-delta
 /test-dump-cache-tree
+/test-dump-split-index
 /test-scrap-cache-tree
 /test-genrandom
 /test-hashmap
diff --git a/Documentation/RelNotes/2.0.2.txt b/Documentation/RelNotes/2.0.2.txt
new file mode 100644 (file)
index 0000000..c48c0aa
--- /dev/null
@@ -0,0 +1,13 @@
+Git v2.0.2 Release Notes
+========================
+
+ * Documentation for "git submodule sync" forgot to say that the subcommand
+   can take the "--recursive" option.
+
+ * Mishandling of patterns in .gitignore that has trailing SPs quoted
+   with backslashes (e.g. ones that end with "\ ") have been
+   corrected.
+
+ * Recent updates to "git repack" started to duplicate objects that
+   are in packfiles marked with .keep flag into the new packfile by
+   mistake.
index 828a439dc11e85fc5a7dbf57a4b602efa356e7b6..3b65461c0b5a4384fbe28dfbd0e3a30e9a9a533a 100644 (file)
@@ -106,11 +106,27 @@ UI, Workflows & Features
  * "git tag" when editing the tag message shows the name of the tag
    being edited as a comment in the editor.
 
+ * "git verify-commit" command to check GPG signature in signed
+   commits, in a way similar to "git verify-tag" is used to check
+   signed tags, was added.
+
 
 Performance, Internal Implementation, etc.
 
  * Build procedure for 'subtree' (in contrib/) has been cleaned up.
 
+ * Effort to shrink the size of patches Windows folks maintain on top
+   by upstreaming them continues.
+
+ * Patches maintained by msysgit folks for Windows port are being
+   upstreamed here a bit by bit.
+
+ * The leaf function to check validity of a refname format has been
+   micro-optimized, using SSE2 instructions when available.  A few
+   breakages during its development have been caught and fixed already
+   but there might remain some more still; please test and report if
+   you find any.
+
  * The `core.deltabasecachelimit` used to default to 16 MiB , but this
    proved to be too small, and has been bumped to 96 MiB.
 
@@ -166,6 +182,10 @@ notes for details).
    be checked out currently.
    (merge e3fa568 jc/revision-dash-count-parsing later to maint).
 
+ * Code to avoid adding the same alternate object store twice was
+   subtly broken for a long time, but nobody seems to have noticed.
+   (merge 80b4785 rs/fix-alt-odb-path-comparison later to maint).
+
  * The "%<(10,trunc)%s" pretty format specifier in the log family of
    commands is used to truncate the string to a given length (e.g. 10
    in the example) with padding to column-align the output, but did
@@ -173,6 +193,17 @@ notes for details).
    columns are different.
    (merge 7d50987 as/pretty-truncate later to maint).
 
+ * "%G" (nothing after G) is an invalid pretty format specifier, but
+   the parser did not notice it as garbage.
+   (merge 958b2eb jk/pretty-G-format-fixes later to maint).
+
+ * A handful of code paths had to read the commit object more than
+   once when showing header fields that are usually not parsed.  The
+   internal data structure to keep track of the contents of the commit
+   object has been updated to reduce the need for this double-reading,
+   and to allow the caller find the length of the object.
+   (merge 218aa3a jk/commit-buffer-length later to maint).
+
  * The "mailmap.file" configuration option did not support the tilde
    expansion (i.e. ~user/path and ~/path).
    (merge 9352fd5 ow/config-mailmap-pathname later to maint).
@@ -197,6 +228,11 @@ notes for details).
    line endings.
    (merge 4d4813a bc/blame-crlf-test later to maint).
 
+ * "git clone -b brefs/tags/bar" would have mistakenly thought we were
+   following a single tag, even though it was a name of the branch,
+   because it incorrectly used strstr().
+   (merge 60a5f5f jc/fix-clone-single-starting-at-a-tag later to maint).
+
  * "git commit --allow-empty-messag -C $commit" did not work when the
    commit did not have any log message.
    (merge 076cbd6 jk/commit-C-pick-empty later to maint).
@@ -270,6 +306,10 @@ notes for details).
    emptying the insn sheet.
    (merge ddb5432 rr/rebase-autostash-fix later to maint).
 
+ * During "git rebase --merge", a conflicted patch could not be
+   skipped with "--skip" if the next one also conflicted.
+   (merge 95104c7 bc/fix-rebase-merge-skip later to maint).
+
  * "git show -s" (i.e. show log message only) used to incorrectly emit
    an extra blank line after a merge commit.
    (merge ad2f725 mk/show-s-no-extra-blank-line-for-merges later to maint).
index 987395d22af915832c535840ef59927fc5125294..9bd76a5a6b9327136871dd49cdad5c1ecd8dbcde 100644 (file)
@@ -245,6 +245,10 @@ print a message to stderr and exit with nonzero status.
 --show-toplevel::
        Show the absolute path of the top-level directory.
 
+--shared-index-path::
+       Show the path to the shared index file in split index mode, or
+       empty if not in split-index mode.
+
 Other Options
 ~~~~~~~~~~~~~
 
index d6de4a008ce74b9aa3f83035b4392e8d4505fae7..dfc09d93d85f5ca598447e98327518082c8e1a16 100644 (file)
@@ -161,6 +161,17 @@ may not support it yet.
        Only meaningful with `--stdin` or `--index-info`; paths are
        separated with NUL character instead of LF.
 
+--split-index::
+--no-split-index::
+       Enable or disable split index mode. If enabled, the index is
+       split into two files, $GIT_DIR/index and $GIT_DIR/sharedindex.<SHA-1>.
+       Changes are accumulated in $GIT_DIR/index while the shared
+       index file contains all index entries stays unchanged. If
+       split-index mode is already enabled and `--split-index` is
+       given again, all changes in $GIT_DIR/index are pushed back to
+       the shared index file. This mode is designed for very large
+       indexes that take a signficant amount of time to read or write.
+
 \--::
        Do not interpret any more arguments as options.
 
diff --git a/Documentation/git-verify-commit.txt b/Documentation/git-verify-commit.txt
new file mode 100644 (file)
index 0000000..9413e28
--- /dev/null
@@ -0,0 +1,28 @@
+git-verify-commit(1)
+====================
+
+NAME
+----
+git-verify-commit - Check the GPG signature of commits
+
+SYNOPSIS
+--------
+[verse]
+'git verify-commit' <commit>...
+
+DESCRIPTION
+-----------
+Validates the gpg signature created by 'git commit -S'.
+
+OPTIONS
+-------
+-v::
+--verbose::
+       Print the contents of the commit object before validating it.
+
+<commit>...::
+       SHA-1 identifiers of Git commit objects.
+
+GIT
+---
+Part of the linkgit:git[1] suite
index 17d2ea6c1e48733d85a8caa49f6143c76edb09aa..79653f313474fa658f072c97d8744e5afccb77d7 100644 (file)
@@ -155,6 +155,10 @@ index::
        The current index file for the repository.  It is
        usually not found in a bare repository.
 
+sharedindex.<SHA-1>::
+       The shared index part, to be referenced by $GIT_DIR/index and
+       other temporary index files. Only valid in split index mode.
+
 info::
        Additional information about the repository is recorded
        in this directory.
index f352a9b22e3138fa674c393d99d420de4f2ff144..fe6f31667d7d5eba34c57e322b557ab29d618011 100644 (file)
@@ -129,6 +129,9 @@ Git index format
   (Version 4) In version 4, the padding after the pathname does not
   exist.
 
+  Interpretation of index entries in split index mode is completely
+  different. See below for details.
+
 == Extensions
 
 === Cached tree
@@ -198,3 +201,35 @@ Git index format
   - At most three 160-bit object names of the entry in stages from 1 to 3
     (nothing is written for a missing stage).
 
+=== Split index
+
+  In split index mode, the majority of index entries could be stored
+  in a separate file. This extension records the changes to be made on
+  top of that to produce the final index.
+
+  The signature for this extension is { 'l', 'i, 'n', 'k' }.
+
+  The extension consists of:
+
+  - 160-bit SHA-1 of the shared index file. The shared index file path
+    is $GIT_DIR/sharedindex.<SHA-1>. If all 160 bits are zero, the
+    index does not require a shared index file.
+
+  - An ewah-encoded delete bitmap, each bit represents an entry in the
+    shared index. If a bit is set, its corresponding entry in the
+    shared index will be removed from the final index.  Note, because
+    a delete operation changes index entry positions, but we do need
+    original positions in replace phase, it's best to just mark
+    entries for removal, then do a mass deletion after replacement.
+
+  - An ewah-encoded replace bitmap, each bit represents an entry in
+    the shared index. If a bit is set, its corresponding entry in the
+    shared index will be replaced with an entry in this index
+    file. All replaced entries are stored in sorted order in this
+    index. The first "1" bit in the replace bitmap corresponds to the
+    first index entry, the second "1" bit to the second entry and so
+    on. Replaced entries may have empty path names to save space.
+
+  The remaining index entries after replaced ones will be added to the
+  final index. These added entries are also sorted by entry namme then
+  stage.
index 07ea1058379ab963648d0df7fd2917e0d2efa8a7..840057cba7ebd5fea25d84af367e49f0fcfc41c7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -552,6 +552,7 @@ TEST_PROGRAMS_NEED_X += test-ctype
 TEST_PROGRAMS_NEED_X += test-date
 TEST_PROGRAMS_NEED_X += test-delta
 TEST_PROGRAMS_NEED_X += test-dump-cache-tree
+TEST_PROGRAMS_NEED_X += test-dump-split-index
 TEST_PROGRAMS_NEED_X += test-genrandom
 TEST_PROGRAMS_NEED_X += test-hashmap
 TEST_PROGRAMS_NEED_X += test-index-version
@@ -875,6 +876,7 @@ LIB_OBJS += sha1_name.o
 LIB_OBJS += shallow.o
 LIB_OBJS += sideband.o
 LIB_OBJS += sigchain.o
+LIB_OBJS += split-index.o
 LIB_OBJS += strbuf.o
 LIB_OBJS += streaming.o
 LIB_OBJS += string-list.o
@@ -999,6 +1001,7 @@ BUILTIN_OBJS += builtin/update-ref.o
 BUILTIN_OBJS += builtin/update-server-info.o
 BUILTIN_OBJS += builtin/upload-archive.o
 BUILTIN_OBJS += builtin/var.o
+BUILTIN_OBJS += builtin/verify-commit.o
 BUILTIN_OBJS += builtin/verify-pack.o
 BUILTIN_OBJS += builtin/verify-tag.o
 BUILTIN_OBJS += builtin/write-tree.o
index c50ebdf5fe82d88b901ca26668806c94e03fbb37..9b420331926c6bf1aa371f973d6a96886f419b06 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -61,9 +61,12 @@ void advise(const char *advice, ...)
 
 int git_default_advice_config(const char *var, const char *value)
 {
-       const char *k = skip_prefix(var, "advice.");
+       const char *k;
        int i;
 
+       if (!skip_prefix(var, "advice.", &k))
+               return 0;
+
        for (i = 0; i < ARRAY_SIZE(advice_config); i++) {
                if (strcmp(k, advice_config[i].name))
                        continue;
diff --git a/alias.c b/alias.c
index 5efc3d69866731d0f56464c522b00f253f84c187..758c8671494ad003a6e7e33f8a14b5450311bf54 100644 (file)
--- a/alias.c
+++ b/alias.c
@@ -5,7 +5,8 @@ static char *alias_val;
 
 static int alias_lookup_cb(const char *k, const char *v, void *cb)
 {
-       if (starts_with(k, "alias.") && !strcmp(k + 6, alias_key)) {
+       const char *name;
+       if (skip_prefix(k, "alias.", &name) && !strcmp(name, alias_key)) {
                if (!v)
                        return config_error_nonbool(k);
                alias_val = xstrdup(v);
diff --git a/alloc.c b/alloc.c
index f3ee745695643c00f175288a9592ab073dd589b0..eb22a45c9d0acf08a6024094bd0a2090a7307945 100644 (file)
--- a/alloc.c
+++ b/alloc.c
@@ -47,23 +47,32 @@ union any_object {
 
 DEFINE_ALLOCATOR(blob, struct blob)
 DEFINE_ALLOCATOR(tree, struct tree)
-DEFINE_ALLOCATOR(commit, struct commit)
+DEFINE_ALLOCATOR(raw_commit, struct commit)
 DEFINE_ALLOCATOR(tag, struct tag)
 DEFINE_ALLOCATOR(object, union any_object)
 
+void *alloc_commit_node(void)
+{
+       static int commit_count;
+       struct commit *c = alloc_raw_commit_node();
+       c->index = commit_count++;
+       return c;
+}
+
 static void report(const char *name, unsigned int count, size_t size)
 {
        fprintf(stderr, "%10s: %8u (%"PRIuMAX" kB)\n",
                        name, count, (uintmax_t) size);
 }
 
-#define REPORT(name)   \
-    report(#name, name##_allocs, name##_allocs * sizeof(struct name) >> 10)
+#define REPORT(name, type)     \
+    report(#name, name##_allocs, name##_allocs * sizeof(type) >> 10)
 
 void alloc_report(void)
 {
-       REPORT(blob);
-       REPORT(tree);
-       REPORT(commit);
-       REPORT(tag);
+       REPORT(blob, struct blob);
+       REPORT(tree, struct tree);
+       REPORT(raw_commit, struct commit);
+       REPORT(tag, struct tag);
+       REPORT(object, union any_object);
 }
index 660097bc29a682c4481308ba245e5e02f0661681..46e8aa86df1811ff19899869a33ecb48cacb107f 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -50,11 +50,11 @@ static int should_setup_rebase(const char *origin)
 
 void install_branch_config(int flag, const char *local, const char *origin, const char *remote)
 {
-       const char *shortname = skip_prefix(remote, "refs/heads/");
+       const char *shortname = NULL;
        struct strbuf key = STRBUF_INIT;
        int rebasing = should_setup_rebase(origin);
 
-       if (shortname
+       if (skip_prefix(remote, "refs/heads/", &shortname)
            && !strcmp(local, shortname)
            && !origin) {
                warning(_("Not setting branch %s as its own upstream."),
index c47c110e0f18f3183d54c9afb63cffef686a21db..5d91f31ca25e0c16fca0e8c23d83706840bf6c57 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -128,6 +128,7 @@ extern int cmd_update_server_info(int argc, const char **argv, const char *prefi
 extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
 extern int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix);
 extern int cmd_var(int argc, const char **argv, const char *prefix);
+extern int cmd_verify_commit(int argc, const char **argv, const char *prefix);
 extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
 extern int cmd_version(int argc, const char **argv, const char *prefix);
 extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
index 459208a326cd4cebfa813168d81a06c27fc54fc5..4baf3a563510b10e592ff06ae62df2f405413f6c 100644 (file)
@@ -299,7 +299,6 @@ static int add_files(struct dir_struct *dir, int flags)
 int cmd_add(int argc, const char **argv, const char *prefix)
 {
        int exit_status = 0;
-       int newfd;
        struct pathspec pathspec;
        struct dir_struct dir;
        int flags;
@@ -345,7 +344,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        add_new_files = !take_worktree_changes && !refresh_only;
        require_pathspec = !take_worktree_changes;
 
-       newfd = hold_locked_index(&lock_file, 1);
+       hold_locked_index(&lock_file, 1);
 
        flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
                 (show_only ? ADD_CACHE_PRETEND : 0) |
@@ -443,8 +442,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
 finish:
        if (active_cache_changed) {
-               if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_locked_index(&lock_file))
+               if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                        die(_("Unable to write new index file"));
        }
 
index 9c5724eaccfaee62ff10eb097adc39ace351cade..5fd099ed40859ddfd838ea99191921d63e6eed12 100644 (file)
@@ -1281,9 +1281,7 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct
         */
        patch->def_name = git_header_name(line, len);
        if (patch->def_name && root) {
-               char *s = xmalloc(root_len + strlen(patch->def_name) + 1);
-               strcpy(s, root);
-               strcpy(s + root_len, patch->def_name);
+               char *s = xstrfmt("%s%s", root, patch->def_name);
                free(patch->def_name);
                patch->def_name = s;
        }
@@ -3086,13 +3084,15 @@ static void prepare_fn_table(struct patch *patch)
        }
 }
 
-static int checkout_target(struct cache_entry *ce, struct stat *st)
+static int checkout_target(struct index_state *istate,
+                          struct cache_entry *ce, struct stat *st)
 {
        struct checkout costate;
 
        memset(&costate, 0, sizeof(costate));
        costate.base_dir = "";
        costate.refresh_cache = 1;
+       costate.istate = istate;
        if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))
                return error(_("cannot checkout %s"), ce->name);
        return 0;
@@ -3259,7 +3259,7 @@ static int load_current(struct image *image, struct patch *patch)
        if (lstat(name, &st)) {
                if (errno != ENOENT)
                        return error(_("%s: %s"), name, strerror(errno));
-               if (checkout_target(ce, &st))
+               if (checkout_target(&the_index, ce, &st))
                        return -1;
        }
        if (verify_index_match(ce, &st))
@@ -3413,7 +3413,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
                }
                *ce = active_cache[pos];
                if (stat_ret < 0) {
-                       if (checkout_target(*ce, st))
+                       if (checkout_target(&the_index, *ce, st))
                                return -1;
                }
                if (!cached && verify_index_match(*ce, st))
@@ -3646,7 +3646,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
 {
        struct patch *patch;
        struct index_state result = { NULL };
-       int fd;
+       static struct lock_file lock;
 
        /* Once we start supporting the reverse patch, it may be
         * worth showing the new sha1 prefix, but until then...
@@ -3684,8 +3684,8 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
                        die ("Could not add %s to temporary index", name);
        }
 
-       fd = open(filename, O_WRONLY | O_CREAT, 0666);
-       if (fd < 0 || write_index(&result, fd) || close(fd))
+       hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
+       if (write_locked_index(&result, &lock, COMMIT_LOCK))
                die ("Could not write temporary index to %s", filename);
 
        discard_index(&result);
@@ -3847,9 +3847,10 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
        ce->ce_flags = create_ce_flags(0);
        ce->ce_namelen = namelen;
        if (S_ISGITLINK(mode)) {
-               const char *s = buf;
+               const char *s;
 
-               if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
+               if (!skip_prefix(buf, "Subproject commit ", &s) ||
+                   get_sha1_hex(s, ce->sha1))
                        die(_("corrupt patch for submodule %s"), path);
        } else {
                if (!cached) {
@@ -4503,8 +4504,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
        }
 
        if (update_index) {
-               if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_locked_index(&lock_file))
+               if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                        die(_("Unable to write new index file"));
        }
 
index 662e3fec44666901de4f8feda8a51385260967e0..c59e70202175fc89992c23b48ea5f0738664a2f6 100644 (file)
@@ -1655,7 +1655,7 @@ static void get_commit_info(struct commit *commit,
 {
        int len;
        const char *subject, *encoding;
-       char *message;
+       const char *message;
 
        commit_info_init(ret);
 
@@ -1666,7 +1666,7 @@ static void get_commit_info(struct commit *commit,
                    &ret->author_time, &ret->author_tz);
 
        if (!detailed) {
-               logmsg_free(message, commit);
+               unuse_commit_buffer(commit, message);
                return;
        }
 
@@ -1680,7 +1680,7 @@ static void get_commit_info(struct commit *commit,
        else
                strbuf_addf(&ret->summary, "(%s)", sha1_to_hex(commit->object.sha1));
 
-       logmsg_free(message, commit);
+       unuse_commit_buffer(commit, message);
 }
 
 /*
@@ -2251,6 +2251,18 @@ static void append_merge_parents(struct commit_list **tail)
        strbuf_release(&line);
 }
 
+/*
+ * This isn't as simple as passing sb->buf and sb->len, because we
+ * want to transfer ownership of the buffer to the commit (so we
+ * must use detach).
+ */
+static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
+{
+       size_t len;
+       void *buf = strbuf_detach(sb, &len);
+       set_commit_buffer(c, buf, len);
+}
+
 /*
  * Prepare a dummy commit that represents the work tree (or staged) item.
  * Note that annotating work tree item never works in the reverse.
@@ -2272,7 +2284,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
        struct strbuf msg = STRBUF_INIT;
 
        time(&now);
-       commit = xcalloc(1, sizeof(*commit));
+       commit = alloc_commit_node();
        commit->object.parsed = 1;
        commit->date = now;
        commit->object.type = OBJ_COMMIT;
@@ -2299,7 +2311,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
                    ident, ident, path,
                    (!contents_from ? path :
                     (!strcmp(contents_from, "-") ? "standard input" : contents_from)));
-       commit->buffer = strbuf_detach(&msg, NULL);
+       set_commit_buffer_from_strbuf(commit, &msg);
 
        if (!contents_from || strcmp("-", contents_from)) {
                struct stat st;
@@ -2377,7 +2389,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
         * right now, but someday we might optimize diff-index --cached
         * with cache-tree information.
         */
-       cache_tree_invalidate_path(active_cache_tree, path);
+       cache_tree_invalidate_path(&the_index, path);
 
        return commit;
 }
index 652b1d2d1484032fab51165f1d0b0a41426d114f..0591b22a483619ac9a7d889a49e45ffa9d68ab72 100644 (file)
@@ -294,13 +294,13 @@ static char *resolve_symref(const char *src, const char *prefix)
 {
        unsigned char sha1[20];
        int flag;
-       const char *dst, *cp;
+       const char *dst;
 
        dst = resolve_ref_unsafe(src, sha1, 0, &flag);
        if (!(dst && (flag & REF_ISSYMREF)))
                return NULL;
-       if (prefix && (cp = skip_prefix(dst, prefix)))
-               dst = cp;
+       if (prefix)
+               skip_prefix(dst, prefix, &dst);
        return xstrdup(dst);
 }
 
index 61e75eb60c992e38ddf195abad564a76cbf934cb..05edd9e1df52e63a1644cddc0fb0e6a0d764d90b 100644 (file)
@@ -135,6 +135,7 @@ static int option_parse_u(const struct option *opt,
        int *newfd = opt->value;
 
        state.refresh_cache = 1;
+       state.istate = &the_index;
        if (*newfd < 0)
                *newfd = hold_locked_index(&lock_file, 1);
        return 0;
@@ -279,8 +280,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
                checkout_all(prefix, prefix_length);
 
        if (0 <= newfd &&
-           (write_cache(newfd, active_cache, active_nr) ||
-            commit_locked_index(&lock_file)))
+           write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die("Unable to write new index file");
        return 0;
 }
index f1dc56e55f7b2200412142b10517458ccfda2952..f71e74531d2a7d195ff2987f6dca3c69c636aa7c 100644 (file)
@@ -225,7 +225,6 @@ static int checkout_paths(const struct checkout_opts *opts,
        int flag;
        struct commit *head;
        int errs = 0;
-       int newfd;
        struct lock_file *lock_file;
 
        if (opts->track != BRANCH_TRACK_UNSPECIFIED)
@@ -256,7 +255,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 
        lock_file = xcalloc(1, sizeof(struct lock_file));
 
-       newfd = hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, 1);
        if (read_cache_preload(&opts->pathspec) < 0)
                return error(_("corrupt index file"));
 
@@ -337,6 +336,7 @@ static int checkout_paths(const struct checkout_opts *opts,
        memset(&state, 0, sizeof(state));
        state.force = 1;
        state.refresh_cache = 1;
+       state.istate = &the_index;
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
                if (ce->ce_flags & CE_MATCHED) {
@@ -352,8 +352,7 @@ static int checkout_paths(const struct checkout_opts *opts,
                }
        }
 
-       if (write_cache(newfd, active_cache, active_nr) ||
-           commit_locked_index(lock_file))
+       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
        read_ref_full("HEAD", rev, 0, &flag);
@@ -444,8 +443,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
 {
        int ret;
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-       int newfd = hold_locked_index(lock_file, 1);
 
+       hold_locked_index(lock_file, 1);
        if (read_cache_preload(NULL) < 0)
                return error(_("corrupt index file"));
 
@@ -553,8 +552,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
                }
        }
 
-       if (write_cache(newfd, active_cache, active_nr) ||
-           commit_locked_index(lock_file))
+       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
        if (!opts->force && !opts->quiet)
@@ -776,8 +774,8 @@ static int switch_branches(const struct checkout_opts *opts,
        if (!(flag & REF_ISSYMREF))
                old.path = NULL;
 
-       if (old.path && starts_with(old.path, "refs/heads/"))
-               old.name = old.path + strlen("refs/heads/");
+       if (old.path)
+               skip_prefix(old.path, "refs/heads/", &old.name);
 
        if (!new->name) {
                new->name = "HEAD";
index 9a9151575d3a8d4eb16d73e432835d3cda4374b8..27701d222c78ed84691b65334a7909f90c589464 100644 (file)
@@ -48,7 +48,7 @@ enum color_clean {
        CLEAN_COLOR_PROMPT = 2,
        CLEAN_COLOR_HEADER = 3,
        CLEAN_COLOR_HELP = 4,
-       CLEAN_COLOR_ERROR = 5,
+       CLEAN_COLOR_ERROR = 5
 };
 
 #define MENU_OPTS_SINGLETON            01
index b12989d1caecb48128e8a468014f07b237b1a4cc..f0dabecca88d8f16528fb68489e5b7a7700e2a47 100644 (file)
@@ -584,11 +584,11 @@ static void update_remote_refs(const struct ref *refs,
 static void update_head(const struct ref *our, const struct ref *remote,
                        const char *msg)
 {
-       if (our && starts_with(our->name, "refs/heads/")) {
+       const char *head;
+       if (our && skip_prefix(our->name, "refs/heads/", &head)) {
                /* Local default branch link */
                create_symref("HEAD", our->name, NULL);
                if (!option_bare) {
-                       const char *head = skip_prefix(our->name, "refs/heads/");
                        update_ref(msg, "HEAD", our->old_sha1, NULL, 0,
                                   UPDATE_REFS_DIE_ON_ERR);
                        install_branch_config(0, head, option_origin, our->name);
@@ -617,7 +617,7 @@ static int checkout(void)
        struct unpack_trees_options opts;
        struct tree *tree;
        struct tree_desc t;
-       int err = 0, fd;
+       int err = 0;
 
        if (option_no_checkout)
                return 0;
@@ -641,7 +641,7 @@ static int checkout(void)
        setup_work_tree();
 
        lock_file = xcalloc(1, sizeof(struct lock_file));
-       fd = hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, 1);
 
        memset(&opts, 0, sizeof opts);
        opts.update = 1;
@@ -657,8 +657,7 @@ static int checkout(void)
        if (unpack_trees(1, &t, &opts) < 0)
                die(_("unable to checkout working tree"));
 
-       if (write_cache(fd, active_cache, active_nr) ||
-           commit_locked_index(lock_file))
+       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
        err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
@@ -696,16 +695,19 @@ static void write_refspec_config(const char* src_ref_prefix,
        if (option_mirror || !option_bare) {
                if (option_single_branch && !option_mirror) {
                        if (option_branch) {
-                               if (strstr(our_head_points_at->name, "refs/tags/"))
+                               if (starts_with(our_head_points_at->name, "refs/tags/"))
                                        strbuf_addf(&value, "+%s:%s", our_head_points_at->name,
                                                our_head_points_at->name);
                                else
                                        strbuf_addf(&value, "+%s:%s%s", our_head_points_at->name,
                                                branch_top->buf, option_branch);
                        } else if (remote_head_points_at) {
+                               const char *head = remote_head_points_at->name;
+                               if (!skip_prefix(head, "refs/heads/", &head))
+                                       die("BUG: remote HEAD points at non-head?");
+
                                strbuf_addf(&value, "+%s:%s%s", remote_head_points_at->name,
-                                               branch_top->buf,
-                                               skip_prefix(remote_head_points_at->name, "refs/heads/"));
+                                               branch_top->buf, head);
                        }
                        /*
                         * otherwise, the next "git fetch" will
index 987a4c3d731d7cf01f61e4fddec208b4cb51a4af..8a66c74e0fd405a06e6c767d385013ff7150db04 100644 (file)
@@ -123,8 +123,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
                        die_errno("git commit-tree: failed to read");
        }
 
-       if (commit_tree(&buffer, tree_sha1, parents, commit_sha1,
-                       NULL, sign_commit)) {
+       if (commit_tree(buffer.buf, buffer.len, tree_sha1, parents,
+                       commit_sha1, NULL, sign_commit)) {
                strbuf_release(&buffer);
                return 1;
        }
index 5e2221c8e8c842314cb177d55223e87328b3da26..72eb3beb36cd06f6e41d80d5761a953a1c3a1d56 100644 (file)
@@ -305,7 +305,6 @@ static void refresh_cache_or_die(int refresh_flags)
 static char *prepare_index(int argc, const char **argv, const char *prefix,
                           const struct commit *current_head, int is_status)
 {
-       int fd;
        struct string_list partial;
        struct pathspec pathspec;
        int refresh_flags = REFRESH_QUIET;
@@ -321,12 +320,11 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
 
        if (interactive) {
                char *old_index_env = NULL;
-               fd = hold_locked_index(&index_lock, 1);
+               hold_locked_index(&index_lock, 1);
 
                refresh_cache_or_die(refresh_flags);
 
-               if (write_cache(fd, active_cache, active_nr) ||
-                   close_lock_file(&index_lock))
+               if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
                        die(_("unable to create temporary index"));
 
                old_index_env = getenv(INDEX_ENVIRONMENT);
@@ -360,12 +358,11 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
         * (B) on failure, rollback the real index.
         */
        if (all || (also && pathspec.nr)) {
-               fd = hold_locked_index(&index_lock, 1);
+               hold_locked_index(&index_lock, 1);
                add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
                refresh_cache_or_die(refresh_flags);
                update_main_cache_tree(WRITE_TREE_SILENT);
-               if (write_cache(fd, active_cache, active_nr) ||
-                   close_lock_file(&index_lock))
+               if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
                        die(_("unable to write new_index file"));
                commit_style = COMMIT_NORMAL;
                return index_lock.filename;
@@ -381,12 +378,12 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
         * We still need to refresh the index here.
         */
        if (!only && !pathspec.nr) {
-               fd = hold_locked_index(&index_lock, 1);
+               hold_locked_index(&index_lock, 1);
                refresh_cache_or_die(refresh_flags);
                if (active_cache_changed) {
                        update_main_cache_tree(WRITE_TREE_SILENT);
-                       if (write_cache(fd, active_cache, active_nr) ||
-                           commit_locked_index(&index_lock))
+                       if (write_locked_index(&the_index, &index_lock,
+                                              COMMIT_LOCK))
                                die(_("unable to write new_index file"));
                } else {
                        rollback_lock_file(&index_lock);
@@ -432,24 +429,22 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
        if (read_cache() < 0)
                die(_("cannot read the index"));
 
-       fd = hold_locked_index(&index_lock, 1);
+       hold_locked_index(&index_lock, 1);
        add_remove_files(&partial);
        refresh_cache(REFRESH_QUIET);
-       if (write_cache(fd, active_cache, active_nr) ||
-           close_lock_file(&index_lock))
+       if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
                die(_("unable to write new_index file"));
 
-       fd = hold_lock_file_for_update(&false_lock,
-                                      git_path("next-index-%"PRIuMAX,
-                                               (uintmax_t) getpid()),
-                                      LOCK_DIE_ON_ERROR);
+       hold_lock_file_for_update(&false_lock,
+                                 git_path("next-index-%"PRIuMAX,
+                                          (uintmax_t) getpid()),
+                                 LOCK_DIE_ON_ERROR);
 
        create_base_index(current_head);
        add_remove_files(&partial);
        refresh_cache(REFRESH_QUIET);
 
-       if (write_cache(fd, active_cache, active_nr) ||
-           close_lock_file(&false_lock))
+       if (write_locked_index(&the_index, &false_lock, CLOSE_LOCK))
                die(_("unable to write temporary index file"));
 
        discard_cache();
@@ -1020,7 +1015,7 @@ static int message_is_empty(struct strbuf *sb)
 static int template_untouched(struct strbuf *sb)
 {
        struct strbuf tmpl = STRBUF_INIT;
-       char *start;
+       const char *start;
 
        if (cleanup_mode == CLEANUP_NONE && sb->len)
                return 0;
@@ -1029,8 +1024,7 @@ static int template_untouched(struct strbuf *sb)
                return 0;
 
        stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
-       start = (char *)skip_prefix(sb->buf, tmpl.buf);
-       if (!start)
+       if (!skip_prefix(sb->buf, tmpl.buf, &start))
                start = sb->buf;
        strbuf_release(&tmpl);
        return rest_is_empty(sb, start - sb->buf);
@@ -1745,8 +1739,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                append_merge_tag_headers(parents, &tail);
        }
 
-       if (commit_tree_extended(&sb, active_cache_tree->sha1, parents, sha1,
-                                author_ident.buf, sign_commit, extra)) {
+       if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1,
+                        parents, sha1, author_ident.buf, sign_commit, extra)) {
                rollback_index_files();
                die(_("failed to write commit object"));
        }
index be6417d166abf428d379a70b9d67f894da4641d1..ce0e019e0caa358e1f5e39e5ed51bec3945197e0 100644 (file)
@@ -22,14 +22,10 @@ static int stdin_diff_commit(struct commit *commit, char *line, int len)
        if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) {
                /* Graft the fake parents locally to the commit */
                int pos = 41;
-               struct commit_list **pptr, *parents;
+               struct commit_list **pptr;
 
                /* Free the real parent list */
-               for (parents = commit->parents; parents; ) {
-                       struct commit_list *tmp = parents->next;
-                       free(parents);
-                       parents = tmp;
-               }
+               free_commit_list(commit->parents);
                commit->parents = NULL;
                pptr = &(commit->parents);
                while (line[pos] && !get_sha1_hex(line + pos, sha1)) {
index ef4481615fd2f4d60cd3cd6a4bbcceaca93c92d5..92b4624a4b93ff50c661e44242d508aed31877eb 100644 (file)
@@ -282,6 +282,7 @@ static const char *find_encoding(const char *begin, const char *end)
 static void handle_commit(struct commit *commit, struct rev_info *rev)
 {
        int saved_output_format = rev->diffopt.output_format;
+       const char *commit_buffer;
        const char *author, *author_end, *committer, *committer_end;
        const char *encoding, *message;
        char *reencoded = NULL;
@@ -291,7 +292,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
        rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
 
        parse_commit_or_die(commit);
-       author = strstr(commit->buffer, "\nauthor ");
+       commit_buffer = get_commit_buffer(commit, NULL);
+       author = strstr(commit_buffer, "\nauthor ");
        if (!author)
                die ("Could not find author in commit %s",
                     sha1_to_hex(commit->object.sha1));
@@ -338,6 +340,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
                          ? strlen(message) : 0),
               reencoded ? reencoded : message ? message : "");
        free(reencoded);
+       unuse_commit_buffer(commit, commit_buffer);
 
        for (i = 0, p = commit->parents; p; p = p->next) {
                int mark = get_object_mark(&p->item->object);
index dd46b61d9a31475e3d5289e41e6d7f57a0264b6f..e8d0cca3e4110e1ae6db82f21c899c44460cb849 100644 (file)
@@ -1082,16 +1082,11 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
                refs = xcalloc(argc + 1, sizeof(const char *));
                for (i = 0; i < argc; i++) {
                        if (!strcmp(argv[i], "tag")) {
-                               char *ref;
                                i++;
                                if (i >= argc)
                                        die(_("You need to specify a tag name."));
-                               ref = xmalloc(strlen(argv[i]) * 2 + 22);
-                               strcpy(ref, "refs/tags/");
-                               strcat(ref, argv[i]);
-                               strcat(ref, ":refs/tags/");
-                               strcat(ref, argv[i]);
-                               refs[j++] = ref;
+                               refs[j++] = xstrfmt("refs/tags/%s:refs/tags/%s",
+                                                   argv[i], argv[i]);
                        } else
                                refs[j++] = argv[i];
                }
index 3906eda87712ea099e0e5876fff1fe28fc5716be..79df05ef526bcd1a3be2971c9d467a930001639d 100644 (file)
@@ -100,7 +100,8 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
 {
        int i, len = strlen(line);
        struct origin_data *origin_data;
-       char *src, *origin;
+       char *src;
+       const char *origin;
        struct src_data *src_data;
        struct string_list_item *item;
        int pulling_head = 0;
@@ -164,8 +165,7 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
                origin = line;
                string_list_append(&src_data->tag, origin + 4);
                src_data->head_status |= 2;
-       } else if (starts_with(line, "remote-tracking branch ")) {
-               origin = line + strlen("remote-tracking branch ");
+       } else if (skip_prefix(line, "remote-tracking branch ", &origin)) {
                string_list_append(&src_data->r_branch, origin);
                src_data->head_status |= 2;
        } else {
@@ -178,11 +178,8 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
                int len = strlen(origin);
                if (origin[0] == '\'' && origin[len - 1] == '\'')
                        origin = xmemdupz(origin + 1, len - 2);
-       } else {
-               char *new_origin = xmalloc(strlen(origin) + strlen(src) + 5);
-               sprintf(new_origin, "%s of %s", origin, src);
-               origin = new_origin;
-       }
+       } else
+               origin = xstrfmt("%s of %s", origin, src);
        if (strcmp(".", src))
                origin_data->is_local_branch = 0;
        string_list_append(&origins, origin)->util = origin_data;
@@ -230,12 +227,14 @@ static void add_branch_desc(struct strbuf *out, const char *name)
 static void record_person(int which, struct string_list *people,
                          struct commit *commit)
 {
+       const char *buffer;
        char *name_buf, *name, *name_end;
        struct string_list_item *elem;
        const char *field;
 
        field = (which == 'a') ? "\nauthor " : "\ncommitter ";
-       name = strstr(commit->buffer, field);
+       buffer = get_commit_buffer(commit, NULL);
+       name = strstr(buffer, field);
        if (!name)
                return;
        name += strlen(field);
@@ -247,6 +246,7 @@ static void record_person(int which, struct string_list *people,
        if (name_end < name)
                return;
        name_buf = xmemdupz(name, name_end - name + 1);
+       unuse_commit_buffer(commit, buffer);
 
        elem = string_list_lookup(people, name_buf);
        if (!elem) {
@@ -297,8 +297,8 @@ static void credit_people(struct strbuf *out,
        if (!them->nr ||
            (them->nr == 1 &&
             me &&
-            (me = skip_prefix(me, them->items->string)) != NULL &&
-            skip_prefix(me, " <")))
+            skip_prefix(me, them->items->string, &me) &&
+            starts_with(me, " <")))
                return;
        strbuf_addf(out, "\n%c %s ", comment_line_char, label);
        add_people_count(out, them);
index fc150c882122c53965fd246ad862e3a75d8768e2..8aadca160e857be8a87ec1f152e51eb6f51c90b5 100644 (file)
@@ -310,8 +310,7 @@ static int fsck_obj(struct object *obj)
        if (obj->type == OBJ_COMMIT) {
                struct commit *commit = (struct commit *) obj;
 
-               free(commit->buffer);
-               commit->buffer = NULL;
+               free_commit_buffer(commit);
 
                if (!commit->parents && show_root)
                        printf("root %s\n", sha1_to_hex(commit->object.sha1));
index 18f57de58b02de33909a40ea9720e6602cc9dcb5..8b3bd29dbcf22668040c53fffb6e2414ca090f5d 100644 (file)
@@ -786,7 +786,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
                        }
                        if (obj->type == OBJ_COMMIT) {
                                struct commit *commit = (struct commit *) obj;
-                               commit->buffer = NULL;
+                               if (detach_commit_buffer(commit, NULL) != data)
+                                       die("BUG: parse_object_buffer transmogrified our buffer");
                        }
                        obj->flags |= FLAG_CHECKED;
                }
index a7ba211731ecc02523c4509878679f050c151f0b..27c1b65db46bb1bead9045b7b6be08a1ee199835 100644 (file)
@@ -347,8 +347,7 @@ static int cmd_log_walk(struct rev_info *rev)
                        rev->max_count++;
                if (!rev->reflog_info) {
                        /* we allow cycles in reflog ancestry */
-                       free(commit->buffer);
-                       commit->buffer = NULL;
+                       free_commit_buffer(commit);
                }
                free_commit_list(commit->parents);
                commit->parents = NULL;
@@ -872,7 +871,7 @@ static char *find_branch_name(struct rev_info *rev)
        int i, positive = -1;
        unsigned char branch_sha1[20];
        const unsigned char *tip_sha1;
-       const char *ref;
+       const char *ref, *v;
        char *full_ref, *branch = NULL;
 
        for (i = 0; i < rev->cmdline.nr; i++) {
@@ -888,9 +887,9 @@ static char *find_branch_name(struct rev_info *rev)
        ref = rev->cmdline.rev[positive].name;
        tip_sha1 = rev->cmdline.rev[positive].item->sha1;
        if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
-           starts_with(full_ref, "refs/heads/") &&
+           skip_prefix(full_ref, "refs/heads/", &v) &&
            !hashcmp(tip_sha1, branch_sha1))
-               branch = xstrdup(full_ref + strlen("refs/heads/"));
+               branch = xstrdup(v);
        free(full_ref);
        return branch;
 }
@@ -925,9 +924,12 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
                                &need_8bit_cte);
 
-       for (i = 0; !need_8bit_cte && i < nr; i++)
-               if (has_non_ascii(list[i]->buffer))
+       for (i = 0; !need_8bit_cte && i < nr; i++) {
+               const char *buf = get_commit_buffer(list[i], NULL);
+               if (has_non_ascii(buf))
                        need_8bit_cte = 1;
+               unuse_commit_buffer(list[i], buf);
+       }
 
        if (!branch_name)
                branch_name = find_branch_name(rev);
@@ -1394,10 +1396,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
                if (check_head) {
                        unsigned char sha1[20];
-                       const char *ref;
+                       const char *ref, *v;
                        ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
-                       if (ref && starts_with(ref, "refs/heads/"))
-                               branch_name = xstrdup(ref + strlen("refs/heads/"));
+                       if (ref && skip_prefix(ref, "refs/heads/", &v))
+                               branch_name = xstrdup(v);
                        else
                                branch_name = xstrdup(""); /* no branch */
                }
@@ -1528,8 +1530,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                    reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
                        die(_("Failed to create output files"));
                shown = log_tree_commit(&rev, commit);
-               free(commit->buffer);
-               commit->buffer = NULL;
+               free_commit_buffer(commit);
 
                /* We put one extra blank line between formatted
                 * patches and this flag is used by log-tree code
index 428ca247bd17e0f18776b46d40a09282b0b955de..22491a1c2b906f4e4ae607cda8925b1254b202b3 100644 (file)
@@ -657,14 +657,12 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                              struct commit_list *remoteheads,
                              struct commit *head, const char *head_arg)
 {
-       int index_fd;
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 
-       index_fd = hold_locked_index(lock, 1);
+       hold_locked_index(lock, 1);
        refresh_cache(REFRESH_QUIET);
        if (active_cache_changed &&
-                       (write_cache(index_fd, active_cache, active_nr) ||
-                        commit_locked_index(lock)))
+           write_locked_index(&the_index, lock, COMMIT_LOCK))
                return error(_("Unable to write index."));
        rollback_lock_file(lock);
 
@@ -672,7 +670,6 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                int clean, x;
                struct commit *result;
                struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-               int index_fd;
                struct commit_list *reversed = NULL;
                struct merge_options o;
                struct commit_list *j;
@@ -700,12 +697,11 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                for (j = common; j; j = j->next)
                        commit_list_insert(j->item, &reversed);
 
-               index_fd = hold_locked_index(lock, 1);
+               hold_locked_index(lock, 1);
                clean = merge_recursive(&o, head,
                                remoteheads->item, reversed, &result);
                if (active_cache_changed &&
-                               (write_cache(index_fd, active_cache, active_nr) ||
-                                commit_locked_index(lock)))
+                   write_locked_index(&the_index, lock, COMMIT_LOCK))
                        die (_("unable to write %s"), get_index_file());
                rollback_lock_file(lock);
                return clean ? 0 : 1;
@@ -852,8 +848,8 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
        parent->next->item = remoteheads->item;
        parent->next->next = NULL;
        prepare_to_commit(remoteheads);
-       if (commit_tree(&merge_msg, result_tree, parent, result_commit, NULL,
-                       sign_commit))
+       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parent,
+                       result_commit, NULL, sign_commit))
                die(_("failed to write commit object"));
        finish(head, remoteheads, result_commit, "In-index merge");
        drop_save();
@@ -877,8 +873,8 @@ static int finish_automerge(struct commit *head,
                commit_list_insert(head, &parents);
        strbuf_addch(&merge_msg, '\n');
        prepare_to_commit(remoteheads);
-       if (commit_tree(&merge_msg, result_tree, parents, result_commit,
-                       NULL, sign_commit))
+       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
+                       result_commit, NULL, sign_commit))
                die(_("failed to write commit object"));
        strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
        finish(head, remoteheads, result_commit, buf.buf);
@@ -1282,10 +1278,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                                printf(_("Commit %s has a good GPG signature by %s\n"),
                                       hex, signature_check.signer);
 
-                       free(signature_check.gpg_output);
-                       free(signature_check.gpg_status);
-                       free(signature_check.signer);
-                       free(signature_check.key);
+                       signature_check_clear(&signature_check);
                }
        }
 
index 180ef99127d47d0e8fe9f12a590fd9ba9e524f08..6ffe540c202f69a41fcf40d443f73073d3ba08bd 100644 (file)
@@ -63,7 +63,7 @@ static struct lock_file lock_file;
 
 int cmd_mv(int argc, const char **argv, const char *prefix)
 {
-       int i, newfd, gitmodules_modified = 0;
+       int i, gitmodules_modified = 0;
        int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
        struct option builtin_mv_options[] = {
                OPT__VERBOSE(&verbose, N_("be verbose")),
@@ -85,7 +85,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        if (--argc < 1)
                usage_with_options(builtin_mv_usage, builtin_mv_options);
 
-       newfd = hold_locked_index(&lock_file, 1);
+       hold_locked_index(&lock_file, 1);
        if (read_cache() < 0)
                die(_("index file corrupt"));
 
@@ -276,8 +276,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                stage_updated_gitmodules();
 
        if (active_cache_changed) {
-               if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_locked_index(&lock_file))
+               if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                        die(_("Unable to write new index file"));
        }
 
index c824d4ec5f1c076e4833c690dad378125dde6da9..3c8f319be675d14a6ee60d303335190b7bdce9c8 100644 (file)
@@ -33,10 +33,7 @@ static void name_rev(struct commit *commit,
                return;
 
        if (deref) {
-               char *new_name = xmalloc(strlen(tip_name)+3);
-               strcpy(new_name, tip_name);
-               strcat(new_name, "^0");
-               tip_name = new_name;
+               tip_name = xstrfmt("%s^0", tip_name);
 
                if (generation)
                        die("generation: %d, but deref?", generation);
index f8dfea41e1ad8b6d888c1a2adc13eee87083491e..f50e3d5e77db6842ff3450560aad330f6a4bd91d 100644 (file)
@@ -127,11 +127,10 @@ static NORETURN int die_push_simple(struct branch *branch, struct remote *remote
         * them the big ugly fully qualified ref.
         */
        const char *advice_maybe = "";
-       const char *short_upstream =
-               skip_prefix(branch->merge[0]->src, "refs/heads/");
+       const char *short_upstream = branch->merge[0]->src;
+
+       skip_prefix(short_upstream, "refs/heads/", &short_upstream);
 
-       if (!short_upstream)
-               short_upstream = branch->merge[0]->src;
        /*
         * Don't show advice for people who explicitly set
         * push.default.
index 0d7ef847a70581214fb88c3a981581670f7decbe..e7e1c33a7fb1f1eb7828f4d632fb492f128f66fe 100644 (file)
@@ -99,7 +99,7 @@ static struct lock_file lock_file;
 
 int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
 {
-       int i, newfd, stage = 0;
+       int i, stage = 0;
        unsigned char sha1[20];
        struct tree_desc t[MAX_UNPACK_TREES];
        struct unpack_trees_options opts;
@@ -149,12 +149,21 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
        argc = parse_options(argc, argv, unused_prefix, read_tree_options,
                             read_tree_usage, 0);
 
-       newfd = hold_locked_index(&lock_file, 1);
+       hold_locked_index(&lock_file, 1);
 
        prefix_set = opts.prefix ? 1 : 0;
        if (1 < opts.merge + opts.reset + prefix_set)
                die("Which one? -m, --reset, or --prefix?");
 
+       /*
+        * NEEDSWORK
+        *
+        * The old index should be read anyway even if we're going to
+        * destroy all index entries because we still need to preserve
+        * certain information such as index version or split-index
+        * mode.
+        */
+
        if (opts.reset || opts.merge || opts.prefix) {
                if (read_cache_unmerged() && (opts.prefix || opts.merge))
                        die("You need to resolve your current index first");
@@ -231,10 +240,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
         * what came from the tree.
         */
        if (nr_trees == 1 && !opts.prefix)
-               prime_cache_tree(&active_cache_tree, trees[0]);
+               prime_cache_tree(&the_index, trees[0]);
 
-       if (write_cache(newfd, active_cache, active_nr) ||
-           commit_locked_index(&lock_file))
+       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die("unable to write new index file");
        return 0;
 }
index c3230817db4a7676eb74335b254f30597e66edd9..18458e81c6351d53afe1a50efff183889e5ab81b 100644 (file)
@@ -614,12 +614,9 @@ static void run_update_post_hook(struct command *commands)
        argv[0] = hook;
 
        for (argc = 1, cmd = commands; cmd; cmd = cmd->next) {
-               char *p;
                if (cmd->error_string || cmd->did_not_exist)
                        continue;
-               p = xmalloc(strlen(cmd->ref_name) + 1);
-               strcpy(p, cmd->ref_name);
-               argv[argc] = p;
+               argv[argc] = xstrdup(cmd->ref_name);
                argc++;
        }
        argv[argc] = NULL;
index c9102e8fe94b41af5136a9cc23db939effae147a..a8efe3da4d7dad3b17582c91671f0f475d54a9ac 100644 (file)
@@ -250,9 +250,7 @@ static struct string_list branch_list;
 
 static const char *abbrev_ref(const char *name, const char *prefix)
 {
-       const char *abbrev = skip_prefix(name, prefix);
-       if (abbrev)
-               return abbrev;
+       skip_prefix(name, prefix, &name);
        return name;
 }
 #define abbrev_branch(name) abbrev_ref((name), "refs/heads/")
index f368266762fae1e6c783d7351e289e423e408b3c..855d478e3b9d2a815ecdcfbf39c261f98ad0994b 100644 (file)
@@ -84,7 +84,7 @@ static int reset_index(const unsigned char *sha1, int reset_type, int quiet)
 
        if (reset_type == MIXED || reset_type == HARD) {
                tree = parse_tree_indirect(sha1);
-               prime_cache_tree(&active_cache_tree, tree);
+               prime_cache_tree(&the_index, tree);
        }
 
        return 0;
@@ -93,7 +93,7 @@ static int reset_index(const unsigned char *sha1, int reset_type, int quiet)
 static void print_new_head_line(struct commit *commit)
 {
        const char *hex, *body;
-       char *msg;
+       const char *msg;
 
        hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
        printf(_("HEAD is now at %s"), hex);
@@ -109,7 +109,7 @@ static void print_new_head_line(struct commit *commit)
        }
        else
                printf("\n");
-       logmsg_free(msg, commit);
+       unuse_commit_buffer(commit, msg);
 }
 
 static void update_index_from_diff(struct diff_queue_struct *q,
@@ -353,7 +353,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
        if (reset_type != SOFT) {
                struct lock_file *lock = xcalloc(1, sizeof(*lock));
-               int newfd = hold_locked_index(lock, 1);
+               hold_locked_index(lock, 1);
                if (reset_type == MIXED) {
                        int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
                        if (read_from_tree(&pathspec, sha1, intent_to_add))
@@ -369,8 +369,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                                die(_("Could not reset index file to revision '%s'."), rev);
                }
 
-               if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_locked_index(lock))
+               if (write_locked_index(&the_index, lock, COMMIT_LOCK))
                        die(_("Could not write new index file."));
        }
 
index 9f92905379d1801a35bfad87bc05002d76dc23f4..ff84a825ff776abc01087f37782223ee26a1af7a 100644 (file)
@@ -106,7 +106,7 @@ static void show_commit(struct commit *commit, void *data)
        else
                putchar('\n');
 
-       if (revs->verbose_header && commit->buffer) {
+       if (revs->verbose_header && get_cached_commit_buffer(commit, NULL)) {
                struct strbuf buf = STRBUF_INIT;
                struct pretty_print_context ctx = {0};
                ctx.abbrev = revs->abbrev;
@@ -173,8 +173,7 @@ static void finish_commit(struct commit *commit, void *data)
                free_commit_list(commit->parents);
                commit->parents = NULL;
        }
-       free(commit->buffer);
-       commit->buffer = NULL;
+       free_commit_buffer(commit);
 }
 
 static void finish_object(struct object *obj,
index 1a6122d3ae29609c30c26959fbb1ff799e13a4b2..8102aaa9248d55055761f9899903cac2a2c7cc1e 100644 (file)
@@ -11,6 +11,7 @@
 #include "parse-options.h"
 #include "diff.h"
 #include "revision.h"
+#include "split-index.h"
 
 #define DO_REVS                1
 #define DO_NOREV       2
@@ -775,6 +776,15 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                                : "false");
                                continue;
                        }
+                       if (!strcmp(arg, "--shared-index-path")) {
+                               if (read_cache() < 0)
+                                       die(_("Could not read the index"));
+                               if (the_index.split_index) {
+                                       const unsigned char *sha1 = the_index.split_index->base_sha1;
+                                       puts(git_path("sharedindex.%s", sha1_to_hex(sha1)));
+                               }
+                               continue;
+                       }
                        if (starts_with(arg, "--since=")) {
                                show_datestring("--max-age=", arg+8);
                                continue;
index 960634dd0c52f1da689e8a54980e5e81cf2d2f36..bc6490b8bca554c568973997568543e206ffdb3e 100644 (file)
@@ -278,7 +278,7 @@ static struct option builtin_rm_options[] = {
 
 int cmd_rm(int argc, const char **argv, const char *prefix)
 {
-       int i, newfd;
+       int i;
        struct pathspec pathspec;
        char *seen;
 
@@ -293,7 +293,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
        if (!index_only)
                setup_work_tree();
 
-       newfd = hold_locked_index(&lock_file, 1);
+       hold_locked_index(&lock_file, 1);
 
        if (read_cache() < 0)
                die(_("index file corrupt"));
@@ -427,8 +427,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
        }
 
        if (active_cache_changed) {
-               if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_locked_index(&lock_file))
+               if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                        die(_("Unable to write new index file"));
        }
 
index d87317290c0bbfffb7ac66fb7cff5c608f092d93..5fd4e4e48839a9dc2998d3fba0e48008b902b12a 100644 (file)
@@ -755,7 +755,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                }
 
                for (i = 0; i < reflog; i++) {
-                       char *logmsg, *m;
+                       char *logmsg;
                        const char *msg;
                        unsigned long timestamp;
                        int tz;
@@ -770,11 +770,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                                msg = "(none)";
                        else
                                msg++;
-                       m = xmalloc(strlen(msg) + 200);
-                       sprintf(m, "(%s) %s",
-                               show_date(timestamp, tz, 1),
-                               msg);
-                       reflog_msg[i] = m;
+                       reflog_msg[i] = xstrfmt("(%s) %s",
+                                               show_date(timestamp, tz, 1),
+                                               msg);
                        free(logmsg);
                        sprintf(nth_desc, "%s@{%d}", *av, base+i);
                        append_ref(nth_desc, sha1, 1);
index c6e8a7112700c03b2615f9c99c9858884128ab25..ef765563388c384a127d27eaf7ee6fd1badfe84f 100644 (file)
@@ -83,7 +83,7 @@ static int in_commit_list(const struct commit_list *want, struct commit *c)
 enum contains_result {
        CONTAINS_UNKNOWN = -1,
        CONTAINS_NO = 0,
-       CONTAINS_YES = 1,
+       CONTAINS_YES = 1
 };
 
 /*
index ebea285e1b6863bdb8e4b2a30d1487d18ecc5084..e8c7fd4d4957b83de42e7af2ef9f2676bfc294b2 100644 (file)
@@ -13,6 +13,7 @@
 #include "parse-options.h"
 #include "pathspec.h"
 #include "dir.h"
+#include "split-index.h"
 
 /*
  * Default to not allowing changes to the list of files. The
@@ -55,8 +56,9 @@ static int mark_ce_flags(const char *path, int flag, int mark)
                        active_cache[pos]->ce_flags |= flag;
                else
                        active_cache[pos]->ce_flags &= ~flag;
-               cache_tree_invalidate_path(active_cache_tree, path);
-               active_cache_changed = 1;
+               active_cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
+               cache_tree_invalidate_path(&the_index, path);
+               active_cache_changed |= CE_ENTRY_CHANGED;
                return 0;
        }
        return -1;
@@ -267,8 +269,9 @@ static void chmod_path(int flip, const char *path)
        default:
                goto fail;
        }
-       cache_tree_invalidate_path(active_cache_tree, path);
-       active_cache_changed = 1;
+       cache_tree_invalidate_path(&the_index, path);
+       ce->ce_flags |= CE_UPDATE_IN_BASE;
+       active_cache_changed |= CE_ENTRY_CHANGED;
        report("chmod %cx '%s'", flip, path);
        return;
  fail:
@@ -743,6 +746,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        char set_executable_bit = 0;
        struct refresh_params refresh_args = {0, &has_errors};
        int lock_error = 0;
+       int split_index = -1;
        struct lock_file *lock_file;
        struct parse_opt_ctx_t ctx;
        int parseopt_state = PARSE_OPT_UNKNOWN;
@@ -825,6 +829,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                        resolve_undo_clear_callback},
                OPT_INTEGER(0, "index-version", &preferred_index_format,
                        N_("write index in this format")),
+               OPT_BOOL(0, "split-index", &split_index,
+                       N_("enable or disable split index")),
                OPT_END()
        };
 
@@ -892,7 +898,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                            INDEX_FORMAT_LB, INDEX_FORMAT_UB);
 
                if (the_index.version != preferred_index_format)
-                       active_cache_changed = 1;
+                       active_cache_changed |= SOMETHING_CHANGED;
                the_index.version = preferred_index_format;
        }
 
@@ -918,14 +924,27 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                strbuf_release(&buf);
        }
 
+       if (split_index > 0) {
+               init_split_index(&the_index);
+               the_index.cache_changed |= SPLIT_INDEX_ORDERED;
+       } else if (!split_index && the_index.split_index) {
+               /*
+                * can't discard_split_index(&the_index); because that
+                * will destroy split_index->base->cache[], which may
+                * be shared with the_index.cache[]. So yeah we're
+                * leaking a bit here.
+                */
+               the_index.split_index = NULL;
+               the_index.cache_changed |= SOMETHING_CHANGED;
+       }
+
        if (active_cache_changed) {
                if (newfd < 0) {
                        if (refresh_args.flags & REFRESH_QUIET)
                                exit(128);
                        unable_to_lock_index_die(get_index_file(), lock_error);
                }
-               if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_locked_index(lock_file))
+               if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                        die("Unable to write new index file");
        }
 
diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c
new file mode 100644 (file)
index 0000000..b0f8504
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Builtin "git commit-commit"
+ *
+ * Copyright (c) 2014 Michael J Gruber <git@drmicha.warpmail.net>
+ *
+ * Based on git-verify-tag
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "commit.h"
+#include "run-command.h"
+#include <signal.h>
+#include "parse-options.h"
+#include "gpg-interface.h"
+
+static const char * const verify_commit_usage[] = {
+               N_("git verify-commit [-v|--verbose] <commit>..."),
+               NULL
+};
+
+static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned long size, int verbose)
+{
+       struct signature_check signature_check;
+
+       memset(&signature_check, 0, sizeof(signature_check));
+
+       check_commit_signature(lookup_commit(sha1), &signature_check);
+
+       if (verbose && signature_check.payload)
+               fputs(signature_check.payload, stdout);
+
+       if (signature_check.gpg_output)
+               fputs(signature_check.gpg_output, stderr);
+
+       signature_check_clear(&signature_check);
+       return signature_check.result != 'G';
+}
+
+static int verify_commit(const char *name, int verbose)
+{
+       enum object_type type;
+       unsigned char sha1[20];
+       char *buf;
+       unsigned long size;
+       int ret;
+
+       if (get_sha1(name, sha1))
+               return error("commit '%s' not found.", name);
+
+       buf = read_sha1_file(sha1, &type, &size);
+       if (!buf)
+               return error("%s: unable to read file.", name);
+       if (type != OBJ_COMMIT)
+               return error("%s: cannot verify a non-commit object of type %s.",
+                               name, typename(type));
+
+       ret = run_gpg_verify(sha1, buf, size, verbose);
+
+       free(buf);
+       return ret;
+}
+
+static int git_verify_commit_config(const char *var, const char *value, void *cb)
+{
+       int status = git_gpg_config(var, value, cb);
+       if (status)
+               return status;
+       return git_default_config(var, value, cb);
+}
+
+int cmd_verify_commit(int argc, const char **argv, const char *prefix)
+{
+       int i = 1, verbose = 0, had_error = 0;
+       const struct option verify_commit_options[] = {
+               OPT__VERBOSE(&verbose, N_("print commit contents")),
+               OPT_END()
+       };
+
+       git_config(git_verify_commit_config, NULL);
+
+       argc = parse_options(argc, argv, prefix, verify_commit_options,
+                            verify_commit_usage, PARSE_OPT_KEEP_ARGV0);
+       if (argc <= i)
+               usage_with_options(verify_commit_usage, verify_commit_options);
+
+       /* sometimes the program was terminated because this signal
+        * was received in the process of writing the gpg input: */
+       signal(SIGPIPE, SIG_IGN);
+       while (i < argc)
+               if (verify_commit(argv[i++], verbose))
+                       had_error = 1;
+       return had_error;
+}
index 7fa524a11323621d24c352b0a1108ae953a60cf5..c53f7de2b13acfc1b97c60b04552e57791377442 100644 (file)
@@ -98,7 +98,7 @@ struct cache_tree_sub *cache_tree_sub(struct cache_tree *it, const char *path)
        return find_subtree(it, path, pathlen, 1);
 }
 
-void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
+static int do_invalidate_path(struct cache_tree *it, const char *path)
 {
        /* a/b/c
         * ==> invalidate self
@@ -116,7 +116,7 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
 #endif
 
        if (!it)
-               return;
+               return 0;
        slash = strchrnul(path, '/');
        namelen = slash - path;
        it->entry_count = -1;
@@ -137,14 +137,21 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
                                (it->subtree_nr - pos - 1));
                        it->subtree_nr--;
                }
-               return;
+               return 1;
        }
        down = find_subtree(it, path, namelen, 0);
        if (down)
-               cache_tree_invalidate_path(down->cache_tree, slash + 1);
+               do_invalidate_path(down->cache_tree, slash + 1);
+       return 1;
 }
 
-static int verify_cache(const struct cache_entry * const *cache,
+void cache_tree_invalidate_path(struct index_state *istate, const char *path)
+{
+       if (do_invalidate_path(istate->cache_tree, path))
+               istate->cache_changed |= CACHE_TREE_CHANGED;
+}
+
+static int verify_cache(struct cache_entry **cache,
                        int entries, int flags)
 {
        int i, funny;
@@ -229,7 +236,7 @@ int cache_tree_fully_valid(struct cache_tree *it)
 }
 
 static int update_one(struct cache_tree *it,
-                     const struct cache_entry * const *cache,
+                     struct cache_entry **cache,
                      int entries,
                      const char *base,
                      int baselen,
@@ -391,18 +398,19 @@ static int update_one(struct cache_tree *it,
        return i;
 }
 
-int cache_tree_update(struct cache_tree *it,
-                     const struct cache_entry * const *cache,
-                     int entries,
-                     int flags)
+int cache_tree_update(struct index_state *istate, int flags)
 {
-       int i, skip;
-       i = verify_cache(cache, entries, flags);
+       struct cache_tree *it = istate->cache_tree;
+       struct cache_entry **cache = istate->cache;
+       int entries = istate->cache_nr;
+       int skip, i = verify_cache(cache, entries, flags);
+
        if (i)
                return i;
        i = update_one(it, cache, entries, "", 0, &skip, flags);
        if (i < 0)
                return i;
+       istate->cache_changed |= CACHE_TREE_CHANGED;
        return 0;
 }
 
@@ -590,13 +598,10 @@ int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
 
        was_valid = cache_tree_fully_valid(active_cache_tree);
        if (!was_valid) {
-               if (cache_tree_update(active_cache_tree,
-                                     (const struct cache_entry * const *)active_cache,
-                                     active_nr, flags) < 0)
+               if (cache_tree_update(&the_index, flags) < 0)
                        return WRITE_TREE_UNMERGED_INDEX;
                if (0 <= newfd) {
-                       if (!write_cache(newfd, active_cache, active_nr) &&
-                           !commit_lock_file(lock_file))
+                       if (!write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                                newfd = -1;
                }
                /* Not being able to write is fine -- we are only interested
@@ -649,11 +654,12 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
        it->entry_count = cnt;
 }
 
-void prime_cache_tree(struct cache_tree **it, struct tree *tree)
+void prime_cache_tree(struct index_state *istate, struct tree *tree)
 {
-       cache_tree_free(it);
-       *it = cache_tree();
-       prime_cache_tree_rec(*it, tree);
+       cache_tree_free(&istate->cache_tree);
+       istate->cache_tree = cache_tree();
+       prime_cache_tree_rec(istate->cache_tree, tree);
+       istate->cache_changed |= CACHE_TREE_CHANGED;
 }
 
 /*
@@ -692,7 +698,5 @@ int update_main_cache_tree(int flags)
 {
        if (!the_index.cache_tree)
                the_index.cache_tree = cache_tree();
-       return cache_tree_update(the_index.cache_tree,
-                                (const struct cache_entry * const *)the_index.cache,
-                                the_index.cache_nr, flags);
+       return cache_tree_update(&the_index, flags);
 }
index f1923ad1e9ddd3ad9b77a436e66ad083843094a0..b47ccec7f626d7cda34ed17661c1dc3e4f035d6a 100644 (file)
@@ -23,14 +23,14 @@ struct cache_tree {
 
 struct cache_tree *cache_tree(void);
 void cache_tree_free(struct cache_tree **);
-void cache_tree_invalidate_path(struct cache_tree *, const char *);
+void cache_tree_invalidate_path(struct index_state *, const char *);
 struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const char *);
 
 void cache_tree_write(struct strbuf *, struct cache_tree *root);
 struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
 
 int cache_tree_fully_valid(struct cache_tree *);
-int cache_tree_update(struct cache_tree *, const struct cache_entry * const *, int, int);
+int cache_tree_update(struct index_state *, int);
 
 int update_main_cache_tree(int);
 
@@ -46,7 +46,7 @@ int update_main_cache_tree(int);
 #define WRITE_TREE_PREFIX_ERROR (-3)
 
 int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix);
-void prime_cache_tree(struct cache_tree **, struct tree *);
+void prime_cache_tree(struct index_state *, struct tree *);
 
 extern int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info);
 
diff --git a/cache.h b/cache.h
index df65231ac3db7ae08b5d9cfd37c505c4573c1675..6db3ae1b4343181b86e653e252d65a9435a4b654 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -150,6 +150,7 @@ struct cache_entry {
        unsigned int ce_mode;
        unsigned int ce_flags;
        unsigned int ce_namelen;
+       unsigned int index;     /* for link extension */
        unsigned char sha1[20];
        char name[FLEX_ARRAY]; /* more */
 };
@@ -160,7 +161,7 @@ struct cache_entry {
 #define CE_STAGESHIFT 12
 
 /*
- * Range 0xFFFF0000 in ce_flags is divided into
+ * Range 0xFFFF0FFF in ce_flags is divided into
  * two parts: in-memory flags and on-disk ones.
  * Flags in CE_EXTENDED_FLAGS will get saved on-disk
  * if you want to save a new flag, add it in
@@ -183,6 +184,9 @@ struct cache_entry {
 /* used to temporarily mark paths matched by pathspecs */
 #define CE_MATCHED           (1 << 26)
 
+#define CE_UPDATE_IN_BASE    (1 << 27)
+#define CE_STRIP_NAME        (1 << 28)
+
 /*
  * Extended on-disk flags
  */
@@ -283,12 +287,22 @@ static inline unsigned int canon_mode(unsigned int mode)
 
 #define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
 
+#define SOMETHING_CHANGED      (1 << 0) /* unclassified changes go here */
+#define CE_ENTRY_CHANGED       (1 << 1)
+#define CE_ENTRY_REMOVED       (1 << 2)
+#define CE_ENTRY_ADDED         (1 << 3)
+#define RESOLVE_UNDO_CHANGED   (1 << 4)
+#define CACHE_TREE_CHANGED     (1 << 5)
+#define SPLIT_INDEX_ORDERED    (1 << 6)
+
+struct split_index;
 struct index_state {
        struct cache_entry **cache;
        unsigned int version;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
+       struct split_index *split_index;
        struct cache_time timestamp;
        unsigned name_hash_initialized : 1,
                 initialized : 1;
@@ -317,7 +331,6 @@ extern void free_name_hash(struct index_state *istate);
 #define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
 #define is_cache_unborn() is_index_unborn(&the_index)
 #define read_cache_unmerged() read_index_unmerged(&the_index)
-#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
 #define discard_cache() discard_index(&the_index)
 #define unmerged_cache() unmerged_index(&the_index)
 #define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
@@ -472,12 +485,17 @@ extern int daemonize(void);
        } while (0)
 
 /* Initialize and use the cache information */
+struct lock_file;
 extern int read_index(struct index_state *);
 extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
+extern int do_read_index(struct index_state *istate, const char *path,
+                        int must_exist); /* for testting only! */
 extern int read_index_from(struct index_state *, const char *path);
 extern int is_index_unborn(struct index_state *);
 extern int read_index_unmerged(struct index_state *);
-extern int write_index(struct index_state *, int newfd);
+#define COMMIT_LOCK            (1 << 0)
+#define CLOSE_LOCK             (1 << 1)
+extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
 extern int discard_index(struct index_state *);
 extern int unmerged_index(const struct index_state *);
 extern int verify_path(const char *path);
@@ -489,6 +507,7 @@ extern int index_name_pos(const struct index_state *, const char *name, int name
 #define ADD_CACHE_SKIP_DFCHECK 4       /* Ok to skip DF conflict checks */
 #define ADD_CACHE_JUST_APPEND 8                /* Append only; tree.c::read_tree() */
 #define ADD_CACHE_NEW_ONLY 16          /* Do not replace existing ones */
+#define ADD_CACHE_KEEP_CACHE_TREE 32   /* Do not invalidate cache-tree */
 extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
 extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
 extern int remove_index_entry_at(struct index_state *, int pos);
@@ -566,7 +585,6 @@ extern int commit_lock_file(struct lock_file *);
 extern void update_index_if_able(struct index_state *, struct lock_file *);
 
 extern int hold_locked_index(struct lock_file *, int);
-extern int commit_locked_index(struct lock_file *);
 extern void set_alternate_index_output(const char *);
 extern int close_lock_file(struct lock_file *);
 extern void rollback_lock_file(struct lock_file *);
@@ -1078,6 +1096,7 @@ const char *show_ident_date(const struct ident_split *id, enum date_mode mode);
 extern int ident_cmp(const struct ident_split *, const struct ident_split *);
 
 struct checkout {
+       struct index_state *istate;
        const char *base_dir;
        int base_dir_len;
        unsigned force:1,
@@ -1090,12 +1109,16 @@ struct checkout {
 extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
 
 struct cache_def {
-       char path[PATH_MAX + 1];
-       int len;
+       struct strbuf path;
        int flags;
        int track_flags;
        int prefix_len_stat_func;
 };
+#define CACHE_DEF_INIT { STRBUF_INIT, 0, 0, 0 }
+static inline void cache_def_free(struct cache_def *cache)
+{
+       strbuf_release(&cache->path);
+}
 
 extern int has_symlink_leading_path(const char *name, int len);
 extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
index 1a468debb4abdda3cbb90fcc4972b3d40da6f2ed..ca878bcea7a42476a7c2b03e74897a2af87bec3f 100644 (file)
--- a/column.c
+++ b/column.c
@@ -336,8 +336,9 @@ static int column_config(const char *var, const char *value,
 int git_column_config(const char *var, const char *value,
                      const char *command, unsigned int *colopts)
 {
-       const char *it = skip_prefix(var, "column.");
-       if (!it)
+       const char *it;
+
+       if (!skip_prefix(var, "column.", &it))
                return 0;
 
        if (!strcmp(it, "ui"))
index cf36c3d71e3b8fc3382162ef939f3ed1f7d20775..a3ff0c9e60148e6dc98a6bd0d71d98cba4a7eae2 100644 (file)
@@ -132,6 +132,7 @@ git-update-server-info                  synchingrepositories
 git-upload-archive                      synchelpers
 git-upload-pack                         synchelpers
 git-var                                 plumbinginterrogators
+git-verify-commit                       ancillaryinterrogators
 git-verify-pack                         plumbinginterrogators
 git-verify-tag                          ancillaryinterrogators
 gitweb                                  ancillaryinterrogators
index cc114b53b096c6fc8574629b86659a639fdfed61..375c9c751adbf165c757832667102b761e1358c3 100644 (file)
@@ -117,4 +117,16 @@ static int stat_ ##slabname## realloc
  * catch because GCC silently parses it by default.
  */
 
+/*
+ * Statically initialize a commit slab named "var". Note that this
+ * evaluates "stride" multiple times! Example:
+ *
+ *   struct indegree indegrees = COMMIT_SLAB_INIT(1, indegrees);
+ *
+ */
+#define COMMIT_SLAB_INIT(stride, var) { \
+       COMMIT_SLAB_SIZE / sizeof(**((var).slab)) / (stride), \
+       (stride), 0, NULL \
+}
+
 #endif /* COMMIT_SLAB_H */
index 881be3baa3ccc70afab916331588afb9330258ed..acb74b55d4ee72ddf626754535a9febeb05ba945 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -17,7 +17,6 @@ static struct commit_extra_header *read_commit_extra_header_lines(const char *bu
 int save_commit_buffer = 1;
 
 const char *commit_type = "commit";
-static int commit_count;
 
 static struct commit *check_commit(struct object *obj,
                                   const unsigned char *sha1,
@@ -64,7 +63,6 @@ struct commit *lookup_commit(const unsigned char *sha1)
        struct object *obj = lookup_object(sha1);
        if (!obj) {
                struct commit *c = alloc_commit_node();
-               c->index = commit_count++;
                return create_object(sha1, OBJ_COMMIT, c);
        }
        if (!obj->type)
@@ -247,6 +245,76 @@ int unregister_shallow(const unsigned char *sha1)
        return 0;
 }
 
+struct commit_buffer {
+       void *buffer;
+       unsigned long size;
+};
+define_commit_slab(buffer_slab, struct commit_buffer);
+static struct buffer_slab buffer_slab = COMMIT_SLAB_INIT(1, buffer_slab);
+
+void set_commit_buffer(struct commit *commit, void *buffer, unsigned long size)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       v->buffer = buffer;
+       v->size = size;
+}
+
+const void *get_cached_commit_buffer(const struct commit *commit, unsigned long *sizep)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       if (sizep)
+               *sizep = v->size;
+       return v->buffer;
+}
+
+const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
+{
+       const void *ret = get_cached_commit_buffer(commit, sizep);
+       if (!ret) {
+               enum object_type type;
+               unsigned long size;
+               ret = read_sha1_file(commit->object.sha1, &type, &size);
+               if (!ret)
+                       die("cannot read commit object %s",
+                           sha1_to_hex(commit->object.sha1));
+               if (type != OBJ_COMMIT)
+                       die("expected commit for %s, got %s",
+                           sha1_to_hex(commit->object.sha1), typename(type));
+               if (sizep)
+                       *sizep = size;
+       }
+       return ret;
+}
+
+void unuse_commit_buffer(const struct commit *commit, const void *buffer)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       if (v->buffer != buffer)
+               free((void *)buffer);
+}
+
+void free_commit_buffer(struct commit *commit)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       free(v->buffer);
+       v->buffer = NULL;
+       v->size = 0;
+}
+
+const void *detach_commit_buffer(struct commit *commit, unsigned long *sizep)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       void *ret;
+
+       ret = v->buffer;
+       if (sizep)
+               *sizep = v->size;
+
+       v->buffer = NULL;
+       v->size = 0;
+       return ret;
+}
+
 int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size)
 {
        const char *tail = buffer;
@@ -324,7 +392,7 @@ int parse_commit(struct commit *item)
        }
        ret = parse_commit_buffer(item, buffer, size);
        if (save_commit_buffer && !ret) {
-               item->buffer = buffer;
+               set_commit_buffer(item, buffer, size);
                return 0;
        }
        free(buffer);
@@ -539,25 +607,14 @@ static void record_author_date(struct author_date_slab *author_date,
                               struct commit *commit)
 {
        const char *buf, *line_end, *ident_line;
-       char *buffer = NULL;
+       const char *buffer = get_commit_buffer(commit, NULL);
        struct ident_split ident;
        char *date_end;
        unsigned long date;
 
-       if (!commit->buffer) {
-               unsigned long size;
-               enum object_type type;
-               buffer = read_sha1_file(commit->object.sha1, &type, &size);
-               if (!buffer)
-                       return;
-       }
-
-       for (buf = commit->buffer ? commit->buffer : buffer;
-            buf;
-            buf = line_end + 1) {
+       for (buf = buffer; buf; buf = line_end + 1) {
                line_end = strchrnul(buf, '\n');
-               ident_line = skip_prefix(buf, "author ");
-               if (!ident_line) {
+               if (!skip_prefix(buf, "author ", &ident_line)) {
                        if (!line_end[0] || line_end[1] == '\n')
                                return; /* end of header */
                        continue;
@@ -575,7 +632,7 @@ static void record_author_date(struct author_date_slab *author_date,
        *(author_date_slab_at(author_date, commit)) = date;
 
 fail_exit:
-       free(buffer);
+       unuse_commit_buffer(commit, buffer);
 }
 
 static int compare_commits_by_author_date(const void *a_, const void *b_,
@@ -1080,17 +1137,14 @@ static int do_sign_commit(struct strbuf *buf, const char *keyid)
        return 0;
 }
 
-int parse_signed_commit(const unsigned char *sha1,
+int parse_signed_commit(const struct commit *commit,
                        struct strbuf *payload, struct strbuf *signature)
 {
+
        unsigned long size;
-       enum object_type type;
-       char *buffer = read_sha1_file(sha1, &type, &size);
+       const char *buffer = get_commit_buffer(commit, &size);
        int in_signature, saw_signature = -1;
-       char *line, *tail;
-
-       if (!buffer || type != OBJ_COMMIT)
-               goto cleanup;
+       const char *line, *tail;
 
        line = buffer;
        tail = buffer + size;
@@ -1098,7 +1152,7 @@ int parse_signed_commit(const unsigned char *sha1,
        saw_signature = 0;
        while (line < tail) {
                const char *sig = NULL;
-               char *next = memchr(line, '\n', tail - line);
+               const char *next = memchr(line, '\n', tail - line);
 
                next = next ? next + 1 : tail;
                if (in_signature && line[0] == ' ')
@@ -1119,8 +1173,7 @@ int parse_signed_commit(const unsigned char *sha1,
                }
                line = next;
        }
- cleanup:
-       free(buffer);
+       unuse_commit_buffer(commit, buffer);
        return saw_signature;
 }
 
@@ -1183,8 +1236,7 @@ static void parse_gpg_output(struct signature_check *sigc)
        for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
                const char *found, *next;
 
-               found = skip_prefix(buf, sigcheck_gpg_status[i].check + 1);
-               if (!found) {
+               if (!skip_prefix(buf, sigcheck_gpg_status[i].check + 1, &found)) {
                        found = strstr(buf, sigcheck_gpg_status[i].check);
                        if (!found)
                                continue;
@@ -1211,14 +1263,14 @@ void check_commit_signature(const struct commit* commit, struct signature_check
 
        sigc->result = 'N';
 
-       if (parse_signed_commit(commit->object.sha1,
-                               &payload, &signature) <= 0)
+       if (parse_signed_commit(commit, &payload, &signature) <= 0)
                goto out;
        status = verify_signed_buffer(payload.buf, payload.len,
                                      signature.buf, signature.len,
                                      &gpg_output, &gpg_status);
        if (status && !gpg_output.len)
                goto out;
+       sigc->payload = strbuf_detach(&payload, NULL);
        sigc->gpg_output = strbuf_detach(&gpg_output, NULL);
        sigc->gpg_status = strbuf_detach(&gpg_status, NULL);
        parse_gpg_output(sigc);
@@ -1257,11 +1309,9 @@ struct commit_extra_header *read_commit_extra_headers(struct commit *commit,
 {
        struct commit_extra_header *extra = NULL;
        unsigned long size;
-       enum object_type type;
-       char *buffer = read_sha1_file(commit->object.sha1, &type, &size);
-       if (buffer && type == OBJ_COMMIT)
-               extra = read_commit_extra_header_lines(buffer, size, exclude);
-       free(buffer);
+       const char *buffer = get_commit_buffer(commit, &size);
+       extra = read_commit_extra_header_lines(buffer, size, exclude);
+       unuse_commit_buffer(commit, buffer);
        return extra;
 }
 
@@ -1344,7 +1394,8 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
        }
 }
 
-int commit_tree(const struct strbuf *msg, const unsigned char *tree,
+int commit_tree(const char *msg, size_t msg_len,
+               const unsigned char *tree,
                struct commit_list *parents, unsigned char *ret,
                const char *author, const char *sign_commit)
 {
@@ -1352,7 +1403,7 @@ int commit_tree(const struct strbuf *msg, const unsigned char *tree,
        int result;
 
        append_merge_tag_headers(parents, &tail);
-       result = commit_tree_extended(msg, tree, parents, ret,
+       result = commit_tree_extended(msg, msg_len, tree, parents, ret,
                                      author, sign_commit, extra);
        free_commit_extra_headers(extra);
        return result;
@@ -1473,7 +1524,8 @@ static const char commit_utf8_warn[] =
 "You may want to amend it after fixing the message, or set the config\n"
 "variable i18n.commitencoding to the encoding your project uses.\n";
 
-int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
+int commit_tree_extended(const char *msg, size_t msg_len,
+                        const unsigned char *tree,
                         struct commit_list *parents, unsigned char *ret,
                         const char *author, const char *sign_commit,
                         struct commit_extra_header *extra)
@@ -1484,7 +1536,7 @@ int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
 
        assert_sha1_type(tree, OBJ_TREE);
 
-       if (memchr(msg->buf, '\0', msg->len))
+       if (memchr(msg, '\0', msg_len))
                return error("a NUL byte in commit log message not allowed.");
 
        /* Not having i18n.commitencoding is the same as having utf-8 */
@@ -1523,7 +1575,7 @@ int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
        strbuf_addch(&buffer, '\n');
 
        /* And add the comment */
-       strbuf_addbuf(&buffer, msg);
+       strbuf_add(&buffer, msg, msg_len);
 
        /* And check the encoding */
        if (encoding_is_utf8 && !verify_utf8(&buffer))
index a9f177ba488a7002409eb2cdee1f7e9c3673529f..2e1492a6e4d1c5fc5bea3a48180f128601c3461e 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -20,7 +20,6 @@ struct commit {
        unsigned long date;
        struct commit_list *parents;
        struct tree *tree;
-       char *buffer;
 };
 
 extern int save_commit_buffer;
@@ -51,6 +50,44 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
 int parse_commit(struct commit *item);
 void parse_commit_or_die(struct commit *item);
 
+/*
+ * Associate an object buffer with the commit. The ownership of the
+ * memory is handed over to the commit, and must be free()-able.
+ */
+void set_commit_buffer(struct commit *, void *buffer, unsigned long size);
+
+/*
+ * Get any cached object buffer associated with the commit. Returns NULL
+ * if none. The resulting memory should not be freed.
+ */
+const void *get_cached_commit_buffer(const struct commit *, unsigned long *size);
+
+/*
+ * Get the commit's object contents, either from cache or by reading the object
+ * from disk. The resulting memory should not be modified, and must be given
+ * to unuse_commit_buffer when the caller is done.
+ */
+const void *get_commit_buffer(const struct commit *, unsigned long *size);
+
+/*
+ * Tell the commit subsytem that we are done with a particular commit buffer.
+ * The commit and buffer should be the input and return value, respectively,
+ * from an earlier call to get_commit_buffer.  The buffer may or may not be
+ * freed by this call; callers should not access the memory afterwards.
+ */
+void unuse_commit_buffer(const struct commit *, const void *buffer);
+
+/*
+ * Free any cached object buffer associated with the commit.
+ */
+void free_commit_buffer(struct commit *);
+
+/*
+ * Disassociate any cached object buffer from the commit, but do not free it.
+ * The buffer (or NULL, if none) is returned.
+ */
+const void *detach_commit_buffer(struct commit *, unsigned long *sizep);
+
 /* Find beginning and length of commit subject. */
 int find_commit_subject(const char *commit_buffer, const char **subject);
 
@@ -115,10 +152,9 @@ struct userformat_want {
 
 extern int has_non_ascii(const char *text);
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
-extern char *logmsg_reencode(const struct commit *commit,
-                            char **commit_encoding,
-                            const char *output_encoding);
-extern void logmsg_free(char *msg, const struct commit *commit);
+extern const char *logmsg_reencode(const struct commit *commit,
+                                  char **commit_encoding,
+                                  const char *output_encoding);
 extern void get_commit_format(const char *arg, struct rev_info *);
 extern const char *format_subject(struct strbuf *sb, const char *msg,
                                  const char *line_separator);
@@ -261,11 +297,13 @@ struct commit_extra_header {
 extern void append_merge_tag_headers(struct commit_list *parents,
                                     struct commit_extra_header ***tail);
 
-extern int commit_tree(const struct strbuf *msg, const unsigned char *tree,
+extern int commit_tree(const char *msg, size_t msg_len,
+                      const unsigned char *tree,
                       struct commit_list *parents, unsigned char *ret,
                       const char *author, const char *sign_commit);
 
-extern int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
+extern int commit_tree_extended(const char *msg, size_t msg_len,
+                               const unsigned char *tree,
                                struct commit_list *parents, unsigned char *ret,
                                const char *author, const char *sign_commit,
                                struct commit_extra_header *);
@@ -287,7 +325,7 @@ struct merge_remote_desc {
  */
 struct commit *get_merge_parent(const char *name);
 
-extern int parse_signed_commit(const unsigned char *sha1,
+extern int parse_signed_commit(const struct commit *commit,
                               struct strbuf *message, struct strbuf *signature);
 extern void print_commit_list(struct commit_list *list,
                              const char *format_cur,
index a0e13bc862c052d8c18a6cba1c47a5a15fd12f7b..3baaa4dfaedd1e451f810a6bead28b2f7e9aa95d 100644 (file)
@@ -441,7 +441,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
 static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
 {
        int namelen;
-       static char alt_name[PATH_MAX];
+       char alt_name[PATH_MAX];
 
        if (!do_lstat(follow, file_name, buf))
                return 0;
@@ -831,9 +831,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
                              const char *dir,
                              int prepend_cmd, int fhin, int fhout, int fherr)
 {
-       STARTUPINFO si;
+       STARTUPINFOW si;
        PROCESS_INFORMATION pi;
        struct strbuf envblk, args;
+       wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs;
        unsigned flags;
        BOOL ret;
 
@@ -865,9 +866,14 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
        memset(&si, 0, sizeof(si));
        si.cb = sizeof(si);
        si.dwFlags = STARTF_USESTDHANDLES;
-       si.hStdInput = (HANDLE) _get_osfhandle(fhin);
-       si.hStdOutput = (HANDLE) _get_osfhandle(fhout);
-       si.hStdError = (HANDLE) _get_osfhandle(fherr);
+       si.hStdInput = winansi_get_osfhandle(fhin);
+       si.hStdOutput = winansi_get_osfhandle(fhout);
+       si.hStdError = winansi_get_osfhandle(fherr);
+
+       if (xutftowcs_path(wcmd, cmd) < 0)
+               return -1;
+       if (dir && xutftowcs_path(wdir, dir) < 0)
+               return -1;
 
        /* concatenate argv, quoting args as we go */
        strbuf_init(&args, 0);
@@ -886,6 +892,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
                        free(quoted);
        }
 
+       wargs = xmalloc((2 * args.len + 1) * sizeof(wchar_t));
+       xutftowcs(wargs, args.buf, 2 * args.len + 1);
+       strbuf_release(&args);
+
        if (env) {
                int count = 0;
                char **e, **sorted_env;
@@ -907,12 +917,12 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
        }
 
        memset(&pi, 0, sizeof(pi));
-       ret = CreateProcess(cmd, args.buf, NULL, NULL, TRUE, flags,
-               env ? envblk.buf : NULL, dir, &si, &pi);
+       ret = CreateProcessW(wcmd, wargs, NULL, NULL, TRUE, flags,
+               env ? envblk.buf : NULL, dir ? wdir : NULL, &si, &pi);
 
        if (env)
                strbuf_release(&envblk);
-       strbuf_release(&args);
+       free(wargs);
 
        if (!ret) {
                errno = ENOENT;
@@ -941,10 +951,9 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
        return (pid_t)pi.dwProcessId;
 }
 
-static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
-                          int prepend_cmd)
+static pid_t mingw_spawnv(const char *cmd, const char **argv, int prepend_cmd)
 {
-       return mingw_spawnve_fd(cmd, argv, env, NULL, prepend_cmd, 0, 1, 2);
+       return mingw_spawnve_fd(cmd, argv, environ, NULL, prepend_cmd, 0, 1, 2);
 }
 
 pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
@@ -986,7 +995,7 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
        return pid;
 }
 
-static int try_shell_exec(const char *cmd, char *const *argv, char **env)
+static int try_shell_exec(const char *cmd, char *const *argv)
 {
        const char *interpr = parse_interpreter(cmd);
        char **path;
@@ -1004,7 +1013,7 @@ static int try_shell_exec(const char *cmd, char *const *argv, char **env)
                argv2 = xmalloc(sizeof(*argv) * (argc+1));
                argv2[0] = (char *)cmd; /* full path to the script file */
                memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
-               pid = mingw_spawnve(prog, argv2, env, 1);
+               pid = mingw_spawnv(prog, argv2, 1);
                if (pid >= 0) {
                        int status;
                        if (waitpid(pid, &status, 0) < 0)
@@ -1019,19 +1028,20 @@ static int try_shell_exec(const char *cmd, char *const *argv, char **env)
        return pid;
 }
 
-static void mingw_execve(const char *cmd, char *const *argv, char *const *env)
+int mingw_execv(const char *cmd, char *const *argv)
 {
        /* check if git_command is a shell script */
-       if (!try_shell_exec(cmd, argv, (char **)env)) {
+       if (!try_shell_exec(cmd, argv)) {
                int pid, status;
 
-               pid = mingw_spawnve(cmd, (const char **)argv, (char **)env, 0);
+               pid = mingw_spawnv(cmd, (const char **)argv, 0);
                if (pid < 0)
-                       return;
+                       return -1;
                if (waitpid(pid, &status, 0) < 0)
                        status = 255;
                exit(status);
        }
+       return -1;
 }
 
 int mingw_execvp(const char *cmd, char *const *argv)
@@ -1040,7 +1050,7 @@ int mingw_execvp(const char *cmd, char *const *argv)
        char *prog = path_lookup(cmd, path, 0);
 
        if (prog) {
-               mingw_execve(prog, argv, environ);
+               mingw_execv(prog, argv);
                free(prog);
        } else
                errno = ENOENT;
@@ -1049,12 +1059,6 @@ int mingw_execvp(const char *cmd, char *const *argv)
        return -1;
 }
 
-int mingw_execv(const char *cmd, char *const *argv)
-{
-       mingw_execve(cmd, argv, environ);
-       return -1;
-}
-
 int mingw_kill(pid_t pid, int sig)
 {
        if (pid > 0 && sig == SIGTERM) {
@@ -1847,3 +1851,150 @@ int mingw_offset_1st_component(const char *path)
 
        return offset + is_dir_sep(path[offset]);
 }
+
+int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)
+{
+       int upos = 0, wpos = 0;
+       const unsigned char *utf = (const unsigned char*) utfs;
+       if (!utf || !wcs || wcslen < 1) {
+               errno = EINVAL;
+               return -1;
+       }
+       /* reserve space for \0 */
+       wcslen--;
+       if (utflen < 0)
+               utflen = INT_MAX;
+
+       while (upos < utflen) {
+               int c = utf[upos++] & 0xff;
+               if (utflen == INT_MAX && c == 0)
+                       break;
+
+               if (wpos >= wcslen) {
+                       wcs[wpos] = 0;
+                       errno = ERANGE;
+                       return -1;
+               }
+
+               if (c < 0x80) {
+                       /* ASCII */
+                       wcs[wpos++] = c;
+               } else if (c >= 0xc2 && c < 0xe0 && upos < utflen &&
+                               (utf[upos] & 0xc0) == 0x80) {
+                       /* 2-byte utf-8 */
+                       c = ((c & 0x1f) << 6);
+                       c |= (utf[upos++] & 0x3f);
+                       wcs[wpos++] = c;
+               } else if (c >= 0xe0 && c < 0xf0 && upos + 1 < utflen &&
+                               !(c == 0xe0 && utf[upos] < 0xa0) && /* over-long encoding */
+                               (utf[upos] & 0xc0) == 0x80 &&
+                               (utf[upos + 1] & 0xc0) == 0x80) {
+                       /* 3-byte utf-8 */
+                       c = ((c & 0x0f) << 12);
+                       c |= ((utf[upos++] & 0x3f) << 6);
+                       c |= (utf[upos++] & 0x3f);
+                       wcs[wpos++] = c;
+               } else if (c >= 0xf0 && c < 0xf5 && upos + 2 < utflen &&
+                               wpos + 1 < wcslen &&
+                               !(c == 0xf0 && utf[upos] < 0x90) && /* over-long encoding */
+                               !(c == 0xf4 && utf[upos] >= 0x90) && /* > \u10ffff */
+                               (utf[upos] & 0xc0) == 0x80 &&
+                               (utf[upos + 1] & 0xc0) == 0x80 &&
+                               (utf[upos + 2] & 0xc0) == 0x80) {
+                       /* 4-byte utf-8: convert to \ud8xx \udcxx surrogate pair */
+                       c = ((c & 0x07) << 18);
+                       c |= ((utf[upos++] & 0x3f) << 12);
+                       c |= ((utf[upos++] & 0x3f) << 6);
+                       c |= (utf[upos++] & 0x3f);
+                       c -= 0x10000;
+                       wcs[wpos++] = 0xd800 | (c >> 10);
+                       wcs[wpos++] = 0xdc00 | (c & 0x3ff);
+               } else if (c >= 0xa0) {
+                       /* invalid utf-8 byte, printable unicode char: convert 1:1 */
+                       wcs[wpos++] = c;
+               } else {
+                       /* invalid utf-8 byte, non-printable unicode: convert to hex */
+                       static const char *hex = "0123456789abcdef";
+                       wcs[wpos++] = hex[c >> 4];
+                       if (wpos < wcslen)
+                               wcs[wpos++] = hex[c & 0x0f];
+               }
+       }
+       wcs[wpos] = 0;
+       return wpos;
+}
+
+int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
+{
+       if (!wcs || !utf || utflen < 1) {
+               errno = EINVAL;
+               return -1;
+       }
+       utflen = WideCharToMultiByte(CP_UTF8, 0, wcs, -1, utf, utflen, NULL, NULL);
+       if (utflen)
+               return utflen - 1;
+       errno = ERANGE;
+       return -1;
+}
+
+/*
+ * Disable MSVCRT command line wildcard expansion (__getmainargs called from
+ * mingw startup code, see init.c in mingw runtime).
+ */
+int _CRT_glob = 0;
+
+typedef struct {
+       int newmode;
+} _startupinfo;
+
+extern int __wgetmainargs(int *argc, wchar_t ***argv, wchar_t ***env, int glob,
+               _startupinfo *si);
+
+static NORETURN void die_startup()
+{
+       fputs("fatal: not enough memory for initialization", stderr);
+       exit(128);
+}
+
+void mingw_startup()
+{
+       int i, len, maxlen, argc;
+       char *buffer;
+       wchar_t **wenv, **wargv;
+       _startupinfo si;
+
+       /* get wide char arguments and environment */
+       si.newmode = 0;
+       if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)
+               die_startup();
+
+       /* determine size of argv and environ conversion buffer */
+       maxlen = wcslen(_wpgmptr);
+       for (i = 1; i < argc; i++)
+               maxlen = max(maxlen, wcslen(wargv[i]));
+
+       /* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */
+       maxlen = 3 * maxlen + 1;
+       buffer = xmalloc(maxlen);
+
+       /* convert command line arguments and environment to UTF-8 */
+       len = xwcstoutf(buffer, _wpgmptr, maxlen);
+       __argv[0] = xmemdupz(buffer, len);
+       for (i = 1; i < argc; i++) {
+               len = xwcstoutf(buffer, wargv[i], maxlen);
+               __argv[i] = xmemdupz(buffer, len);
+       }
+       free(buffer);
+
+       /* initialize critical section for waitpid pinfo_t list */
+       InitializeCriticalSection(&pinfo_cs);
+
+       /* set up default file mode and file modes for stdin/out/err */
+       _fmode = _O_BINARY;
+       _setmode(_fileno(stdin), _O_BINARY);
+       _setmode(_fileno(stdout), _O_BINARY);
+       _setmode(_fileno(stderr), _O_BINARY);
+
+       /* initialize Unicode console */
+       winansi_init();
+}
index 3eaf822e28e23e6e46e82a9bb7c9db1ed42d252c..7ff2376335dea7a092c626f74f52413be1c81e74 100644 (file)
@@ -35,6 +35,9 @@ typedef int socklen_t;
 #ifndef EWOULDBLOCK
 #define EWOULDBLOCK EAGAIN
 #endif
+#ifndef ELOOP
+#define ELOOP EMLINK
+#endif
 #define SHUT_WR SD_SEND
 
 #define SIGHUP 1
@@ -317,12 +320,8 @@ int mingw_raise(int sig);
  * ANSI emulation wrappers
  */
 
-int winansi_fputs(const char *str, FILE *stream);
-int winansi_printf(const char *format, ...) __attribute__((format (printf, 1, 2)));
-int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3)));
-#define fputs winansi_fputs
-#define printf(...) winansi_printf(__VA_ARGS__)
-#define fprintf(...) winansi_fprintf(__VA_ARGS__)
+void winansi_init(void);
+HANDLE winansi_get_osfhandle(int fd);
 
 /*
  * git specific compatibility
@@ -355,6 +354,110 @@ void mingw_open_html(const char *path);
 char **make_augmented_environ(const char *const *vars);
 void free_environ(char **env);
 
+/**
+ * Converts UTF-8 encoded string to UTF-16LE.
+ *
+ * To support repositories with legacy-encoded file names, invalid UTF-8 bytes
+ * 0xa0 - 0xff are converted to corresponding printable Unicode chars \u00a0 -
+ * \u00ff, and invalid UTF-8 bytes 0x80 - 0x9f (which would make non-printable
+ * Unicode) are converted to hex-code.
+ *
+ * Lead-bytes not followed by an appropriate number of trail-bytes, over-long
+ * encodings and 4-byte encodings > \u10ffff are detected as invalid UTF-8.
+ *
+ * Maximum space requirement for the target buffer is two wide chars per UTF-8
+ * char (((strlen(utf) * 2) + 1) [* sizeof(wchar_t)]).
+ *
+ * The maximum space is needed only if the entire input string consists of
+ * invalid UTF-8 bytes in range 0x80-0x9f, as per the following table:
+ *
+ *               |                   | UTF-8 | UTF-16 |
+ *   Code point  |  UTF-8 sequence   | bytes | words  | ratio
+ * --------------+-------------------+-------+--------+-------
+ * 000000-00007f | 0-7f              |   1   |   1    |  1
+ * 000080-0007ff | c2-df + 80-bf     |   2   |   1    |  0.5
+ * 000800-00ffff | e0-ef + 2 * 80-bf |   3   |   1    |  0.33
+ * 010000-10ffff | f0-f4 + 3 * 80-bf |   4   |  2 (a) |  0.5
+ * invalid       | 80-9f             |   1   |  2 (b) |  2
+ * invalid       | a0-ff             |   1   |   1    |  1
+ *
+ * (a) encoded as UTF-16 surrogate pair
+ * (b) encoded as two hex digits
+ *
+ * Note that, while the UTF-8 encoding scheme can be extended to 5-byte, 6-byte
+ * or even indefinite-byte sequences, the largest valid code point \u10ffff
+ * encodes as only 4 UTF-8 bytes.
+ *
+ * Parameters:
+ * wcs: wide char target buffer
+ * utf: string to convert
+ * wcslen: size of target buffer (in wchar_t's)
+ * utflen: size of string to convert, or -1 if 0-terminated
+ *
+ * Returns:
+ * length of converted string (_wcslen(wcs)), or -1 on failure
+ *
+ * Errors:
+ * EINVAL: one of the input parameters is invalid (e.g. NULL)
+ * ERANGE: the output buffer is too small
+ */
+int xutftowcsn(wchar_t *wcs, const char *utf, size_t wcslen, int utflen);
+
+/**
+ * Simplified variant of xutftowcsn, assumes input string is \0-terminated.
+ */
+static inline int xutftowcs(wchar_t *wcs, const char *utf, size_t wcslen)
+{
+       return xutftowcsn(wcs, utf, wcslen, -1);
+}
+
+/**
+ * Simplified file system specific variant of xutftowcsn, assumes output
+ * buffer size is MAX_PATH wide chars and input string is \0-terminated,
+ * fails with ENAMETOOLONG if input string is too long.
+ */
+static inline int xutftowcs_path(wchar_t *wcs, const char *utf)
+{
+       int result = xutftowcsn(wcs, utf, MAX_PATH, -1);
+       if (result < 0 && errno == ERANGE)
+               errno = ENAMETOOLONG;
+       return result;
+}
+
+/**
+ * Converts UTF-16LE encoded string to UTF-8.
+ *
+ * Maximum space requirement for the target buffer is three UTF-8 chars per
+ * wide char ((_wcslen(wcs) * 3) + 1).
+ *
+ * The maximum space is needed only if the entire input string consists of
+ * UTF-16 words in range 0x0800-0xd7ff or 0xe000-0xffff (i.e. \u0800-\uffff
+ * modulo surrogate pairs), as per the following table:
+ *
+ *               |                       | UTF-16 | UTF-8 |
+ *   Code point  |  UTF-16 sequence      | words  | bytes | ratio
+ * --------------+-----------------------+--------+-------+-------
+ * 000000-00007f | 0000-007f             |   1    |   1   |  1
+ * 000080-0007ff | 0080-07ff             |   1    |   2   |  2
+ * 000800-00ffff | 0800-d7ff / e000-ffff |   1    |   3   |  3
+ * 010000-10ffff | d800-dbff + dc00-dfff |   2    |   4   |  2
+ *
+ * Note that invalid code points > 10ffff cannot be represented in UTF-16.
+ *
+ * Parameters:
+ * utf: target buffer
+ * wcs: wide string to convert
+ * utflen: size of target buffer
+ *
+ * Returns:
+ * length of converted string, or -1 on failure
+ *
+ * Errors:
+ * EINVAL: one of the input parameters is invalid (e.g. NULL)
+ * ERANGE: the output buffer is too small
+ */
+int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen);
+
 /*
  * A critical section used in the implementation of the spawn
  * functions (mingw_spawnv[p]e()) and waitpid(). Intialised in
@@ -363,22 +466,16 @@ void free_environ(char **env);
 extern CRITICAL_SECTION pinfo_cs;
 
 /*
- * A replacement of main() that ensures that argv[0] has a path
- * and that default fmode and std(in|out|err) are in binary mode
+ * A replacement of main() that adds win32 specific initialization.
  */
 
+void mingw_startup();
 #define main(c,v) dummy_decl_mingw_main(); \
 static int mingw_main(c,v); \
 int main(int argc, char **argv) \
 { \
-       extern CRITICAL_SECTION pinfo_cs; \
-       _fmode = _O_BINARY; \
-       _setmode(_fileno(stdin), _O_BINARY); \
-       _setmode(_fileno(stdout), _O_BINARY); \
-       _setmode(_fileno(stderr), _O_BINARY); \
-       argv[0] = xstrdup(_pgmptr); \
-       InitializeCriticalSection(&pinfo_cs); \
-       return mingw_main(argc, argv); \
+       mingw_startup(); \
+       return mingw_main(__argc, (void *)__argv); \
 } \
 static int mingw_main(c,v)
 
index 7a0debe51bcad9f8d46c9c675aa3a6bbaaf7f537..82a515c21b322a0e50c7b5dbdd12e74341b20e98 100644 (file)
@@ -1,96 +1,91 @@
-#include "../git-compat-util.h"
-#include "dirent.h"
+#include "../../git-compat-util.h"
 
 struct DIR {
        struct dirent dd_dir; /* includes d_type */
        HANDLE dd_handle;     /* FindFirstFile handle */
        int dd_stat;          /* 0-based index */
-       char dd_name[1];      /* extend struct */
 };
 
+static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata)
+{
+       /* copy file name from WIN32_FIND_DATA to dirent */
+       memcpy(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
+
+       /* Set file type, based on WIN32_FIND_DATA */
+       if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+               ent->d_type = DT_DIR;
+       else
+               ent->d_type = DT_REG;
+}
+
 DIR *opendir(const char *name)
 {
-       DWORD attrs = GetFileAttributesA(name);
+       char pattern[MAX_PATH];
+       WIN32_FIND_DATAA fdata;
+       HANDLE h;
        int len;
-       DIR *p;
+       DIR *dir;
 
-       /* check for valid path */
-       if (attrs == INVALID_FILE_ATTRIBUTES) {
-               errno = ENOENT;
+       /* check that name is not NULL */
+       if (!name) {
+               errno = EINVAL;
                return NULL;
        }
-
-       /* check if it's a directory */
-       if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
-               errno = ENOTDIR;
-               return NULL;
-       }
-
        /* check that the pattern won't be too long for FindFirstFileA */
        len = strlen(name);
-       if (is_dir_sep(name[len - 1]))
-               len--;
        if (len + 2 >= MAX_PATH) {
                errno = ENAMETOOLONG;
                return NULL;
        }
-
-       p = malloc(sizeof(DIR) + len + 2);
-       if (!p)
+       /* copy name to temp buffer */
+       memcpy(pattern, name, len + 1);
+
+       /* append optional '/' and wildcard '*' */
+       if (len && !is_dir_sep(pattern[len - 1]))
+               pattern[len++] = '/';
+       pattern[len++] = '*';
+       pattern[len] = 0;
+
+       /* open find handle */
+       h = FindFirstFileA(pattern, &fdata);
+       if (h == INVALID_HANDLE_VALUE) {
+               DWORD err = GetLastError();
+               errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
                return NULL;
+       }
 
-       memset(p, 0, sizeof(DIR) + len + 2);
-       strcpy(p->dd_name, name);
-       p->dd_name[len] = '/';
-       p->dd_name[len+1] = '*';
-
-       p->dd_handle = INVALID_HANDLE_VALUE;
-       return p;
+       /* initialize DIR structure and copy first dir entry */
+       dir = xmalloc(sizeof(DIR));
+       dir->dd_handle = h;
+       dir->dd_stat = 0;
+       finddata2dirent(&dir->dd_dir, &fdata);
+       return dir;
 }
 
 struct dirent *readdir(DIR *dir)
 {
-       WIN32_FIND_DATAA buf;
-       HANDLE handle;
-
-       if (!dir || !dir->dd_handle) {
+       if (!dir) {
                errno = EBADF; /* No set_errno for mingw */
                return NULL;
        }
 
-       if (dir->dd_handle == INVALID_HANDLE_VALUE && dir->dd_stat == 0) {
-               DWORD lasterr;
-               handle = FindFirstFileA(dir->dd_name, &buf);
-               lasterr = GetLastError();
-               dir->dd_handle = handle;
-               if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) {
-                       errno = err_win_to_posix(lasterr);
+       /* if first entry, dirent has already been set up by opendir */
+       if (dir->dd_stat) {
+               /* get next entry and convert from WIN32_FIND_DATA to dirent */
+               WIN32_FIND_DATAA fdata;
+               if (FindNextFileA(dir->dd_handle, &fdata)) {
+                       finddata2dirent(&dir->dd_dir, &fdata);
+               } else {
+                       DWORD lasterr = GetLastError();
+                       /* POSIX says you shouldn't set errno when readdir can't
+                          find any more files; so, if another error we leave it set. */
+                       if (lasterr != ERROR_NO_MORE_FILES)
+                               errno = err_win_to_posix(lasterr);
                        return NULL;
                }
-       } else if (dir->dd_handle == INVALID_HANDLE_VALUE) {
-               return NULL;
-       } else if (!FindNextFileA(dir->dd_handle, &buf)) {
-               DWORD lasterr = GetLastError();
-               FindClose(dir->dd_handle);
-               dir->dd_handle = INVALID_HANDLE_VALUE;
-               /* POSIX says you shouldn't set errno when readdir can't
-                  find any more files; so, if another error we leave it set. */
-               if (lasterr != ERROR_NO_MORE_FILES)
-                       errno = err_win_to_posix(lasterr);
-               return NULL;
        }
 
-       /* We get here if `buf' contains valid data.  */
-       strcpy(dir->dd_dir.d_name, buf.cFileName);
        ++dir->dd_stat;
-
-       /* Set file type, based on WIN32_FIND_DATA */
-       dir->dd_dir.d_type = 0;
-       if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-               dir->dd_dir.d_type |= DT_DIR;
-       else
-               dir->dd_dir.d_type |= DT_REG;
-
        return &dir->dd_dir;
 }
 
@@ -101,8 +96,7 @@ int closedir(DIR *dir)
                return -1;
        }
 
-       if (dir->dd_handle != INVALID_HANDLE_VALUE)
-               FindClose(dir->dd_handle);
+       FindClose(dir->dd_handle);
        free(dir);
        return 0;
 }
index 927a25ca765ef4f393ba28d54e2478b29fca878e..8838cd61fc282b3713db76e088130f82b6c6fe33 100644 (file)
@@ -9,12 +9,8 @@ typedef struct DIR DIR;
 #define DT_LNK     3
 
 struct dirent {
-       long d_ino;                      /* Always zero. */
-       char d_name[FILENAME_MAX];       /* File name. */
-       union {
-               unsigned short d_reclen; /* Always zero. */
-               unsigned char  d_type;   /* Reimplementation adds this */
-       };
+       unsigned char d_type;      /* file type to prevent lstat after readdir */
+       char d_name[MAX_PATH];     /* file name */
 };
 
 DIR *opendir(const char *dirname);
index dedce2104eaf5cefbb1abef1b7921eb99c67a75e..efc5bb3a4b63166eccb33f6ec6a8ad57e4c9ac36 100644 (file)
@@ -2,15 +2,10 @@
  * Copyright 2008 Peter Harris <git@peter.is-a-geek.org>
  */
 
+#undef NOGDI
 #include "../git-compat-util.h"
-
-/*
- Functions to be wrapped:
-*/
-#undef printf
-#undef fprintf
-#undef fputs
-/* TODO: write */
+#include <wingdi.h>
+#include <winreg.h>
 
 /*
  ANSI codes used by git: m, K
@@ -23,29 +18,114 @@ static HANDLE console;
 static WORD plain_attr;
 static WORD attr;
 static int negative;
+static int non_ascii_used = 0;
+static HANDLE hthread, hread, hwrite;
+static HANDLE hconsole1, hconsole2;
+
+#ifdef __MINGW32__
+typedef struct _CONSOLE_FONT_INFOEX {
+       ULONG cbSize;
+       DWORD nFont;
+       COORD dwFontSize;
+       UINT FontFamily;
+       UINT FontWeight;
+       WCHAR FaceName[LF_FACESIZE];
+} CONSOLE_FONT_INFOEX, *PCONSOLE_FONT_INFOEX;
+#endif
+
+typedef BOOL (WINAPI *PGETCURRENTCONSOLEFONTEX)(HANDLE, BOOL,
+               PCONSOLE_FONT_INFOEX);
+
+static void warn_if_raster_font(void)
+{
+       DWORD fontFamily = 0;
+       PGETCURRENTCONSOLEFONTEX pGetCurrentConsoleFontEx;
+
+       /* don't bother if output was ascii only */
+       if (!non_ascii_used)
+               return;
+
+       /* GetCurrentConsoleFontEx is available since Vista */
+       pGetCurrentConsoleFontEx = (PGETCURRENTCONSOLEFONTEX) GetProcAddress(
+                       GetModuleHandle("kernel32.dll"),
+                       "GetCurrentConsoleFontEx");
+       if (pGetCurrentConsoleFontEx) {
+               CONSOLE_FONT_INFOEX cfi;
+               cfi.cbSize = sizeof(cfi);
+               if (pGetCurrentConsoleFontEx(console, 0, &cfi))
+                       fontFamily = cfi.FontFamily;
+       } else {
+               /* pre-Vista: check default console font in registry */
+               HKEY hkey;
+               if (ERROR_SUCCESS == RegOpenKeyExA(HKEY_CURRENT_USER, "Console",
+                               0, KEY_READ, &hkey)) {
+                       DWORD size = sizeof(fontFamily);
+                       RegQueryValueExA(hkey, "FontFamily", NULL, NULL,
+                                       (LPVOID) &fontFamily, &size);
+                       RegCloseKey(hkey);
+               }
+       }
+
+       if (!(fontFamily & TMPF_TRUETYPE)) {
+               const wchar_t *msg = L"\nWarning: Your console font probably "
+                       L"doesn\'t support Unicode. If you experience strange "
+                       L"characters in the output, consider switching to a "
+                       L"TrueType font such as Consolas!\n";
+               DWORD dummy;
+               WriteConsoleW(console, msg, wcslen(msg), &dummy, NULL);
+       }
+}
 
-static void init(void)
+static int is_console(int fd)
 {
        CONSOLE_SCREEN_BUFFER_INFO sbi;
+       HANDLE hcon;
 
        static int initialized = 0;
-       if (initialized)
-               return;
 
-       console = GetStdHandle(STD_OUTPUT_HANDLE);
-       if (console == INVALID_HANDLE_VALUE)
-               console = NULL;
+       /* get OS handle of the file descriptor */
+       hcon = (HANDLE) _get_osfhandle(fd);
+       if (hcon == INVALID_HANDLE_VALUE)
+               return 0;
 
-       if (!console)
-               return;
+       /* check if its a device (i.e. console, printer, serial port) */
+       if (GetFileType(hcon) != FILE_TYPE_CHAR)
+               return 0;
 
-       GetConsoleScreenBufferInfo(console, &sbi);
-       attr = plain_attr = sbi.wAttributes;
-       negative = 0;
+       /* check if its a handle to a console output screen buffer */
+       if (!GetConsoleScreenBufferInfo(hcon, &sbi))
+               return 0;
+
+       /* initialize attributes */
+       if (!initialized) {
+               console = hcon;
+               attr = plain_attr = sbi.wAttributes;
+               negative = 0;
+               initialized = 1;
+       }
 
-       initialized = 1;
+       return 1;
 }
 
+#define BUFFER_SIZE 4096
+#define MAX_PARAMS 16
+
+static void write_console(unsigned char *str, size_t len)
+{
+       /* only called from console_thread, so a static buffer will do */
+       static wchar_t wbuf[2 * BUFFER_SIZE + 1];
+       DWORD dummy;
+
+       /* convert utf-8 to utf-16 */
+       int wlen = xutftowcsn(wbuf, (char*) str, ARRAY_SIZE(wbuf), len);
+
+       /* write directly to console */
+       WriteConsoleW(console, wbuf, wlen, &dummy, NULL);
+
+       /* remember if non-ascii characters are printed */
+       if (wlen != len)
+               non_ascii_used = 1;
+}
 
 #define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
 #define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
@@ -90,18 +170,13 @@ static void erase_in_line(void)
                &dummy);
 }
 
-
-static const char *set_attr(const char *str)
+static void set_attr(char func, const int *params, int paramlen)
 {
-       const char *func;
-       size_t len = strspn(str, "0123456789;");
-       func = str + len;
-
-       switch (*func) {
+       int i;
+       switch (func) {
        case 'm':
-               do {
-                       long val = strtol(str, (char **)&str, 10);
-                       switch (val) {
+               for (i = 0; i < paramlen; i++) {
+                       switch (params[i]) {
                        case 0: /* reset */
                                attr = plain_attr;
                                negative = 0;
@@ -224,9 +299,7 @@ static const char *set_attr(const char *str)
                                /* Unsupported code */
                                break;
                        }
-                       str++;
-               } while (*(str-1) == ';');
-
+               }
                set_console_attr();
                break;
        case 'K':
@@ -236,122 +309,271 @@ static const char *set_attr(const char *str)
                /* Unsupported code */
                break;
        }
-
-       return func + 1;
 }
 
-static int ansi_emulate(const char *str, FILE *stream)
+enum {
+       TEXT = 0, ESCAPE = 033, BRACKET = '['
+};
+
+static DWORD WINAPI console_thread(LPVOID unused)
 {
-       int rv = 0;
-       const char *pos = str;
-
-       while (*pos) {
-               pos = strstr(str, "\033[");
-               if (pos) {
-                       size_t len = pos - str;
-
-                       if (len) {
-                               size_t out_len = fwrite(str, 1, len, stream);
-                               rv += out_len;
-                               if (out_len < len)
-                                       return rv;
+       unsigned char buffer[BUFFER_SIZE];
+       DWORD bytes;
+       int start, end = 0, c, parampos = 0, state = TEXT;
+       int params[MAX_PARAMS];
+
+       while (1) {
+               /* read next chunk of bytes from the pipe */
+               if (!ReadFile(hread, buffer + end, BUFFER_SIZE - end, &bytes,
+                               NULL)) {
+                       /* exit if pipe has been closed or disconnected */
+                       if (GetLastError() == ERROR_PIPE_NOT_CONNECTED ||
+                                       GetLastError() == ERROR_BROKEN_PIPE)
+                               break;
+                       /* ignore other errors */
+                       continue;
+               }
+
+               /* scan the bytes and handle ANSI control codes */
+               bytes += end;
+               start = end = 0;
+               while (end < bytes) {
+                       c = buffer[end++];
+                       switch (state) {
+                       case TEXT:
+                               if (c == ESCAPE) {
+                                       /* print text seen so far */
+                                       if (end - 1 > start)
+                                               write_console(buffer + start,
+                                                       end - 1 - start);
+
+                                       /* then start parsing escape sequence */
+                                       start = end - 1;
+                                       memset(params, 0, sizeof(params));
+                                       parampos = 0;
+                                       state = ESCAPE;
+                               }
+                               break;
+
+                       case ESCAPE:
+                               /* continue if "\033[", otherwise bail out */
+                               state = (c == BRACKET) ? BRACKET : TEXT;
+                               break;
+
+                       case BRACKET:
+                               /* parse [0-9;]* into array of parameters */
+                               if (c >= '0' && c <= '9') {
+                                       params[parampos] *= 10;
+                                       params[parampos] += c - '0';
+                               } else if (c == ';') {
+                                       /*
+                                        * next parameter, bail out if out of
+                                        * bounds
+                                        */
+                                       parampos++;
+                                       if (parampos >= MAX_PARAMS)
+                                               state = TEXT;
+                               } else {
+                                       /*
+                                        * end of escape sequence, change
+                                        * console attributes
+                                        */
+                                       set_attr(c, params, parampos + 1);
+                                       start = end;
+                                       state = TEXT;
+                               }
+                               break;
                        }
+               }
 
-                       str = pos + 2;
-                       rv += 2;
+               /* print remaining text unless parsing an escape sequence */
+               if (state == TEXT && end > start) {
+                       /* check for incomplete UTF-8 sequences and fix end */
+                       if (buffer[end - 1] >= 0x80) {
+                               if (buffer[end -1] >= 0xc0)
+                                       end--;
+                               else if (end - 1 > start &&
+                                               buffer[end - 2] >= 0xe0)
+                                       end -= 2;
+                               else if (end - 2 > start &&
+                                               buffer[end - 3] >= 0xf0)
+                                       end -= 3;
+                       }
 
-                       fflush(stream);
+                       /* print remaining complete UTF-8 sequences */
+                       if (end > start)
+                               write_console(buffer + start, end - start);
 
-                       pos = set_attr(str);
-                       rv += pos - str;
-                       str = pos;
+                       /* move remaining bytes to the front */
+                       if (end < bytes)
+                               memmove(buffer, buffer + end, bytes - end);
+                       end = bytes - end;
                } else {
-                       rv += strlen(str);
-                       fputs(str, stream);
-                       return rv;
+                       /* all data has been consumed, mark buffer empty */
+                       end = 0;
                }
        }
-       return rv;
+
+       /* check if the console font supports unicode */
+       warn_if_raster_font();
+
+       CloseHandle(hread);
+       return 0;
 }
 
-int winansi_fputs(const char *str, FILE *stream)
+static void winansi_exit(void)
 {
-       int rv;
-
-       if (!isatty(fileno(stream)))
-               return fputs(str, stream);
+       /* flush all streams */
+       _flushall();
 
-       init();
+       /* signal console thread to exit */
+       FlushFileBuffers(hwrite);
+       DisconnectNamedPipe(hwrite);
 
-       if (!console)
-               return fputs(str, stream);
+       /* wait for console thread to copy remaining data */
+       WaitForSingleObject(hthread, INFINITE);
 
-       rv = ansi_emulate(str, stream);
+       /* cleanup handles... */
+       CloseHandle(hwrite);
+       CloseHandle(hthread);
+}
 
-       if (rv >= 0)
-               return 0;
-       else
-               return EOF;
+static void die_lasterr(const char *fmt, ...)
+{
+       va_list params;
+       va_start(params, fmt);
+       errno = err_win_to_posix(GetLastError());
+       die_errno(fmt, params);
+       va_end(params);
 }
 
-static int winansi_vfprintf(FILE *stream, const char *format, va_list list)
+static HANDLE duplicate_handle(HANDLE hnd)
 {
-       int len, rv;
-       char small_buf[256];
-       char *buf = small_buf;
-       va_list cp;
+       HANDLE hresult, hproc = GetCurrentProcess();
+       if (!DuplicateHandle(hproc, hnd, hproc, &hresult, 0, TRUE,
+                       DUPLICATE_SAME_ACCESS))
+               die_lasterr("DuplicateHandle(%li) failed", (long) hnd);
+       return hresult;
+}
 
-       if (!isatty(fileno(stream)))
-               goto abort;
 
-       init();
+/*
+ * Make MSVCRT's internal file descriptor control structure accessible
+ * so that we can tweak OS handles and flags directly (we need MSVCRT
+ * to treat our pipe handle as if it were a console).
+ *
+ * We assume that the ioinfo structure (exposed by MSVCRT.dll via
+ * __pioinfo) starts with the OS handle and the flags. The exact size
+ * varies between MSVCRT versions, so we try different sizes until
+ * toggling the FDEV bit of _pioinfo(1)->osflags is reflected in
+ * isatty(1).
+ */
+typedef struct {
+       HANDLE osfhnd;
+       char osflags;
+} ioinfo;
 
-       if (!console)
-               goto abort;
+extern __declspec(dllimport) ioinfo *__pioinfo[];
 
-       va_copy(cp, list);
-       len = vsnprintf(small_buf, sizeof(small_buf), format, cp);
-       va_end(cp);
+static size_t sizeof_ioinfo = 0;
 
-       if (len > sizeof(small_buf) - 1) {
-               buf = malloc(len + 1);
-               if (!buf)
-                       goto abort;
+#define IOINFO_L2E 5
+#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
 
-               len = vsnprintf(buf, len + 1, format, list);
-       }
+#define FDEV  0x40
 
-       rv = ansi_emulate(buf, stream);
+static inline ioinfo* _pioinfo(int fd)
+{
+       return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] +
+                       (fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
+}
 
-       if (buf != small_buf)
-               free(buf);
-       return rv;
+static int init_sizeof_ioinfo()
+{
+       int istty, wastty;
+       /* don't init twice */
+       if (sizeof_ioinfo)
+               return sizeof_ioinfo >= 256;
+
+       sizeof_ioinfo = sizeof(ioinfo);
+       wastty = isatty(1);
+       while (sizeof_ioinfo < 256) {
+               /* toggle FDEV flag, check isatty, then toggle back */
+               _pioinfo(1)->osflags ^= FDEV;
+               istty = isatty(1);
+               _pioinfo(1)->osflags ^= FDEV;
+               /* return if we found the correct size */
+               if (istty != wastty)
+                       return 0;
+               sizeof_ioinfo += sizeof(void*);
+       }
+       error("Tweaking file descriptors doesn't work with this MSVCRT.dll");
+       return 1;
+}
 
-abort:
-       rv = vfprintf(stream, format, list);
-       return rv;
+static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
+{
+       ioinfo *pioinfo;
+       HANDLE old_handle;
+
+       /* init ioinfo size if we haven't done so */
+       if (init_sizeof_ioinfo())
+               return INVALID_HANDLE_VALUE;
+
+       /* get ioinfo pointer and change the handles */
+       pioinfo = _pioinfo(fd);
+       old_handle = pioinfo->osfhnd;
+       pioinfo->osfhnd = new_handle;
+       return old_handle;
 }
 
-int winansi_fprintf(FILE *stream, const char *format, ...)
+void winansi_init(void)
 {
-       va_list list;
-       int rv;
+       int con1, con2;
+       char name[32];
 
-       va_start(list, format);
-       rv = winansi_vfprintf(stream, format, list);
-       va_end(list);
+       /* check if either stdout or stderr is a console output screen buffer */
+       con1 = is_console(1);
+       con2 = is_console(2);
+       if (!con1 && !con2)
+               return;
 
-       return rv;
+       /* create a named pipe to communicate with the console thread */
+       sprintf(name, "\\\\.\\pipe\\winansi%lu", GetCurrentProcessId());
+       hwrite = CreateNamedPipe(name, PIPE_ACCESS_OUTBOUND,
+               PIPE_TYPE_BYTE | PIPE_WAIT, 1, BUFFER_SIZE, 0, 0, NULL);
+       if (hwrite == INVALID_HANDLE_VALUE)
+               die_lasterr("CreateNamedPipe failed");
+
+       hread = CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+       if (hread == INVALID_HANDLE_VALUE)
+               die_lasterr("CreateFile for named pipe failed");
+
+       /* start console spool thread on the pipe's read end */
+       hthread = CreateThread(NULL, 0, console_thread, NULL, 0, NULL);
+       if (hthread == INVALID_HANDLE_VALUE)
+               die_lasterr("CreateThread(console_thread) failed");
+
+       /* schedule cleanup routine */
+       if (atexit(winansi_exit))
+               die_errno("atexit(winansi_exit) failed");
+
+       /* redirect stdout / stderr to the pipe */
+       if (con1)
+               hconsole1 = swap_osfhnd(1, duplicate_handle(hwrite));
+       if (con2)
+               hconsole2 = swap_osfhnd(2, duplicate_handle(hwrite));
 }
 
-int winansi_printf(const char *format, ...)
+/*
+ * Returns the real console handle if stdout / stderr is a pipe redirecting
+ * to the console. Allows spawn / exec to pass the console to the next process.
+ */
+HANDLE winansi_get_osfhandle(int fd)
 {
-       va_list list;
-       int rv;
-
-       va_start(list, format);
-       rv = winansi_vfprintf(stdout, format, list);
-       va_end(list);
-
-       return rv;
+       HANDLE hnd = (HANDLE) _get_osfhandle(fd);
+       if ((fd == 1 || fd == 2) && isatty(fd)
+           && GetFileType(hnd) == FILE_TYPE_PIPE)
+               return (fd == 1) ? hconsole1 : hconsole2;
+       return hnd;
 }
index a1aef1cf3eca01e328b3e319636269236ea91ae0..ba882a1f51de347dc52fa8c52623dbc376f36d80 100644 (file)
--- a/config.c
+++ b/config.c
@@ -138,8 +138,7 @@ int git_config_include(const char *var, const char *value, void *data)
        if (ret < 0)
                return ret;
 
-       type = skip_prefix(var, "include.");
-       if (!type)
+       if (!skip_prefix(var, "include.", &type))
                return ret;
 
        if (!strcmp(type, "path"))
index 1ae675b05344912d8798b9856b60b03c282cf908..8131c81985d97c23d19c7ef489f0db988f6fbbc7 100644 (file)
@@ -354,6 +354,7 @@ ifeq ($(uname_S),Windows)
        NO_POSIX_GOODIES = UnfortunatelyYes
        NATIVE_CRLF = YesPlease
        DEFAULT_HELP_FORMAT = html
+       NO_D_INO_IN_DIRENT = YesPlease
 
        CC = compat/vcbuild/scripts/clink.pl
        AR = compat/vcbuild/scripts/lib.pl
@@ -503,6 +504,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_INET_NTOP = YesPlease
        NO_POSIX_GOODIES = UnfortunatelyYes
        DEFAULT_HELP_FORMAT = html
+       NO_D_INO_IN_DIRENT = YesPlease
        COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -D_USE_32BIT_TIME_T -DNOGDI -Icompat -Icompat/win32
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
        COMPAT_OBJS += compat/mingw.o compat/winansi.o \
index 94a66502464400d5dfc47a4151cc2fd01780a0a4..37ff018f13f6f8f3137a9aec973b85848a8138eb 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -129,6 +129,7 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                char *name;
                int len, name_len;
                char *buffer = packet_buffer;
+               const char *arg;
 
                len = packet_read(in, &src_buf, &src_len,
                                  packet_buffer, sizeof(packet_buffer),
@@ -140,12 +141,12 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                if (!len)
                        break;
 
-               if (len > 4 && starts_with(buffer, "ERR "))
-                       die("remote error: %s", buffer + 4);
+               if (len > 4 && skip_prefix(buffer, "ERR ", &arg))
+                       die("remote error: %s", arg);
 
-               if (len == 48 && starts_with(buffer, "shallow ")) {
-                       if (get_sha1_hex(buffer + 8, old_sha1))
-                               die("protocol error: expected shallow sha-1, got '%s'", buffer + 8);
+               if (len == 48 && skip_prefix(buffer, "shallow ", &arg)) {
+                       if (get_sha1_hex(arg, old_sha1))
+                               die("protocol error: expected shallow sha-1, got '%s'", arg);
                        if (!shallow_points)
                                die("repository on the other end cannot be shallow");
                        sha1_array_append(shallow_points, old_sha1);
index ab80b723574ff88b6c9c4a7c98c8821600f3e6bc..cb5fbb45ea04cead65316ecf6b44730b17108122 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1121,9 +1121,9 @@ static int is_foreign_ident(const char *str)
 {
        int i;
 
-       if (!starts_with(str, "$Id: "))
+       if (!skip_prefix(str, "$Id: ", &str))
                return 0;
-       for (i = 5; str[i]; i++) {
+       for (i = 0; str[i]; i++) {
                if (isspace(str[i]) && str[i+1] != '$')
                        return 1;
        }
index 390f194252cc6d7c0c9f43e2a48692324bf0b665..3b370ca5e529c74e17e0f39da5c689ebc4ccfc25 100644 (file)
@@ -109,14 +109,12 @@ static int read_request(FILE *fh, struct credential *c,
        const char *p;
 
        strbuf_getline(&item, fh, '\n');
-       p = skip_prefix(item.buf, "action=");
-       if (!p)
+       if (!skip_prefix(item.buf, "action=", &p))
                return error("client sent bogus action line: %s", item.buf);
        strbuf_addstr(action, p);
 
        strbuf_getline(&item, fh, '\n');
-       p = skip_prefix(item.buf, "timeout=");
-       if (!p)
+       if (!skip_prefix(item.buf, "timeout=", &p))
                return error("client sent bogus timeout line: %s", item.buf);
        *timeout = atoi(p);
 
index e54753c75d1c2abf7916f1aa7075d4ada3cfc61f..4d79d320f89e956aa9233a170120f05b2473ca59 100644 (file)
@@ -40,8 +40,7 @@ static int credential_config_callback(const char *var, const char *value,
        struct credential *c = data;
        const char *key, *dot;
 
-       key = skip_prefix(var, "credential.");
-       if (!key)
+       if (!skip_prefix(var, "credential.", &key))
                return 0;
 
        if (!value)
index f9c63e96137aff6b5ae2376af3fca789ff03e975..1eb663172338ae24ef9229594f893e80c2e52f96 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -39,8 +39,8 @@ static int strict_paths;
 static int export_all_trees;
 
 /* Take all paths relative to this one if non-NULL */
-static char *base_path;
-static char *interpolated_path;
+static const char *base_path;
+static const char *interpolated_path;
 static int base_path_relaxed;
 
 /* Flag indicating client sent extra args. */
@@ -106,12 +106,12 @@ static void NORETURN daemon_die(const char *err, va_list params)
        exit(1);
 }
 
-static const char *path_ok(char *directory)
+static const char *path_ok(const char *directory)
 {
        static char rpath[PATH_MAX];
        static char interp_path[PATH_MAX];
        const char *path;
-       char *dir;
+       const char *dir;
 
        dir = directory;
 
@@ -131,7 +131,7 @@ static const char *path_ok(char *directory)
                         * "~alice/%s/foo".
                         */
                        int namlen, restlen = strlen(dir);
-                       char *slash = strchr(dir, '/');
+                       const char *slash = strchr(dir, '/');
                        if (!slash)
                                slash = dir + restlen;
                        namlen = slash - dir;
@@ -235,8 +235,10 @@ static int service_enabled;
 
 static int git_daemon_config(const char *var, const char *value, void *cb)
 {
-       if (starts_with(var, "daemon.") &&
-           !strcmp(var + 7, service_looking_at->config_name)) {
+       const char *service;
+
+       if (skip_prefix(var, "daemon.", &service) &&
+           !strcmp(service, service_looking_at->config_name)) {
                service_enabled = git_config_bool(var, value);
                return 0;
        }
@@ -253,7 +255,7 @@ static int daemon_error(const char *dir, const char *msg)
        return -1;
 }
 
-static char *access_hook;
+static const char *access_hook;
 
 static int run_access_hook(struct daemon_service *service, const char *dir, const char *path)
 {
@@ -318,7 +320,7 @@ static int run_access_hook(struct daemon_service *service, const char *dir, cons
        return -1;
 }
 
-static int run_service(char *dir, struct daemon_service *service)
+static int run_service(const char *dir, struct daemon_service *service)
 {
        const char *path;
        int enabled = service->enabled;
@@ -624,15 +626,16 @@ static int execute(void)
 
        for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
                struct daemon_service *s = &(daemon_service[i]);
-               int namelen = strlen(s->name);
-               if (starts_with(line, "git-") &&
-                   !strncmp(s->name, line + 4, namelen) &&
-                   line[namelen + 4] == ' ') {
+               const char *arg;
+
+               if (skip_prefix(line, "git-", &arg) &&
+                   skip_prefix(arg, s->name, &arg) &&
+                   *arg++ == ' ') {
                        /*
                         * Note: The directory here is probably context sensitive,
                         * and might depend on the actual service being performed.
                         */
-                       return run_service(line + namelen + 5, s);
+                       return run_service(arg, s);
                }
        }
 
@@ -1133,16 +1136,17 @@ int main(int argc, char **argv)
 
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
+               const char *v;
 
-               if (starts_with(arg, "--listen=")) {
-                       string_list_append(&listen_addr, xstrdup_tolower(arg + 9));
+               if (skip_prefix(arg, "--listen=", &v)) {
+                       string_list_append(&listen_addr, xstrdup_tolower(v));
                        continue;
                }
-               if (starts_with(arg, "--port=")) {
+               if (skip_prefix(arg, "--port=", &v)) {
                        char *end;
                        unsigned long n;
-                       n = strtoul(arg+7, &end, 0);
-                       if (arg[7] && !*end) {
+                       n = strtoul(v, &end, 0);
+                       if (*v && !*end) {
                                listen_port = n;
                                continue;
                        }
@@ -1168,20 +1172,20 @@ int main(int argc, char **argv)
                        export_all_trees = 1;
                        continue;
                }
-               if (starts_with(arg, "--access-hook=")) {
-                       access_hook = arg + 14;
+               if (skip_prefix(arg, "--access-hook=", &v)) {
+                       access_hook = v;
                        continue;
                }
-               if (starts_with(arg, "--timeout=")) {
-                       timeout = atoi(arg+10);
+               if (skip_prefix(arg, "--timeout=", &v)) {
+                       timeout = atoi(v);
                        continue;
                }
-               if (starts_with(arg, "--init-timeout=")) {
-                       init_timeout = atoi(arg+15);
+               if (skip_prefix(arg, "--init-timeout=", &v)) {
+                       init_timeout = atoi(v);
                        continue;
                }
-               if (starts_with(arg, "--max-connections=")) {
-                       max_connections = atoi(arg+18);
+               if (skip_prefix(arg, "--max-connections=", &v)) {
+                       max_connections = atoi(v);
                        if (max_connections < 0)
                                max_connections = 0;            /* unlimited */
                        continue;
@@ -1190,16 +1194,16 @@ int main(int argc, char **argv)
                        strict_paths = 1;
                        continue;
                }
-               if (starts_with(arg, "--base-path=")) {
-                       base_path = arg+12;
+               if (skip_prefix(arg, "--base-path=", &v)) {
+                       base_path = v;
                        continue;
                }
                if (!strcmp(arg, "--base-path-relaxed")) {
                        base_path_relaxed = 1;
                        continue;
                }
-               if (starts_with(arg, "--interpolated-path=")) {
-                       interpolated_path = arg+20;
+               if (skip_prefix(arg, "--interpolated-path=", &v)) {
+                       interpolated_path = v;
                        continue;
                }
                if (!strcmp(arg, "--reuseaddr")) {
@@ -1210,12 +1214,12 @@ int main(int argc, char **argv)
                        user_path = "";
                        continue;
                }
-               if (starts_with(arg, "--user-path=")) {
-                       user_path = arg + 12;
+               if (skip_prefix(arg, "--user-path=", &v)) {
+                       user_path = v;
                        continue;
                }
-               if (starts_with(arg, "--pid-file=")) {
-                       pid_file = arg + 11;
+               if (skip_prefix(arg, "--pid-file=", &v)) {
+                       pid_file = v;
                        continue;
                }
                if (!strcmp(arg, "--detach")) {
@@ -1223,28 +1227,28 @@ int main(int argc, char **argv)
                        log_syslog = 1;
                        continue;
                }
-               if (starts_with(arg, "--user=")) {
-                       user_name = arg + 7;
+               if (skip_prefix(arg, "--user=", &v)) {
+                       user_name = v;
                        continue;
                }
-               if (starts_with(arg, "--group=")) {
-                       group_name = arg + 8;
+               if (skip_prefix(arg, "--group=", &v)) {
+                       group_name = v;
                        continue;
                }
-               if (starts_with(arg, "--enable=")) {
-                       enable_service(arg + 9, 1);
+               if (skip_prefix(arg, "--enable=", &v)) {
+                       enable_service(v, 1);
                        continue;
                }
-               if (starts_with(arg, "--disable=")) {
-                       enable_service(arg + 10, 0);
+               if (skip_prefix(arg, "--disable=", &v)) {
+                       enable_service(v, 0);
                        continue;
                }
-               if (starts_with(arg, "--allow-override=")) {
-                       make_service_overridable(arg + 17, 1);
+               if (skip_prefix(arg, "--allow-override=", &v)) {
+                       make_service_overridable(v, 1);
                        continue;
                }
-               if (starts_with(arg, "--forbid-override=")) {
-                       make_service_overridable(arg + 18, 0);
+               if (skip_prefix(arg, "--forbid-override=", &v)) {
+                       make_service_overridable(v, 0);
                        continue;
                }
                if (!strcmp(arg, "--informative-errors")) {
diff --git a/diff.c b/diff.c
index bba9a558a60a490b7edd6dec370181a0941bf985..06bdfb8ae5acab2c6090ca4154a26fca6da8b74c 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -52,23 +52,23 @@ static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_NORMAL,       /* FUNCINFO */
 };
 
-static int parse_diff_color_slot(const char *var, int ofs)
+static int parse_diff_color_slot(const char *var)
 {
-       if (!strcasecmp(var+ofs, "plain"))
+       if (!strcasecmp(var, "plain"))
                return DIFF_PLAIN;
-       if (!strcasecmp(var+ofs, "meta"))
+       if (!strcasecmp(var, "meta"))
                return DIFF_METAINFO;
-       if (!strcasecmp(var+ofs, "frag"))
+       if (!strcasecmp(var, "frag"))
                return DIFF_FRAGINFO;
-       if (!strcasecmp(var+ofs, "old"))
+       if (!strcasecmp(var, "old"))
                return DIFF_FILE_OLD;
-       if (!strcasecmp(var+ofs, "new"))
+       if (!strcasecmp(var, "new"))
                return DIFF_FILE_NEW;
-       if (!strcasecmp(var+ofs, "commit"))
+       if (!strcasecmp(var, "commit"))
                return DIFF_COMMIT;
-       if (!strcasecmp(var+ofs, "whitespace"))
+       if (!strcasecmp(var, "whitespace"))
                return DIFF_WHITESPACE;
-       if (!strcasecmp(var+ofs, "func"))
+       if (!strcasecmp(var, "func"))
                return DIFF_FUNCINFO;
        return -1;
 }
@@ -231,6 +231,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
 
 int git_diff_basic_config(const char *var, const char *value, void *cb)
 {
+       const char *name;
+
        if (!strcmp(var, "diff.renamelimit")) {
                diff_rename_limit_default = git_config_int(var, value);
                return 0;
@@ -239,8 +241,9 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
        if (userdiff_config(var, value) < 0)
                return -1;
 
-       if (starts_with(var, "diff.color.") || starts_with(var, "color.diff.")) {
-               int slot = parse_diff_color_slot(var, 11);
+       if (skip_prefix(var, "diff.color.", &name) ||
+           skip_prefix(var, "color.diff.", &name)) {
+               int slot = parse_diff_color_slot(name);
                if (slot < 0)
                        return 0;
                if (!value)
@@ -2341,6 +2344,7 @@ static void builtin_diff(const char *name_a,
        } else {
                /* Crazy xdl interfaces.. */
                const char *diffopts = getenv("GIT_DIFF_OPTS");
+               const char *v;
                xpparam_t xpp;
                xdemitconf_t xecfg;
                struct emit_callback ecbdata;
@@ -2379,10 +2383,10 @@ static void builtin_diff(const char *name_a,
                        xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
                if (!diffopts)
                        ;
-               else if (starts_with(diffopts, "--unified="))
-                       xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
-               else if (starts_with(diffopts, "-u"))
-                       xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
+               else if (skip_prefix(diffopts, "--unified=", &v))
+                       xecfg.ctxlen = strtoul(v, NULL, 10);
+               else if (skip_prefix(diffopts, "-u", &v))
+                       xecfg.ctxlen = strtoul(v, NULL, 10);
                if (o->word_diff)
                        init_diff_words_data(&ecbdata, o, one, two);
                xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
@@ -3391,12 +3395,10 @@ int parse_long_opt(const char *opt, const char **argv,
                   const char **optarg)
 {
        const char *arg = argv[0];
-       if (arg[0] != '-' || arg[1] != '-')
+       if (!skip_prefix(arg, "--", &arg))
                return 0;
-       arg += strlen("--");
-       if (!starts_with(arg, opt))
+       if (!skip_prefix(arg, opt, &arg))
                return 0;
-       arg += strlen(opt);
        if (*arg == '=') { /* stuck form: --option=value */
                *optarg = arg + 1;
                return 1;
@@ -3420,13 +3422,13 @@ static int stat_opt(struct diff_options *options, const char **av)
        int count = options->stat_count;
        int argcount = 1;
 
-       arg += strlen("--stat");
+       if (!skip_prefix(arg, "--stat", &arg))
+               die("BUG: stat option does not begin with --stat: %s", arg);
        end = (char *)arg;
 
        switch (*arg) {
        case '-':
-               if (starts_with(arg, "-width")) {
-                       arg += strlen("-width");
+               if (skip_prefix(arg, "-width", &arg)) {
                        if (*arg == '=')
                                width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
@@ -3435,8 +3437,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                                width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
-               } else if (starts_with(arg, "-name-width")) {
-                       arg += strlen("-name-width");
+               } else if (skip_prefix(arg, "-name-width", &arg)) {
                        if (*arg == '=')
                                name_width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
@@ -3445,8 +3446,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                                name_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
-               } else if (starts_with(arg, "-graph-width")) {
-                       arg += strlen("-graph-width");
+               } else if (skip_prefix(arg, "-graph-width", &arg)) {
                        if (*arg == '=')
                                graph_width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
@@ -3455,8 +3455,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                                graph_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
-               } else if (starts_with(arg, "-count")) {
-                       arg += strlen("-count");
+               } else if (skip_prefix(arg, "-count", &arg)) {
                        if (*arg == '=')
                                count = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
@@ -3609,17 +3608,17 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->output_format |= DIFF_FORMAT_SHORTSTAT;
        else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat"))
                return parse_dirstat_opt(options, "");
-       else if (starts_with(arg, "-X"))
-               return parse_dirstat_opt(options, arg + 2);
-       else if (starts_with(arg, "--dirstat="))
-               return parse_dirstat_opt(options, arg + 10);
+       else if (skip_prefix(arg, "-X", &arg))
+               return parse_dirstat_opt(options, arg);
+       else if (skip_prefix(arg, "--dirstat=", &arg))
+               return parse_dirstat_opt(options, arg);
        else if (!strcmp(arg, "--cumulative"))
                return parse_dirstat_opt(options, "cumulative");
        else if (!strcmp(arg, "--dirstat-by-file"))
                return parse_dirstat_opt(options, "files");
-       else if (starts_with(arg, "--dirstat-by-file=")) {
+       else if (skip_prefix(arg, "--dirstat-by-file=", &arg)) {
                parse_dirstat_opt(options, "files");
-               return parse_dirstat_opt(options, arg + 18);
+               return parse_dirstat_opt(options, arg);
        }
        else if (!strcmp(arg, "--check"))
                options->output_format |= DIFF_FORMAT_CHECKDIFF;
@@ -3669,9 +3668,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_OPT_CLR(options, RENAME_EMPTY);
        else if (!strcmp(arg, "--relative"))
                DIFF_OPT_SET(options, RELATIVE_NAME);
-       else if (starts_with(arg, "--relative=")) {
+       else if (skip_prefix(arg, "--relative=", &arg)) {
                DIFF_OPT_SET(options, RELATIVE_NAME);
-               options->prefix = arg + 11;
+               options->prefix = arg;
        }
 
        /* xdiff options */
@@ -3722,8 +3721,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_OPT_CLR(options, FOLLOW_RENAMES);
        else if (!strcmp(arg, "--color"))
                options->use_color = 1;
-       else if (starts_with(arg, "--color=")) {
-               int value = git_config_colorbool(NULL, arg+8);
+       else if (skip_prefix(arg, "--color=", &arg)) {
+               int value = git_config_colorbool(NULL, arg);
                if (value < 0)
                        return error("option `color' expects \"always\", \"auto\", or \"never\"");
                options->use_color = value;
@@ -3734,29 +3733,28 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
        }
-       else if (starts_with(arg, "--color-words=")) {
+       else if (skip_prefix(arg, "--color-words=", &arg)) {
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
-               options->word_regex = arg + 14;
+               options->word_regex = arg;
        }
        else if (!strcmp(arg, "--word-diff")) {
                if (options->word_diff == DIFF_WORDS_NONE)
                        options->word_diff = DIFF_WORDS_PLAIN;
        }
-       else if (starts_with(arg, "--word-diff=")) {
-               const char *type = arg + 12;
-               if (!strcmp(type, "plain"))
+       else if (skip_prefix(arg, "--word-diff=", &arg)) {
+               if (!strcmp(arg, "plain"))
                        options->word_diff = DIFF_WORDS_PLAIN;
-               else if (!strcmp(type, "color")) {
+               else if (!strcmp(arg, "color")) {
                        options->use_color = 1;
                        options->word_diff = DIFF_WORDS_COLOR;
                }
-               else if (!strcmp(type, "porcelain"))
+               else if (!strcmp(arg, "porcelain"))
                        options->word_diff = DIFF_WORDS_PORCELAIN;
-               else if (!strcmp(type, "none"))
+               else if (!strcmp(arg, "none"))
                        options->word_diff = DIFF_WORDS_NONE;
                else
-                       die("bad --word-diff argument: %s", type);
+                       die("bad --word-diff argument: %s", arg);
        }
        else if ((argcount = parse_long_opt("word-diff-regex", av, &optarg))) {
                if (options->word_diff == DIFF_WORDS_NONE)
@@ -3779,13 +3777,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (!strcmp(arg, "--ignore-submodules")) {
                DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(options, "all");
-       } else if (starts_with(arg, "--ignore-submodules=")) {
+       } else if (skip_prefix(arg, "--ignore-submodules=", &arg)) {
                DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
-               handle_ignore_submodules_arg(options, arg + 20);
+               handle_ignore_submodules_arg(options, arg);
        } else if (!strcmp(arg, "--submodule"))
                DIFF_OPT_SET(options, SUBMODULE_LOG);
-       else if (starts_with(arg, "--submodule="))
-               return parse_submodule_opt(options, arg + 12);
+       else if (skip_prefix(arg, "--submodule=", &arg))
+               return parse_submodule_opt(options, arg);
 
        /* misc options */
        else if (!strcmp(arg, "-z"))
@@ -3820,8 +3818,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        }
        else if (!strcmp(arg, "--abbrev"))
                options->abbrev = DEFAULT_ABBREV;
-       else if (starts_with(arg, "--abbrev=")) {
-               options->abbrev = strtoul(arg + 9, NULL, 10);
+       else if (skip_prefix(arg, "--abbrev=", &arg)) {
+               options->abbrev = strtoul(arg, NULL, 10);
                if (options->abbrev < MINIMUM_ABBREV)
                        options->abbrev = MINIMUM_ABBREV;
                else if (40 < options->abbrev)
@@ -3902,16 +3900,13 @@ static int diff_scoreopt_parse(const char *opt)
        cmd = *opt++;
        if (cmd == '-') {
                /* convert the long-form arguments into short-form versions */
-               if (starts_with(opt, "break-rewrites")) {
-                       opt += strlen("break-rewrites");
+               if (skip_prefix(opt, "break-rewrites", &opt)) {
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'B';
-               } else if (starts_with(opt, "find-copies")) {
-                       opt += strlen("find-copies");
+               } else if (skip_prefix(opt, "find-copies", &opt)) {
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'C';
-               } else if (starts_with(opt, "find-renames")) {
-                       opt += strlen("find-renames");
+               } else if (skip_prefix(opt, "find-renames", &opt)) {
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'M';
                }
diff --git a/entry.c b/entry.c
index 77c688262477e783b4bcbc237ef281eecab0661d..1eda8e94714bbd6020d61b4b627f721b1042673c 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -210,9 +210,12 @@ static int write_entry(struct cache_entry *ce,
 
 finish:
        if (state->refresh_cache) {
+               assert(state->istate);
                if (!fstat_done)
                        lstat(ce->name, &st);
                fill_stat_cache_info(ce, &st);
+               ce->ce_flags |= CE_UPDATE_IN_BASE;
+               state->istate->cache_changed |= CE_ENTRY_CHANGED;
        }
        return 0;
 }
index 4dac5e9edd75499ccf52debc847807d2fc180730..565f65293bb25ebdcb6ada262bcca9164c3ea113 100644 (file)
@@ -124,6 +124,12 @@ static char *expand_namespace(const char *raw_namespace)
        return strbuf_detach(&buf, NULL);
 }
 
+static char *git_path_from_env(const char *envvar, const char *path)
+{
+       const char *value = getenv(envvar);
+       return value ? xstrdup(value) : git_pathdup("%s", path);
+}
+
 static void setup_git_env(void)
 {
        const char *gitfile;
@@ -134,19 +140,9 @@ static void setup_git_env(void)
                git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
        gitfile = read_gitfile(git_dir);
        git_dir = xstrdup(gitfile ? gitfile : git_dir);
-       git_object_dir = getenv(DB_ENVIRONMENT);
-       if (!git_object_dir) {
-               git_object_dir = xmalloc(strlen(git_dir) + 9);
-               sprintf(git_object_dir, "%s/objects", git_dir);
-       }
-       git_index_file = getenv(INDEX_ENVIRONMENT);
-       if (!git_index_file) {
-               git_index_file = xmalloc(strlen(git_dir) + 7);
-               sprintf(git_index_file, "%s/index", git_dir);
-       }
-       git_graft_file = getenv(GRAFT_ENVIRONMENT);
-       if (!git_graft_file)
-               git_graft_file = git_pathdup("info/grafts");
+       git_object_dir = git_path_from_env(DB_ENVIRONMENT, "objects");
+       git_index_file = git_path_from_env(INDEX_ENVIRONMENT, "index");
+       git_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, "info/grafts");
        if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
                check_replace_refs = 0;
        namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
index f7f700ef516eea2fe908f66b1518b5e3a1d9fd33..1c2d7afd4cb9b70a324d355c3d69b732b181012b 100644 (file)
@@ -110,9 +110,9 @@ int ewah_serialize(struct ewah_bitmap *self, int fd)
        return ewah_serialize_to(self, write_helper, (void *)(intptr_t)fd);
 }
 
-int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len)
+int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len)
 {
-       uint8_t *ptr = map;
+       const uint8_t *ptr = map;
        size_t i;
 
        self->bit_size = get_be32(ptr);
index 43adeb5c6893381918ea948efa70b4756f831583..f6ad190a038a55e39a0b8b135c995413192f80e3 100644 (file)
@@ -99,8 +99,7 @@ int ewah_serialize(struct ewah_bitmap *self, int fd);
 int ewah_serialize_native(struct ewah_bitmap *self, int fd);
 
 int ewah_deserialize(struct ewah_bitmap *self, int fd);
-int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len);
-int ewah_read_mmap_native(struct ewah_bitmap *self, void *map, size_t len);
+int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len);
 
 uint32_t ewah_checksum(struct ewah_bitmap *self);
 
index 6707a66471f2038a0c826c840a201a7362de95ed..fa635f75c328354f7a4b44ca1e22461b1eb16de1 100644 (file)
@@ -371,8 +371,8 @@ static volatile sig_atomic_t checkpoint_requested;
 static int cat_blob_fd = STDOUT_FILENO;
 
 static void parse_argv(void);
-static void parse_cat_blob(void);
-static void parse_ls(struct branch *b);
+static void parse_cat_blob(const char *p);
+static void parse_ls(const char *p, struct branch *b);
 
 static void write_branch_report(FILE *rpt, struct branch *b)
 {
@@ -1861,6 +1861,8 @@ static int read_next_command(void)
        }
 
        for (;;) {
+               const char *p;
+
                if (unread_command_buf) {
                        unread_command_buf = 0;
                } else {
@@ -1893,8 +1895,8 @@ static int read_next_command(void)
                        rc->prev->next = rc;
                        cmd_tail = rc;
                }
-               if (starts_with(command_buf.buf, "cat-blob ")) {
-                       parse_cat_blob();
+               if (skip_prefix(command_buf.buf, "cat-blob ", &p)) {
+                       parse_cat_blob(p);
                        continue;
                }
                if (command_buf.buf[0] == '#')
@@ -1912,8 +1914,9 @@ static void skip_optional_lf(void)
 
 static void parse_mark(void)
 {
-       if (starts_with(command_buf.buf, "mark :")) {
-               next_mark = strtoumax(command_buf.buf + 6, NULL, 10);
+       const char *v;
+       if (skip_prefix(command_buf.buf, "mark :", &v)) {
+               next_mark = strtoumax(v, NULL, 10);
                read_next_command();
        }
        else
@@ -1922,14 +1925,15 @@ static void parse_mark(void)
 
 static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
 {
+       const char *data;
        strbuf_reset(sb);
 
-       if (!starts_with(command_buf.buf, "data "))
+       if (!skip_prefix(command_buf.buf, "data ", &data))
                die("Expected 'data n' command, found: %s", command_buf.buf);
 
-       if (starts_with(command_buf.buf + 5, "<<")) {
-               char *term = xstrdup(command_buf.buf + 5 + 2);
-               size_t term_len = command_buf.len - 5 - 2;
+       if (skip_prefix(data, "<<", &data)) {
+               char *term = xstrdup(data);
+               size_t term_len = command_buf.len - (data - command_buf.buf);
 
                strbuf_detach(&command_buf, NULL);
                for (;;) {
@@ -1944,7 +1948,7 @@ static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
                free(term);
        }
        else {
-               uintmax_t len = strtoumax(command_buf.buf + 5, NULL, 10);
+               uintmax_t len = strtoumax(data, NULL, 10);
                size_t n = 0, length = (size_t)len;
 
                if (limit && limit < len) {
@@ -2265,15 +2269,14 @@ static uintmax_t parse_mark_ref_space(const char **p)
        char *end;
 
        mark = parse_mark_ref(*p, &end);
-       if (*end != ' ')
+       if (*end++ != ' ')
                die("Missing space after mark: %s", command_buf.buf);
        *p = end;
        return mark;
 }
 
-static void file_change_m(struct branch *b)
+static void file_change_m(const char *p, struct branch *b)
 {
-       const char *p = command_buf.buf + 2;
        static struct strbuf uq = STRBUF_INIT;
        const char *endp;
        struct object_entry *oe;
@@ -2301,20 +2304,17 @@ static void file_change_m(struct branch *b)
        if (*p == ':') {
                oe = find_mark(parse_mark_ref_space(&p));
                hashcpy(sha1, oe->idx.sha1);
-       } else if (starts_with(p, "inline ")) {
+       } else if (skip_prefix(p, "inline ", &p)) {
                inline_data = 1;
                oe = NULL; /* not used with inline_data, but makes gcc happy */
-               p += strlen("inline");  /* advance to space */
        } else {
                if (get_sha1_hex(p, sha1))
                        die("Invalid dataref: %s", command_buf.buf);
                oe = find_object(sha1);
                p += 40;
-               if (*p != ' ')
+               if (*p++ != ' ')
                        die("Missing space after SHA1: %s", command_buf.buf);
        }
-       assert(*p == ' ');
-       p++;  /* skip space */
 
        strbuf_reset(&uq);
        if (!unquote_c_style(&uq, p, &endp)) {
@@ -2374,9 +2374,8 @@ static void file_change_m(struct branch *b)
        tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
 }
 
-static void file_change_d(struct branch *b)
+static void file_change_d(const char *p, struct branch *b)
 {
-       const char *p = command_buf.buf + 2;
        static struct strbuf uq = STRBUF_INIT;
        const char *endp;
 
@@ -2389,15 +2388,14 @@ static void file_change_d(struct branch *b)
        tree_content_remove(&b->branch_tree, p, NULL, 1);
 }
 
-static void file_change_cr(struct branch *b, int rename)
+static void file_change_cr(const char *s, struct branch *b, int rename)
 {
-       const char *s, *d;
+       const char *d;
        static struct strbuf s_uq = STRBUF_INIT;
        static struct strbuf d_uq = STRBUF_INIT;
        const char *endp;
        struct tree_entry leaf;
 
-       s = command_buf.buf + 2;
        strbuf_reset(&s_uq);
        if (!unquote_c_style(&s_uq, s, &endp)) {
                if (*endp != ' ')
@@ -2442,9 +2440,8 @@ static void file_change_cr(struct branch *b, int rename)
                leaf.tree);
 }
 
-static void note_change_n(struct branch *b, unsigned char *old_fanout)
+static void note_change_n(const char *p, struct branch *b, unsigned char *old_fanout)
 {
-       const char *p = command_buf.buf + 2;
        static struct strbuf uq = STRBUF_INIT;
        struct object_entry *oe;
        struct branch *s;
@@ -2474,20 +2471,17 @@ static void note_change_n(struct branch *b, unsigned char *old_fanout)
        if (*p == ':') {
                oe = find_mark(parse_mark_ref_space(&p));
                hashcpy(sha1, oe->idx.sha1);
-       } else if (starts_with(p, "inline ")) {
+       } else if (skip_prefix(p, "inline ", &p)) {
                inline_data = 1;
                oe = NULL; /* not used with inline_data, but makes gcc happy */
-               p += strlen("inline");  /* advance to space */
        } else {
                if (get_sha1_hex(p, sha1))
                        die("Invalid dataref: %s", command_buf.buf);
                oe = find_object(sha1);
                p += 40;
-               if (*p != ' ')
+               if (*p++ != ' ')
                        die("Missing space after SHA1: %s", command_buf.buf);
        }
-       assert(*p == ' ');
-       p++;  /* skip space */
 
        /* <commit-ish> */
        s = lookup_branch(p);
@@ -2585,7 +2579,7 @@ static int parse_from(struct branch *b)
        const char *from;
        struct branch *s;
 
-       if (!starts_with(command_buf.buf, "from "))
+       if (!skip_prefix(command_buf.buf, "from ", &from))
                return 0;
 
        if (b->branch_tree.tree) {
@@ -2593,7 +2587,6 @@ static int parse_from(struct branch *b)
                b->branch_tree.tree = NULL;
        }
 
-       from = strchr(command_buf.buf, ' ') + 1;
        s = lookup_branch(from);
        if (b == s)
                die("Can't create a branch from itself: %s", b->name);
@@ -2634,8 +2627,7 @@ static struct hash_list *parse_merge(unsigned int *count)
        struct branch *s;
 
        *count = 0;
-       while (starts_with(command_buf.buf, "merge ")) {
-               from = strchr(command_buf.buf, ' ') + 1;
+       while (skip_prefix(command_buf.buf, "merge ", &from)) {
                n = xmalloc(sizeof(*n));
                s = lookup_branch(from);
                if (s)
@@ -2666,31 +2658,29 @@ static struct hash_list *parse_merge(unsigned int *count)
        return list;
 }
 
-static void parse_new_commit(void)
+static void parse_new_commit(const char *arg)
 {
        static struct strbuf msg = STRBUF_INIT;
        struct branch *b;
-       char *sp;
        char *author = NULL;
        char *committer = NULL;
        struct hash_list *merge_list = NULL;
        unsigned int merge_count;
        unsigned char prev_fanout, new_fanout;
+       const char *v;
 
-       /* Obtain the branch name from the rest of our command */
-       sp = strchr(command_buf.buf, ' ') + 1;
-       b = lookup_branch(sp);
+       b = lookup_branch(arg);
        if (!b)
-               b = new_branch(sp);
+               b = new_branch(arg);
 
        read_next_command();
        parse_mark();
-       if (starts_with(command_buf.buf, "author ")) {
-               author = parse_ident(command_buf.buf + 7);
+       if (skip_prefix(command_buf.buf, "author ", &v)) {
+               author = parse_ident(v);
                read_next_command();
        }
-       if (starts_with(command_buf.buf, "committer ")) {
-               committer = parse_ident(command_buf.buf + 10);
+       if (skip_prefix(command_buf.buf, "committer ", &v)) {
+               committer = parse_ident(v);
                read_next_command();
        }
        if (!committer)
@@ -2710,20 +2700,20 @@ static void parse_new_commit(void)
 
        /* file_change* */
        while (command_buf.len > 0) {
-               if (starts_with(command_buf.buf, "M "))
-                       file_change_m(b);
-               else if (starts_with(command_buf.buf, "D "))
-                       file_change_d(b);
-               else if (starts_with(command_buf.buf, "R "))
-                       file_change_cr(b, 1);
-               else if (starts_with(command_buf.buf, "C "))
-                       file_change_cr(b, 0);
-               else if (starts_with(command_buf.buf, "N "))
-                       note_change_n(b, &prev_fanout);
+               if (skip_prefix(command_buf.buf, "M ", &v))
+                       file_change_m(v, b);
+               else if (skip_prefix(command_buf.buf, "D ", &v))
+                       file_change_d(v, b);
+               else if (skip_prefix(command_buf.buf, "R ", &v))
+                       file_change_cr(v, b, 1);
+               else if (skip_prefix(command_buf.buf, "C ", &v))
+                       file_change_cr(v, b, 0);
+               else if (skip_prefix(command_buf.buf, "N ", &v))
+                       note_change_n(v, b, &prev_fanout);
                else if (!strcmp("deleteall", command_buf.buf))
                        file_change_deleteall(b);
-               else if (starts_with(command_buf.buf, "ls "))
-                       parse_ls(b);
+               else if (skip_prefix(command_buf.buf, "ls ", &v))
+                       parse_ls(v, b);
                else {
                        unread_command_buf = 1;
                        break;
@@ -2766,10 +2756,9 @@ static void parse_new_commit(void)
        b->last_commit = object_count_by_type[OBJ_COMMIT];
 }
 
-static void parse_new_tag(void)
+static void parse_new_tag(const char *arg)
 {
        static struct strbuf msg = STRBUF_INIT;
-       char *sp;
        const char *from;
        char *tagger;
        struct branch *s;
@@ -2777,12 +2766,11 @@ static void parse_new_tag(void)
        uintmax_t from_mark = 0;
        unsigned char sha1[20];
        enum object_type type;
+       const char *v;
 
-       /* Obtain the new tag name from the rest of our command */
-       sp = strchr(command_buf.buf, ' ') + 1;
        t = pool_alloc(sizeof(struct tag));
        memset(t, 0, sizeof(struct tag));
-       t->name = pool_strdup(sp);
+       t->name = pool_strdup(arg);
        if (last_tag)
                last_tag->next_tag = t;
        else
@@ -2791,9 +2779,8 @@ static void parse_new_tag(void)
        read_next_command();
 
        /* from ... */
-       if (!starts_with(command_buf.buf, "from "))
+       if (!skip_prefix(command_buf.buf, "from ", &from))
                die("Expected from command, got %s", command_buf.buf);
-       from = strchr(command_buf.buf, ' ') + 1;
        s = lookup_branch(from);
        if (s) {
                if (is_null_sha1(s->sha1))
@@ -2819,8 +2806,8 @@ static void parse_new_tag(void)
        read_next_command();
 
        /* tagger ... */
-       if (starts_with(command_buf.buf, "tagger ")) {
-               tagger = parse_ident(command_buf.buf + 7);
+       if (skip_prefix(command_buf.buf, "tagger ", &v)) {
+               tagger = parse_ident(v);
                read_next_command();
        } else
                tagger = NULL;
@@ -2849,14 +2836,11 @@ static void parse_new_tag(void)
                t->pack_id = pack_id;
 }
 
-static void parse_reset_branch(void)
+static void parse_reset_branch(const char *arg)
 {
        struct branch *b;
-       char *sp;
 
-       /* Obtain the branch name from the rest of our command */
-       sp = strchr(command_buf.buf, ' ') + 1;
-       b = lookup_branch(sp);
+       b = lookup_branch(arg);
        if (b) {
                hashclr(b->sha1);
                hashclr(b->branch_tree.versions[0].sha1);
@@ -2867,7 +2851,7 @@ static void parse_reset_branch(void)
                }
        }
        else
-               b = new_branch(sp);
+               b = new_branch(arg);
        read_next_command();
        parse_from(b);
        if (command_buf.len > 0)
@@ -2925,14 +2909,12 @@ static void cat_blob(struct object_entry *oe, unsigned char sha1[20])
                free(buf);
 }
 
-static void parse_cat_blob(void)
+static void parse_cat_blob(const char *p)
 {
-       const char *p;
        struct object_entry *oe = oe;
        unsigned char sha1[20];
 
        /* cat-blob SP <object> LF */
-       p = command_buf.buf + strlen("cat-blob ");
        if (*p == ':') {
                oe = find_mark(parse_mark_ref_eol(p));
                if (!oe)
@@ -3015,6 +2997,8 @@ static struct object_entry *parse_treeish_dataref(const char **p)
                        die("Invalid dataref: %s", command_buf.buf);
                e = find_object(sha1);
                *p += 40;
+               if (*(*p)++ != ' ')
+                       die("Missing space after tree-ish: %s", command_buf.buf);
        }
 
        while (!e || e->type != OBJ_TREE)
@@ -3049,14 +3033,12 @@ static void print_ls(int mode, const unsigned char *sha1, const char *path)
        cat_blob_write(line.buf, line.len);
 }
 
-static void parse_ls(struct branch *b)
+static void parse_ls(const char *p, struct branch *b)
 {
-       const char *p;
        struct tree_entry *root = NULL;
        struct tree_entry leaf = {NULL};
 
        /* ls SP (<tree-ish> SP)? <path> */
-       p = command_buf.buf + strlen("ls ");
        if (*p == '"') {
                if (!b)
                        die("Not in a commit: %s", command_buf.buf);
@@ -3068,8 +3050,6 @@ static void parse_ls(struct branch *b)
                if (!is_null_sha1(root->versions[1].sha1))
                        root->versions[1].mode = S_IFDIR;
                load_tree(root);
-               if (*p++ != ' ')
-                       die("Missing space after tree-ish: %s", command_buf.buf);
        }
        if (*p == '"') {
                static struct strbuf uq = STRBUF_INIT;
@@ -3207,9 +3187,9 @@ static void option_export_pack_edges(const char *edges)
 
 static int parse_one_option(const char *option)
 {
-       if (starts_with(option, "max-pack-size=")) {
+       if (skip_prefix(option, "max-pack-size=", &option)) {
                unsigned long v;
-               if (!git_parse_ulong(option + 14, &v))
+               if (!git_parse_ulong(option, &v))
                        return 0;
                if (v < 8192) {
                        warning("max-pack-size is now in bytes, assuming --max-pack-size=%lum", v);
@@ -3219,17 +3199,17 @@ static int parse_one_option(const char *option)
                        v = 1024 * 1024;
                }
                max_packsize = v;
-       } else if (starts_with(option, "big-file-threshold=")) {
+       } else if (skip_prefix(option, "big-file-threshold=", &option)) {
                unsigned long v;
-               if (!git_parse_ulong(option + 19, &v))
+               if (!git_parse_ulong(option, &v))
                        return 0;
                big_file_threshold = v;
-       } else if (starts_with(option, "depth=")) {
-               option_depth(option + 6);
-       } else if (starts_with(option, "active-branches=")) {
-               option_active_branches(option + 16);
-       } else if (starts_with(option, "export-pack-edges=")) {
-               option_export_pack_edges(option + 18);
+       } else if (skip_prefix(option, "depth=", &option)) {
+               option_depth(option);
+       } else if (skip_prefix(option, "active-branches=", &option)) {
+               option_active_branches(option);
+       } else if (skip_prefix(option, "export-pack-edges=", &option)) {
+               option_export_pack_edges(option);
        } else if (starts_with(option, "quiet")) {
                show_stats = 0;
        } else if (starts_with(option, "stats")) {
@@ -3243,15 +3223,16 @@ static int parse_one_option(const char *option)
 
 static int parse_one_feature(const char *feature, int from_stream)
 {
-       if (starts_with(feature, "date-format=")) {
-               option_date_format(feature + 12);
-       } else if (starts_with(feature, "import-marks=")) {
-               option_import_marks(feature + 13, from_stream, 0);
-       } else if (starts_with(feature, "import-marks-if-exists=")) {
-               option_import_marks(feature + strlen("import-marks-if-exists="),
-                                       from_stream, 1);
-       } else if (starts_with(feature, "export-marks=")) {
-               option_export_marks(feature + 13);
+       const char *arg;
+
+       if (skip_prefix(feature, "date-format=", &arg)) {
+               option_date_format(arg);
+       } else if (skip_prefix(feature, "import-marks=", &arg)) {
+               option_import_marks(arg, from_stream, 0);
+       } else if (skip_prefix(feature, "import-marks-if-exists=", &arg)) {
+               option_import_marks(arg, from_stream, 1);
+       } else if (skip_prefix(feature, "export-marks=", &arg)) {
+               option_export_marks(arg);
        } else if (!strcmp(feature, "cat-blob")) {
                ; /* Don't die - this feature is supported */
        } else if (!strcmp(feature, "relative-marks")) {
@@ -3271,10 +3252,8 @@ static int parse_one_feature(const char *feature, int from_stream)
        return 1;
 }
 
-static void parse_feature(void)
+static void parse_feature(const char *feature)
 {
-       char *feature = command_buf.buf + 8;
-
        if (seen_data_command)
                die("Got feature command '%s' after data command", feature);
 
@@ -3284,10 +3263,8 @@ static void parse_feature(void)
        die("This version of fast-import does not support feature %s.", feature);
 }
 
-static void parse_option(void)
+static void parse_option(const char *option)
 {
-       char *option = command_buf.buf + 11;
-
        if (seen_data_command)
                die("Got option command '%s' after data command", option);
 
@@ -3342,18 +3319,21 @@ static void parse_argv(void)
                if (*a != '-' || !strcmp(a, "--"))
                        break;
 
-               if (parse_one_option(a + 2))
+               if (!skip_prefix(a, "--", &a))
+                       die("unknown option %s", a);
+
+               if (parse_one_option(a))
                        continue;
 
-               if (parse_one_feature(a + 2, 0))
+               if (parse_one_feature(a, 0))
                        continue;
 
-               if (starts_with(a + 2, "cat-blob-fd=")) {
-                       option_cat_blob_fd(a + 2 + strlen("cat-blob-fd="));
+               if (skip_prefix(a, "cat-blob-fd=", &a)) {
+                       option_cat_blob_fd(a);
                        continue;
                }
 
-               die("unknown option %s", a);
+               die("unknown option --%s", a);
        }
        if (i != global_argc)
                usage(fast_import_usage);
@@ -3400,26 +3380,27 @@ int main(int argc, char **argv)
        set_die_routine(die_nicely);
        set_checkpoint_signal();
        while (read_next_command() != EOF) {
+               const char *v;
                if (!strcmp("blob", command_buf.buf))
                        parse_new_blob();
-               else if (starts_with(command_buf.buf, "ls "))
-                       parse_ls(NULL);
-               else if (starts_with(command_buf.buf, "commit "))
-                       parse_new_commit();
-               else if (starts_with(command_buf.buf, "tag "))
-                       parse_new_tag();
-               else if (starts_with(command_buf.buf, "reset "))
-                       parse_reset_branch();
+               else if (skip_prefix(command_buf.buf, "ls ", &v))
+                       parse_ls(v, NULL);
+               else if (skip_prefix(command_buf.buf, "commit ", &v))
+                       parse_new_commit(v);
+               else if (skip_prefix(command_buf.buf, "tag ", &v))
+                       parse_new_tag(v);
+               else if (skip_prefix(command_buf.buf, "reset ", &v))
+                       parse_reset_branch(v);
                else if (!strcmp("checkpoint", command_buf.buf))
                        parse_checkpoint();
                else if (!strcmp("done", command_buf.buf))
                        break;
                else if (starts_with(command_buf.buf, "progress "))
                        parse_progress();
-               else if (starts_with(command_buf.buf, "feature "))
-                       parse_feature();
-               else if (starts_with(command_buf.buf, "option git "))
-                       parse_option();
+               else if (skip_prefix(command_buf.buf, "feature ", &v))
+                       parse_feature(v);
+               else if (skip_prefix(command_buf.buf, "option git ", &v))
+                       parse_option(v);
                else if (starts_with(command_buf.buf, "option "))
                        /* ignore non-git options*/;
                else
index b12bd4c59a1105f4d3a82bbbd5208e3f7ebae0bb..b8a58fa7a542fe166ad71c9b17a4908c81d24a4f 100644 (file)
@@ -189,20 +189,23 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)
 {
        int len;
        char *line = packet_read_line(fd, &len);
+       const char *arg;
 
        if (!len)
                die("git fetch-pack: expected ACK/NAK, got EOF");
        if (!strcmp(line, "NAK"))
                return NAK;
-       if (starts_with(line, "ACK ")) {
-               if (!get_sha1_hex(line+4, result_sha1)) {
-                       if (len < 45)
+       if (skip_prefix(line, "ACK ", &arg)) {
+               if (!get_sha1_hex(arg, result_sha1)) {
+                       arg += 40;
+                       len -= arg - line;
+                       if (len < 1)
                                return ACK;
-                       if (strstr(line+45, "continue"))
+                       if (strstr(arg, "continue"))
                                return ACK_continue;
-                       if (strstr(line+45, "common"))
+                       if (strstr(arg, "common"))
                                return ACK_common;
-                       if (strstr(line+45, "ready"))
+                       if (strstr(arg, "ready"))
                                return ACK_ready;
                        return ACK;
                }
@@ -319,18 +322,19 @@ static int find_common(struct fetch_pack_args *args,
 
        if (args->depth > 0) {
                char *line;
+               const char *arg;
                unsigned char sha1[20];
 
                send_request(args, fd[1], &req_buf);
                while ((line = packet_read_line(fd[0], NULL))) {
-                       if (starts_with(line, "shallow ")) {
-                               if (get_sha1_hex(line + 8, sha1))
+                       if (skip_prefix(line, "shallow ", &arg)) {
+                               if (get_sha1_hex(arg, sha1))
                                        die("invalid shallow line: %s", line);
                                register_shallow(sha1);
                                continue;
                        }
-                       if (starts_with(line, "unshallow ")) {
-                               if (get_sha1_hex(line + 10, sha1))
+                       if (skip_prefix(line, "unshallow ", &arg)) {
+                               if (get_sha1_hex(arg, sha1))
                                        die("invalid unshallow line: %s", line);
                                if (!lookup_object(sha1))
                                        die("object not found: %s", line);
diff --git a/fsck.c b/fsck.c
index abed62bac77c67e7c7447a18fea75f228406baac..a4e8593e78c85c244d2dc8bac0cca838c14bbd4a 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -276,22 +276,20 @@ static int fsck_ident(const char **ident, struct object *obj, fsck_error error_f
        return 0;
 }
 
-static int fsck_commit(struct commit *commit, fsck_error error_func)
+static int fsck_commit_buffer(struct commit *commit, const char *buffer,
+                             fsck_error error_func)
 {
-       const char *buffer = commit->buffer, *tmp;
        unsigned char tree_sha1[20], sha1[20];
        struct commit_graft *graft;
        int parents = 0;
        int err;
 
-       buffer = skip_prefix(buffer, "tree ");
-       if (!buffer)
+       if (!skip_prefix(buffer, "tree ", &buffer))
                return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line");
        if (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\n')
                return error_func(&commit->object, FSCK_ERROR, "invalid 'tree' line format - bad sha1");
        buffer += 41;
-       while ((tmp = skip_prefix(buffer, "parent "))) {
-               buffer = tmp;
+       while (skip_prefix(buffer, "parent ", &buffer)) {
                if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n')
                        return error_func(&commit->object, FSCK_ERROR, "invalid 'parent' line format - bad sha1");
                buffer += 41;
@@ -318,14 +316,12 @@ static int fsck_commit(struct commit *commit, fsck_error error_func)
                if (p || parents)
                        return error_func(&commit->object, FSCK_ERROR, "parent objects missing");
        }
-       buffer = skip_prefix(buffer, "author ");
-       if (!buffer)
+       if (!skip_prefix(buffer, "author ", &buffer))
                return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'author' line");
        err = fsck_ident(&buffer, &commit->object, error_func);
        if (err)
                return err;
-       buffer = skip_prefix(buffer, "committer ");
-       if (!buffer)
+       if (!skip_prefix(buffer, "committer ", &buffer))
                return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'committer' line");
        err = fsck_ident(&buffer, &commit->object, error_func);
        if (err)
@@ -336,6 +332,14 @@ static int fsck_commit(struct commit *commit, fsck_error error_func)
        return 0;
 }
 
+static int fsck_commit(struct commit *commit, fsck_error error_func)
+{
+       const char *buffer = get_commit_buffer(commit, NULL);
+       int ret = fsck_commit_buffer(commit, buffer, error_func);
+       unuse_commit_buffer(commit, buffer);
+       return ret;
+}
+
 static int fsck_tag(struct tag *tag, fsck_error error_func)
 {
        struct object *tagged = tag->tagged;
index b6f03b36dc762b891e77a5ca44e7ca5f270c5af9..9de31807108322aac327f66822d4929974d7ffd5 100644 (file)
@@ -349,13 +349,32 @@ extern void set_die_is_recursing_routine(int (*routine)(void));
 extern int starts_with(const char *str, const char *prefix);
 extern int ends_with(const char *str, const char *suffix);
 
-static inline const char *skip_prefix(const char *str, const char *prefix)
+/*
+ * If the string "str" begins with the string found in "prefix", return 1.
+ * The "out" parameter is set to "str + strlen(prefix)" (i.e., to the point in
+ * the string right after the prefix).
+ *
+ * Otherwise, return 0 and leave "out" untouched.
+ *
+ * Examples:
+ *
+ *   [extract branch name, fail if not a branch]
+ *   if (!skip_prefix(ref, "refs/heads/", &branch)
+ *     return -1;
+ *
+ *   [skip prefix if present, otherwise use whole string]
+ *   skip_prefix(name, "refs/heads/", &name);
+ */
+static inline int skip_prefix(const char *str, const char *prefix,
+                             const char **out)
 {
        do {
-               if (!*prefix)
-                       return str;
+               if (!*prefix) {
+                       *out = str;
+                       return 1;
+               }
        } while (*str++ == *prefix++);
-       return NULL;
+       return 0;
 }
 
 #if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
@@ -685,6 +704,17 @@ void git_qsort(void *base, size_t nmemb, size_t size,
 #endif
 #endif
 
+#if defined(__GNUC__) && defined(__x86_64__)
+#include <emmintrin.h>
+/*
+ * This is the system memory page size; it's used so that we can read
+ * outside the bounds of an allocation without segfaulting.
+ */
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 4096
+#endif
+#endif
+
 #ifdef UNRELIABLE_FSTAT
 #define fstat_is_reliable() 0
 #else
index 6d77b3ca9118e6fdd36615757a24c109cf0530ee..d3fb67d75bd3cdfebd048594cdb67eb179b1c435 100644 (file)
@@ -53,11 +53,12 @@ continue_merge () {
 }
 
 call_merge () {
-       cmt="$(cat "$state_dir/cmt.$1")"
+       msgnum="$1"
+       echo "$msgnum" >"$state_dir/msgnum"
+       cmt="$(cat "$state_dir/cmt.$msgnum")"
        echo "$cmt" > "$state_dir/current"
        hd=$(git rev-parse --verify HEAD)
        cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
-       msgnum=$(cat "$state_dir/msgnum")
        eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
        eval GITHEAD_$hd='$onto_name'
        export GITHEAD_$cmt GITHEAD_$hd
diff --git a/git.c b/git.c
index d6b4a5543f77071aff977f6d58e3ab6c5495f19d..5b6c7611be93c535491bbc9ba027e3acbad0db8f 100644 (file)
--- a/git.c
+++ b/git.c
@@ -91,8 +91,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                /*
                 * Check remaining flags.
                 */
-               if (starts_with(cmd, "--exec-path")) {
-                       cmd += 11;
+               if (skip_prefix(cmd, "--exec-path", &cmd)) {
                        if (*cmd == '=')
                                git_set_argv_exec_path(cmd + 1);
                        else {
@@ -129,8 +128,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
-               } else if (starts_with(cmd, "--git-dir=")) {
-                       setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
+               } else if (skip_prefix(cmd, "--git-dir=", &cmd)) {
+                       setenv(GIT_DIR_ENVIRONMENT, cmd, 1);
                        if (envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--namespace")) {
@@ -143,8 +142,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
-               } else if (starts_with(cmd, "--namespace=")) {
-                       setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1);
+               } else if (skip_prefix(cmd, "--namespace=", &cmd)) {
+                       setenv(GIT_NAMESPACE_ENVIRONMENT, cmd, 1);
                        if (envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--work-tree")) {
@@ -157,8 +156,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
-               } else if (starts_with(cmd, "--work-tree=")) {
-                       setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
+               } else if (skip_prefix(cmd, "--work-tree=", &cmd)) {
+                       setenv(GIT_WORK_TREE_ENVIRONMENT, cmd, 1);
                        if (envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--bare")) {
@@ -479,6 +478,7 @@ static struct cmd_struct commands[] = {
        { "upload-archive", cmd_upload_archive },
        { "upload-archive--writer", cmd_upload_archive_writer },
        { "var", cmd_var, RUN_SETUP_GENTLY },
+       { "verify-commit", cmd_verify_commit, RUN_SETUP },
        { "verify-pack", cmd_verify_pack },
        { "verify-tag", cmd_verify_tag, RUN_SETUP },
        { "version", cmd_version },
@@ -623,8 +623,7 @@ int main(int argc, char **av)
         * So we just directly call the builtin handler, and die if
         * that one cannot handle it.
         */
-       if (starts_with(cmd, "git-")) {
-               cmd += 4;
+       if (skip_prefix(cmd, "git-", &cmd)) {
                argv[0] = cmd;
                handle_builtin(argc, argv);
                die("cannot handle %s as a builtin", cmd);
@@ -635,8 +634,8 @@ int main(int argc, char **av)
        argc--;
        handle_options(&argv, &argc, NULL);
        if (argc > 0) {
-               if (starts_with(argv[0], "--"))
-                       argv[0] += 2;
+               /* translate --help and --version into commands */
+               skip_prefix(argv[0], "--", &argv[0]);
        } else {
                /* The user didn't specify a command; give them help */
                commit_pager_choice();
index 8b0e87436b687ce26e3e4987129a2fc171069280..ff07012726ea28daa2551966d0555d2e8efa2375 100644 (file)
@@ -7,6 +7,20 @@
 static char *configured_signing_key;
 static const char *gpg_program = "gpg";
 
+void signature_check_clear(struct signature_check *sigc)
+{
+       free(sigc->payload);
+       free(sigc->gpg_output);
+       free(sigc->gpg_status);
+       free(sigc->signer);
+       free(sigc->key);
+       sigc->payload = NULL;
+       sigc->gpg_output = NULL;
+       sigc->gpg_status = NULL;
+       sigc->signer = NULL;
+       sigc->key = NULL;
+}
+
 void set_signing_key(const char *key)
 {
        free(configured_signing_key);
index a85cb5bc97cdd61000b4c48c54faa656aa3cfaca..37c23daff010b0de18fa12ff6a6167f45ff41ffc 100644 (file)
@@ -2,6 +2,7 @@
 #define GPG_INTERFACE_H
 
 struct signature_check {
+       char *payload;
        char *gpg_output;
        char *gpg_status;
        char result; /* 0 (not checked),
@@ -13,6 +14,7 @@ struct signature_check {
        char *key;
 };
 
+extern void signature_check_clear(struct signature_check *sigc);
 extern int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key);
 extern int verify_signed_buffer(const char *payload, size_t payload_size, const char *signature, size_t signature_size, struct strbuf *gpg_output, struct strbuf *gpg_status);
 extern int git_gpg_config(const char *, const char *, void *);
diff --git a/help.c b/help.c
index b266b09320c067221eb4fffa56e4778d5f8b3a9e..f31f29ac421e224a59726fc1f2dfc16d92faad0f 100644 (file)
--- a/help.c
+++ b/help.c
@@ -129,7 +129,6 @@ static void list_commands_in_dir(struct cmdnames *cmds,
                                         const char *path,
                                         const char *prefix)
 {
-       int prefix_len;
        DIR *dir = opendir(path);
        struct dirent *de;
        struct strbuf buf = STRBUF_INIT;
@@ -139,15 +138,15 @@ static void list_commands_in_dir(struct cmdnames *cmds,
                return;
        if (!prefix)
                prefix = "git-";
-       prefix_len = strlen(prefix);
 
        strbuf_addf(&buf, "%s/", path);
        len = buf.len;
 
        while ((de = readdir(dir)) != NULL) {
+               const char *ent;
                int entlen;
 
-               if (!starts_with(de->d_name, prefix))
+               if (!skip_prefix(de->d_name, prefix, &ent))
                        continue;
 
                strbuf_setlen(&buf, len);
@@ -155,11 +154,11 @@ static void list_commands_in_dir(struct cmdnames *cmds,
                if (!is_executable(buf.buf))
                        continue;
 
-               entlen = strlen(de->d_name) - prefix_len;
-               if (has_extension(de->d_name, ".exe"))
+               entlen = strlen(ent);
+               if (has_extension(ent, ".exe"))
                        entlen -= 4;
 
-               add_cmdname(cmds, de->d_name + prefix_len, entlen);
+               add_cmdname(cmds, ent, entlen);
        }
        closedir(dir);
        strbuf_release(&buf);
@@ -251,11 +250,13 @@ static struct cmdnames aliases;
 
 static int git_unknown_cmd_config(const char *var, const char *value, void *cb)
 {
+       const char *p;
+
        if (!strcmp(var, "help.autocorrect"))
                autocorrect = git_config_int(var,value);
        /* Also use aliases for command lookup */
-       if (starts_with(var, "alias."))
-               add_cmdname(&aliases, var + 6, strlen(var + 6));
+       if (skip_prefix(var, "alias.", &p))
+               add_cmdname(&aliases, p, strlen(p));
 
        return git_default_config(var, value, cb);
 }
@@ -412,11 +413,12 @@ static int append_similar_ref(const char *refname, const unsigned char *sha1,
 {
        struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
        char *branch = strrchr(refname, '/') + 1;
+       const char *remote;
+
        /* A remote branch of the same name is deemed similar */
-       if (starts_with(refname, "refs/remotes/") &&
+       if (skip_prefix(refname, "refs/remotes/", &remote) &&
            !strcmp(branch, cb->base_ref))
-               string_list_append(cb->similar_refs,
-                                  refname + strlen("refs/remotes/"));
+               string_list_append(cb->similar_refs, remote);
        return 0;
 }
 
index d2c0a625cef558df252af6927cd3a86424674862..57290d9bdafc730afaac3ddf8564d85ead39eb31 100644 (file)
@@ -221,17 +221,19 @@ static void get_idx_file(char *name)
 
 static int http_config(const char *var, const char *value, void *cb)
 {
+       const char *p;
+
        if (!strcmp(var, "http.getanyfile")) {
                getanyfile = git_config_bool(var, value);
                return 0;
        }
 
-       if (starts_with(var, "http.")) {
+       if (skip_prefix(var, "http.", &p)) {
                int i;
 
                for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
                        struct rpc_service *svc = &rpc_service[i];
-                       if (!strcmp(var + 5, svc->config_name)) {
+                       if (!strcmp(p, svc->config_name)) {
                                svc->enabled = git_config_bool(var, value);
                                return 0;
                        }
@@ -244,15 +246,16 @@ static int http_config(const char *var, const char *value, void *cb)
 
 static struct rpc_service *select_service(const char *name)
 {
+       const char *svc_name;
        struct rpc_service *svc = NULL;
        int i;
 
-       if (!starts_with(name, "git-"))
+       if (!skip_prefix(name, "git-", &svc_name))
                forbidden("Unsupported service: '%s'", name);
 
        for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
                struct rpc_service *s = &rpc_service[i];
-               if (!strcmp(s->name, name + 4)) {
+               if (!strcmp(s->name, svc_name)) {
                        svc = s;
                        break;
                }
index de00d1693af902713ab5562e9d87fe621b02f955..6c3cc1725a9461876c7db11b9818eb553afb2fc2 100644 (file)
@@ -719,14 +719,10 @@ static int fetch_indices(void)
        return ret;
 }
 
-static void one_remote_object(const char *hex)
+static void one_remote_object(const unsigned char *sha1)
 {
-       unsigned char sha1[20];
        struct object *obj;
 
-       if (get_sha1_hex(hex, sha1) != 0)
-               return;
-
        obj = lookup_object(sha1);
        if (!obj)
                obj = parse_object(sha1);
@@ -767,15 +763,13 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
 
        if (tag_closed && ctx->cdata) {
                if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) {
-                       lock->owner = xmalloc(strlen(ctx->cdata) + 1);
-                       strcpy(lock->owner, ctx->cdata);
+                       lock->owner = xstrdup(ctx->cdata);
                } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TIMEOUT)) {
-                       if (starts_with(ctx->cdata, "Second-"))
-                               lock->timeout =
-                                       strtol(ctx->cdata + 7, NULL, 10);
+                       const char *arg;
+                       if (skip_prefix(ctx->cdata, "Second-", &arg))
+                               lock->timeout = strtol(arg, NULL, 10);
                } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
-                       lock->token = xmalloc(strlen(ctx->cdata) + 1);
-                       strcpy(lock->token, ctx->cdata);
+                       lock->token = xstrdup(ctx->cdata);
 
                        git_SHA1_Init(&sha_ctx);
                        git_SHA1_Update(&sha_ctx, lock->token, strlen(lock->token));
@@ -856,8 +850,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
        struct xml_ctx ctx;
        char *escaped;
 
-       url = xmalloc(strlen(repo->url) + strlen(path) + 1);
-       sprintf(url, "%s%s", repo->url, path);
+       url = xstrfmt("%s%s", repo->url, path);
 
        /* Make sure leading directories exist for the remote ref */
        ep = strchr(url + strlen(repo->url) + 1, '/');
@@ -1020,26 +1013,38 @@ static void remote_ls(const char *path, int flags,
                      void (*userFunc)(struct remote_ls_ctx *ls),
                      void *userData);
 
+/* extract hex from sharded "xx/x{40}" filename */
+static int get_sha1_hex_from_objpath(const char *path, unsigned char *sha1)
+{
+       char hex[40];
+
+       if (strlen(path) != 41)
+               return -1;
+
+       memcpy(hex, path, 2);
+       path += 2;
+       path++; /* skip '/' */
+       memcpy(hex, path, 38);
+
+       return get_sha1_hex(hex, sha1);
+}
+
 static void process_ls_object(struct remote_ls_ctx *ls)
 {
        unsigned int *parent = (unsigned int *)ls->userData;
-       char *path = ls->dentry_name;
-       char *obj_hex;
+       const char *path = ls->dentry_name;
+       unsigned char sha1[20];
 
        if (!strcmp(ls->path, ls->dentry_name) && (ls->flags & IS_DIR)) {
                remote_dir_exists[*parent] = 1;
                return;
        }
 
-       if (strlen(path) != 49)
+       if (!skip_prefix(path, "objects/", &path) ||
+           get_sha1_hex_from_objpath(path, sha1))
                return;
-       path += 8;
-       obj_hex = xmalloc(strlen(path));
-       /* NB: path is not null-terminated, can not use strlcpy here */
-       memcpy(obj_hex, path, 2);
-       strcpy(obj_hex + 2, path + 3);
-       one_remote_object(obj_hex);
-       free(obj_hex);
+
+       one_remote_object(sha1);
 }
 
 static void process_ls_ref(struct remote_ls_ctx *ls)
@@ -1117,7 +1122,7 @@ static void remote_ls(const char *path, int flags,
                      void (*userFunc)(struct remote_ls_ctx *ls),
                      void *userData)
 {
-       char *url = xmalloc(strlen(repo->url) + strlen(path) + 1);
+       char *url = xstrfmt("%s%s", repo->url, path);
        struct active_request_slot *slot;
        struct slot_results results;
        struct strbuf in_buffer = STRBUF_INIT;
@@ -1133,8 +1138,6 @@ static void remote_ls(const char *path, int flags,
        ls.userData = userData;
        ls.userFunc = userFunc;
 
-       sprintf(url, "%s%s", repo->url, path);
-
        strbuf_addf(&out_buffer.buf, PROPFIND_ALL_REQUEST);
 
        dav_headers = curl_slist_append(dav_headers, "Depth: 1");
@@ -1536,10 +1539,9 @@ static void update_remote_info_refs(struct remote_lock *lock)
 
 static int remote_exists(const char *path)
 {
-       char *url = xmalloc(strlen(repo->url) + strlen(path) + 1);
+       char *url = xstrfmt("%s%s", repo->url, path);
        int ret;
 
-       sprintf(url, "%s%s", repo->url, path);
 
        switch (http_get_strbuf(url, NULL, NULL)) {
        case HTTP_OK:
@@ -1559,11 +1561,9 @@ static int remote_exists(const char *path)
 
 static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
 {
-       char *url;
+       char *url = xstrfmt("%s%s", repo->url, path);
        struct strbuf buffer = STRBUF_INIT;
-
-       url = xmalloc(strlen(repo->url) + strlen(path) + 1);
-       sprintf(url, "%s%s", repo->url, path);
+       const char *name;
 
        if (http_get_strbuf(url, &buffer, NULL) != HTTP_OK)
                die("Couldn't get %s for remote symref\n%s", url,
@@ -1578,8 +1578,8 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
                return;
 
        /* If it's a symref, set the refname; otherwise try for a sha1 */
-       if (starts_with((char *)buffer.buf, "ref: ")) {
-               *symref = xmemdupz((char *)buffer.buf + 5, buffer.len - 6);
+       if (skip_prefix(buffer.buf, "ref: ", &name)) {
+               *symref = xmemdupz(name, buffer.len - (name - buffer.buf));
        } else {
                get_sha1_hex(buffer.buf, sha1);
        }
@@ -1673,8 +1673,7 @@ static int delete_remote_branch(const char *pattern, int force)
        fprintf(stderr, "Removing remote branch '%s'\n", remote_ref->name);
        if (dry_run)
                return 0;
-       url = xmalloc(strlen(repo->url) + strlen(remote_ref->name) + 1);
-       sprintf(url, "%s%s", repo->url, remote_ref->name);
+       url = xstrfmt("%s%s", repo->url, remote_ref->name);
        slot = get_active_slot();
        slot->results = &results;
        curl_setup_http_get(slot->curl, url, DAV_DELETE);
index 1516c5eb290746bad4551515fda7930d2f55d32b..dbddfaa1773265a2917d4715158f16845d7139e5 100644 (file)
@@ -341,8 +341,7 @@ static void fetch_alternates(struct walker *walker, const char *base)
        if (walker->get_verbosely)
                fprintf(stderr, "Getting alternates list for %s\n", base);
 
-       url = xmalloc(strlen(base) + 31);
-       sprintf(url, "%s/objects/info/http-alternates", base);
+       url = xstrfmt("%s/objects/info/http-alternates", base);
 
        /*
         * Use a callback to process the result, since another request
@@ -566,8 +565,7 @@ struct walker *get_http_walker(const char *url)
        struct walker *walker = xmalloc(sizeof(struct walker));
 
        data->alt = xmalloc(sizeof(*data->alt));
-       data->alt->base = xmalloc(strlen(url) + 1);
-       strcpy(data->alt->base, url);
+       data->alt->base = xstrdup(url);
        for (s = data->alt->base + strlen(data->alt->base) - 1; *s == '/'; --s)
                *s = 0;
 
diff --git a/http.c b/http.c
index 2b4f6a357c39f3685107da3cf63a9891470a6de9..c8cd50dd0c2b2213a86957cd4b24c03c0d85717d 100644 (file)
--- a/http.c
+++ b/http.c
@@ -927,7 +927,7 @@ static int extract_param(const char *raw, const char *name,
                return -1;
        raw++;
 
-       while (*raw && !isspace(*raw))
+       while (*raw && !isspace(*raw) && *raw != ';')
                strbuf_addch(out, *raw++);
        return 0;
 }
@@ -971,7 +971,7 @@ static void extract_content_type(struct strbuf *raw, struct strbuf *type,
 
        strbuf_reset(charset);
        while (*p) {
-               while (isspace(*p))
+               while (isspace(*p) || *p == ';')
                        p++;
                if (!extract_param(p, "charset", charset))
                        return;
@@ -1087,11 +1087,10 @@ static int update_url_from_redirect(struct strbuf *base,
        if (!strcmp(asked, got->buf))
                return 0;
 
-       if (!starts_with(asked, base->buf))
+       if (!skip_prefix(asked, base->buf, &tail))
                die("BUG: update_url_from_redirect: %s is not a superset of %s",
                    asked, base->buf);
 
-       tail = asked + base->len;
        tail_len = strlen(tail);
 
        if (got->len < tail_len ||
index 83a6ed2ac338d7fc82f3bed6fcb93ffe8dae5739..524fbabc96f450f1faed196c1743d2f8d697b587 100644 (file)
@@ -1328,13 +1328,9 @@ static char *imap_folder;
 
 static int git_imap_config(const char *key, const char *val, void *cb)
 {
-       char imap_key[] = "imap.";
-
-       if (strncmp(key, imap_key, sizeof imap_key - 1))
+       if (!skip_prefix(key, "imap.", &key))
                return 0;
 
-       key += sizeof imap_key - 1;
-
        /* check booleans first, and barf on others */
        if (!strcmp("sslverify", key))
                server.ssl_verify = git_config_bool(key, val);
index 150010105806291180d9e6bdfcf1939fb242f2ac..afcc98db930a36358283ff199a428b6c5bbbaf8f 100644 (file)
@@ -1174,9 +1174,7 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm
                         */
                        add_line_range(rev, parents[i], cand[i]);
                        clear_commit_line_range(rev, commit);
-                       commit->parents = xmalloc(sizeof(struct commit_list));
-                       commit->parents->item = parents[i];
-                       commit->parents->next = NULL;
+                       commit_list_append(parents[i], &commit->parents);
                        free(parents);
                        free(cand);
                        free_diffqueues(nparents, diffqueues);
index 8fbcb6a98aae85b48c6f8de29c6f95af58823411..b7066143494bcfa3e629dd718daf8959af6c4b86 100644 (file)
@@ -5,7 +5,6 @@
 #include "sigchain.h"
 
 static struct lock_file *lock_file_list;
-static const char *alternate_index_output;
 
 static void remove_lock_file(void)
 {
@@ -252,25 +251,6 @@ int hold_locked_index(struct lock_file *lk, int die_on_error)
                                         : 0);
 }
 
-void set_alternate_index_output(const char *name)
-{
-       alternate_index_output = name;
-}
-
-int commit_locked_index(struct lock_file *lk)
-{
-       if (alternate_index_output) {
-               if (lk->fd >= 0 && close_lock_file(lk))
-                       return -1;
-               if (rename(lk->filename, alternate_index_output))
-                       return -1;
-               lk->filename[0] = 0;
-               return 0;
-       }
-       else
-               return commit_lock_file(lk);
-}
-
 void rollback_lock_file(struct lock_file *lk)
 {
        if (lk->filename[0]) {
index cf2f86c86632c6df8862fc065299e6cc35aa4829..10e68442b35c9bd9ca1c00c52609a4827b8bace5 100644 (file)
@@ -376,7 +376,7 @@ static void show_signature(struct rev_info *opt, struct commit *commit)
        struct strbuf gpg_output = STRBUF_INIT;
        int status;
 
-       if (parse_signed_commit(commit->object.sha1, &payload, &signature) <= 0)
+       if (parse_signed_commit(commit, &payload, &signature) <= 0)
                goto out;
 
        status = verify_signed_buffer(payload.buf, payload.len,
@@ -588,7 +588,7 @@ void show_log(struct rev_info *opt)
                show_mergetag(opt, commit);
        }
 
-       if (!commit->buffer)
+       if (!get_cached_commit_buffer(commit, NULL))
                return;
 
        if (opt->show_notes) {
index e80b4af354f87caa7a5130660e9c9c530246b8be..1ce0954a3e5915a6bcc30448c9d99fdaba32bcab 100644 (file)
@@ -140,17 +140,12 @@ static void match_trees(const unsigned char *hash1,
                        goto next;
                score = score_trees(elem, hash2);
                if (*best_score < score) {
-                       char *newpath;
-                       newpath = xmalloc(strlen(base) + strlen(path) + 1);
-                       sprintf(newpath, "%s%s", base, path);
                        free(*best_match);
-                       *best_match = newpath;
+                       *best_match = xstrfmt("%s%s", base, path);
                        *best_score = score;
                }
                if (recurse_limit) {
-                       char *newbase;
-                       newbase = xmalloc(strlen(base) + strlen(path) + 2);
-                       sprintf(newbase, "%s%s/", base, path);
+                       char *newbase = xstrfmt("%s%s/", base, path);
                        match_trees(elem, hash2, best_score, best_match,
                                    newbase, recurse_limit - 1);
                        free(newbase);
index cab16fafb5c2b7792c78d3a7fbace1f43ab099cc..8e44d7e5f3d72ee71580dc98d5e7d13c4d56779f 100644 (file)
@@ -40,7 +40,7 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two,
 
 static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
 {
-       struct commit *commit = xcalloc(1, sizeof(struct commit));
+       struct commit *commit = alloc_commit_node();
        struct merge_remote_desc *desc = xmalloc(sizeof(*desc));
 
        desc->name = comment;
@@ -190,9 +190,11 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)
                        printf(_("(bad commit)\n"));
                else {
                        const char *title;
-                       int len = find_commit_subject(commit->buffer, &title);
+                       const char *msg = get_commit_buffer(commit, NULL);
+                       int len = find_commit_subject(msg, &title);
                        if (len)
                                printf("%.*s\n", len, title);
+                       unuse_commit_buffer(commit, msg);
                }
        }
 }
@@ -265,9 +267,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)
                active_cache_tree = cache_tree();
 
        if (!cache_tree_fully_valid(active_cache_tree) &&
-           cache_tree_update(active_cache_tree,
-                             (const struct cache_entry * const *)active_cache,
-                             active_nr, 0) < 0)
+           cache_tree_update(&the_index, 0) < 0)
                die(_("error building trees"));
 
        result = lookup_tree(active_cache_tree->sha1);
@@ -601,25 +601,36 @@ static int remove_file(struct merge_options *o, int clean,
        return 0;
 }
 
+/* add a string to a strbuf, but converting "/" to "_" */
+static void add_flattened_path(struct strbuf *out, const char *s)
+{
+       size_t i = out->len;
+       strbuf_addstr(out, s);
+       for (; i < out->len; i++)
+               if (out->buf[i] == '/')
+                       out->buf[i] = '_';
+}
+
 static char *unique_path(struct merge_options *o, const char *path, const char *branch)
 {
-       char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);
+       struct strbuf newpath = STRBUF_INIT;
        int suffix = 0;
        struct stat st;
-       char *p = newpath + strlen(path);
-       strcpy(newpath, path);
-       *(p++) = '~';
-       strcpy(p, branch);
-       for (; *p; ++p)
-               if ('/' == *p)
-                       *p = '_';
-       while (string_list_has_string(&o->current_file_set, newpath) ||
-              string_list_has_string(&o->current_directory_set, newpath) ||
-              lstat(newpath, &st) == 0)
-               sprintf(p, "_%d", suffix++);
-
-       string_list_insert(&o->current_file_set, newpath);
-       return newpath;
+       size_t base_len;
+
+       strbuf_addf(&newpath, "%s~", path);
+       add_flattened_path(&newpath, branch);
+
+       base_len = newpath.len;
+       while (string_list_has_string(&o->current_file_set, newpath.buf) ||
+              string_list_has_string(&o->current_directory_set, newpath.buf) ||
+              lstat(newpath.buf, &st) == 0) {
+               strbuf_setlen(&newpath, base_len);
+               strbuf_addf(&newpath, "_%d", suffix++);
+       }
+
+       string_list_insert(&o->current_file_set, newpath.buf);
+       return strbuf_detach(&newpath, NULL);
 }
 
 static int dir_in_way(const char *path, int check_working_copy)
@@ -969,14 +980,10 @@ merge_file_special_markers(struct merge_options *o,
        char *side2 = NULL;
        struct merge_file_info mfi;
 
-       if (filename1) {
-               side1 = xmalloc(strlen(branch1) + strlen(filename1) + 2);
-               sprintf(side1, "%s:%s", branch1, filename1);
-       }
-       if (filename2) {
-               side2 = xmalloc(strlen(branch2) + strlen(filename2) + 2);
-               sprintf(side2, "%s:%s", branch2, filename2);
-       }
+       if (filename1)
+               side1 = xstrfmt("%s:%s", branch1, filename1);
+       if (filename2)
+               side2 = xstrfmt("%s:%s", branch2, filename2);
 
        mfi = merge_file_1(o, one, a, b,
                           side1 ? side1 : branch1, side2 ? side2 : branch2);
@@ -1992,7 +1999,7 @@ int merge_recursive_generic(struct merge_options *o,
                            const unsigned char **base_list,
                            struct commit **result)
 {
-       int clean, index_fd;
+       int clean;
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
        struct commit *head_commit = get_ref(head, o->branch1);
        struct commit *next_commit = get_ref(merge, o->branch2);
@@ -2009,12 +2016,11 @@ int merge_recursive_generic(struct merge_options *o,
                }
        }
 
-       index_fd = hold_locked_index(lock, 1);
+       hold_locked_index(lock, 1);
        clean = merge_recursive(o, head_commit, next_commit, ca,
                        result);
        if (active_cache_changed &&
-                       (write_cache(index_fd, active_cache, active_nr) ||
-                        commit_locked_index(lock)))
+           write_locked_index(&the_index, lock, COMMIT_LOCK))
                return error(_("Unable to write index."));
 
        return clean ? 0 : 1;
@@ -2063,6 +2069,8 @@ void init_merge_options(struct merge_options *o)
 
 int parse_merge_opt(struct merge_options *o, const char *s)
 {
+       const char *arg;
+
        if (!s || !*s)
                return -1;
        if (!strcmp(s, "ours"))
@@ -2071,14 +2079,14 @@ int parse_merge_opt(struct merge_options *o, const char *s)
                o->recursive_variant = MERGE_RECURSIVE_THEIRS;
        else if (!strcmp(s, "subtree"))
                o->subtree_shift = "";
-       else if (starts_with(s, "subtree="))
-               o->subtree_shift = s + strlen("subtree=");
+       else if (skip_prefix(s, "subtree=", &arg))
+               o->subtree_shift = arg;
        else if (!strcmp(s, "patience"))
                o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);
        else if (!strcmp(s, "histogram"))
                o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);
-       else if (starts_with(s, "diff-algorithm=")) {
-               long value = parse_algorithm_value(s + strlen("diff-algorithm="));
+       else if (skip_prefix(s, "diff-algorithm=", &arg)) {
+               long value = parse_algorithm_value(arg);
                if (value < 0)
                        return -1;
                /* clear out previous settings */
@@ -2096,9 +2104,8 @@ int parse_merge_opt(struct merge_options *o, const char *s)
                o->renormalize = 1;
        else if (!strcmp(s, "no-renormalize"))
                o->renormalize = 0;
-       else if (starts_with(s, "rename-threshold=")) {
-               const char *score = s + strlen("rename-threshold=");
-               if ((o->rename_score = parse_rename_score(&score)) == -1 || *score != 0)
+       else if (skip_prefix(s, "rename-threshold=", &arg)) {
+               if ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
                        return -1;
        }
        else
diff --git a/merge.c b/merge.c
index 70f1000fcbef5cb04e5b25791b482b5f40fab204..74ced7f70b5beec045c134b5719c926fc791f1e2 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -18,39 +18,23 @@ int try_merge_command(const char *strategy, size_t xopts_nr,
                      const char **xopts, struct commit_list *common,
                      const char *head_arg, struct commit_list *remotes)
 {
-       const char **args;
-       int i = 0, x = 0, ret;
+       struct argv_array args = ARGV_ARRAY_INIT;
+       int i, ret;
        struct commit_list *j;
-       struct strbuf buf = STRBUF_INIT;
 
-       args = xmalloc((4 + xopts_nr + commit_list_count(common) +
-                       commit_list_count(remotes)) * sizeof(char *));
-       strbuf_addf(&buf, "merge-%s", strategy);
-       args[i++] = buf.buf;
-       for (x = 0; x < xopts_nr; x++) {
-               char *s = xmalloc(strlen(xopts[x])+2+1);
-               strcpy(s, "--");
-               strcpy(s+2, xopts[x]);
-               args[i++] = s;
-       }
-       for (j = common; j; j = j->next)
-               args[i++] = xstrdup(merge_argument(j->item));
-       args[i++] = "--";
-       args[i++] = head_arg;
-       for (j = remotes; j; j = j->next)
-               args[i++] = xstrdup(merge_argument(j->item));
-       args[i] = NULL;
-       ret = run_command_v_opt(args, RUN_GIT_CMD);
-       strbuf_release(&buf);
-       i = 1;
-       for (x = 0; x < xopts_nr; x++)
-               free((void *)args[i++]);
+       argv_array_pushf(&args, "merge-%s", strategy);
+       for (i = 0; i < xopts_nr; i++)
+               argv_array_pushf(&args, "--%s", xopts[i]);
        for (j = common; j; j = j->next)
-               free((void *)args[i++]);
-       i += 2;
+               argv_array_push(&args, merge_argument(j->item));
+       argv_array_push(&args, "--");
+       argv_array_push(&args, head_arg);
        for (j = remotes; j; j = j->next)
-               free((void *)args[i++]);
-       free(args);
+               argv_array_push(&args, merge_argument(j->item));
+
+       ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
+       argv_array_clear(&args);
+
        discard_cache();
        if (read_cache() < 0)
                die(_("failed to read the cache"));
@@ -66,13 +50,13 @@ int checkout_fast_forward(const unsigned char *head,
        struct tree *trees[MAX_UNPACK_TREES];
        struct unpack_trees_options opts;
        struct tree_desc t[MAX_UNPACK_TREES];
-       int i, fd, nr_trees = 0;
+       int i, nr_trees = 0;
        struct dir_struct dir;
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
        refresh_cache(REFRESH_QUIET);
 
-       fd = hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, 1);
 
        memset(&trees, 0, sizeof(trees));
        memset(&opts, 0, sizeof(opts));
@@ -105,8 +89,7 @@ int checkout_fast_forward(const unsigned char *head,
        }
        if (unpack_trees(nr_trees, t, &opts))
                return -1;
-       if (write_cache(fd, active_cache, active_nr) ||
-               commit_locked_index(lock_file))
+       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
        return 0;
 }
index 97dfd63c9bf564ac052addc4a3f62a325bbc1303..c4e9bb7f6c0bde97c7c6d34094baa5ef7e318157 100644 (file)
@@ -48,7 +48,6 @@ int notes_cache_write(struct notes_cache *c)
 {
        unsigned char tree_sha1[20];
        unsigned char commit_sha1[20];
-       struct strbuf msg = STRBUF_INIT;
 
        if (!c || !c->tree.initialized || !c->tree.ref || !*c->tree.ref)
                return -1;
@@ -57,9 +56,8 @@ int notes_cache_write(struct notes_cache *c)
 
        if (write_notes_tree(&c->tree, tree_sha1))
                return -1;
-       strbuf_attach(&msg, c->validity,
-                     strlen(c->validity), strlen(c->validity) + 1);
-       if (commit_tree(&msg, tree_sha1, NULL, commit_sha1, NULL, NULL) < 0)
+       if (commit_tree(c->validity, strlen(c->validity), tree_sha1, NULL,
+                       commit_sha1, NULL, NULL) < 0)
                return -1;
        if (update_ref("update notes cache", c->tree.ref, commit_sha1, NULL,
                       0, UPDATE_REFS_QUIET_ON_ERR) < 0)
index 94a1a8ae466733b207ae5030f8a8894d66535f91..fd5fae255d2760d6b7895e95cee627b5e28f776e 100644 (file)
@@ -644,7 +644,8 @@ int notes_merge(struct notes_merge_options *o,
                struct commit_list *parents = NULL;
                commit_list_insert(remote, &parents); /* LIFO order */
                commit_list_insert(local, &parents);
-               create_notes_commit(local_tree, parents, &o->commit_msg,
+               create_notes_commit(local_tree, parents,
+                                   o->commit_msg.buf, o->commit_msg.len,
                                    result_sha1);
        }
 
@@ -671,8 +672,8 @@ int notes_merge_commit(struct notes_merge_options *o,
        DIR *dir;
        struct dirent *e;
        struct strbuf path = STRBUF_INIT;
-       char *msg = strstr(partial_commit->buffer, "\n\n");
-       struct strbuf sb_msg = STRBUF_INIT;
+       const char *buffer = get_commit_buffer(partial_commit, NULL);
+       const char *msg = strstr(buffer, "\n\n");
        int baselen;
 
        strbuf_addstr(&path, git_path(NOTES_MERGE_WORKTREE));
@@ -719,9 +720,9 @@ int notes_merge_commit(struct notes_merge_options *o,
                strbuf_setlen(&path, baselen);
        }
 
-       strbuf_attach(&sb_msg, msg, strlen(msg), strlen(msg) + 1);
-       create_notes_commit(partial_tree, partial_commit->parents, &sb_msg,
-                           result_sha1);
+       create_notes_commit(partial_tree, partial_commit->parents,
+                           msg, strlen(msg), result_sha1);
+       unuse_commit_buffer(partial_commit, buffer);
        if (o->verbosity >= 4)
                printf("Finalized notes merge commit: %s\n",
                        sha1_to_hex(result_sha1));
index a0b1d7be98253368921a6e95653ceb36dbda28b2..b64dc1b0219928ac149894c99a63dcc9fd419668 100644 (file)
@@ -4,7 +4,8 @@
 #include "notes-utils.h"
 
 void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
-                        const struct strbuf *msg, unsigned char *result_sha1)
+                        const char *msg, size_t msg_len,
+                        unsigned char *result_sha1)
 {
        unsigned char tree_sha1[20];
 
@@ -25,7 +26,7 @@ void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
                /* else: t->ref points to nothing, assume root/orphan commit */
        }
 
-       if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL, NULL))
+       if (commit_tree(msg, msg_len, tree_sha1, parents, result_sha1, NULL, NULL))
                die("Failed to commit notes tree to database");
 }
 
@@ -46,7 +47,7 @@ void commit_notes(struct notes_tree *t, const char *msg)
        if (buf.buf[buf.len - 1] != '\n')
                strbuf_addch(&buf, '\n'); /* Make sure msg ends with newline */
 
-       create_notes_commit(t, NULL, &buf, commit_sha1);
+       create_notes_commit(t, NULL, buf.buf, buf.len, commit_sha1);
        strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
        update_ref(buf.buf, t->ref, commit_sha1, NULL, 0,
                   UPDATE_REFS_DIE_ON_ERR);
index 564e30cccd550f9acbd493f43c940bab1abba427..890ddb33e13ad6b9d43887216acbe5430ed534cc 100644 (file)
@@ -15,7 +15,7 @@
  * The resulting commit SHA1 is stored in result_sha1.
  */
 void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
-                        const struct strbuf *msg, unsigned char *result_sha1);
+                        const char *msg, size_t msg_len, unsigned char *result_sha1);
 
 void commit_notes(struct notes_tree *t, const char *msg);
 
index 57a0890a87b66ea493337409606e57fd44d29f90..9c31e9a5e0e0d913044501089e09037e47894f03 100644 (file)
--- a/object.c
+++ b/object.c
@@ -197,8 +197,8 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
                if (commit) {
                        if (parse_commit_buffer(commit, buffer, size))
                                return NULL;
-                       if (!commit->buffer) {
-                               commit->buffer = buffer;
+                       if (!get_cached_commit_buffer(commit, NULL)) {
+                               set_commit_buffer(commit, buffer, size);
                                *eaten_p = 1;
                        }
                        obj = &commit->object;
index b536896f2689dee23afae7d3a169c065592aaabe..e7dafa80d55adb9b863c23f711389e5eaa6b2c5e 100644 (file)
@@ -231,7 +231,8 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
                        continue;
 
 again:
-               rest = skip_prefix(arg, long_name);
+               if (!skip_prefix(arg, long_name, &rest))
+                       rest = NULL;
                if (options->type == OPTION_ARGUMENT) {
                        if (!rest)
                                continue;
@@ -280,12 +281,13 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
                                continue;
                        }
                        flags |= OPT_UNSET;
-                       rest = skip_prefix(arg + 3, long_name);
-                       /* abbreviated and negated? */
-                       if (!rest && starts_with(long_name, arg + 3))
-                               goto is_abbreviated;
-                       if (!rest)
-                               continue;
+                       if (!skip_prefix(arg + 3, long_name, &rest)) {
+                               /* abbreviated and negated? */
+                               if (starts_with(long_name, arg + 3))
+                                       goto is_abbreviated;
+                               else
+                                       continue;
+                       }
                }
                if (*rest) {
                        if (*rest != '=')
index 968ee25eae13f82fe4f93254d77660456732a5c6..79ce8a998b0008f9182fa127b85d63feff4592d0 100644 (file)
@@ -37,9 +37,8 @@ static void *preload_thread(void *_data)
        struct thread_data *p = _data;
        struct index_state *index = p->index;
        struct cache_entry **cep = index->cache + p->offset;
-       struct cache_def cache;
+       struct cache_def cache = CACHE_DEF_INIT;
 
-       memset(&cache, 0, sizeof(cache));
        nr = p->nr;
        if (nr + p->offset > index->cache_nr)
                nr = index->cache_nr - p->offset;
@@ -64,6 +63,7 @@ static void *preload_thread(void *_data)
                        continue;
                ce_mark_uptodate(ce);
        } while (--nr > 0);
+       cache_def_free(&cache);
        return NULL;
 }
 
index 4f512876ceb20f538256bac30dd98288abe2d682..14357e233f3174963add310a29bb35c3de229121 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -40,10 +40,9 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
        const char *fmt;
        int i;
 
-       if (!starts_with(var, "pretty."))
+       if (!skip_prefix(var, "pretty.", &name))
                return 0;
 
-       name = var + strlen("pretty.");
        for (i = 0; i < builtin_formats_len; i++) {
                if (!strcmp(commit_formats[i].name, name))
                        return 0;
@@ -274,7 +273,7 @@ static void add_rfc822_quoted(struct strbuf *out, const char *s, int len)
 
 enum rfc2047_type {
        RFC2047_SUBJECT,
-       RFC2047_ADDRESS,
+       RFC2047_ADDRESS
 };
 
 static int is_rfc2047_special(char ch, enum rfc2047_type type)
@@ -606,29 +605,16 @@ static char *replace_encoding_header(char *buf, const char *encoding)
        return strbuf_detach(&tmp, NULL);
 }
 
-char *logmsg_reencode(const struct commit *commit,
-                     char **commit_encoding,
-                     const char *output_encoding)
+const char *logmsg_reencode(const struct commit *commit,
+                           char **commit_encoding,
+                           const char *output_encoding)
 {
        static const char *utf8 = "UTF-8";
        const char *use_encoding;
        char *encoding;
-       char *msg = commit->buffer;
+       const char *msg = get_commit_buffer(commit, NULL);
        char *out;
 
-       if (!msg) {
-               enum object_type type;
-               unsigned long size;
-
-               msg = read_sha1_file(commit->object.sha1, &type, &size);
-               if (!msg)
-                       die("Cannot read commit object %s",
-                           sha1_to_hex(commit->object.sha1));
-               if (type != OBJ_COMMIT)
-                       die("Expected commit for '%s', got %s",
-                           sha1_to_hex(commit->object.sha1), typename(type));
-       }
-
        if (!output_encoding || !*output_encoding) {
                if (commit_encoding)
                        *commit_encoding =
@@ -652,12 +638,13 @@ char *logmsg_reencode(const struct commit *commit,
                 * Otherwise, we still want to munge the encoding header in the
                 * result, which will be done by modifying the buffer. If we
                 * are using a fresh copy, we can reuse it. But if we are using
-                * the cached copy from commit->buffer, we need to duplicate it
-                * to avoid munging commit->buffer.
+                * the cached copy from get_commit_buffer, we need to duplicate it
+                * to avoid munging the cached copy.
                 */
-               out = msg;
-               if (out == commit->buffer)
-                       out = xstrdup(out);
+               if (msg == get_cached_commit_buffer(commit, NULL))
+                       out = xstrdup(msg);
+               else
+                       out = (char *)msg;
        }
        else {
                /*
@@ -667,8 +654,8 @@ char *logmsg_reencode(const struct commit *commit,
                 * copy, we can free it.
                 */
                out = reencode_string(msg, output_encoding, use_encoding);
-               if (out && msg != commit->buffer)
-                       free(msg);
+               if (out)
+                       unuse_commit_buffer(commit, msg);
        }
 
        /*
@@ -687,12 +674,6 @@ char *logmsg_reencode(const struct commit *commit,
        return out ? out : msg;
 }
 
-void logmsg_free(char *msg, const struct commit *commit)
-{
-       if (msg != commit->buffer)
-               free(msg);
-}
-
 static int mailmap_name(const char **email, size_t *email_len,
                        const char **name, size_t *name_len)
 {
@@ -796,7 +777,7 @@ struct format_commit_context {
        struct signature_check signature_check;
        enum flush_type flush_type;
        enum trunc_type truncate;
-       char *message;
+       const char *message;
        char *commit_encoding;
        size_t width, indent1, indent2;
        int auto_color;
@@ -1267,6 +1248,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                        if (c->signature_check.key)
                                strbuf_addstr(sb, c->signature_check.key);
                        break;
+               default:
+                       return 0;
                }
                return 2;
        }
@@ -1536,9 +1519,7 @@ void format_commit_message(const struct commit *commit,
        }
 
        free(context.commit_encoding);
-       logmsg_free(context.message, commit);
-       free(context.signature_check.gpg_output);
-       free(context.signature_check.signer);
+       unuse_commit_buffer(commit, context.message);
 }
 
 static void pp_header(struct pretty_print_context *pp,
@@ -1705,7 +1686,7 @@ void pretty_print_commit(struct pretty_print_context *pp,
        unsigned long beginning_of_body;
        int indent = 4;
        const char *msg;
-       char *reencoded;
+       const char *reencoded;
        const char *encoding;
        int need_8bit_cte = pp->need_8bit_cte;
 
@@ -1772,7 +1753,7 @@ void pretty_print_commit(struct pretty_print_context *pp,
        if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
 
-       logmsg_free(reencoded, commit);
+       unuse_commit_buffer(commit, reencoded);
 }
 
 void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
index 6a45966ec499b47efbc76719046b53633e8839a1..5d3c8bd4aaffda9915a3fd62d9d9800f4ac8baff 100644 (file)
@@ -14,6 +14,8 @@
 #include "resolve-undo.h"
 #include "strbuf.h"
 #include "varint.h"
+#include "split-index.h"
+#include "sigchain.h"
 
 static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
                                               unsigned int options);
@@ -34,8 +36,15 @@ static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
 #define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )
 #define CACHE_EXT_TREE 0x54524545      /* "TREE" */
 #define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */
+#define CACHE_EXT_LINK 0x6c696e6b        /* "link" */
+
+/* changes that can be kept in $GIT_DIR/index (basically all extensions) */
+#define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
+                CE_ENTRY_ADDED | CE_ENTRY_REMOVED | CE_ENTRY_CHANGED | \
+                SPLIT_INDEX_ORDERED)
 
 struct index_state the_index;
+static const char *alternate_index_output;
 
 static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
 {
@@ -47,10 +56,12 @@ static void replace_index_entry(struct index_state *istate, int nr, struct cache
 {
        struct cache_entry *old = istate->cache[nr];
 
+       replace_index_entry_in_base(istate, old, ce);
        remove_name_hash(istate, old);
        free(old);
        set_index_entry(istate, nr, ce);
-       istate->cache_changed = 1;
+       ce->ce_flags |= CE_UPDATE_IN_BASE;
+       istate->cache_changed |= CE_ENTRY_CHANGED;
 }
 
 void rename_index_entry_at(struct index_state *istate, int nr, const char *new_name)
@@ -62,9 +73,10 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
        copy_cache_entry(new, old);
        new->ce_flags &= ~CE_HASHED;
        new->ce_namelen = namelen;
+       new->index = 0;
        memcpy(new->name, new_name, namelen + 1);
 
-       cache_tree_invalidate_path(istate->cache_tree, old->name);
+       cache_tree_invalidate_path(istate, old->name);
        remove_index_entry_at(istate, nr);
        add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
@@ -483,8 +495,8 @@ int remove_index_entry_at(struct index_state *istate, int pos)
 
        record_resolve_undo(istate, ce);
        remove_name_hash(istate, ce);
-       free(ce);
-       istate->cache_changed = 1;
+       save_or_free_index_entry(istate, ce);
+       istate->cache_changed |= CE_ENTRY_REMOVED;
        istate->cache_nr--;
        if (pos >= istate->cache_nr)
                return 0;
@@ -507,12 +519,14 @@ void remove_marked_cache_entries(struct index_state *istate)
        for (i = j = 0; i < istate->cache_nr; i++) {
                if (ce_array[i]->ce_flags & CE_REMOVE) {
                        remove_name_hash(istate, ce_array[i]);
-                       free(ce_array[i]);
+                       save_or_free_index_entry(istate, ce_array[i]);
                }
                else
                        ce_array[j++] = ce_array[i];
        }
-       istate->cache_changed = 1;
+       if (j == istate->cache_nr)
+               return;
+       istate->cache_changed |= CE_ENTRY_REMOVED;
        istate->cache_nr = j;
 }
 
@@ -521,7 +535,7 @@ int remove_file_from_index(struct index_state *istate, const char *path)
        int pos = index_name_pos(istate, path, strlen(path));
        if (pos < 0)
                pos = -pos-1;
-       cache_tree_invalidate_path(istate->cache_tree, path);
+       cache_tree_invalidate_path(istate, path);
        while (pos < istate->cache_nr && !strcmp(istate->cache[pos]->name, path))
                remove_index_entry_at(istate, pos);
        return 0;
@@ -570,7 +584,9 @@ static int different_name(struct cache_entry *ce, struct cache_entry *alias)
  * So we use the CE_ADDED flag to verify that the alias was an old
  * one before we accept it as
  */
-static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_entry *alias)
+static struct cache_entry *create_alias_ce(struct index_state *istate,
+                                          struct cache_entry *ce,
+                                          struct cache_entry *alias)
 {
        int len;
        struct cache_entry *new;
@@ -583,7 +599,7 @@ static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_
        new = xcalloc(1, cache_entry_size(len));
        memcpy(new->name, alias->name, len);
        copy_cache_entry(new, ce);
-       free(ce);
+       save_or_free_index_entry(istate, ce);
        return new;
 }
 
@@ -676,7 +692,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
                set_object_name_for_intent_to_add_entry(ce);
 
        if (ignore_case && alias && different_name(ce, alias))
-               ce = create_alias_ce(ce, alias);
+               ce = create_alias_ce(istate, ce, alias);
        ce->ce_flags |= CE_ADDED;
 
        /* It was suspected to be racily clean, but it turns out to be Ok */
@@ -939,7 +955,8 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
        int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
        int new_only = option & ADD_CACHE_NEW_ONLY;
 
-       cache_tree_invalidate_path(istate->cache_tree, ce->name);
+       if (!(option & ADD_CACHE_KEEP_CACHE_TREE))
+               cache_tree_invalidate_path(istate, ce->name);
        pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
 
        /* existing match? Just replace it. */
@@ -1002,7 +1019,7 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
                        istate->cache + pos,
                        (istate->cache_nr - pos - 1) * sizeof(ce));
        set_index_entry(istate, pos, ce);
-       istate->cache_changed = 1;
+       istate->cache_changed |= CE_ENTRY_ADDED;
        return 0;
 }
 
@@ -1101,6 +1118,7 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
            !(ce->ce_flags & CE_VALID))
                updated->ce_flags &= ~CE_VALID;
 
+       /* istate->cache_changed is updated in the caller */
        return updated;
 }
 
@@ -1182,7 +1200,8 @@ int refresh_index(struct index_state *istate, unsigned int flags,
                                 * means the index is not valid anymore.
                                 */
                                ce->ce_flags &= ~CE_VALID;
-                               istate->cache_changed = 1;
+                               ce->ce_flags |= CE_UPDATE_IN_BASE;
+                               istate->cache_changed |= CE_ENTRY_CHANGED;
                        }
                        if (quiet)
                                continue;
@@ -1334,6 +1353,10 @@ static int read_index_extension(struct index_state *istate,
        case CACHE_EXT_RESOLVE_UNDO:
                istate->resolve_undo = resolve_undo_read(data, sz);
                break;
+       case CACHE_EXT_LINK:
+               if (read_link_extension(istate, data, sz))
+                       return -1;
+               break;
        default:
                if (*ext < 'A' || 'Z' < *ext)
                        return error("index uses %.4s extension, which we do not understand",
@@ -1368,6 +1391,7 @@ static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *on
        ce->ce_stat_data.sd_size  = get_be32(&ondisk->size);
        ce->ce_flags = flags & ~CE_NAMEMASK;
        ce->ce_namelen = len;
+       ce->index = 0;
        hashcpy(ce->sha1, ondisk->sha1);
        memcpy(ce->name, name, len);
        ce->name[len] = '\0';
@@ -1442,7 +1466,7 @@ static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk,
 }
 
 /* remember to discard_cache() before reading a different cache! */
-int read_index_from(struct index_state *istate, const char *path)
+int do_read_index(struct index_state *istate, const char *path, int must_exist)
 {
        int fd, i;
        struct stat st;
@@ -1459,9 +1483,9 @@ int read_index_from(struct index_state *istate, const char *path)
        istate->timestamp.nsec = 0;
        fd = open(path, O_RDONLY);
        if (fd < 0) {
-               if (errno == ENOENT)
+               if (!must_exist && errno == ENOENT)
                        return 0;
-               die_errno("index file open failed");
+               die_errno("%s: index file open failed", path);
        }
 
        if (fstat(fd, &st))
@@ -1480,7 +1504,7 @@ int read_index_from(struct index_state *istate, const char *path)
        if (verify_hdr(hdr, mmap_size) < 0)
                goto unmap;
 
-       hashcpy(istate->sha1, (unsigned char *)hdr + mmap_size - 20);
+       hashcpy(istate->sha1, (const unsigned char *)hdr + mmap_size - 20);
        istate->version = ntohl(hdr->hdr_version);
        istate->cache_nr = ntohl(hdr->hdr_entries);
        istate->cache_alloc = alloc_nr(istate->cache_nr);
@@ -1534,6 +1558,40 @@ int read_index_from(struct index_state *istate, const char *path)
        die("index file corrupt");
 }
 
+int read_index_from(struct index_state *istate, const char *path)
+{
+       struct split_index *split_index;
+       int ret;
+
+       /* istate->initialized covers both .git/index and .git/sharedindex.xxx */
+       if (istate->initialized)
+               return istate->cache_nr;
+
+       ret = do_read_index(istate, path, 0);
+       split_index = istate->split_index;
+       if (!split_index)
+               return ret;
+
+       if (is_null_sha1(split_index->base_sha1))
+               return ret;
+
+       if (split_index->base)
+               discard_index(split_index->base);
+       else
+               split_index->base = xcalloc(1, sizeof(*split_index->base));
+       ret = do_read_index(split_index->base,
+                           git_path("sharedindex.%s",
+                                    sha1_to_hex(split_index->base_sha1)), 1);
+       if (hashcmp(split_index->base_sha1, split_index->base->sha1))
+               die("broken index, expect %s in %s, got %s",
+                   sha1_to_hex(split_index->base_sha1),
+                   git_path("sharedindex.%s",
+                                    sha1_to_hex(split_index->base_sha1)),
+                   sha1_to_hex(split_index->base->sha1));
+       merge_base_index(istate);
+       return ret;
+}
+
 int is_index_unborn(struct index_state *istate)
 {
        return (!istate->cache_nr && !istate->timestamp.sec);
@@ -1543,8 +1601,15 @@ int discard_index(struct index_state *istate)
 {
        int i;
 
-       for (i = 0; i < istate->cache_nr; i++)
+       for (i = 0; i < istate->cache_nr; i++) {
+               if (istate->cache[i]->index &&
+                   istate->split_index &&
+                   istate->split_index->base &&
+                   istate->cache[i]->index <= istate->split_index->base->cache_nr &&
+                   istate->cache[i] == istate->split_index->base->cache[istate->cache[i]->index - 1])
+                       continue;
                free(istate->cache[i]);
+       }
        resolve_undo_clear_index(istate);
        istate->cache_nr = 0;
        istate->cache_changed = 0;
@@ -1556,6 +1621,7 @@ int discard_index(struct index_state *istate)
        free(istate->cache);
        istate->cache = NULL;
        istate->cache_alloc = 0;
+       discard_split_index(istate);
        return 0;
 }
 
@@ -1616,7 +1682,7 @@ static int write_index_ext_header(git_SHA_CTX *context, int fd,
                (ce_write(context, fd, &sz, 4) < 0)) ? -1 : 0;
 }
 
-static int ce_flush(git_SHA_CTX *context, int fd)
+static int ce_flush(git_SHA_CTX *context, int fd, unsigned char *sha1)
 {
        unsigned int left = write_buffer_len;
 
@@ -1634,6 +1700,7 @@ static int ce_flush(git_SHA_CTX *context, int fd)
 
        /* Append the SHA1 signature at the end */
        git_SHA1_Final(write_buffer + left, context);
+       hashcpy(sha1, write_buffer + left);
        left += 20;
        return (write_in_full(fd, write_buffer, left) != left) ? -1 : 0;
 }
@@ -1705,7 +1772,7 @@ static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
        ondisk->size = htonl(ce->ce_stat_data.sd_size);
        hashcpy(ondisk->sha1, ce->sha1);
 
-       flags = ce->ce_flags;
+       flags = ce->ce_flags & ~CE_NAMEMASK;
        flags |= (ce_namelen(ce) >= CE_NAMEMASK ? CE_NAMEMASK : ce_namelen(ce));
        ondisk->flags = htons(flags);
        if (ce->ce_flags & CE_EXTENDED) {
@@ -1724,9 +1791,15 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
 {
        int size;
        struct ondisk_cache_entry *ondisk;
+       int saved_namelen = saved_namelen; /* compiler workaround */
        char *name;
        int result;
 
+       if (ce->ce_flags & CE_STRIP_NAME) {
+               saved_namelen = ce_namelen(ce);
+               ce->ce_namelen = 0;
+       }
+
        if (!previous_name) {
                size = ondisk_ce_size(ce);
                ondisk = xcalloc(1, size);
@@ -1758,6 +1831,10 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
                strbuf_splice(previous_name, common, to_remove,
                              ce->name + common, ce_namelen(ce) - common);
        }
+       if (ce->ce_flags & CE_STRIP_NAME) {
+               ce->ce_namelen = saved_namelen;
+               ce->ce_flags &= ~CE_STRIP_NAME;
+       }
 
        result = ce_write(c, fd, ondisk, size);
        free(ondisk);
@@ -1827,13 +1904,13 @@ static int has_racy_timestamp(struct index_state *istate)
 void update_index_if_able(struct index_state *istate, struct lock_file *lockfile)
 {
        if ((istate->cache_changed || has_racy_timestamp(istate)) &&
-           verify_index(istate) && !write_index(istate, lockfile->fd))
-               commit_locked_index(lockfile);
-       else
+           verify_index(istate) &&
+           write_locked_index(istate, lockfile, COMMIT_LOCK))
                rollback_lock_file(lockfile);
 }
 
-int write_index(struct index_state *istate, int newfd)
+static int do_write_index(struct index_state *istate, int newfd,
+                         int strip_extensions)
 {
        git_SHA_CTX c;
        struct cache_header hdr;
@@ -1855,8 +1932,11 @@ int write_index(struct index_state *istate, int newfd)
                }
        }
 
-       if (!istate->version)
+       if (!istate->version) {
                istate->version = get_index_format_default();
+               if (getenv("GIT_TEST_SPLIT_INDEX"))
+                       init_split_index(istate);
+       }
 
        /* demote version 3 to version 2 when the latter suffices */
        if (istate->version == 3 || istate->version == 2)
@@ -1896,7 +1976,18 @@ int write_index(struct index_state *istate, int newfd)
        strbuf_release(&previous_name_buf);
 
        /* Write extension data here */
-       if (istate->cache_tree) {
+       if (!strip_extensions && istate->split_index) {
+               struct strbuf sb = STRBUF_INIT;
+
+               err = write_link_extension(&sb, istate) < 0 ||
+                       write_index_ext_header(&c, newfd, CACHE_EXT_LINK,
+                                              sb.len) < 0 ||
+                       ce_write(&c, newfd, sb.buf, sb.len) < 0;
+               strbuf_release(&sb);
+               if (err)
+                       return -1;
+       }
+       if (!strip_extensions && istate->cache_tree) {
                struct strbuf sb = STRBUF_INIT;
 
                cache_tree_write(&sb, istate->cache_tree);
@@ -1906,7 +1997,7 @@ int write_index(struct index_state *istate, int newfd)
                if (err)
                        return -1;
        }
-       if (istate->resolve_undo) {
+       if (!strip_extensions && istate->resolve_undo) {
                struct strbuf sb = STRBUF_INIT;
 
                resolve_undo_write(&sb, istate->resolve_undo);
@@ -1918,13 +2009,138 @@ int write_index(struct index_state *istate, int newfd)
                        return -1;
        }
 
-       if (ce_flush(&c, newfd) || fstat(newfd, &st))
+       if (ce_flush(&c, newfd, istate->sha1) || fstat(newfd, &st))
                return -1;
        istate->timestamp.sec = (unsigned int)st.st_mtime;
        istate->timestamp.nsec = ST_MTIME_NSEC(st);
        return 0;
 }
 
+void set_alternate_index_output(const char *name)
+{
+       alternate_index_output = name;
+}
+
+static int commit_locked_index(struct lock_file *lk)
+{
+       if (alternate_index_output) {
+               if (lk->fd >= 0 && close_lock_file(lk))
+                       return -1;
+               if (rename(lk->filename, alternate_index_output))
+                       return -1;
+               lk->filename[0] = 0;
+               return 0;
+       } else {
+               return commit_lock_file(lk);
+       }
+}
+
+static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
+                                unsigned flags)
+{
+       int ret = do_write_index(istate, lock->fd, 0);
+       if (ret)
+               return ret;
+       assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
+              (COMMIT_LOCK | CLOSE_LOCK));
+       if (flags & COMMIT_LOCK)
+               return commit_locked_index(lock);
+       else if (flags & CLOSE_LOCK)
+               return close_lock_file(lock);
+       else
+               return ret;
+}
+
+static int write_split_index(struct index_state *istate,
+                            struct lock_file *lock,
+                            unsigned flags)
+{
+       int ret;
+       prepare_to_write_split_index(istate);
+       ret = do_write_locked_index(istate, lock, flags);
+       finish_writing_split_index(istate);
+       return ret;
+}
+
+static char *temporary_sharedindex;
+
+static void remove_temporary_sharedindex(void)
+{
+       if (temporary_sharedindex) {
+               unlink_or_warn(temporary_sharedindex);
+               free(temporary_sharedindex);
+               temporary_sharedindex = NULL;
+       }
+}
+
+static void remove_temporary_sharedindex_on_signal(int signo)
+{
+       remove_temporary_sharedindex();
+       sigchain_pop(signo);
+       raise(signo);
+}
+
+static int write_shared_index(struct index_state *istate,
+                             struct lock_file *lock, unsigned flags)
+{
+       struct split_index *si = istate->split_index;
+       static int installed_handler;
+       int fd, ret;
+
+       temporary_sharedindex = git_pathdup("sharedindex_XXXXXX");
+       fd = mkstemp(temporary_sharedindex);
+       if (fd < 0) {
+               free(temporary_sharedindex);
+               temporary_sharedindex = NULL;
+               hashclr(si->base_sha1);
+               return do_write_locked_index(istate, lock, flags);
+       }
+       if (!installed_handler) {
+               atexit(remove_temporary_sharedindex);
+               sigchain_push_common(remove_temporary_sharedindex_on_signal);
+       }
+       move_cache_to_base_index(istate);
+       ret = do_write_index(si->base, fd, 1);
+       close(fd);
+       if (ret) {
+               remove_temporary_sharedindex();
+               return ret;
+       }
+       ret = rename(temporary_sharedindex,
+                    git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
+       free(temporary_sharedindex);
+       temporary_sharedindex = NULL;
+       if (!ret)
+               hashcpy(si->base_sha1, si->base->sha1);
+       return ret;
+}
+
+int write_locked_index(struct index_state *istate, struct lock_file *lock,
+                      unsigned flags)
+{
+       struct split_index *si = istate->split_index;
+
+       if (!si || alternate_index_output ||
+           (istate->cache_changed & ~EXTMASK)) {
+               if (si)
+                       hashclr(si->base_sha1);
+               return do_write_locked_index(istate, lock, flags);
+       }
+
+       if (getenv("GIT_TEST_SPLIT_INDEX")) {
+               int v = si->base_sha1[0];
+               if ((v & 15) < 6)
+                       istate->cache_changed |= SPLIT_INDEX_ORDERED;
+       }
+       if (istate->cache_changed & SPLIT_INDEX_ORDERED) {
+               int ret = write_shared_index(istate, lock, flags);
+               if (ret)
+                       return ret;
+       }
+
+       return write_split_index(istate, lock, flags);
+}
+
 /*
  * Read the index file that is potentially unmerged into given
  * index_state, dropping any unmerged entries.  Returns true if
diff --git a/refs.c b/refs.c
index dc457742eade241ec6b0001580d1ce1a880fee31..82e4842a36ef803d6406f852f2415b3b77f477f7 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -7,21 +7,27 @@
 
 /*
  * How to handle various characters in refnames:
+ * This table is used by both the SIMD and non-SIMD code.  It has
+ * some cases that are only useful for the SIMD; these are handled
+ * equivalently to the listed disposition in the non-SIMD code.
  * 0: An acceptable character for refs
- * 1: End-of-component
- * 2: ., look for a preceding . to reject .. in refs
- * 3: {, look for a preceding @ to reject @{ in refs
- * 4: A bad character: ASCII control characters, "~", "^", ":" or SP
+ * 1: @, look for a following { to reject @{ in refs (SIMD or = 0)
+ * 2: \0: End-of-component and string
+ * 3: /: End-of-component (SIMD or = 2)
+ * 4: ., look for a preceding . to reject .. in refs
+ * 5: {, look for a preceding @ to reject @{ in refs
+ * 6: *, usually a bad character except, once as a wildcard (SIMD or = 7)
+ * 7: A bad character except * (see check_refname_component below)
  */
 static unsigned char refname_disposition[256] = {
-       1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-       4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
+       2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+       7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+       7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 4, 3,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 7,
+       1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 7, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 7, 7
 };
 
 /*
@@ -33,8 +39,9 @@ static unsigned char refname_disposition[256] = {
  * - any path component of it begins with ".", or
  * - it has double dots "..", or
  * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
- * - it ends with a "/".
- * - it ends with ".lock"
+ * - it has pattern-matching notation "*", "?", "[", anywhere, or
+ * - it ends with a "/", or
+ * - it ends with ".lock", or
  * - it contains a "\" (backslash)
  */
 static int check_refname_component(const char *refname, int flags)
@@ -46,17 +53,19 @@ static int check_refname_component(const char *refname, int flags)
                int ch = *cp & 255;
                unsigned char disp = refname_disposition[ch];
                switch (disp) {
-               case 1:
+               case 2: /* fall-through */
+               case 3:
                        goto out;
-               case 2:
+               case 4:
                        if (last == '.')
                                return -1; /* Refname contains "..". */
                        break;
-               case 3:
+               case 5:
                        if (last == '@')
                                return -1; /* Refname contains "@{". */
                        break;
-               case 4:
+               case 6: /* fall-through */
+               case 7:
                        return -1;
                }
                last = ch;
@@ -79,7 +88,7 @@ static int check_refname_component(const char *refname, int flags)
        return cp - refname;
 }
 
-int check_refname_format(const char *refname, int flags)
+static int check_refname_format_bytewise(const char *refname, int flags)
 {
        int component_len, component_count = 0;
 
@@ -115,6 +124,196 @@ int check_refname_format(const char *refname, int flags)
        return 0;
 }
 
+#if defined(__GNUC__) && defined(__x86_64__)
+#define SSE_VECTOR_BYTES 16
+
+/* Vectorized version of check_refname_format. */
+int check_refname_format(const char *refname, int flags)
+{
+       const char *cp = refname;
+
+       const __m128i dot = _mm_set1_epi8('.');
+       const __m128i at = _mm_set1_epi8('@');
+       const __m128i curly = _mm_set1_epi8('{');
+       const __m128i slash = _mm_set1_epi8('/');
+       const __m128i zero = _mm_set1_epi8('\000');
+       const __m128i el = _mm_set1_epi8('l');
+
+       /* below '*', all characters are forbidden or rare */
+       const __m128i star_ub = _mm_set1_epi8('*' + 1);
+
+       const __m128i colon = _mm_set1_epi8(':');
+       const __m128i question = _mm_set1_epi8('?');
+
+       /* '['..'^' contains 4 characters: 3 forbidden and 1 rare */
+       const __m128i bracket_lb = _mm_set1_epi8('[' - 1);
+       const __m128i caret_ub = _mm_set1_epi8('^' + 1);
+
+       /* '~' and above are forbidden */
+       const __m128i tilde_lb = _mm_set1_epi8('~' - 1);
+
+       int component_count = 0;
+       int orig_flags = flags;
+
+       if (refname[0] == 0 || refname[0] == '/') {
+               /* entirely empty ref or initial ref component */
+               return -1;
+       }
+
+       /*
+        * Initial ref component of '.'; below we look for /. so we'll
+        * miss this.
+        */
+       if (refname[0] == '.') {
+               if (refname[1] == '/' || refname[1] == '\0')
+                       return -1;
+               if (!(flags & REFNAME_DOT_COMPONENT))
+                       return -1;
+       }
+       while(1) {
+               __m128i tmp, tmp1, result;
+               uint64_t mask;
+
+               if ((uintptr_t) cp % PAGE_SIZE > PAGE_SIZE - SSE_VECTOR_BYTES  - 1)
+                       /*
+                        * End-of-page; fall back to slow method for
+                        * this entire ref.
+                        */
+                       return check_refname_format_bytewise(refname, orig_flags);
+
+               tmp = _mm_loadu_si128((__m128i *)cp);
+               tmp1 = _mm_loadu_si128((__m128i *)(cp + 1));
+
+               /*
+                * This range (note the lt) contains some
+                * permissible-but-rare characters (including all
+                * characters >= 128), which we handle later.  It also
+                * includes \000.
+                */
+               result = _mm_cmplt_epi8(tmp, star_ub);
+
+               result = _mm_or_si128(result, _mm_cmpeq_epi8(tmp, question));
+               result = _mm_or_si128(result, _mm_cmpeq_epi8(tmp, colon));
+
+               /* This range contains the permissible ] as bycatch */
+               result = _mm_or_si128(result, _mm_and_si128(
+                                             _mm_cmpgt_epi8(tmp, bracket_lb),
+                                             _mm_cmplt_epi8(tmp, caret_ub)));
+
+               result = _mm_or_si128(result, _mm_cmpgt_epi8(tmp, tilde_lb));
+
+               /* .. */
+               result = _mm_or_si128(result, _mm_and_si128(
+                                             _mm_cmpeq_epi8(tmp, dot),
+                                             _mm_cmpeq_epi8(tmp1, dot)));
+               /* @{ */
+               result = _mm_or_si128(result, _mm_and_si128(
+                                             _mm_cmpeq_epi8(tmp, at),
+                                             _mm_cmpeq_epi8(tmp1, curly)));
+               /* // */
+               result = _mm_or_si128(result, _mm_and_si128(
+                                             _mm_cmpeq_epi8(tmp, slash),
+                                             _mm_cmpeq_epi8(tmp1, slash)));
+               /* trailing / */
+               result = _mm_or_si128(result, _mm_and_si128(
+                                             _mm_cmpeq_epi8(tmp, slash),
+                                             _mm_cmpeq_epi8(tmp1, zero)));
+               /* .l, beginning of .lock */
+               result = _mm_or_si128(result, _mm_and_si128(
+                                             _mm_cmpeq_epi8(tmp, dot),
+                                             _mm_cmpeq_epi8(tmp1, el)));
+               /*
+                * Even though /. is not necessarily an error, we flag
+                * it anyway. If we find it, we'll check if it's valid
+                * and if so we'll advance just past it.
+                */
+               result = _mm_or_si128(result, _mm_and_si128(
+                                             _mm_cmpeq_epi8(tmp, slash),
+                                             _mm_cmpeq_epi8(tmp1, dot)));
+
+               mask = _mm_movemask_epi8(result);
+               if (mask) {
+                       /*
+                        * We've found either end-of-string, or some
+                        * probably-bad character or substring.
+                        */
+                       int i = __builtin_ctz(mask);
+                       switch (refname_disposition[cp[i] & 255]) {
+                       case 0: /* fall-through */
+                       case 5:
+                               /*
+                                * bycatch: a good character that's in
+                                * one of the ranges of mostly-forbidden
+                                * characters
+                                */
+                               cp += i + 1;
+                               break;
+                       case 1:
+                               if (cp[i + 1] == '{')
+                                       return -1;
+                               cp += i + 1;
+                               break;
+                       case 2:
+                               if (!(flags & REFNAME_ALLOW_ONELEVEL)
+                                   && !component_count && !strchr(refname, '/'))
+                                       /* Refname has only one component. */
+                                       return -1;
+                               return 0;
+                       case 3:
+                               component_count ++;
+                               /*
+                                * Even if leading dots are allowed, don't
+                                * allow "." as a component (".." is
+                                * prevented by case 4 below).
+                                */
+                               if (cp[i + 1] == '.') {
+                                       if (cp[i + 2] == '\0')
+                                               return -1;
+                                       if (flags & REFNAME_DOT_COMPONENT) {
+                                               /* skip to just after the /. */
+                                               cp += i + 2;
+                                               break;
+                                       }
+                                       return -1;
+                               } else if (cp[i + 1] == '/' || cp[i + 1] == '\0')
+                                       return -1;
+                               break;
+                       case 4:
+                               if (cp[i + 1] == '.' || cp[i + 1] == '\0')
+                                       return -1;
+                               /* .lock as end-of-component or end-of-string */
+                               if ((!strncmp(cp + i, ".lock", 5))
+                                   && (cp[i + 5] == '/' || cp[i + 5] == 0))
+                                       return -1;
+                               cp += 1;
+                               break;
+                       case 6:
+                               if (((cp == refname + i) || cp[i - 1] == '/')
+                                   && (cp[i + 1] == '/' || cp[i + 1] == 0))
+                                       if (flags & REFNAME_REFSPEC_PATTERN) {
+                                               flags &= ~REFNAME_REFSPEC_PATTERN;
+                                               /* restart after the * */
+                                               cp += i + 1;
+                                               continue;
+                                       }
+                               /* fall-through */
+                       case 7:
+                               return -1;
+                       }
+               } else
+                       cp += SSE_VECTOR_BYTES;
+       }
+}
+
+#else
+
+int check_refname_format (const char *refname, int flags)
+{
+       return check_refname_format_bytewise(refname, flags);
+}
+
+#endif
+
 struct ref_entry;
 
 /*
index 4493b389ded6a814bd8c40481f3d06855c963bd7..cdcca2903b100c775d4e004312086581f5839ad4 100644 (file)
@@ -791,9 +791,9 @@ static void parse_fetch(struct strbuf *buf)
        int alloc_heads = 0, nr_heads = 0;
 
        do {
-               if (starts_with(buf->buf, "fetch ")) {
-                       char *p = buf->buf + strlen("fetch ");
-                       char *name;
+               const char *p;
+               if (skip_prefix(buf->buf, "fetch ", &p)) {
+                       const char *name;
                        struct ref *ref;
                        unsigned char old_sha1[20];
 
@@ -968,6 +968,8 @@ int main(int argc, const char **argv)
        http_init(remote, url.buf, 0);
 
        do {
+               const char *arg;
+
                if (strbuf_getline(&buf, stdin, '\n') == EOF) {
                        if (ferror(stdin))
                                fprintf(stderr, "Error reading command stream\n");
@@ -989,9 +991,8 @@ int main(int argc, const char **argv)
                } else if (starts_with(buf.buf, "push ")) {
                        parse_push(&buf);
 
-               } else if (starts_with(buf.buf, "option ")) {
-                       char *name = buf.buf + strlen("option ");
-                       char *value = strchr(name, ' ');
+               } else if (skip_prefix(buf.buf, "option ", &arg)) {
+                       char *value = strchr(arg, ' ');
                        int result;
 
                        if (value)
@@ -999,7 +1000,7 @@ int main(int argc, const char **argv)
                        else
                                value = "true";
 
-                       result = set_option(name, value);
+                       result = set_option(arg, value);
                        if (!result)
                                printf("ok\n");
                        else if (result < 0)
index ae040432037f7463233c5add8257b17ace365b23..3d6c86a36f26406305d58505b10db3f747a2546c 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -170,7 +170,6 @@ static struct branch *make_branch(const char *name, int len)
 {
        struct branch *ret;
        int i;
-       char *refname;
 
        for (i = 0; i < branches_nr; i++) {
                if (len ? (!strncmp(name, branches[i]->name, len) &&
@@ -186,10 +185,7 @@ static struct branch *make_branch(const char *name, int len)
                ret->name = xstrndup(name, len);
        else
                ret->name = xstrdup(name);
-       refname = xmalloc(strlen(name) + strlen("refs/heads/") + 1);
-       strcpy(refname, "refs/heads/");
-       strcpy(refname + strlen("refs/heads/"), ret->name);
-       ret->refname = refname;
+       ret->refname = xstrfmt("refs/heads/%s", ret->name);
 
        return ret;
 }
@@ -488,9 +484,8 @@ static void read_config(void)
        current_branch = NULL;
        head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
        if (head_ref && (flag & REF_ISSYMREF) &&
-           starts_with(head_ref, "refs/heads/")) {
-               current_branch =
-                       make_branch(head_ref + strlen("refs/heads/"), 0);
+           skip_prefix(head_ref, "refs/heads/", &head_ref)) {
+               current_branch = make_branch(head_ref, 0);
        }
        git_config(handle_config, NULL);
        if (branch_pushremote_name) {
index d55aa8a01b4a4f200f24972795f2887644a06d43..ffc6a5b168304726b5fe326211f26dafa460c7fa 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -492,8 +492,7 @@ static int update_paths(struct string_list *update)
        }
 
        if (!status && active_cache_changed) {
-               if (write_cache(fd, active_cache, active_nr) ||
-                   commit_locked_index(&index_lock))
+               if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
                        die("Unable to write new index file");
        } else if (fd >= 0)
                rollback_lock_file(&index_lock);
index 44c697c36d0b406330aeb084db1d6a4be715d6c6..468a2eb92c6d72c519faf5c8203d3d41d520c12d 100644 (file)
@@ -110,7 +110,7 @@ void resolve_undo_clear_index(struct index_state *istate)
        string_list_clear(resolve_undo, 1);
        free(resolve_undo);
        istate->resolve_undo = NULL;
-       istate->cache_changed = 1;
+       istate->cache_changed |= RESOLVE_UNDO_CHANGED;
 }
 
 int unmerge_index_entry_at(struct index_state *istate, int pos)
index 8351e794df943ea6ea7acde40c01c97c20be4576..2571ada6bf66ce6e945c539be4b0123e5b9648a9 100644 (file)
@@ -2791,7 +2791,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
 {
        int retval;
        const char *encoding;
-       char *message;
+       const char *message;
        struct strbuf buf = STRBUF_INIT;
 
        if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
@@ -2833,14 +2833,21 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
                format_display_notes(commit->object.sha1, &buf, encoding, 1);
        }
 
-       /* Find either in the original commit message, or in the temporary */
+       /*
+        * Find either in the original commit message, or in the temporary.
+        * Note that we cast away the constness of "message" here. It is
+        * const because it may come from the cached commit buffer. That's OK,
+        * because we know that it is modifiable heap memory, and that while
+        * grep_buffer may modify it for speed, it will restore any
+        * changes before returning.
+        */
        if (buf.len)
                retval = grep_buffer(&opt->grep_filter, buf.buf, buf.len);
        else
                retval = grep_buffer(&opt->grep_filter,
-                                    message, strlen(message));
+                                    (char *)message, strlen(message));
        strbuf_release(&buf);
-       logmsg_free(message, commit);
+       unuse_commit_buffer(commit, message);
        return retval;
 }
 
index 923047459bd3079da74245f2a0185ec437c9e8cd..3c060e054720b7b17f9a868cabb1a0ef62aa1b50 100644 (file)
@@ -116,39 +116,23 @@ static const char *action_name(const struct replay_opts *opts)
        return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
 }
 
-static char *get_encoding(const char *message);
-
 struct commit_message {
        char *parent_label;
        const char *label;
        const char *subject;
-       char *reencoded_message;
        const char *message;
 };
 
 static int get_message(struct commit *commit, struct commit_message *out)
 {
-       const char *encoding;
        const char *abbrev, *subject;
        int abbrev_len, subject_len;
        char *q;
 
-       if (!commit->buffer)
-               return -1;
-       encoding = get_encoding(commit->buffer);
-       if (!encoding)
-               encoding = "UTF-8";
        if (!git_commit_encoding)
                git_commit_encoding = "UTF-8";
 
-       out->reencoded_message = NULL;
-       out->message = commit->buffer;
-       if (same_encoding(encoding, git_commit_encoding))
-               out->reencoded_message = reencode_string(commit->buffer,
-                                       git_commit_encoding, encoding);
-       if (out->reencoded_message)
-               out->message = out->reencoded_message;
-
+       out->message = logmsg_reencode(commit, NULL, git_commit_encoding);
        abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
        abbrev_len = strlen(abbrev);
 
@@ -167,29 +151,10 @@ static int get_message(struct commit *commit, struct commit_message *out)
        return 0;
 }
 
-static void free_message(struct commit_message *msg)
+static void free_message(struct commit *commit, struct commit_message *msg)
 {
        free(msg->parent_label);
-       free(msg->reencoded_message);
-}
-
-static char *get_encoding(const char *message)
-{
-       const char *p = message, *eol;
-
-       while (*p && *p != '\n') {
-               for (eol = p + 1; *eol && *eol != '\n'; eol++)
-                       ; /* do nothing */
-               if (starts_with(p, "encoding ")) {
-                       char *result = xmalloc(eol - 8 - p);
-                       strlcpy(result, p + 9, eol - 8 - p);
-                       return result;
-               }
-               p = eol;
-               if (*p == '\n')
-                       p++;
-       }
-       return NULL;
+       unuse_commit_buffer(commit, msg->message);
 }
 
 static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
@@ -298,11 +263,11 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
 {
        struct merge_options o;
        struct tree *result, *next_tree, *base_tree, *head_tree;
-       int clean, index_fd;
+       int clean;
        const char **xopt;
        static struct lock_file index_lock;
 
-       index_fd = hold_locked_index(&index_lock, 1);
+       hold_locked_index(&index_lock, 1);
 
        read_cache();
 
@@ -323,8 +288,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
                            next_tree, base_tree, &result);
 
        if (active_cache_changed &&
-           (write_cache(index_fd, active_cache, active_nr) ||
-            commit_locked_index(&index_lock)))
+           write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
                /* TRANSLATORS: %s will be "revert" or "cherry-pick" */
                die(_("%s: Unable to write new index file"), action_name(opts));
        rollback_lock_file(&index_lock);
@@ -376,9 +340,7 @@ static int is_index_unchanged(void)
                active_cache_tree = cache_tree();
 
        if (!cache_tree_fully_valid(active_cache_tree))
-               if (cache_tree_update(active_cache_tree,
-                                     (const struct cache_entry * const *)active_cache,
-                                     active_nr, 0))
+               if (cache_tree_update(&the_index, 0))
                        return error(_("Unable to update cache tree\n"));
 
        return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.sha1);
@@ -396,18 +358,13 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 {
        struct argv_array array;
        int rc;
-       char *gpg_sign;
 
        argv_array_init(&array);
        argv_array_push(&array, "commit");
        argv_array_push(&array, "-n");
 
-       if (opts->gpg_sign) {
-               gpg_sign = xmalloc(3 + strlen(opts->gpg_sign));
-               sprintf(gpg_sign, "-S%s", opts->gpg_sign);
-               argv_array_push(&array, gpg_sign);
-               free(gpg_sign);
-       }
+       if (opts->gpg_sign)
+               argv_array_pushf(&array, "-S%s", opts->gpg_sign);
        if (opts->signoff)
                argv_array_push(&array, "-s");
        if (!opts->edit) {
@@ -489,7 +446,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
        unsigned char head[20];
        struct commit *base, *next, *parent;
        const char *base_label, *next_label;
-       struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
+       struct commit_message msg = { NULL, NULL, NULL, NULL };
        char *defmsg = NULL;
        struct strbuf msgbuf = STRBUF_INIT;
        int res, unborn = 0, allow;
@@ -654,7 +611,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                res = run_git_commit(defmsg, opts, allow);
 
 leave:
-       free_message(&msg);
+       free_message(commit, &msg);
        free(defmsg);
 
        return res;
@@ -683,9 +640,8 @@ static void read_and_refresh_cache(struct replay_opts *opts)
        if (read_index_preload(&the_index, NULL) < 0)
                die(_("git %s: failed to read the index"), action_name(opts));
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
-       if (the_index.cache_changed) {
-               if (write_index(&the_index, index_fd) ||
-                   commit_locked_index(&index_lock))
+       if (the_index.cache_changed && index_fd >= 0) {
+               if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
                        die(_("git %s: failed to refresh the index"), action_name(opts));
        }
        rollback_lock_file(&index_lock);
@@ -701,10 +657,12 @@ static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
        int subject_len;
 
        for (cur = todo_list; cur; cur = cur->next) {
+               const char *commit_buffer = get_commit_buffer(cur->item, NULL);
                sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
-               subject_len = find_commit_subject(cur->item->buffer, &subject);
+               subject_len = find_commit_subject(commit_buffer, &subject);
                strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
                        subject_len, subject);
+               unuse_commit_buffer(cur->item, commit_buffer);
        }
        return 0;
 }
index 34d527f6708fe242f75b7fa0fa98e36a5c96a3be..a38854ce553c1e59294d5542a37a5404ccd66dc5 100644 (file)
@@ -315,7 +315,8 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base, int
         * thing twice, or object directory itself.
         */
        for (alt = alt_odb_list; alt; alt = alt->next) {
-               if (!memcmp(ent->base, alt->base, pfxlen)) {
+               if (pfxlen == alt->name - alt->base - 1 &&
+                   !memcmp(ent->base, alt->base, pfxlen)) {
                        free(ent);
                        return -1;
                }
index 2b6322fad064845f4c782286fe9764f6b958f30d..5bfa8416999d35d6bb5c615d86763f013b7df748 100644 (file)
@@ -862,27 +862,17 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
                commit_list_insert(l->item, &backup);
        }
        while (list) {
-               char *p, *to_free = NULL;
+               const char *p, *buf;
                struct commit *commit;
-               enum object_type type;
-               unsigned long size;
                int matches;
 
                commit = pop_most_recent_commit(&list, ONELINE_SEEN);
                if (!parse_object(commit->object.sha1))
                        continue;
-               if (commit->buffer)
-                       p = commit->buffer;
-               else {
-                       p = read_sha1_file(commit->object.sha1, &type, &size);
-                       if (!p)
-                               continue;
-                       to_free = p;
-               }
-
-               p = strstr(p, "\n\n");
+               buf = get_commit_buffer(commit, NULL);
+               p = strstr(buf, "\n\n");
                matches = p && !regexec(&regex, p + 2, 0, NULL, 0);
-               free(to_free);
+               unuse_commit_buffer(commit, buf);
 
                if (matches) {
                        hashcpy(sha1, commit->object.sha1);
@@ -911,10 +901,8 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
        const char *match = NULL, *target = NULL;
        size_t len;
 
-       if (starts_with(message, "checkout: moving from ")) {
-               match = message + strlen("checkout: moving from ");
+       if (skip_prefix(message, "checkout: moving from ", &match))
                target = strstr(match, " to ");
-       }
 
        if (!match || !target)
                return 0;
@@ -1252,10 +1240,7 @@ static void diagnose_invalid_sha1_path(const char *prefix,
                die("Path '%s' exists on disk, but not in '%.*s'.",
                    filename, object_name_len, object_name);
        if (errno == ENOENT || errno == ENOTDIR) {
-               char *fullname = xmalloc(strlen(filename)
-                                            + strlen(prefix) + 1);
-               strcpy(fullname, prefix);
-               strcat(fullname, filename);
+               char *fullname = xstrfmt("%s%s", prefix, filename);
 
                if (!get_tree_entry(tree_sha1, fullname,
                                    sha1, &mode)) {
diff --git a/shell.c b/shell.c
index 5c0d47a5cc1ae85a03e40321e97ddfa859ff7e1a..ace62e4b6503d821962242f9cf3bd9af22bc39b9 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -46,11 +46,7 @@ static int is_valid_cmd_name(const char *cmd)
 
 static char *make_cmd(const char *prog)
 {
-       char *prefix = xmalloc((strlen(prog) + strlen(COMMAND_DIR) + 2));
-       strcpy(prefix, COMMAND_DIR);
-       strcat(prefix, "/");
-       strcat(prefix, prog);
-       return prefix;
+       return xstrfmt("%s/%s", COMMAND_DIR, prog);
 }
 
 static void cd_to_homedir(void)
diff --git a/split-index.c b/split-index.c
new file mode 100644 (file)
index 0000000..21485e2
--- /dev/null
@@ -0,0 +1,328 @@
+#include "cache.h"
+#include "split-index.h"
+#include "ewah/ewok.h"
+
+struct split_index *init_split_index(struct index_state *istate)
+{
+       if (!istate->split_index) {
+               istate->split_index = xcalloc(1, sizeof(*istate->split_index));
+               istate->split_index->refcount = 1;
+       }
+       return istate->split_index;
+}
+
+int read_link_extension(struct index_state *istate,
+                        const void *data_, unsigned long sz)
+{
+       const unsigned char *data = data_;
+       struct split_index *si;
+       int ret;
+
+       if (sz < 20)
+               return error("corrupt link extension (too short)");
+       si = init_split_index(istate);
+       hashcpy(si->base_sha1, data);
+       data += 20;
+       sz -= 20;
+       if (!sz)
+               return 0;
+       si->delete_bitmap = ewah_new();
+       ret = ewah_read_mmap(si->delete_bitmap, data, sz);
+       if (ret < 0)
+               return error("corrupt delete bitmap in link extension");
+       data += ret;
+       sz -= ret;
+       si->replace_bitmap = ewah_new();
+       ret = ewah_read_mmap(si->replace_bitmap, data, sz);
+       if (ret < 0)
+               return error("corrupt replace bitmap in link extension");
+       if (ret != sz)
+               return error("garbage at the end of link extension");
+       return 0;
+}
+
+static int write_strbuf(void *user_data, const void *data, size_t len)
+{
+       struct strbuf *sb = user_data;
+       strbuf_add(sb, data, len);
+       return len;
+}
+
+int write_link_extension(struct strbuf *sb,
+                        struct index_state *istate)
+{
+       struct split_index *si = istate->split_index;
+       strbuf_add(sb, si->base_sha1, 20);
+       if (!si->delete_bitmap && !si->replace_bitmap)
+               return 0;
+       ewah_serialize_to(si->delete_bitmap, write_strbuf, sb);
+       ewah_serialize_to(si->replace_bitmap, write_strbuf, sb);
+       return 0;
+}
+
+static void mark_base_index_entries(struct index_state *base)
+{
+       int i;
+       /*
+        * To keep track of the shared entries between
+        * istate->base->cache[] and istate->cache[], base entry
+        * position is stored in each base entry. All positions start
+        * from 1 instead of 0, which is resrved to say "this is a new
+        * entry".
+        */
+       for (i = 0; i < base->cache_nr; i++)
+               base->cache[i]->index = i + 1;
+}
+
+void move_cache_to_base_index(struct index_state *istate)
+{
+       struct split_index *si = istate->split_index;
+       int i;
+
+       /*
+        * do not delete old si->base, its index entries may be shared
+        * with istate->cache[]. Accept a bit of leaking here because
+        * this code is only used by short-lived update-index.
+        */
+       si->base = xcalloc(1, sizeof(*si->base));
+       si->base->version = istate->version;
+       /* zero timestamp disables racy test in ce_write_index() */
+       si->base->timestamp = istate->timestamp;
+       ALLOC_GROW(si->base->cache, istate->cache_nr, si->base->cache_alloc);
+       si->base->cache_nr = istate->cache_nr;
+       memcpy(si->base->cache, istate->cache,
+              sizeof(*istate->cache) * istate->cache_nr);
+       mark_base_index_entries(si->base);
+       for (i = 0; i < si->base->cache_nr; i++)
+               si->base->cache[i]->ce_flags &= ~CE_UPDATE_IN_BASE;
+}
+
+static void mark_entry_for_delete(size_t pos, void *data)
+{
+       struct index_state *istate = data;
+       if (pos >= istate->cache_nr)
+               die("position for delete %d exceeds base index size %d",
+                   (int)pos, istate->cache_nr);
+       istate->cache[pos]->ce_flags |= CE_REMOVE;
+       istate->split_index->nr_deletions = 1;
+}
+
+static void replace_entry(size_t pos, void *data)
+{
+       struct index_state *istate = data;
+       struct split_index *si = istate->split_index;
+       struct cache_entry *dst, *src;
+
+       if (pos >= istate->cache_nr)
+               die("position for replacement %d exceeds base index size %d",
+                   (int)pos, istate->cache_nr);
+       if (si->nr_replacements >= si->saved_cache_nr)
+               die("too many replacements (%d vs %d)",
+                   si->nr_replacements, si->saved_cache_nr);
+       dst = istate->cache[pos];
+       if (dst->ce_flags & CE_REMOVE)
+               die("entry %d is marked as both replaced and deleted",
+                   (int)pos);
+       src = si->saved_cache[si->nr_replacements];
+       if (ce_namelen(src))
+               die("corrupt link extension, entry %d should have "
+                   "zero length name", (int)pos);
+       src->index = pos + 1;
+       src->ce_flags |= CE_UPDATE_IN_BASE;
+       src->ce_namelen = dst->ce_namelen;
+       copy_cache_entry(dst, src);
+       free(src);
+       si->nr_replacements++;
+}
+
+void merge_base_index(struct index_state *istate)
+{
+       struct split_index *si = istate->split_index;
+       unsigned int i;
+
+       mark_base_index_entries(si->base);
+
+       si->saved_cache     = istate->cache;
+       si->saved_cache_nr  = istate->cache_nr;
+       istate->cache_nr    = si->base->cache_nr;
+       istate->cache       = NULL;
+       istate->cache_alloc = 0;
+       ALLOC_GROW(istate->cache, istate->cache_nr, istate->cache_alloc);
+       memcpy(istate->cache, si->base->cache,
+              sizeof(*istate->cache) * istate->cache_nr);
+
+       si->nr_deletions = 0;
+       si->nr_replacements = 0;
+       ewah_each_bit(si->replace_bitmap, replace_entry, istate);
+       ewah_each_bit(si->delete_bitmap, mark_entry_for_delete, istate);
+       if (si->nr_deletions)
+               remove_marked_cache_entries(istate);
+
+       for (i = si->nr_replacements; i < si->saved_cache_nr; i++) {
+               if (!ce_namelen(si->saved_cache[i]))
+                       die("corrupt link extension, entry %d should "
+                           "have non-zero length name", i);
+               add_index_entry(istate, si->saved_cache[i],
+                               ADD_CACHE_OK_TO_ADD |
+                               ADD_CACHE_KEEP_CACHE_TREE |
+                               /*
+                                * we may have to replay what
+                                * merge-recursive.c:update_stages()
+                                * does, which has this flag on
+                                */
+                               ADD_CACHE_SKIP_DFCHECK);
+               si->saved_cache[i] = NULL;
+       }
+
+       ewah_free(si->delete_bitmap);
+       ewah_free(si->replace_bitmap);
+       free(si->saved_cache);
+       si->delete_bitmap  = NULL;
+       si->replace_bitmap = NULL;
+       si->saved_cache    = NULL;
+       si->saved_cache_nr = 0;
+}
+
+void prepare_to_write_split_index(struct index_state *istate)
+{
+       struct split_index *si = init_split_index(istate);
+       struct cache_entry **entries = NULL, *ce;
+       int i, nr_entries = 0, nr_alloc = 0;
+
+       si->delete_bitmap = ewah_new();
+       si->replace_bitmap = ewah_new();
+
+       if (si->base) {
+               /* Go through istate->cache[] and mark CE_MATCHED to
+                * entry with positive index. We'll go through
+                * base->cache[] later to delete all entries in base
+                * that are not marked eith either CE_MATCHED or
+                * CE_UPDATE_IN_BASE. If istate->cache[i] is a
+                * duplicate, deduplicate it.
+                */
+               for (i = 0; i < istate->cache_nr; i++) {
+                       struct cache_entry *base;
+                       /* namelen is checked separately */
+                       const unsigned int ondisk_flags =
+                               CE_STAGEMASK | CE_VALID | CE_EXTENDED_FLAGS;
+                       unsigned int ce_flags, base_flags, ret;
+                       ce = istate->cache[i];
+                       if (!ce->index)
+                               continue;
+                       if (ce->index > si->base->cache_nr) {
+                               ce->index = 0;
+                               continue;
+                       }
+                       ce->ce_flags |= CE_MATCHED; /* or "shared" */
+                       base = si->base->cache[ce->index - 1];
+                       if (ce == base)
+                               continue;
+                       if (ce->ce_namelen != base->ce_namelen ||
+                           strcmp(ce->name, base->name)) {
+                               ce->index = 0;
+                               continue;
+                       }
+                       ce_flags = ce->ce_flags;
+                       base_flags = base->ce_flags;
+                       /* only on-disk flags matter */
+                       ce->ce_flags   &= ondisk_flags;
+                       base->ce_flags &= ondisk_flags;
+                       ret = memcmp(&ce->ce_stat_data, &base->ce_stat_data,
+                                    offsetof(struct cache_entry, name) -
+                                    offsetof(struct cache_entry, ce_stat_data));
+                       ce->ce_flags = ce_flags;
+                       base->ce_flags = base_flags;
+                       if (ret)
+                               ce->ce_flags |= CE_UPDATE_IN_BASE;
+                       free(base);
+                       si->base->cache[ce->index - 1] = ce;
+               }
+               for (i = 0; i < si->base->cache_nr; i++) {
+                       ce = si->base->cache[i];
+                       if ((ce->ce_flags & CE_REMOVE) ||
+                           !(ce->ce_flags & CE_MATCHED))
+                               ewah_set(si->delete_bitmap, i);
+                       else if (ce->ce_flags & CE_UPDATE_IN_BASE) {
+                               ewah_set(si->replace_bitmap, i);
+                               ce->ce_flags |= CE_STRIP_NAME;
+                               ALLOC_GROW(entries, nr_entries+1, nr_alloc);
+                               entries[nr_entries++] = ce;
+                       }
+               }
+       }
+
+       for (i = 0; i < istate->cache_nr; i++) {
+               ce = istate->cache[i];
+               if ((!si->base || !ce->index) && !(ce->ce_flags & CE_REMOVE)) {
+                       assert(!(ce->ce_flags & CE_STRIP_NAME));
+                       ALLOC_GROW(entries, nr_entries+1, nr_alloc);
+                       entries[nr_entries++] = ce;
+               }
+               ce->ce_flags &= ~CE_MATCHED;
+       }
+
+       /*
+        * take cache[] out temporarily, put entries[] in its place
+        * for writing
+        */
+       si->saved_cache = istate->cache;
+       si->saved_cache_nr = istate->cache_nr;
+       istate->cache = entries;
+       istate->cache_nr = nr_entries;
+}
+
+void finish_writing_split_index(struct index_state *istate)
+{
+       struct split_index *si = init_split_index(istate);
+
+       ewah_free(si->delete_bitmap);
+       ewah_free(si->replace_bitmap);
+       si->delete_bitmap = NULL;
+       si->replace_bitmap = NULL;
+       free(istate->cache);
+       istate->cache = si->saved_cache;
+       istate->cache_nr = si->saved_cache_nr;
+}
+
+void discard_split_index(struct index_state *istate)
+{
+       struct split_index *si = istate->split_index;
+       if (!si)
+               return;
+       istate->split_index = NULL;
+       si->refcount--;
+       if (si->refcount)
+               return;
+       if (si->base) {
+               discard_index(si->base);
+               free(si->base);
+       }
+       free(si);
+}
+
+void save_or_free_index_entry(struct index_state *istate, struct cache_entry *ce)
+{
+       if (ce->index &&
+           istate->split_index &&
+           istate->split_index->base &&
+           ce->index <= istate->split_index->base->cache_nr &&
+           ce == istate->split_index->base->cache[ce->index - 1])
+               ce->ce_flags |= CE_REMOVE;
+       else
+               free(ce);
+}
+
+void replace_index_entry_in_base(struct index_state *istate,
+                                struct cache_entry *old,
+                                struct cache_entry *new)
+{
+       if (old->index &&
+           istate->split_index &&
+           istate->split_index->base &&
+           old->index <= istate->split_index->base->cache_nr) {
+               new->index = old->index;
+               if (old != istate->split_index->base->cache[new->index - 1])
+                       free(istate->split_index->base->cache[new->index - 1]);
+               istate->split_index->base->cache[new->index - 1] = new;
+       }
+}
diff --git a/split-index.h b/split-index.h
new file mode 100644 (file)
index 0000000..c1324f5
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef SPLIT_INDEX_H
+#define SPLIT_INDEX_H
+
+struct index_state;
+struct strbuf;
+struct ewah_bitmap;
+
+struct split_index {
+       unsigned char base_sha1[20];
+       struct index_state *base;
+       struct ewah_bitmap *delete_bitmap;
+       struct ewah_bitmap *replace_bitmap;
+       struct cache_entry **saved_cache;
+       unsigned int saved_cache_nr;
+       unsigned int nr_deletions;
+       unsigned int nr_replacements;
+       int refcount;
+};
+
+struct split_index *init_split_index(struct index_state *istate);
+void save_or_free_index_entry(struct index_state *istate, struct cache_entry *ce);
+void replace_index_entry_in_base(struct index_state *istate,
+                                struct cache_entry *old,
+                                struct cache_entry *new);
+int read_link_extension(struct index_state *istate,
+                       const void *data, unsigned long sz);
+int write_link_extension(struct strbuf *sb,
+                        struct index_state *istate);
+void move_cache_to_base_index(struct index_state *istate);
+void merge_base_index(struct index_state *istate);
+void prepare_to_write_split_index(struct index_state *istate);
+void finish_writing_split_index(struct index_state *istate);
+void discard_split_index(struct index_state *istate);
+
+#endif
index ac62982e672c22457468f601b35716dc9a52a81c..12c78656ca9ddb51fb62b95be29d18056911803f 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -600,3 +600,22 @@ char *xstrdup_tolower(const char *string)
        result[i] = '\0';
        return result;
 }
+
+char *xstrvfmt(const char *fmt, va_list ap)
+{
+       struct strbuf buf = STRBUF_INIT;
+       strbuf_vaddf(&buf, fmt, ap);
+       return strbuf_detach(&buf, NULL);
+}
+
+char *xstrfmt(const char *fmt, ...)
+{
+       va_list ap;
+       char *ret;
+
+       va_start(ap, fmt);
+       ret = xstrvfmt(fmt, ap);
+       va_end(ap);
+
+       return ret;
+}
index e9ad03eabe72dc2ee6e7e0086baca71d343264ce..a594c24b2b0e9e82c16de1bb787758d561cb2e7b 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -187,4 +187,13 @@ extern int fprintf_ln(FILE *fp, const char *fmt, ...);
 
 char *xstrdup_tolower(const char *);
 
+/*
+ * Create a newly allocated string using printf format. You can do this easily
+ * with a strbuf, but this provides a shortcut to save a few lines.
+ */
+__attribute__((format (printf, 1, 0)))
+char *xstrvfmt(const char *fmt, va_list ap);
+__attribute__((format (printf, 1, 2)))
+char *xstrfmt(const char *fmt, ...);
+
 #endif /* STRBUF_H */
index c2b41a85013eec87a412badc4097f364d547726d..5261e8cf499006c1d84fc42a3e96e4dee7f09ba1 100644 (file)
@@ -35,12 +35,11 @@ static int longest_path_match(const char *name_a, int len_a,
        return match_len;
 }
 
-static struct cache_def default_cache;
+static struct cache_def default_cache = CACHE_DEF_INIT;
 
 static inline void reset_lstat_cache(struct cache_def *cache)
 {
-       cache->path[0] = '\0';
-       cache->len = 0;
+       strbuf_reset(&cache->path);
        cache->flags = 0;
        /*
         * The track_flags and prefix_len_stat_func members is only
@@ -73,7 +72,7 @@ static int lstat_cache_matchlen(struct cache_def *cache,
                                int prefix_len_stat_func)
 {
        int match_len, last_slash, last_slash_dir, previous_slash;
-       int save_flags, max_len, ret;
+       int save_flags, ret;
        struct stat st;
 
        if (cache->track_flags != track_flags ||
@@ -93,14 +92,14 @@ static int lstat_cache_matchlen(struct cache_def *cache,
                 * the 2 "excluding" path types.
                 */
                match_len = last_slash =
-                       longest_path_match(name, len, cache->path, cache->len,
-                                          &previous_slash);
+                       longest_path_match(name, len, cache->path.buf,
+                                          cache->path.len, &previous_slash);
                *ret_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK);
 
                if (!(track_flags & FL_FULLPATH) && match_len == len)
                        match_len = last_slash = previous_slash;
 
-               if (*ret_flags && match_len == cache->len)
+               if (*ret_flags && match_len == cache->path.len)
                        return match_len;
                /*
                 * If we now have match_len > 0, we would know that
@@ -121,21 +120,22 @@ static int lstat_cache_matchlen(struct cache_def *cache,
         */
        *ret_flags = FL_DIR;
        last_slash_dir = last_slash;
-       max_len = len < PATH_MAX ? len : PATH_MAX;
-       while (match_len < max_len) {
+       if (len > cache->path.len)
+               strbuf_grow(&cache->path, len - cache->path.len);
+       while (match_len < len) {
                do {
-                       cache->path[match_len] = name[match_len];
+                       cache->path.buf[match_len] = name[match_len];
                        match_len++;
-               } while (match_len < max_len && name[match_len] != '/');
-               if (match_len >= max_len && !(track_flags & FL_FULLPATH))
+               } while (match_len < len && name[match_len] != '/');
+               if (match_len >= len && !(track_flags & FL_FULLPATH))
                        break;
                last_slash = match_len;
-               cache->path[last_slash] = '\0';
+               cache->path.buf[last_slash] = '\0';
 
                if (last_slash <= prefix_len_stat_func)
-                       ret = stat(cache->path, &st);
+                       ret = stat(cache->path.buf, &st);
                else
-                       ret = lstat(cache->path, &st);
+                       ret = lstat(cache->path.buf, &st);
 
                if (ret) {
                        *ret_flags = FL_LSTATERR;
@@ -158,12 +158,11 @@ static int lstat_cache_matchlen(struct cache_def *cache,
         * for the moment!
         */
        save_flags = *ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
-       if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) {
-               cache->path[last_slash] = '\0';
-               cache->len = last_slash;
+       if (save_flags && last_slash > 0) {
+               cache->path.buf[last_slash] = '\0';
+               cache->path.len = last_slash;
                cache->flags = save_flags;
-       } else if ((track_flags & FL_DIR) &&
-                  last_slash_dir > 0 && last_slash_dir <= PATH_MAX) {
+       } else if ((track_flags & FL_DIR) && last_slash_dir > 0) {
                /*
                 * We have a separate test for the directory case,
                 * since it could be that we have found a symlink or a
@@ -175,8 +174,8 @@ static int lstat_cache_matchlen(struct cache_def *cache,
                 * can still cache the path components before the last
                 * one (the found symlink or non-existing component).
                 */
-               cache->path[last_slash_dir] = '\0';
-               cache->len = last_slash_dir;
+               cache->path.buf[last_slash_dir] = '\0';
+               cache->path.len = last_slash_dir;
                cache->flags = FL_DIR;
        } else {
                reset_lstat_cache(cache);
@@ -273,21 +272,18 @@ static int threaded_has_dirs_only_path(struct cache_def *cache, const char *name
                FL_DIR;
 }
 
-static struct removal_def {
-       char path[PATH_MAX];
-       int len;
-} removal;
+static struct strbuf removal = STRBUF_INIT;
 
 static void do_remove_scheduled_dirs(int new_len)
 {
        while (removal.len > new_len) {
-               removal.path[removal.len] = '\0';
-               if (rmdir(removal.path))
+               removal.buf[removal.len] = '\0';
+               if (rmdir(removal.buf))
                        break;
                do {
                        removal.len--;
                } while (removal.len > new_len &&
-                        removal.path[removal.len] != '/');
+                        removal.buf[removal.len] != '/');
        }
        removal.len = new_len;
 }
@@ -297,7 +293,7 @@ void schedule_dir_for_removal(const char *name, int len)
        int match_len, last_slash, i, previous_slash;
 
        match_len = last_slash = i =
-               longest_path_match(name, len, removal.path, removal.len,
+               longest_path_match(name, len, removal.buf, removal.len,
                                   &previous_slash);
        /* Find last slash inside 'name' */
        while (i < len) {
@@ -317,11 +313,8 @@ void schedule_dir_for_removal(const char *name, int len)
         * If we go deeper down the directory tree, we only need to
         * save the new path components as we go down.
         */
-       if (match_len < last_slash) {
-               memcpy(&removal.path[match_len], &name[match_len],
-                      last_slash - match_len);
-               removal.len = last_slash;
-       }
+       if (match_len < last_slash)
+               strbuf_add(&removal, &name[match_len], last_slash - match_len);
 }
 
 void remove_scheduled_dirs(void)
index eafc9d2d90ff7646062edd9a376839e469334760..a77b8e546926308fbe348bf01d625bad4996a095 100755 (executable)
@@ -19,6 +19,10 @@ case "$PATH_INFO" in
        printf "text/plain; charset=utf-16"
        charset=utf-16
        ;;
+*odd-spacing*)
+       printf "text/plain; foo=bar ;charset=utf-16; other=nonsense"
+       charset=utf-16
+       ;;
 esac
 printf "\n"
 
index 1a5a5f39fd94f9eb98824b3d84983782b9ff71bb..9aeb352b3dba3fd7e46a5a0732425cd2686fc9f3 100755 (executable)
@@ -64,6 +64,7 @@ valid_ref "$(printf 'heads/fu\303\237')"
 invalid_ref 'heads/*foo/bar' --refspec-pattern
 invalid_ref 'heads/foo*/bar' --refspec-pattern
 invalid_ref 'heads/f*o/bar' --refspec-pattern
+invalid_ref 'heads/foo*//bar' --refspec-pattern
 
 ref='foo'
 invalid_ref "$ref"
@@ -128,6 +129,20 @@ valid_ref NOT_MINGW "$ref" '--allow-onelevel --normalize'
 invalid_ref NOT_MINGW "$ref" '--refspec-pattern --normalize'
 valid_ref NOT_MINGW "$ref" '--refspec-pattern --allow-onelevel --normalize'
 
+
+valid_ref 'refs/heads/a-very-long-refname'
+invalid_ref 'refs/heads/.a-very-long-refname'
+invalid_ref 'refs/heads/abcdefgh0123..'
+invalid_ref 'refs/heads/abcdefgh01234..'
+invalid_ref 'refs/heads/abcdefgh012345..'
+invalid_ref 'refs/heads/abcdefgh0123456..'
+invalid_ref 'refs/heads/abcdefgh01234567..'
+valid_ref 'refs/heads/abcdefgh0123.a'
+valid_ref 'refs/heads/abcdefgh01234.a'
+valid_ref 'refs/heads/abcdefgh012345.a'
+valid_ref 'refs/heads/abcdefgh0123456.a'
+valid_ref 'refs/heads/abcdefgh01234567.a'
+
 test_expect_success "check-ref-format --branch @{-1}" '
        T=$(git write-tree) &&
        sha1=$(echo A | git commit-tree $T) &&
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
new file mode 100755 (executable)
index 0000000..94fb473
--- /dev/null
@@ -0,0 +1,194 @@
+#!/bin/sh
+
+test_description='split index mode tests'
+
+. ./test-lib.sh
+
+# We need total control of index splitting here
+sane_unset GIT_TEST_SPLIT_INDEX
+
+test_expect_success 'enable split index' '
+       git update-index --split-index &&
+       test-dump-split-index .git/index >actual &&
+       cat >expect <<EOF &&
+own 8299b0bcd1ac364e5f1d7768efb62fa2da79a339
+base 39d890139ee5356c7ef572216cebcd27aa41f9df
+replacements:
+deletions:
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'add one file' '
+       : >one &&
+       git update-index --add one &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<EOF &&
+base 39d890139ee5356c7ef572216cebcd27aa41f9df
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+replacements:
+deletions:
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'disable split index' '
+       git update-index --no-split-index &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       BASE=`test-dump-split-index .git/index | grep "^own" | sed "s/own/base/"` &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<EOF &&
+not a split index
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'enable split index again, "one" now belongs to base index"' '
+       git update-index --split-index &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<EOF &&
+$BASE
+replacements:
+deletions:
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'modify original file, base index untouched' '
+       echo modified >one &&
+       git update-index one &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0      one
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       q_to_tab >expect <<EOF &&
+$BASE
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+replacements: 0
+deletions:
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'add another file, which stays index' '
+       : >two &&
+       git update-index --add two &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0      one
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      two
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       q_to_tab >expect <<EOF &&
+$BASE
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      two
+replacements: 0
+deletions:
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'remove file not in base index' '
+       git update-index --force-remove two &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0      one
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       q_to_tab >expect <<EOF &&
+$BASE
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+replacements: 0
+deletions:
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'remove file in base index' '
+       git update-index --force-remove one &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<EOF &&
+$BASE
+replacements:
+deletions: 0
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'add original file back' '
+       : >one &&
+       git update-index --add one &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<EOF &&
+$BASE
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+replacements:
+deletions: 0
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'add new file' '
+       : >two &&
+       git update-index --add two &&
+       git ls-files --stage >actual &&
+       cat >expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      two
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'unify index, two files remain' '
+       git update-index --no-split-index &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      two
+EOF
+       test_cmp ls-files.expect ls-files.actual
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<EOF &&
+not a split index
+EOF
+       test_cmp expect actual
+'
+
+test_done
index 29c1fb10cad93818d467780b799ff874e8fdcab0..cc830da58d920718b7b0a990a359afa9dd783b4c 100755 (executable)
@@ -7,6 +7,8 @@ test_description='skip-worktree bit test'
 
 . ./test-lib.sh
 
+sane_unset GIT_TEST_SPLIT_INDEX
+
 test_set_index_version 3
 
 cat >expect.full <<EOF
index be8c1d5ef9fd650c128a9fe7126cbcd8db6c5a95..5a27ec9b5eb7c37145a098d878432948233ba756 100755 (executable)
@@ -33,6 +33,7 @@ test_expect_success setup '
        tr "[a-z]" "[A-Z]" <original >newfile &&
        git add newfile &&
        git commit -a -m"side edits further." &&
+       git branch second-side &&
 
        tr "[a-m]" "[A-M]" <original >newfile &&
        rm -f original &&
@@ -41,6 +42,7 @@ test_expect_success setup '
        git branch test-rebase side &&
        git branch test-rebase-pick side &&
        git branch test-reference-pick side &&
+       git branch test-conflicts side &&
        git checkout -b test-merge side
 '
 
@@ -138,4 +140,17 @@ test_expect_success 'rebase -s funny -Xopt' '
        test -f funny.was.run
 '
 
+test_expect_success 'rebase --skip works with two conflicts in a row' '
+       git checkout second-side  &&
+       tr "[A-Z]" "[a-z]" <newfile >tmp &&
+       mv tmp newfile &&
+       git commit -a -m"edit conflicting with side" &&
+       tr "[d-f]" "[D-F]" <newfile >tmp &&
+       mv tmp newfile &&
+       git commit -a -m"another edit conflicting with side" &&
+       test_must_fail git rebase --merge test-conflicts &&
+       test_must_fail git rebase --skip &&
+       git rebase --skip
+'
+
 test_done
index 899c1c5bbcb2e994a1462eb8048344b68d23767d..7b8babd89b22dc94d3a3f839d72cce86c5aaea59 100755 (executable)
@@ -119,14 +119,10 @@ test_expect_success \
     'echo ignore me >a/ignored &&
      echo ignored export-ignore >.git/info/attributes'
 
-test_expect_success \
-    'add files to repository' \
-    'find a -type f | xargs git update-index --add &&
-     find a -type l | xargs git update-index --add &&
-     treeid=$(git write-tree) &&
-     echo $treeid >treeid &&
-     git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
-     git commit-tree $treeid </dev/null)'
+test_expect_success 'add files to repository' '
+       git add a &&
+       GIT_COMMITTER_DATE="2005-05-27 22:00" git commit -m initial
+'
 
 test_expect_success 'setup export-subst' '
        echo "substfile?" export-subst >>.git/info/attributes &&
index 21a5c93f41e28845a540637eb11bd6eec830a063..c929db563326614e01962b3e322f087640fa19d9 100755 (executable)
@@ -61,14 +61,10 @@ test_expect_success \
     'echo ignore me >a/ignored &&
      echo ignored export-ignore >.git/info/attributes'
 
-test_expect_success \
-    'add files to repository' \
-    'find a -type f | xargs git update-index --add &&
-     find a -type l | xargs git update-index --add &&
-     treeid=`git write-tree` &&
-     echo $treeid >treeid &&
-     git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
-     git commit-tree $treeid </dev/null)'
+test_expect_success 'add files to repository' '
+       git add a &&
+       GIT_COMMITTER_DATE="2005-05-27 22:00" git commit -m initial
+'
 
 test_expect_success 'setup export-subst' '
        echo "substfile?" export-subst >>.git/info/attributes &&
index 01b8aae2ed12eab92ecbfb562cb616e0cfc47202..ac71418a1b26bc17a1252d6831869b96a8d8c176 100755 (executable)
@@ -191,5 +191,10 @@ test_expect_success 'http error messages are reencoded' '
        grep "this is the error message" stderr
 '
 
+test_expect_success 'reencoding is robust to whitespace oddities' '
+       test_must_fail git clone "$HTTPD_URL/error/odd-spacing" 2>stderr &&
+       grep "this is the error message" stderr
+'
+
 stop_httpd
 test_done
index c277db64f75ac9f9c0cd617262824d76959fbfbd..88ed3191e871cd9a164c3c903f69b07e7d9cf937 100755 (executable)
@@ -468,4 +468,10 @@ test_expect_success 'single-character name is parsed correctly' '
        test_cmp expect actual
 '
 
+test_expect_success 'unused %G placeholders are passed through' '
+       echo "%GX %G" >expect &&
+       git log -1 --format="%GX %G" >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 74de814aec1465848ba316e867106a18635139f6..04118ad75be8462fa2ee1e5e870feb0a44ac0482 100755 (executable)
@@ -426,10 +426,10 @@ test_expect_success SANITY 'removal failure' '
 
        mkdir foo &&
        touch foo/bar &&
+       test_when_finished "chmod 755 foo" &&
        (exec <foo/bar &&
         chmod 0 foo &&
-        test_must_fail git clean -f -d &&
-        chmod 755 foo)
+        test_must_fail git clean -f -d)
 '
 
 test_expect_success 'nested git work tree' '
index 5ddac1a9f740c03f1d94f75db2c6e54dd3391eda..474dab381aef027207026cb938df7a09cc7a9056 100755 (executable)
@@ -43,44 +43,74 @@ test_expect_success GPG 'create signed commits' '
 
        test_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^ &&
        git tag seventh-signed
+
+       echo 8 >file && test_tick && git commit -a -m eighth -SB7227189 &&
+       git tag eighth-signed-alt
 '
 
-test_expect_success GPG 'show signatures' '
+test_expect_success GPG 'verify and show signatures' '
        (
-               for commit in initial second merge fourth-signed fifth-signed sixth-signed master
+               for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed
                do
+                       git verify-commit $commit &&
                        git show --pretty=short --show-signature $commit >actual &&
-                       grep "Good signature from" actual || exit 1
-                       ! grep "BAD signature from" actual || exit 1
-                       echo $commit OK
+                       grep "Good signature from" actual &&
+                       ! grep "BAD signature from" actual &&
+                       echo $commit OK || exit 1
                done
        ) &&
        (
                for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned
                do
+                       test_must_fail git verify-commit $commit &&
                        git show --pretty=short --show-signature $commit >actual &&
-                       grep "Good signature from" actual && exit 1
-                       ! grep "BAD signature from" actual || exit 1
-                       echo $commit OK
+                       ! grep "Good signature from" actual &&
+                       ! grep "BAD signature from" actual &&
+                       echo $commit OK || exit 1
+               done
+       ) &&
+       (
+               for commit in eighth-signed-alt
+               do
+                       git show --pretty=short --show-signature $commit >actual &&
+                       grep "Good signature from" actual &&
+                       ! grep "BAD signature from" actual &&
+                       grep "not certified" actual &&
+                       echo $commit OK || exit 1
                done
        )
 '
 
+test_expect_success GPG 'show signed commit with signature' '
+       git show -s initial >commit &&
+       git show -s --show-signature initial >show &&
+       git verify-commit -v initial >verify.1 2>verify.2 &&
+       git cat-file commit initial >cat &&
+       grep -v "gpg: " show >show.commit &&
+       grep "gpg: " show >show.gpg &&
+       grep -v "^ " cat | grep -v "^gpgsig " >cat.commit &&
+       test_cmp show.commit commit &&
+       test_cmp show.gpg verify.2 &&
+       test_cmp cat.commit verify.1
+'
+
 test_expect_success GPG 'detect fudged signature' '
-       git cat-file commit master >raw &&
+       git cat-file commit seventh-signed >raw &&
 
        sed -e "s/seventh/7th forged/" raw >forged1 &&
        git hash-object -w -t commit forged1 >forged1.commit &&
+       ! git verify-commit $(cat forged1.commit) &&
        git show --pretty=short --show-signature $(cat forged1.commit) >actual1 &&
        grep "BAD signature from" actual1 &&
        ! grep "Good signature from" actual1
 '
 
 test_expect_success GPG 'detect fudged signature with NUL' '
-       git cat-file commit master >raw &&
+       git cat-file commit seventh-signed >raw &&
        cat raw >forged2 &&
        echo Qwik | tr "Q" "\000" >>forged2 &&
        git hash-object -w -t commit forged2 >forged2.commit &&
+       ! git verify-commit $(cat forged2.commit) &&
        git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
        grep "BAD signature from" actual2 &&
        ! grep "Good signature from" actual2
@@ -89,9 +119,50 @@ test_expect_success GPG 'detect fudged signature with NUL' '
 test_expect_success GPG 'amending already signed commit' '
        git checkout fourth-signed^0 &&
        git commit --amend -S --no-edit &&
+       git verify-commit HEAD &&
        git show -s --show-signature HEAD >actual &&
        grep "Good signature from" actual &&
        ! grep "BAD signature from" actual
 '
 
+test_expect_success GPG 'show good signature with custom format' '
+       cat >expect <<-\EOF &&
+       G
+       13B6F51ECDDE430D
+       C O Mitter <committer@example.com>
+       EOF
+       git log -1 --format="%G?%n%GK%n%GS" sixth-signed >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG 'show bad signature with custom format' '
+       cat >expect <<-\EOF &&
+       B
+       13B6F51ECDDE430D
+       C O Mitter <committer@example.com>
+       EOF
+       git log -1 --format="%G?%n%GK%n%GS" $(cat forged1.commit) >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG 'show unknown signature with custom format' '
+       cat >expect <<-\EOF &&
+       U
+       61092E85B7227189
+       Eris Discordia <discord@example.net>
+       EOF
+       git log -1 --format="%G?%n%GK%n%GS" eighth-signed-alt >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG 'show lack of signature with custom format' '
+       cat >expect <<-\EOF &&
+       N
+
+
+       EOF
+       git log -1 --format="%G?%n%GK%n%GS" seventh-unsigned >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 0a6724fcc45eed89d612b0e641fd3fe6d4231fcb..9d51c92b74b5b9251470ea3b5d168a4cfbfb9939 100644 (file)
        Memcheck:Addr4
        fun:copy_ref
 }
+{
+       ignore-sse-check_refname_format-addr
+       Memcheck:Addr8
+       fun:check_refname_format
+}
+{
+       ignore-sse-check_refname_format-cond
+       Memcheck:Cond
+       fun:check_refname_format
+}
+{
+       ignore-sse-check_refname_format-value
+       Memcheck:Value8
+       fun:check_refname_format
+}
index 47eab9765f5cd172ca630b2a632a92d123354442..330ba4f4dd23e2cf21acbe8a7023eaca4ec09afe 100644 (file)
@@ -56,11 +56,12 @@ static int dump_cache_tree(struct cache_tree *it,
 
 int main(int ac, char **av)
 {
+       struct index_state istate;
        struct cache_tree *another = cache_tree();
        if (read_cache() < 0)
                die("unable to read index file");
-       cache_tree_update(another,
-                         (const struct cache_entry * const *)active_cache,
-                         active_nr, WRITE_TREE_DRY_RUN);
+       istate = the_index;
+       istate.cache_tree = another;
+       cache_tree_update(&istate, WRITE_TREE_DRY_RUN);
        return dump_cache_tree(active_cache_tree, another, "");
 }
diff --git a/test-dump-split-index.c b/test-dump-split-index.c
new file mode 100644 (file)
index 0000000..9cf3112
--- /dev/null
@@ -0,0 +1,34 @@
+#include "cache.h"
+#include "split-index.h"
+#include "ewah/ewok.h"
+
+static void show_bit(size_t pos, void *data)
+{
+       printf(" %d", (int)pos);
+}
+
+int main(int ac, char **av)
+{
+       struct split_index *si;
+       int i;
+
+       do_read_index(&the_index, av[1], 1);
+       printf("own %s\n", sha1_to_hex(the_index.sha1));
+       si = the_index.split_index;
+       if (!si) {
+               printf("not a split index\n");
+               return 0;
+       }
+       printf("base %s\n", sha1_to_hex(si->base_sha1));
+       for (i = 0; i < the_index.cache_nr; i++) {
+               struct cache_entry *ce = the_index.cache[i];
+               printf("%06o %s %d\t%s\n", ce->ce_mode,
+                      sha1_to_hex(ce->sha1), ce_stage(ce), ce->name);
+       }
+       printf("replacements:");
+       ewah_each_bit(si->replace_bitmap, show_bit, NULL);
+       printf("\ndeletions:");
+       ewah_each_bit(si->delete_bitmap, show_bit, NULL);
+       printf("\n");
+       return 0;
+}
index 4728013910cf1a36dac6a594c403c29de60b1fe6..9ebcbca9d25150e9bfca77a73ca45d2a06423f49 100644 (file)
@@ -6,12 +6,11 @@ static struct lock_file index_lock;
 
 int main(int ac, char **av)
 {
-       int fd = hold_locked_index(&index_lock, 1);
+       hold_locked_index(&index_lock, 1);
        if (read_cache() < 0)
                die("unable to read index file");
        active_cache_tree = NULL;
-       if (write_cache(fd, active_cache, active_nr)
-           || commit_lock_file(&index_lock))
+       if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
                die("unable to write index file");
        return 0;
 }
index 84c616f180bebc730d05f306906558dd0abad2a4..3d8fe7d801293a338f13dfd01ba352e84c658813 100644 (file)
@@ -153,7 +153,7 @@ static struct child_process *get_helper(struct transport *transport)
        write_constant(helper->in, "capabilities\n");
 
        while (1) {
-               const char *capname;
+               const char *capname, *arg;
                int mandatory = 0;
                if (recvline(data, &buf))
                        exit(128);
@@ -183,19 +183,19 @@ static struct child_process *get_helper(struct transport *transport)
                        data->export = 1;
                else if (!strcmp(capname, "check-connectivity"))
                        data->check_connectivity = 1;
-               else if (!data->refspecs && starts_with(capname, "refspec ")) {
+               else if (!data->refspecs && skip_prefix(capname, "refspec ", &arg)) {
                        ALLOC_GROW(refspecs,
                                   refspec_nr + 1,
                                   refspec_alloc);
-                       refspecs[refspec_nr++] = xstrdup(capname + strlen("refspec "));
+                       refspecs[refspec_nr++] = xstrdup(arg);
                } else if (!strcmp(capname, "connect")) {
                        data->connect = 1;
                } else if (!strcmp(capname, "signed-tags")) {
                        data->signed_tags = 1;
-               } else if (starts_with(capname, "export-marks ")) {
-                       data->export_marks = xstrdup(capname + strlen("export-marks "));
-               } else if (starts_with(capname, "import-marks")) {
-                       data->import_marks = xstrdup(capname + strlen("import-marks "));
+               } else if (skip_prefix(capname, "export-marks ", &arg)) {
+                       data->export_marks = xstrdup(arg);
+               } else if (skip_prefix(capname, "import-marks ", &arg)) {
+                       data->import_marks = xstrdup(arg);
                } else if (starts_with(capname, "no-private-update")) {
                        data->no_private_update = 1;
                } else if (mandatory) {
index 325f03e1eef97df296bb1cc9f7d2d33c218dde26..59c9727d8d63d548001513c1560b5c7a7e065b95 100644 (file)
@@ -192,7 +192,9 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
 
 static const char *rsync_url(const char *url)
 {
-       return !starts_with(url, "rsync://") ? skip_prefix(url, "rsync:") : url;
+       if (!starts_with(url, "rsync://"))
+               skip_prefix(url, "rsync:", &url);
+       return url;
 }
 
 static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
index e9a05b908589a0fa8c446b1896821b77aa24596c..c6aa8fb993aa4bd92e4269d7a85c6b45fe9801e8 100644 (file)
@@ -8,6 +8,7 @@
 #include "progress.h"
 #include "refs.h"
 #include "attr.h"
+#include "split-index.h"
 
 /*
  * Error messages expected by scripts out of plumbing commands such as
@@ -56,17 +57,15 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
        int i;
        const char **msgs = opts->msgs;
        const char *msg;
-       char *tmp;
        const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches";
+
        if (advice_commit_before_merge)
                msg = "Your local changes to the following files would be overwritten by %s:\n%%s"
                        "Please, commit your changes or stash them before you can %s.";
        else
                msg = "Your local changes to the following files would be overwritten by %s:\n%%s";
-       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2);
-       sprintf(tmp, msg, cmd, cmd2);
-       msgs[ERROR_WOULD_OVERWRITE] = tmp;
-       msgs[ERROR_NOT_UPTODATE_FILE] = tmp;
+       msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =
+               xstrfmt(msg, cmd, cmd2);
 
        msgs[ERROR_NOT_UPTODATE_DIR] =
                "Updating the following directories would lose untracked files in it:\n%s";
@@ -76,12 +75,9 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
                        "Please move or remove them before you can %s.";
        else
                msg = "The following untracked working tree files would be %s by %s:\n%%s";
-       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("removed") + strlen(cmd2) - 4);
-       sprintf(tmp, msg, "removed", cmd, cmd2);
-       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp;
-       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("overwritten") + strlen(cmd2) - 4);
-       sprintf(tmp, msg, "overwritten", cmd, cmd2);
-       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp;
+
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = xstrfmt(msg, "removed", cmd, cmd2);
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = xstrfmt(msg, "overwritten", cmd, cmd2);
 
        /*
         * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
@@ -246,7 +242,9 @@ static int verify_absent_sparse(const struct cache_entry *ce,
                                enum unpack_trees_error_types,
                                struct unpack_trees_options *o);
 
-static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_options *o)
+static int apply_sparse_checkout(struct index_state *istate,
+                                struct cache_entry *ce,
+                                struct unpack_trees_options *o)
 {
        int was_skip_worktree = ce_skip_worktree(ce);
 
@@ -254,6 +252,10 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt
                ce->ce_flags |= CE_SKIP_WORKTREE;
        else
                ce->ce_flags &= ~CE_SKIP_WORKTREE;
+       if (was_skip_worktree != ce_skip_worktree(ce)) {
+               ce->ce_flags |= CE_UPDATE_IN_BASE;
+               istate->cache_changed |= CE_ENTRY_CHANGED;
+       }
 
        /*
         * if (!was_skip_worktree && !ce_skip_worktree()) {
@@ -1014,6 +1016,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        state.force = 1;
        state.quiet = 1;
        state.refresh_cache = 1;
+       state.istate = &o->result;
 
        memset(&el, 0, sizeof(el));
        if (!core_apply_sparse_checkout || !o->update)
@@ -1030,6 +1033,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        o->result.timestamp.sec = o->src_index->timestamp.sec;
        o->result.timestamp.nsec = o->src_index->timestamp.nsec;
        o->result.version = o->src_index->version;
+       o->result.split_index = o->src_index->split_index;
+       if (o->result.split_index)
+               o->result.split_index->refcount++;
+       hashcpy(o->result.sha1, o->src_index->sha1);
        o->merge_size = len;
        mark_all_ce_unused(o->src_index);
 
@@ -1120,7 +1127,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                                ret = -1;
                        }
 
-                       if (apply_sparse_checkout(ce, o)) {
+                       if (apply_sparse_checkout(&o->result, ce, o)) {
                                if (!o->show_all_errors)
                                        goto return_failed;
                                ret = -1;
@@ -1248,7 +1255,7 @@ static void invalidate_ce_path(const struct cache_entry *ce,
                               struct unpack_trees_options *o)
 {
        if (ce)
-               cache_tree_invalidate_path(o->src_index->cache_tree, ce->name);
+               cache_tree_invalidate_path(o->src_index, ce->name);
 }
 
 /*
index ec87cba75099ebe05e936430ca3b688275e01a4a..3d4c54b5cd5d6f6cb4e0ea97550799eb9d46f033 100644 (file)
@@ -483,8 +483,7 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
        int user_matched = 0;
        int retval;
 
-       key = skip_prefix(var, collect->section);
-       if (!key || *(key++) != '.') {
+       if (!skip_prefix(var, collect->section, &key) || *(key++) != '.') {
                if (collect->cascade_fn)
                        return collect->cascade_fn(var, value, cb);
                return 0; /* not interested */
index 1dd86b8f33e04cfc6c0616578416acb1160b5718..014826464e07c93374868c61673cfe308961dfb7 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -253,7 +253,8 @@ int walker_fetch(struct walker *walker, int targets, char **target,
 {
        struct ref_lock **lock = xcalloc(targets, sizeof(struct ref_lock *));
        unsigned char *sha1 = xmalloc(targets * 20);
-       char *msg;
+       const char *msg;
+       char *to_free = NULL;
        int ret;
        int i;
 
@@ -285,21 +286,19 @@ int walker_fetch(struct walker *walker, int targets, char **target,
        if (loop(walker))
                goto unlock_and_fail;
 
-       if (write_ref_log_details) {
-               msg = xmalloc(strlen(write_ref_log_details) + 12);
-               sprintf(msg, "fetch from %s", write_ref_log_details);
-       } else {
-               msg = NULL;
-       }
+       if (write_ref_log_details)
+               msg = to_free = xstrfmt("fetch from %s", write_ref_log_details);
+       else
+               msg = "fetch (unknown)";
        for (i = 0; i < targets; i++) {
                if (!write_ref || !write_ref[i])
                        continue;
-               ret = write_ref_sha1(lock[i], &sha1[20 * i], msg ? msg : "fetch (unknown)");
+               ret = write_ref_sha1(lock[i], &sha1[20 * i], msg);
                lock[i] = NULL;
                if (ret)
                        goto unlock_and_fail;
        }
-       free(msg);
+       free(to_free);
 
        return 0;
 
@@ -307,6 +306,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
        for (i = 0; i < targets; i++)
                if (lock[i])
                        unlock_ref(lock[i]);
+       free(to_free);
 
        return -1;
 }
index 318a191238fb831da8b72f04886b311f1c2c2567..882cfe9fb050efe84e2330b9aea76685c6a7f891 100644 (file)
@@ -734,37 +734,34 @@ static void wt_status_print_changed(struct wt_status *s)
 static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted)
 {
        struct child_process sm_summary;
-       char summary_limit[64];
-       char index[PATH_MAX];
-       const char *env[] = { NULL, NULL };
+       struct argv_array env = ARGV_ARRAY_INIT;
        struct argv_array argv = ARGV_ARRAY_INIT;
        struct strbuf cmd_stdout = STRBUF_INIT;
        struct strbuf summary = STRBUF_INIT;
        char *summary_content;
        size_t len;
 
-       sprintf(summary_limit, "%d", s->submodule_summary);
-       snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
+       argv_array_pushf(&env, "GIT_INDEX_FILE=%s", s->index_file);
 
-       env[0] = index;
        argv_array_push(&argv, "submodule");
        argv_array_push(&argv, "summary");
        argv_array_push(&argv, uncommitted ? "--files" : "--cached");
        argv_array_push(&argv, "--for-status");
        argv_array_push(&argv, "--summary-limit");
-       argv_array_push(&argv, summary_limit);
+       argv_array_pushf(&argv, "%d", s->submodule_summary);
        if (!uncommitted)
                argv_array_push(&argv, s->amend ? "HEAD^" : "HEAD");
 
        memset(&sm_summary, 0, sizeof(sm_summary));
        sm_summary.argv = argv.argv;
-       sm_summary.env = env;
+       sm_summary.env = env.argv;
        sm_summary.git_cmd = 1;
        sm_summary.no_stdin = 1;
        fflush(s->fp);
        sm_summary.out = -1;
 
        run_command(&sm_summary);
+       argv_array_clear(&env);
        argv_array_clear(&argv);
 
        len = strbuf_read(&cmd_stdout, sm_summary.out, 1024);