Merge branch 'rs/code-cleaning'
authorJunio C Hamano <gitster@pobox.com>
Wed, 16 Jul 2014 18:33:09 +0000 (11:33 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 16 Jul 2014 18:33:09 +0000 (11:33 -0700)
* rs/code-cleaning:
fsck: simplify fsck_commit_buffer() by using commit_list_count()
commit: use commit_list_append() instead of duplicating its code
merge: simplify merge_trivial() by using commit_list_append()
use strbuf_addch for adding single characters
use strbuf_addbuf for adding strbufs

86 files changed:
.gitignore
Documentation/CodingGuidelines
Documentation/RelNotes/2.0.2.txt [new file with mode: 0644]
Documentation/RelNotes/2.1.0.txt
Documentation/git-replace.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
builtin.h
builtin/add.c
builtin/apply.c
builtin/blame.c
builtin/checkout-index.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/index-pack.c
builtin/merge.c
builtin/mv.c
builtin/read-tree.c
builtin/remote.c
builtin/repack.c
builtin/replace.c
builtin/reset.c
builtin/rev-parse.c
builtin/rm.c
builtin/tag.c
builtin/update-index.c
builtin/verify-commit.c [new file with mode: 0644]
builtin/verify-pack.c
cache-tree.c
cache-tree.h
cache.h
command-list.txt
commit.c
compat/mingw.h
connected.c
daemon.c
entry.c
ewah/ewah_io.c
ewah/ewok.h
git-compat-util.h
git-filter-branch.sh
git.c
gpg-interface.c
gpg-interface.h
help.c
lockfile.c
log-tree.c
merge-recursive.c
merge.c
preload-index.c
pretty.c
read-cache.c
refs.c
rerere.c
resolve-undo.c
sequencer.c
sha1_file.c
split-index.c [new file with mode: 0644]
split-index.h [new file with mode: 0644]
strbuf.c
strbuf.h
submodule.c
symlinks.c
t/Makefile
t/t0025-crlf-auto.sh
t/t0027-auto-crlf.sh [new file with mode: 0755]
t/t1700-split-index.sh [new file with mode: 0755]
t/t2104-update-index-skip-worktree.sh
t/t4202-log.sh
t/t5000-tar-tree.sh
t/t5003-archive-zip.sh
t/t6023-merge-file.sh
t/t7003-filter-branch.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
unpack-trees.c
wt-status.c
xdiff/xmerge.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
index 4d90c77c7bd643b11df8d3094efe142493df53b9..894546dd75416fcf09542096a67b2f22a7d0de7a 100644 (file)
@@ -404,6 +404,15 @@ For Python scripts:
    documentation for version 2.6 does not mention this prefix, it has
    been supported since version 2.6.0.
 
+Error Messages
+
+ - Do not end error messages with a full stop.
+
+ - Do not capitalize ("unable to open %s", not "Unable to open %s")
+
+ - Say what the error is first ("cannot open %s", not "%s: cannot open")
+
+
 Writing Documentation:
 
  Most (if not all) of the documentation pages are written in the
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 dbbbc868332e78ac4954c8d297ed13059da3645b..3b65461c0b5a4384fbe28dfbd0e3a30e9a9a533a 100644 (file)
@@ -106,14 +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.
 
@@ -169,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
@@ -211,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).
index 61461b9f3399bd99547407ae0f3ccb4a38b77582..089dcac0478407a89aea702988adcb4135510180 100644 (file)
@@ -73,6 +73,14 @@ OPTIONS
        newly created object. See linkgit:git-var[1] for details about
        how the editor will be chosen.
 
+--raw::
+       When editing, provide the raw object contents rather than
+       pretty-printed ones. Currently this only affects trees, which
+       will be shown in their binary form. This is harder to work with,
+       but can help when repairing a tree that is so corrupted it
+       cannot be pretty-printed. Note that you may need to configure
+       your editor to cleanly read and write binary data.
+
 -l <pattern>::
 --list <pattern>::
        List replace refs for objects that match the given pattern (or
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 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 16cc93587fd78f620bdcc0d0841ca98ac3394d9e..5fd099ed40859ddfd838ea99191921d63e6eed12 100644 (file)
@@ -3084,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;
@@ -3257,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))
@@ -3411,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))
@@ -3644,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...
@@ -3682,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);
@@ -4502,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 d3b256e545a76b096682223539c4faec8d770b2f..c59e70202175fc89992c23b48ea5f0738664a2f6 100644 (file)
@@ -2389,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 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 463cfeea5067fe02d8eb1414018c97ff9a757176..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)
index a5b2d9db360eb59d5a73c2e221fed047a94b0615..f0dabecca88d8f16528fb68489e5b7a7700e2a47 100644 (file)
@@ -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,7 +695,7 @@ 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
index 461c3b1cade4a995d3eb3cc212645791245865e0..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();
index 8b3bd29dbcf22668040c53fffb6e2414ca090f5d..fc40411892f13da7e739bc4801bbace3021c0e2b 100644 (file)
@@ -1506,7 +1506,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        const char *curr_index;
        const char *index_name = NULL, *pack_name = NULL;
        const char *keep_name = NULL, *keep_msg = NULL;
-       char *index_name_buf = NULL, *keep_name_buf = NULL;
+       struct strbuf index_name_buf = STRBUF_INIT,
+                     keep_name_buf = STRBUF_INIT;
        struct pack_idx_entry **idx_objects;
        struct pack_idx_option opts;
        unsigned char pack_sha1[20];
@@ -1603,24 +1604,22 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (fix_thin_pack && !from_stdin)
                die(_("--fix-thin cannot be used without --stdin"));
        if (!index_name && pack_name) {
-               int len = strlen(pack_name);
-               if (!has_extension(pack_name, ".pack"))
+               size_t len;
+               if (!strip_suffix(pack_name, ".pack", &len))
                        die(_("packfile name '%s' does not end with '.pack'"),
                            pack_name);
-               index_name_buf = xmalloc(len);
-               memcpy(index_name_buf, pack_name, len - 5);
-               strcpy(index_name_buf + len - 5, ".idx");
-               index_name = index_name_buf;
+               strbuf_add(&index_name_buf, pack_name, len);
+               strbuf_addstr(&index_name_buf, ".idx");
+               index_name = index_name_buf.buf;
        }
        if (keep_msg && !keep_name && pack_name) {
-               int len = strlen(pack_name);
-               if (!has_extension(pack_name, ".pack"))
+               size_t len;
+               if (!strip_suffix(pack_name, ".pack", &len))
                        die(_("packfile name '%s' does not end with '.pack'"),
                            pack_name);
-               keep_name_buf = xmalloc(len);
-               memcpy(keep_name_buf, pack_name, len - 5);
-               strcpy(keep_name_buf + len - 5, ".keep");
-               keep_name = keep_name_buf;
+               strbuf_add(&keep_name_buf, pack_name, len);
+               strbuf_addstr(&keep_name_buf, ".idx");
+               keep_name = keep_name_buf.buf;
        }
        if (verify) {
                if (!index_name)
@@ -1668,8 +1667,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        else
                close(input_fd);
        free(objects);
-       free(index_name_buf);
-       free(keep_name_buf);
+       strbuf_release(&index_name_buf);
+       strbuf_release(&keep_name_buf);
        if (pack_name == NULL)
                free((void *) curr_pack);
        if (index_name == NULL)
index f50270efb1fd00c41369ce909e0e40f1d10a285f..ce82eb297db3d03394f4e73a9b07574661db7090 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;
@@ -1280,10 +1276,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 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 a8efe3da4d7dad3b17582c91671f0f475d54a9ac..8e1dc39162681503505c9c3897e046514ce2e6a8 100644 (file)
@@ -263,16 +263,17 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                struct string_list_item *item;
                struct branch_info *info;
                enum { REMOTE, MERGE, REBASE } type;
+               size_t key_len;
 
                key += 7;
-               if (ends_with(key, ".remote")) {
-                       name = xstrndup(key, strlen(key) - 7);
+               if (strip_suffix(key, ".remote", &key_len)) {
+                       name = xmemdupz(key, key_len);
                        type = REMOTE;
-               } else if (ends_with(key, ".merge")) {
-                       name = xstrndup(key, strlen(key) - 6);
+               } else if (strip_suffix(key, ".merge", &key_len)) {
+                       name = xmemdupz(key, key_len);
                        type = MERGE;
-               } else if (ends_with(key, ".rebase")) {
-                       name = xstrndup(key, strlen(key) - 7);
+               } else if (strip_suffix(key, ".rebase", &key_len)) {
+                       name = xmemdupz(key, key_len);
                        type = REBASE;
                } else
                        return 0;
index ff2216a7aaefa3b38385301e4e0d8bb8b94e4ea0..a77e743b94036b2d856e6b3f74999170e39b5f17 100644 (file)
@@ -83,16 +83,15 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)
        DIR *dir;
        struct dirent *e;
        char *fname;
-       size_t len;
 
        if (!(dir = opendir(packdir)))
                return;
 
        while ((e = readdir(dir)) != NULL) {
-               if (!ends_with(e->d_name, ".pack"))
+               size_t len;
+               if (!strip_suffix(e->d_name, ".pack", &len))
                        continue;
 
-               len = strlen(e->d_name) - strlen(".pack");
                fname = xmemdupz(e->d_name, len);
 
                if (!file_exists(mkpath("%s/%s.keep", packdir, fname)))
index 1bb491d3c4f282e7f68d25cae0e00f419df1dd93..d1ea2c2e5606f2091efb487dc1599ec0a9116a6e 100644 (file)
@@ -23,9 +23,9 @@ static const char * const git_replace_usage[] = {
 };
 
 enum replace_format {
-      REPLACE_FORMAT_SHORT,
-      REPLACE_FORMAT_MEDIUM,
-      REPLACE_FORMAT_LONG
+       REPLACE_FORMAT_SHORT,
+       REPLACE_FORMAT_MEDIUM,
+       REPLACE_FORMAT_LONG
 };
 
 struct show_data {
@@ -188,27 +188,32 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f
 }
 
 /*
- * Write the contents of the object named by "sha1" to the file "filename",
- * pretty-printed for human editing based on its type.
+ * Write the contents of the object named by "sha1" to the file "filename".
+ * If "raw" is true, then the object's raw contents are printed according to
+ * "type". Otherwise, we pretty-print the contents for human editing.
  */
-static void export_object(const unsigned char *sha1, const char *filename)
+static void export_object(const unsigned char *sha1, enum object_type type,
+                         int raw, const char *filename)
 {
-       const char *argv[] = { "--no-replace-objects", "cat-file", "-p", NULL, NULL };
-       struct child_process cmd = { argv };
+       struct child_process cmd = { NULL };
        int fd;
 
        fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
        if (fd < 0)
                die_errno("unable to open %s for writing", filename);
 
-       argv[3] = sha1_to_hex(sha1);
+       argv_array_push(&cmd.args, "--no-replace-objects");
+       argv_array_push(&cmd.args, "cat-file");
+       if (raw)
+               argv_array_push(&cmd.args, typename(type));
+       else
+               argv_array_push(&cmd.args, "-p");
+       argv_array_push(&cmd.args, sha1_to_hex(sha1));
        cmd.git_cmd = 1;
        cmd.out = fd;
 
        if (run_command(&cmd))
                die("cat-file reported failure");
-
-       close(fd);
 }
 
 /*
@@ -217,7 +222,7 @@ static void export_object(const unsigned char *sha1, const char *filename)
  * The sha1 of the written object is returned via sha1.
  */
 static void import_object(unsigned char *sha1, enum object_type type,
-                         const char *filename)
+                         int raw, const char *filename)
 {
        int fd;
 
@@ -225,7 +230,7 @@ static void import_object(unsigned char *sha1, enum object_type type,
        if (fd < 0)
                die_errno("unable to open %s for reading", filename);
 
-       if (type == OBJ_TREE) {
+       if (!raw && type == OBJ_TREE) {
                const char *argv[] = { "mktree", NULL };
                struct child_process cmd = { argv };
                struct strbuf result = STRBUF_INIT;
@@ -265,7 +270,7 @@ static void import_object(unsigned char *sha1, enum object_type type,
         */
 }
 
-static int edit_and_replace(const char *object_ref, int force)
+static int edit_and_replace(const char *object_ref, int force, int raw)
 {
        char *tmpfile = git_pathdup("REPLACE_EDITOBJ");
        enum object_type type;
@@ -281,10 +286,10 @@ static int edit_and_replace(const char *object_ref, int force)
 
        check_ref_valid(old, prev, ref, sizeof(ref), force);
 
-       export_object(old, tmpfile);
+       export_object(old, type, raw, tmpfile);
        if (launch_editor(tmpfile, NULL, NULL) < 0)
                die("editing object file failed");
-       import_object(new, type, tmpfile);
+       import_object(new, type, raw, tmpfile);
 
        free(tmpfile);
 
@@ -297,6 +302,7 @@ static int edit_and_replace(const char *object_ref, int force)
 int cmd_replace(int argc, const char **argv, const char *prefix)
 {
        int force = 0;
+       int raw = 0;
        const char *format = NULL;
        enum {
                MODE_UNSPECIFIED = 0,
@@ -310,6 +316,7 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
                OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE),
                OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT),
                OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
+               OPT_BOOL(0, "raw", &raw, N_("do not pretty-print contents for --edit")),
                OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
                OPT_END()
        };
@@ -329,6 +336,10 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
                usage_msg_opt("-f only makes sense when writing a replacement",
                              git_replace_usage, options);
 
+       if (raw && cmdmode != MODE_EDIT)
+               usage_msg_opt("--raw only makes sense with --edit",
+                             git_replace_usage, options);
+
        switch (cmdmode) {
        case MODE_DELETE:
                if (argc < 1)
@@ -346,7 +357,7 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
                if (argc != 1)
                        usage_msg_opt("-e needs exactly one argument",
                                      git_replace_usage, options);
-               return edit_and_replace(argv[0], force);
+               return edit_and_replace(argv[0], force, raw);
 
        case MODE_LIST:
                if (argc > 1)
index 850d53229a1ac757d294ffca6261c9a28af014df..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;
@@ -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 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 ef765563388c384a127d27eaf7ee6fd1badfe84f..9d7643f127e7c0ea965ebf599574749c950d769f 100644 (file)
@@ -524,18 +524,14 @@ static int parse_opt_sort(const struct option *opt, const char *arg, int unset)
        int *sort = opt->value;
        int flags = 0;
 
-       if (*arg == '-') {
+       if (skip_prefix(arg, "-", &arg))
                flags |= REVERSE_SORT;
-               arg++;
-       }
-       if (starts_with(arg, "version:")) {
-               *sort = VERCMP_SORT;
-               arg += 8;
-       } else if (starts_with(arg, "v:")) {
+
+       if (skip_prefix(arg, "version:", &arg) || skip_prefix(arg, "v:", &arg))
                *sort = VERCMP_SORT;
-               arg += 2;
-       } else
+       else
                *sort = STRCMP_SORT;
+
        if (strcmp(arg, "refname"))
                die(_("unsupported sort specification %s"), arg);
        *sort |= flags;
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 66cd2df0f878d362a86e8dbc3fcd49b0c2f64c0c..972579f33c4b0adfb2240ef90b22980dc042c70a 100644 (file)
@@ -27,10 +27,9 @@ static int verify_one_pack(const char *path, unsigned int flags)
         * normalize these forms to "foo.pack" for "index-pack --verify".
         */
        strbuf_addstr(&arg, path);
-       if (has_extension(arg.buf, ".idx"))
-               strbuf_splice(&arg, arg.len - 3, 3, "pack", 4);
-       else if (!has_extension(arg.buf, ".pack"))
-               strbuf_add(&arg, ".pack", 5);
+       if (strbuf_strip_suffix(&arg, ".idx") ||
+           !ends_with(arg.buf, ".pack"))
+               strbuf_addstr(&arg, ".pack");
        argv[2] = arg.buf;
 
        memset(&index_pack, 0, sizeof(index_pack));
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..92fc9f1121776fbebf180b5e9d958982e8a98014 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_clear(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 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 61d2e13f4870a47be3f48b7cebeee8ba4ceaee1b..f43970dca1e11baf3b7690df81e13866f536f8db 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1265,6 +1265,7 @@ void check_commit_signature(const struct commit* commit, struct signature_check
                                      &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);
index 8dac6f9d6bc91c7949533d5d6d987530d6993b01..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
index be0253e21bec3617ad3a7f1b0afc7d2049efc4fd..dae9c9972eb3485bd20494efd78d09f1189667cb 100644 (file)
@@ -31,6 +31,7 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
        unsigned char sha1[20];
        int err = 0, ac = 0;
        struct packed_git *new_pack = NULL;
+       size_t base_len;
 
        if (fn(cb_data, sha1))
                return err;
@@ -38,10 +39,9 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
        if (transport && transport->smart_options &&
            transport->smart_options->self_contained_and_connected &&
            transport->pack_lockfile &&
-           ends_with(transport->pack_lockfile, ".keep")) {
+           strip_suffix(transport->pack_lockfile, ".keep", &base_len)) {
                struct strbuf idx_file = STRBUF_INIT;
-               strbuf_addstr(&idx_file, transport->pack_lockfile);
-               strbuf_setlen(&idx_file, idx_file.len - 5); /* ".keep" */
+               strbuf_add(&idx_file, transport->pack_lockfile, base_len);
                strbuf_addstr(&idx_file, ".idx");
                new_pack = add_packed_git(idx_file.buf, idx_file.len, 1);
                strbuf_release(&idx_file);
index 1eb663172338ae24ef9229594f893e80c2e52f96..e6b51ed9981f2e4084504a2a092a3f28fc7d4c8a 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -778,7 +778,6 @@ static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
                logerror("unable to fork");
        else
                add_child(&cld, addr, addrlen);
-       close(incoming);
 }
 
 static void child_handler(int signo)
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 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 9de31807108322aac327f66822d4929974d7ffd5..0b53c9a4af3c7e1d5611065b35ef38f3f1f5e5be 100644 (file)
@@ -347,7 +347,6 @@ extern void set_error_routine(void (*routine)(const char *err, va_list params));
 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);
 
 /*
  * If the string "str" begins with the string found in "prefix", return 1.
@@ -377,6 +376,39 @@ static inline int skip_prefix(const char *str, const char *prefix,
        return 0;
 }
 
+/*
+ * If buf ends with suffix, return 1 and subtract the length of the suffix
+ * from *len. Otherwise, return 0 and leave *len untouched.
+ */
+static inline int strip_suffix_mem(const char *buf, size_t *len,
+                                  const char *suffix)
+{
+       size_t suflen = strlen(suffix);
+       if (*len < suflen || memcmp(buf + (*len - suflen), suffix, suflen))
+               return 0;
+       *len -= suflen;
+       return 1;
+}
+
+/*
+ * If str ends with suffix, return 1 and set *len to the size of the string
+ * without the suffix. Otherwise, return 0 and set *len to the size of the
+ * string.
+ *
+ * Note that we do _not_ NUL-terminate str to the new length.
+ */
+static inline int strip_suffix(const char *str, const char *suffix, size_t *len)
+{
+       *len = strlen(str);
+       return strip_suffix_mem(str, len, suffix);
+}
+
+static inline int ends_with(const char *str, const char *suffix)
+{
+       size_t len;
+       return strip_suffix(str, suffix, &len);
+}
+
 #if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
 
 #ifndef PROT_READ
@@ -581,13 +613,6 @@ static inline size_t xsize_t(off_t len)
        return (size_t)len;
 }
 
-static inline int has_extension(const char *filename, const char *ext)
-{
-       size_t len = strlen(filename);
-       size_t extlen = strlen(ext);
-       return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
-}
-
 /* in ctype.c, for kwset users */
 extern const char tolower_trans_tbl[256];
 
index 86d6994619151fb63327848e6763b07971017ad7..e6e99f5bb5102d394a25f156dbcba956f246054b 100755 (executable)
@@ -332,7 +332,13 @@ while read commit parents; do
        parentstr=
        for parent in $parents; do
                for reparent in $(map "$parent"); do
-                       parentstr="$parentstr -p $reparent"
+                       case "$parentstr " in
+                       *" -p $reparent "*)
+                               ;;
+                       *)
+                               parentstr="$parentstr -p $reparent"
+                               ;;
+                       esac
                done
        done
        if [ "$filter_parent" ]; then
diff --git a/git.c b/git.c
index dd54f5734a246d65436c823dd65fb224b240009f..5b6c7611be93c535491bbc9ba027e3acbad0db8f 100644 (file)
--- a/git.c
+++ b/git.c
@@ -478,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 },
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 f31f29ac421e224a59726fc1f2dfc16d92faad0f..7af65e205ecdf1a01dce009cbf0ada15de68c844 100644 (file)
--- a/help.c
+++ b/help.c
@@ -144,7 +144,7 @@ static void list_commands_in_dir(struct cmdnames *cmds,
 
        while ((de = readdir(dir)) != NULL) {
                const char *ent;
-               int entlen;
+               size_t entlen;
 
                if (!skip_prefix(de->d_name, prefix, &ent))
                        continue;
@@ -155,8 +155,7 @@ static void list_commands_in_dir(struct cmdnames *cmds,
                        continue;
 
                entlen = strlen(ent);
-               if (has_extension(ent, ".exe"))
-                       entlen -= 4;
+               strip_suffix(ent, ".exe", &entlen);
 
                add_cmdname(cmds, ent, entlen);
        }
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 10e68442b35c9bd9ca1c00c52609a4827b8bace5..f87b7e891adc5dfb429bc0ae35227e19f335b90f 100644 (file)
@@ -365,6 +365,7 @@ static void show_sig_lines(struct rev_info *opt, int status, const char *bol)
                eol = strchrnul(bol, '\n');
                printf("%s%.*s%s%s", color, (int)(eol - bol), bol, reset,
                       *eol ? "\n" : "");
+               graph_show_oneline(opt->graph);
                bol = (*eol) ? (eol + 1) : eol;
        }
 }
@@ -446,16 +447,17 @@ static void show_one_mergetag(struct rev_info *opt,
 
        payload_size = parse_signature(extra->value, extra->len);
        status = -1;
-       if (extra->len > payload_size)
-               if (verify_signed_buffer(extra->value, payload_size,
-                                        extra->value + payload_size,
-                                        extra->len - payload_size,
-                                        &verify_message, NULL)) {
-                       if (verify_message.len <= gpg_message_offset)
-                               strbuf_addstr(&verify_message, "No signature\n");
-                       else
-                               status = 0;
-               }
+       if (extra->len > payload_size) {
+               /* could have a good signature */
+               if (!verify_signed_buffer(extra->value, payload_size,
+                                         extra->value + payload_size,
+                                         extra->len - payload_size,
+                                         &verify_message, NULL))
+                       status = 0; /* good */
+               else if (verify_message.len <= gpg_message_offset)
+                       strbuf_addstr(&verify_message, "No signature\n");
+               /* otherwise we couldn't verify, which is shown as bad */
+       }
 
        show_sig_lines(opt, status, verify_message.buf);
        strbuf_release(&verify_message);
index fad7b2c2b8f186fb0129dd1167677c5c2f42b326..5814d056ff9fdfad6de5a748806cf307b1e5d388 100644 (file)
@@ -267,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);
@@ -2001,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);
@@ -2018,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;
diff --git a/merge.c b/merge.c
index 1fa6e52bba8de83820b9a658f738630a9d6f250a..74ced7f70b5beec045c134b5719c926fc791f1e2 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -50,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));
@@ -89,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 968ee25eae13f82fe4f93254d77660456732a5c6..c1fe3a3ef9cba0c155d02475e24315784bd27ae4 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_clear(&cache);
        return NULL;
 }
 
index 6e5493472302dcf452f21c6b6010a3af71a060f6..eb676d6d54b0cf7a0d82fd960ad6dcfbabb84003 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1520,8 +1520,6 @@ void format_commit_message(const struct commit *commit,
 
        free(context.commit_encoding);
        unuse_commit_buffer(commit, context.message);
-       free(context.signature_check.gpg_output);
-       free(context.signature_check.signer);
 }
 
 static void pp_header(struct pretty_print_context *pp,
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 20e2bf18c9da51904d53ec594022eb75e7cefcf0..061d233e4b6404c57ff8d62715758b900d02e051 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -153,6 +153,7 @@ int check_refname_format(const char *refname, int flags)
        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 */
@@ -178,7 +179,7 @@ int check_refname_format(const char *refname, int flags)
                         * End-of-page; fall back to slow method for
                         * this entire ref.
                         */
-                       return check_refname_format_bytewise(refname, flags);
+                       return check_refname_format_bytewise(refname, orig_flags);
 
                tmp = _mm_loadu_si128((__m128i *)cp);
                tmp1 = _mm_loadu_si128((__m128i *)(cp + 1));
@@ -1360,7 +1361,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
 
                if (de->d_name[0] == '.')
                        continue;
-               if (has_extension(de->d_name, ".lock"))
+               if (ends_with(de->d_name, ".lock"))
                        continue;
                strbuf_addstr(&refname, de->d_name);
                refdir = *refs->name
@@ -3431,7 +3432,7 @@ static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data
 
                if (de->d_name[0] == '.')
                        continue;
-               if (has_extension(de->d_name, ".lock"))
+               if (ends_with(de->d_name, ".lock"))
                        continue;
                strbuf_addstr(name, de->d_name);
                if (stat(git_path("logs/%s", name->buf), &st) < 0) {
index 04d923d8cf59d591f133d31d3641cc6cf070605c..d84b495895bc6d42980c388ed03c560174604f92 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 cdd30c07379621561edf7b7fb23c5607571edf4f..3c060e054720b7b17f9a868cabb1a0ef62aa1b50 100644 (file)
@@ -263,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();
 
@@ -288,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);
@@ -341,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);
@@ -643,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);
index 34d527f6708fe242f75b7fa0fa98e36a5c96a3be..4127ae138de6bb002645aa83af00681b1805d982 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;
                }
@@ -1177,48 +1178,42 @@ static void report_pack_garbage(struct string_list *list)
 
 static void prepare_packed_git_one(char *objdir, int local)
 {
-       /* Ensure that this buffer is large enough so that we can
-          append "/pack/" without clobbering the stack even if
-          strlen(objdir) were PATH_MAX.  */
-       char path[PATH_MAX + 1 + 4 + 1 + 1];
-       int len;
+       struct strbuf path = STRBUF_INIT;
+       size_t dirnamelen;
        DIR *dir;
        struct dirent *de;
        struct string_list garbage = STRING_LIST_INIT_DUP;
 
-       sprintf(path, "%s/pack", objdir);
-       len = strlen(path);
-       dir = opendir(path);
+       strbuf_addstr(&path, objdir);
+       strbuf_addstr(&path, "/pack");
+       dir = opendir(path.buf);
        if (!dir) {
                if (errno != ENOENT)
                        error("unable to open object pack directory: %s: %s",
-                             path, strerror(errno));
+                             path.buf, strerror(errno));
+               strbuf_release(&path);
                return;
        }
-       path[len++] = '/';
+       strbuf_addch(&path, '/');
+       dirnamelen = path.len;
        while ((de = readdir(dir)) != NULL) {
-               int namelen = strlen(de->d_name);
                struct packed_git *p;
-
-               if (len + namelen + 1 > sizeof(path)) {
-                       if (report_garbage) {
-                               struct strbuf sb = STRBUF_INIT;
-                               strbuf_addf(&sb, "%.*s/%s", len - 1, path, de->d_name);
-                               report_garbage("path too long", sb.buf);
-                               strbuf_release(&sb);
-                       }
-                       continue;
-               }
+               size_t base_len;
 
                if (is_dot_or_dotdot(de->d_name))
                        continue;
 
-               strcpy(path + len, de->d_name);
+               strbuf_setlen(&path, dirnamelen);
+               strbuf_addstr(&path, de->d_name);
 
-               if (has_extension(de->d_name, ".idx")) {
+               base_len = path.len;
+               if (strip_suffix_mem(path.buf, &base_len, ".idx")) {
                        /* Don't reopen a pack we already have. */
                        for (p = packed_git; p; p = p->next) {
-                               if (!memcmp(path, p->pack_name, len + namelen - 4))
+                               size_t len;
+                               if (strip_suffix(p->pack_name, ".pack", &len) &&
+                                   len == base_len &&
+                                   !memcmp(p->pack_name, path.buf, len))
                                        break;
                        }
                        if (p == NULL &&
@@ -1226,24 +1221,25 @@ static void prepare_packed_git_one(char *objdir, int local)
                             * See if it really is a valid .idx file with
                             * corresponding .pack file that we can map.
                             */
-                           (p = add_packed_git(path, len + namelen, local)) != NULL)
+                           (p = add_packed_git(path.buf, path.len, local)) != NULL)
                                install_packed_git(p);
                }
 
                if (!report_garbage)
                        continue;
 
-               if (has_extension(de->d_name, ".idx") ||
-                   has_extension(de->d_name, ".pack") ||
-                   has_extension(de->d_name, ".bitmap") ||
-                   has_extension(de->d_name, ".keep"))
-                       string_list_append(&garbage, path);
+               if (ends_with(de->d_name, ".idx") ||
+                   ends_with(de->d_name, ".pack") ||
+                   ends_with(de->d_name, ".bitmap") ||
+                   ends_with(de->d_name, ".keep"))
+                       string_list_append(&garbage, path.buf);
                else
-                       report_garbage("garbage found", path);
+                       report_garbage("garbage found", path.buf);
        }
        closedir(dir);
        report_pack_garbage(&garbage);
        string_list_clear(&garbage, 0);
+       strbuf_release(&path);
 }
 
 static int sort_pack(const void *a_, const void *b_)
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 12c78656ca9ddb51fb62b95be29d18056911803f..33018d847f08424211c02cff8002680f5a2f02e9 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -11,15 +11,6 @@ int starts_with(const char *str, const char *prefix)
                        return 0;
 }
 
-int ends_with(const char *str, const char *suffix)
-{
-       int len = strlen(str), suflen = strlen(suffix);
-       if (len < suflen)
-               return 0;
-       else
-               return !strcmp(str + len - suflen, suffix);
-}
-
 /*
  * Used as the default ->buf value, so that people can always assume
  * buf is non NULL and ->buf is NUL terminated even for a freshly
index a594c24b2b0e9e82c16de1bb787758d561cb2e7b..a7c0192e9ee392bd232b325692a9111b3e160e84 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -49,6 +49,15 @@ extern int strbuf_reencode(struct strbuf *sb, const char *from, const char *to);
 extern void strbuf_tolower(struct strbuf *sb);
 extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
 
+static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
+{
+       if (strip_suffix_mem(sb->buf, &sb->len, suffix)) {
+               strbuf_setlen(sb, sb->len);
+               return 1;
+       } else
+               return 0;
+}
+
 /*
  * Split str (of length slen) at the specified terminator character.
  * Return a null-terminated array of pointers to strbuf objects
index b80ecacf60dc87025336b6b6d6a51acc9321f429..48e3b44e219dffec6f435f0f25f1b74409fa29a9 100644 (file)
@@ -965,7 +965,7 @@ static int find_first_merges(struct object_array *result, const char *path,
                        sha1_to_hex(a->object.sha1));
        init_revisions(&revs, NULL);
        rev_opts.submodule = path;
-       setup_revisions(sizeof(rev_args)/sizeof(char *)-1, rev_args, &revs, &rev_opts);
+       setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
 
        /* save all revisions from the above list that contain b */
        if (prepare_revision_walk(&revs))
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 8fd1a723576c773e4a63d7de7b0ad5cab5a04627..43b15e36aeeba4ddc48073f8463592efc5216e3c 100644 (file)
@@ -13,7 +13,7 @@ TAR ?= $(TAR)
 RM ?= rm -f
 PROVE ?= prove
 DEFAULT_TEST_TARGET ?= test
-TEST_LINT ?= test-lint-duplicates test-lint-executable
+TEST_LINT ?= test-lint
 
 ifdef TEST_OUTPUT_DIRECTORY
 TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results
@@ -29,6 +29,7 @@ TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
 T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
 TSVN = $(sort $(wildcard t91[0-9][0-9]-*.sh))
 TGITWEB = $(sort $(wildcard t95[0-9][0-9]-*.sh))
+THELPERS = $(sort $(filter-out $(T),$(wildcard *.sh)))
 
 all: $(DEFAULT_TEST_TARGET)
 
@@ -65,7 +66,7 @@ test-lint-executable:
                echo >&2 "non-executable tests:" $$bad; exit 1; }
 
 test-lint-shell-syntax:
-       @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T)
+       @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T) $(THELPERS)
 
 aggregate-results-and-cleanup: $(T)
        $(MAKE) aggregate-results
index 28102de1a0f987b351a423516d394180175a1f13..c164b4662a06d26bc23b58287f88023ed0a588a2 100755 (executable)
@@ -12,144 +12,144 @@ test_expect_success setup '
 
        git config core.autocrlf false &&
 
-       for w in Hello world how are you; do echo $w; done >one &&
-       for w in I am very very fine thank you; do echo ${w}Q; done | q_to_cr >two &&
-       for w in Oh here is a QNUL byte how alarming; do echo ${w}; done | q_to_nul >three &&
+       for w in Hello world how are you; do echo $w; done >LFonly &&
+       for w in I am very very fine thank you; do echo ${w}Q; done | q_to_cr >CRLFonly &&
+       for w in Oh here is a QNUL byte how alarming; do echo ${w}; done | q_to_nul >LFwithNUL &&
        git add . &&
 
        git commit -m initial &&
 
-       one=$(git rev-parse HEAD:one) &&
-       two=$(git rev-parse HEAD:two) &&
-       three=$(git rev-parse HEAD:three) &&
+       LFonly=$(git rev-parse HEAD:LFonly) &&
+       CRLFonly=$(git rev-parse HEAD:CRLFonly) &&
+       LFwithNUL=$(git rev-parse HEAD:LFwithNUL) &&
 
        echo happy.
 '
 
 test_expect_success 'default settings cause no changes' '
 
-       rm -f .gitattributes tmp one two three &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
        git read-tree --reset -u HEAD &&
 
-       ! has_cr one &&
-       has_cr two &&
-       onediff=$(git diff one) &&
-       twodiff=$(git diff two) &&
-       threediff=$(git diff three) &&
-       test -z "$onediff" && test -z "$twodiff" && test -z "$threediff"
+       ! has_cr LFonly &&
+       has_cr CRLFonly &&
+       LFonlydiff=$(git diff LFonly) &&
+       CRLFonlydiff=$(git diff CRLFonly) &&
+       LFwithNULdiff=$(git diff LFwithNUL) &&
+       test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff"
 '
 
 test_expect_success 'crlf=true causes a CRLF file to be normalized' '
 
        # Backwards compatibility check
-       rm -f .gitattributes tmp one two three &&
-       echo "two crlf" > .gitattributes &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
+       echo "CRLFonly crlf" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
        # Note, "normalized" means that git will normalize it if added
-       has_cr two &&
-       twodiff=$(git diff two) &&
-       test -n "$twodiff"
+       has_cr CRLFonly &&
+       CRLFonlydiff=$(git diff CRLFonly) &&
+       test -n "$CRLFonlydiff"
 '
 
 test_expect_success 'text=true causes a CRLF file to be normalized' '
 
-       rm -f .gitattributes tmp one two three &&
-       echo "two text" > .gitattributes &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
+       echo "CRLFonly text" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
        # Note, "normalized" means that git will normalize it if added
-       has_cr two &&
-       twodiff=$(git diff two) &&
-       test -n "$twodiff"
+       has_cr CRLFonly &&
+       CRLFonlydiff=$(git diff CRLFonly) &&
+       test -n "$CRLFonlydiff"
 '
 
 test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=false' '
 
-       rm -f .gitattributes tmp one two three &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
        git config core.autocrlf false &&
-       echo "one eol=crlf" > .gitattributes &&
+       echo "LFonly eol=crlf" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       has_cr one &&
-       onediff=$(git diff one) &&
-       test -z "$onediff"
+       has_cr LFonly &&
+       LFonlydiff=$(git diff LFonly) &&
+       test -z "$LFonlydiff"
 '
 
 test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=input' '
 
-       rm -f .gitattributes tmp one two three &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
        git config core.autocrlf input &&
-       echo "one eol=crlf" > .gitattributes &&
+       echo "LFonly eol=crlf" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       has_cr one &&
-       onediff=$(git diff one) &&
-       test -z "$onediff"
+       has_cr LFonly &&
+       LFonlydiff=$(git diff LFonly) &&
+       test -z "$LFonlydiff"
 '
 
 test_expect_success 'eol=lf gives a normalized file LFs with autocrlf=true' '
 
-       rm -f .gitattributes tmp one two three &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
        git config core.autocrlf true &&
-       echo "one eol=lf" > .gitattributes &&
+       echo "LFonly eol=lf" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       ! has_cr one &&
-       onediff=$(git diff one) &&
-       test -z "$onediff"
+       ! has_cr LFonly &&
+       LFonlydiff=$(git diff LFonly) &&
+       test -z "$LFonlydiff"
 '
 
 test_expect_success 'autocrlf=true does not normalize CRLF files' '
 
-       rm -f .gitattributes tmp one two three &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
        git config core.autocrlf true &&
        git read-tree --reset -u HEAD &&
 
-       has_cr one &&
-       has_cr two &&
-       onediff=$(git diff one) &&
-       twodiff=$(git diff two) &&
-       threediff=$(git diff three) &&
-       test -z "$onediff" && test -z "$twodiff" && test -z "$threediff"
+       has_cr LFonly &&
+       has_cr CRLFonly &&
+       LFonlydiff=$(git diff LFonly) &&
+       CRLFonlydiff=$(git diff CRLFonly) &&
+       LFwithNULdiff=$(git diff LFwithNUL) &&
+       test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff"
 '
 
 test_expect_success 'text=auto, autocrlf=true _does_ normalize CRLF files' '
 
-       rm -f .gitattributes tmp one two three &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
        git config core.autocrlf true &&
        echo "* text=auto" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       has_cr one &&
-       has_cr two &&
-       onediff=$(git diff one) &&
-       twodiff=$(git diff two) &&
-       threediff=$(git diff three) &&
-       test -z "$onediff" && test -n "$twodiff" && test -z "$threediff"
+       has_cr LFonly &&
+       has_cr CRLFonly &&
+       LFonlydiff=$(git diff LFonly) &&
+       CRLFonlydiff=$(git diff CRLFonly) &&
+       LFwithNULdiff=$(git diff LFwithNUL) &&
+       test -z "$LFonlydiff" -a -n "$CRLFonlydiff" -a -z "$LFwithNULdiff"
 '
 
 test_expect_success 'text=auto, autocrlf=true does not normalize binary files' '
 
-       rm -f .gitattributes tmp one two three &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
        git config core.autocrlf true &&
        echo "* text=auto" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       ! has_cr three &&
-       threediff=$(git diff three) &&
-       test -z "$threediff"
+       ! has_cr LFwithNUL &&
+       LFwithNULdiff=$(git diff LFwithNUL) &&
+       test -z "$LFwithNULdiff"
 '
 
 test_expect_success 'eol=crlf _does_ normalize binary files' '
 
-       rm -f .gitattributes tmp one two three &&
-       echo "three eol=crlf" > .gitattributes &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
+       echo "LFwithNUL eol=crlf" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       has_cr three &&
-       threediff=$(git diff three) &&
-       test -z "$threediff"
+       has_cr LFwithNUL &&
+       LFwithNULdiff=$(git diff LFwithNUL) &&
+       test -z "$LFwithNULdiff"
 '
 
 test_done
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
new file mode 100755 (executable)
index 0000000..72dd3e8
--- /dev/null
@@ -0,0 +1,265 @@
+#!/bin/sh
+
+test_description='CRLF conversion all combinations'
+
+. ./test-lib.sh
+
+if ! test_have_prereq EXPENSIVE
+then
+       skip_all="EXPENSIVE not set"
+       test_done
+fi
+
+
+compare_files()
+{
+       od -c <"$1" >"$1".expect &&
+       od -c <"$2" >"$2".actual &&
+       test_cmp "$1".expect "$2".actual &&
+       rm "$1".expect "$2".actual
+}
+
+compare_ws_file()
+{
+       pfx=$1
+       exp=$2.expect
+       act=$pfx.actual.$3
+       od -c <"$2" >"$exp" &&
+       od -c <"$3" >"$act" &&
+       test_cmp $exp $act &&
+       rm $exp $act
+}
+
+create_gitattributes()
+{
+       txtbin=$1
+       case "$txtbin" in
+               auto)
+               echo "*.txt text=auto" >.gitattributes
+               ;;
+               text)
+               echo "*.txt text" >.gitattributes
+               ;;
+               -text)
+               echo "*.txt -text" >.gitattributes
+               ;;
+               *)
+               echo >.gitattributes
+               ;;
+       esac
+}
+
+create_file_in_repo()
+{
+       crlf=$1
+       txtbin=$2
+       create_gitattributes "$txtbin" &&
+       for f in LF CRLF LF_mix_CR CRLF_mix_LF CRLF_nul
+       do
+               pfx=crlf_${crlf}_attr_${txtbin}_$f.txt &&
+               cp $f $pfx && git -c core.autocrlf=$crlf add $pfx
+       done &&
+       git commit -m "core.autocrlf $crlf"
+}
+
+check_files_in_repo()
+{
+       crlf=$1
+       txtbin=$2
+       lfname=$3
+       crlfname=$4
+       lfmixcrlf=$5
+       lfmixcr=$6
+       crlfnul=$7
+       pfx=crlf_${crlf}_attr_${txtbin}_ &&
+       compare_files $lfname ${pfx}LF.txt &&
+       compare_files $crlfname ${pfx}CRLF.txt &&
+       compare_files $lfmixcrlf ${pfx}CRLF_mix_LF.txt &&
+       compare_files $lfmixcr ${pfx}LF_mix_CR.txt &&
+       compare_files $crlfnul ${pfx}CRLF_nul.txt
+}
+
+
+check_files_in_ws()
+{
+       eol=$1
+       crlf=$2
+       txtbin=$3
+       lfname=$4
+       crlfname=$5
+       lfmixcrlf=$6
+       lfmixcr=$7
+       crlfnul=$8
+       create_gitattributes $txtbin &&
+       git config core.autocrlf $crlf &&
+       pfx=eol_${eol}_crlf_${crlf}_attr_${txtbin}_ &&
+       src=crlf_false_attr__ &&
+       for f in LF CRLF LF_mix_CR CRLF_mix_LF CRLF_nul
+       do
+               rm $src$f.txt &&
+               if test -z "$eol"; then
+                       git checkout $src$f.txt
+               else
+                       git -c core.eol=$eol checkout $src$f.txt
+               fi
+       done
+
+
+       test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=LF" "
+               compare_ws_file $pfx $lfname    ${src}LF.txt
+       "
+       test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF" "
+               compare_ws_file $pfx $crlfname  ${src}CRLF.txt
+       "
+       test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF_mix_LF" "
+               compare_ws_file $pfx $lfmixcrlf ${src}CRLF_mix_LF.txt
+       "
+       test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=LF_mix_CR" "
+               compare_ws_file $pfx $lfmixcr   ${src}LF_mix_CR.txt
+       "
+       test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF_nul" "
+               compare_ws_file $pfx $crlfnul   ${src}CRLF_nul.txt
+       "
+}
+
+#######
+(
+       type od >/dev/null &&
+       printf "line1Q\r\nline2\r\nline3" | q_to_nul >CRLF_nul &&
+       cat >expect <<-EOF &&
+       0000000 l i n e 1 \0 \r \n l i n e 2 \r \n l
+       0000020 i n e 3
+       0000024
+EOF
+       od -c CRLF_nul | sed -e "s/[    ][       ]*/ /g" -e "s/ *$//" >actual
+       test_cmp expect actual &&
+       rm expect actual
+) || {
+               skip_all="od not found or od -c not usable"
+               exit 0
+               test_done
+}
+
+test_expect_success 'setup master' '
+       echo >.gitattributes &&
+       git checkout -b master &&
+       git add .gitattributes &&
+       git commit -m "add .gitattributes" "" &&
+       printf "line1\nline2\nline3"     >LF &&
+       printf "line1\r\nline2\r\nline3" >CRLF &&
+       printf "line1\r\nline2\nline3"   >CRLF_mix_LF &&
+       printf "line1\nline2\rline3"     >LF_mix_CR &&
+       printf "line1\r\nline2\rline3"   >CRLF_mix_CR &&
+       printf "line1Q\nline2\nline3" | q_to_nul >LF_nul
+'
+#  CRLF_nul had been created above
+
+test_expect_success 'create files' '
+       create_file_in_repo false "" &&
+       create_file_in_repo true  "" &&
+       create_file_in_repo input "" &&
+
+       create_file_in_repo false "auto" &&
+       create_file_in_repo true  "auto" &&
+       create_file_in_repo input "auto" &&
+
+       create_file_in_repo false "text" &&
+       create_file_in_repo true  "text" &&
+       create_file_in_repo input "text" &&
+
+       create_file_in_repo false "-text" &&
+       create_file_in_repo true  "-text" &&
+       create_file_in_repo input "-text" &&
+       rm -f *.txt &&
+       git reset --hard
+'
+
+test_expect_success 'commit empty gitattribues' '
+       check_files_in_repo false ""      LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+       check_files_in_repo true  ""      LF LF   LF          LF_mix_CR CRLF_nul &&
+       check_files_in_repo input ""      LF LF   LF          LF_mix_CR CRLF_nul
+'
+
+test_expect_success 'commit text=auto' '
+       check_files_in_repo false "auto"  LF LF   LF          LF_mix_CR CRLF_nul &&
+       check_files_in_repo true  "auto"  LF LF   LF          LF_mix_CR CRLF_nul &&
+       check_files_in_repo input "auto"  LF LF   LF          LF_mix_CR CRLF_nul
+'
+
+test_expect_success 'commit text' '
+       check_files_in_repo false "text"  LF LF   LF          LF_mix_CR LF_nul &&
+       check_files_in_repo true  "text"  LF LF   LF          LF_mix_CR LF_nul &&
+       check_files_in_repo input "text"  LF LF   LF          LF_mix_CR LF_nul
+'
+
+test_expect_success 'commit -text' '
+       check_files_in_repo false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+       check_files_in_repo true  "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+       check_files_in_repo input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+'
+
+################################################################################
+# Check how files in the repo are changed when they are checked out
+# How to read the table below:
+# - check_files_in_ws will check multiple files, see below
+# - parameter $1 : core.eol               lf | crlf
+# - parameter $2 : core.autocrlf          false | true | input
+# - parameter $3 : text in .gitattributs  "" (empty) | auto | text | -text
+# - parameter $4 : reference for a file with only LF in the repo
+# - parameter $5 : reference for a file with only CRLF in the repo
+# - parameter $6 : reference for a file with mixed LF and CRLF in the repo
+# - parameter $7 : reference for a file with LF and CR in the repo (does somebody uses this ?)
+# - parameter $8 : reference for a file with CRLF and a NUL (should be handled as binary when auto)
+
+check_files_in_ws lf      false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws lf      true   ""       CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws lf      input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+
+check_files_in_ws lf      false "auto"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws lf      true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
+check_files_in_ws lf      input "auto"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+
+check_files_in_ws lf      false "text"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws lf      true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+check_files_in_ws lf      input "text"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+
+check_files_in_ws lf      false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws lf      true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws lf      input "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+
+###########
+#core.autocrlf=input is forbidden with core.eol=crlf
+check_files_in_ws crlf    false ""        LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws crlf    true  ""        CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+
+check_files_in_ws crlf    false "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
+check_files_in_ws crlf    true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
+
+check_files_in_ws crlf    false "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+check_files_in_ws crlf    true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+
+check_files_in_ws crlf    false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws crlf    true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+
+if test_have_prereq MINGW
+then
+check_files_in_ws ""      false ""        LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws ""      true  ""        CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws ""      false "auto"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws ""      true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
+check_files_in_ws ""      false "text"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws ""      true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+check_files_in_ws ""      false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws ""      true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+
+check_files_in_ws native  false ""        LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws native  true  ""        CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws native  false "auto"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws native  true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
+check_files_in_ws native  false "text"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws native  true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+check_files_in_ws native  false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws native  true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+fi
+
+test_done
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 cb03d287698f110b66e69636348caccdbe6a94f8..99ab7ca21f2673e646232d9c07f77c8ed2f5f998 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='git log'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
 
 test_expect_success setup '
 
@@ -841,4 +842,34 @@ test_expect_success 'dotdot is a parent directory' '
        test_cmp expect actual
 '
 
+test_expect_success GPG 'log --graph --show-signature' '
+       test_when_finished "git reset --hard && git checkout master" &&
+       git checkout -b signed master &&
+       echo foo >foo &&
+       git add foo &&
+       git commit -S -m signed_commit &&
+       git log --graph --show-signature -n1 signed >actual &&
+       grep "^| gpg: Signature made" actual &&
+       grep "^| gpg: Good signature" actual
+'
+
+test_expect_success GPG 'log --graph --show-signature for merged tag' '
+       test_when_finished "git reset --hard && git checkout master" &&
+       git checkout -b plain master &&
+       echo aaa >bar &&
+       git add bar &&
+       git commit -m bar_commit &&
+       git checkout -b tagged master &&
+       echo bbb >baz &&
+       git add baz &&
+       git commit -m baz_commit &&
+       git tag -s -m signed_tag_msg signed_tag &&
+       git checkout plain &&
+       git merge --no-ff -m msg signed_tag &&
+       git log --graph --show-signature -n1 plain >actual &&
+       grep "^|\\\  merged tag" actual &&
+       grep "^| | gpg: Signature made" actual &&
+       grep "^| | gpg: Good signature" actual
+'
+
 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 432f086c063198e82ad8a2f7c8dd175a8685fdf5..3758961765635c4ce504eec1540605f65efd386a 100755 (executable)
@@ -77,12 +77,29 @@ test_expect_success "merge without conflict (--quiet)" \
        "git merge-file --quiet test.txt orig.txt new2.txt"
 
 cp new1.txt test2.txt
-test_expect_success "merge without conflict (missing LF at EOF)" \
-       "git merge-file test2.txt orig.txt new2.txt"
+test_expect_failure "merge without conflict (missing LF at EOF)" \
+       "git merge-file test2.txt orig.txt new4.txt"
 
-test_expect_success "merge result added missing LF" \
+test_expect_failure "merge result added missing LF" \
        "test_cmp test.txt test2.txt"
 
+cp new4.txt test3.txt
+test_expect_success "merge without conflict (missing LF at EOF, away from change in the other file)" \
+       "git merge-file --quiet test3.txt new2.txt new3.txt"
+
+cat > expect.txt << EOF
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+EOF
+printf "propter nomen suum." >> expect.txt
+
+test_expect_success "merge does not add LF away of change" \
+       "test_cmp test3.txt expect.txt"
+
 cp test.txt backup.txt
 test_expect_success "merge with conflicts" \
        "test_must_fail git merge-file test.txt orig.txt new3.txt"
@@ -107,6 +124,55 @@ EOF
 test_expect_success "expected conflict markers" "test_cmp test.txt expect.txt"
 
 cp backup.txt test.txt
+
+cat > expect.txt << EOF
+Dominus regit me, et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --ours" \
+       "git merge-file --ours test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
+cat > expect.txt << EOF
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --theirs" \
+       "git merge-file --theirs test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
+cat > expect.txt << EOF
+Dominus regit me, et nihil mihi deerit.
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --union" \
+       "git merge-file --union test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
 test_expect_success "merge with conflicts, using -L" \
        "test_must_fail git merge-file -L 1 -L 2 test.txt orig.txt new3.txt"
 
@@ -260,4 +326,23 @@ test_expect_success 'marker size' '
        test_cmp expect actual
 '
 
+printf "line1\nline2\nline3" >nolf-orig.txt
+printf "line1\nline2\nline3x" >nolf-diff1.txt
+printf "line1\nline2\nline3y" >nolf-diff2.txt
+
+test_expect_success 'conflict at EOF without LF resolved by --ours' \
+       'git merge-file -p --ours nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+        printf "line1\nline2\nline3x" >expect.txt &&
+        test_cmp expect.txt output.txt'
+
+test_expect_success 'conflict at EOF without LF resolved by --theirs' \
+       'git merge-file -p --theirs nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+        printf "line1\nline2\nline3y" >expect.txt &&
+        test_cmp expect.txt output.txt'
+
+test_expect_success 'conflict at EOF without LF resolved by --union' \
+       'git merge-file -p --union nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+        printf "line1\nline2\nline3x\nline3y" >expect.txt &&
+        test_cmp expect.txt output.txt'
+
 test_done
index 9496736a89eb6b0b1ece64052cd2726c516c952b..66643e4bd758aa55f4f58d70675e32f1ad173460 100755 (executable)
@@ -308,6 +308,17 @@ test_expect_success 'Prune empty commits' '
        test_cmp expect actual
 '
 
+test_expect_success 'prune empty collapsed merges' '
+       test_config merge.ff false &&
+       git rev-list HEAD >expect &&
+       test_commit to_remove_2 &&
+       git reset --hard HEAD^ &&
+       test_merge non-ff to_remove_2 &&
+       git filter-branch -f --index-filter "git update-index --remove to_remove_2.t" --prune-empty HEAD &&
+       git rev-list HEAD >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success '--remap-to-ancestor with filename filters' '
        git checkout master &&
        git reset --hard A &&
index e97477a3b98a49c80ce01f06b966eacff52a3bd1..474dab381aef027207026cb938df7a09cc7a9056 100755 (executable)
@@ -48,10 +48,11 @@ test_expect_success GPG 'create signed commits' '
        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 seventh-signed
                do
+                       git verify-commit $commit &&
                        git show --pretty=short --show-signature $commit >actual &&
                        grep "Good signature from" actual &&
                        ! grep "BAD signature from" actual &&
@@ -61,6 +62,7 @@ test_expect_success GPG 'show signatures' '
        (
                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 &&
                        ! grep "BAD signature from" actual &&
@@ -79,11 +81,25 @@ test_expect_success GPG 'show signatures' '
        )
 '
 
+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 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
@@ -94,6 +110,7 @@ test_expect_success GPG 'detect fudged signature with NUL' '
        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
@@ -102,6 +119,7 @@ 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
index 332ab1a3b3ee2a2bfa7b25552f9b67f3ca51b2a1..9d51c92b74b5b9251470ea3b5d168a4cfbfb9939 100644 (file)
        fun:copy_ref
 }
 {
-       ignore-sse-check_refname_format
+       ignore-sse-check_refname_format-addr
        Memcheck:Addr8
        fun:check_refname_format
-       fun:cmd_check_ref_format
-       fun:handle_builtin
-       fun:main
+}
+{
+       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 0ac39e93a08733ee46f42dfa1170af7333ae43e5..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
@@ -241,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);
 
@@ -249,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()) {
@@ -1009,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)
@@ -1025,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);
 
@@ -1115,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;
@@ -1243,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 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);
index 9e13b25abc90350de4488276074dc282edc216b1..625198e0585c52c20c778790c82126efd1b57009 100644 (file)
@@ -245,11 +245,11 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
                                              dest ? dest + size : NULL);
                        /* Postimage from side #1 */
                        if (m->mode & 1)
-                               size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+                               size += xdl_recs_copy(xe1, m->i1, m->chg1, (m->mode & 2),
                                                      dest ? dest + size : NULL);
                        /* Postimage from side #2 */
                        if (m->mode & 2)
-                               size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+                               size += xdl_recs_copy(xe2, m->i2, m->chg2, 0,
                                                      dest ? dest + size : NULL);
                } else
                        continue;