Code

Merge branch 'maint'
authorJunio C Hamano <gitster@pobox.com>
Wed, 13 Feb 2008 22:33:19 +0000 (14:33 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 13 Feb 2008 22:33:19 +0000 (14:33 -0800)
* maint:
  config: add test cases for empty value and no value config variables.
  cvsimport: have default merge regex also match beginning of commit message
  git clone -s documentation: force a new paragraph for the NOTE
  status: suggest "git rm --cached" to unstage for initial commit
  Protect get_author_ident_from_commit() from filenames in work tree
  upload-pack: Initialize the exec-path.
  bisect: use verbatim commit subject in the bisect log
  git-cvsimport.txt: fix '-M' description.
  Revert "pack-objects: only throw away data during memory pressure"

123 files changed:
.gitattributes [new file with mode: 0644]
.mailmap
Documentation/.gitattributes [new file with mode: 0644]
Documentation/config.txt
Documentation/git-describe.txt
Documentation/git-pack-objects.txt
Documentation/git-send-email.txt
Documentation/git-svn.txt
Documentation/git.txt
builtin-apply.c
builtin-blame.c
builtin-commit.c
builtin-describe.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-fsck.c
builtin-grep.c
builtin-init-db.c
builtin-ls-files.c
builtin-ls-remote.c
builtin-name-rev.c
builtin-pack-objects.c
builtin-read-tree.c
builtin-rerere.c
builtin-revert.c
builtin-update-index.c
builtin-write-tree.c
builtin.h
cache-tree.c
cache-tree.h
cache.h
config.c
connect.c
contrib/completion/git-completion.bash
contrib/emacs/git-blame.el
contrib/emacs/git.el
contrib/fast-import/git-p4
diff-lib.c
diff.c
dir.c
entry.c
fetch-pack.h
git-cvsexportcommit.perl
git-gui/Makefile
git-gui/git-gui.sh
git-gui/lib/about.tcl
git-gui/lib/checkout_op.tcl
git-gui/lib/commit.tcl
git-gui/lib/index.tcl
git-gui/lib/merge.tcl
git-gui/lib/option.tcl
git-gui/lib/spellcheck.tcl [new file with mode: 0644]
git-gui/macosx/Info.plist
git-gui/po/de.po
git-gui/po/git-gui.pot
git-remote.perl
git-send-email.perl
git-svn.perl
gitk-git/gitk
gitweb/gitweb.perl
merge-index.c
merge-recursive.c
object.c
reachable.c
read-cache.c
sha1_name.c
t/.gitattributes [new file with mode: 0644]
t/README
t/t0000-basic.sh
t/t0030-stripspace.sh
t/t0040-parse-options.sh
t/t1000-read-tree-m-3way.sh
t/t1200-tutorial.sh
t/t1300-repo-config.sh
t/t1302-repo-version.sh
t/t1400-update-ref.sh
t/t2000-checkout-cache-clash.sh
t/t2002-checkout-cache-u.sh
t/t2008-checkout-subdir.sh
t/t2100-update-cache-badpath.sh
t/t3020-ls-files-error-unmatch.sh
t/t3200-branch.sh
t/t3210-pack-refs.sh
t/t3400-rebase.sh
t/t3403-rebase-skip.sh
t/t3600-rm.sh
t/t4103-apply-binary.sh
t/t4113-apply-ending.sh
t/t5300-pack-object.sh
t/t5302-pack-index.sh
t/t5401-update-hooks.sh
t/t5402-post-merge-hook.sh
t/t5500-fetch-pack.sh
t/t5510-fetch.sh
t/t5530-upload-pack-error.sh
t/t5600-clone-fail-cleanup.sh
t/t5710-info-alternate.sh
t/t6009-rev-list-parent.sh [new file with mode: 0755]
t/t6023-merge-file.sh
t/t6024-recursive-merge.sh
t/t6025-merge-symlinks.sh
t/t6101-rev-parse-parents.sh
t/t6300-for-each-ref.sh
t/t7001-mv.sh
t/t7002-grep.sh
t/t7004-tag.sh
t/t7101-reset.sh
t/t7201-co.sh
t/t7501-commit.sh
t/t7503-pre-commit-hook.sh
t/t7504-commit-msg-hook.sh
t/t9100-git-svn-basic.sh
t/t9106-git-svn-commit-diff-clobber.sh
t/t9106-git-svn-dcommit-clobber-series.sh
t/t9200-git-cvsexportcommit.sh
t/t9300-fast-import.sh
t/t9400-git-cvsserver-server.sh
t/test-lib.sh
transport.c
tree.c
unpack-trees.c
unpack-trees.h
wt-status.c

diff --git a/.gitattributes b/.gitattributes
new file mode 100644 (file)
index 0000000..6b9c715
--- /dev/null
@@ -0,0 +1,2 @@
+* whitespace=!indent,trail,space
+*.[ch] whitespace
index 695739ea704ae1530a5b0a31d60c5eabba70a89c..a32d9e2a3f95a4d6cdfb050f2454e31192dea34e 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -43,6 +43,7 @@ Steven Grimm <koreth@midwinter.com>
 Theodore Ts'o <tytso@mit.edu>
 Tony Luck <tony.luck@intel.com>
 Uwe Kleine-König <Uwe_Zeisberger@digi.com>
+Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com>
 Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de>
 Uwe Kleine-König <uzeisberger@io.fsforth.de>
 Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de>
diff --git a/Documentation/.gitattributes b/Documentation/.gitattributes
new file mode 100644 (file)
index 0000000..ddb0301
--- /dev/null
@@ -0,0 +1 @@
+*.txt whitespace
index 6d8cca46ab934826a84647cbd6e441fee286f330..f9bdb164e054ec2d633baab3da69ddd47e14931f 100644 (file)
@@ -766,6 +766,12 @@ pack.indexVersion::
        whenever the corresponding pack is larger than 2 GB.  Otherwise
        the default is 1.
 
+pack.packSizeLimit:
+       The default maximum size of a pack.  This setting only affects
+       packing to a file, i.e. the git:// protocol is unaffected.  It
+       can be overridden by the `\--max-pack-size` option of
+       linkgit:git-repack[1].
+
 pull.octopus::
        The default merge strategy to use when pulling multiple branches
        at once.
index 0742152b811e23ea304feb368c8d949785e2fdfb..1c3dfb40c63350651efb570ed540b69c34ccbc81 100644 (file)
@@ -51,6 +51,10 @@ OPTIONS
        being employed to standard error.  The tag name will still
        be printed to standard out.
 
+--match <pattern>::
+       Only consider tags matching the given pattern (can be used to avoid
+       leaking private tags made from the repository).
+
 EXAMPLES
 --------
 
index 74cc7c1cb831c700c14406f6e306e7a3002054d2..8353be186fcc83092acac16b4fc164d6ea669621 100644 (file)
@@ -99,7 +99,8 @@ base-name::
 --max-pack-size=<n>::
        Maximum size of each output packfile, expressed in MiB.
        If specified,  multiple packfiles may be created.
-       The default is unlimited.
+       The default is unlimited, unless the config variable
+       `pack.packSizeLimit` is set.
 
 --incremental::
        This flag causes an object already in a pack ignored
index 0554f2b3741bef4213bf7197bf1bb6335c1268c4..336d797e80f5d2cc21b633588b3a3461a991ca71 100644 (file)
@@ -96,11 +96,40 @@ The --cc option must be repeated for each user you want on the cc list.
        servers typically listen to smtp port 25 and ssmtp port
        465).
 
---smtp-user, --smtp-pass::
-       Username and password for SMTP-AUTH. Defaults are the values of
-       the configuration values 'sendemail.smtpuser' and
-       'sendemail.smtppass', but see also 'sendemail.identity'.
-       If not set, authentication is not attempted.
+--smtp-user::
+       Username for SMTP-AUTH. In place of this option, the following
+       configuration variables can be specified:
++
+--
+               * sendemail.smtpuser
+               * sendemail.<identity>.smtpuser (see sendemail.identity).
+--
++
+However, --smtp-user always overrides these variables.
++
+If a username is not specified (with --smtp-user or a
+configuration variable), then authentication is not attempted.
+
+--smtp-pass::
+       Password for SMTP-AUTH. The argument is optional: If no
+       argument is specified, then the empty string is used as
+       the password.
++
+In place of this option, the following configuration variables
+can be specified:
++
+--
+               * sendemail.smtppass
+               * sendemail.<identity>.smtppass (see sendemail.identity).
+--
++
+However, --smtp-pass always overrides these variables.
++
+Furthermore, passwords need not be specified in configuration files
+or on the command line. If a username has been specified (with
+--smtp-user or a configuration variable), but no password has been
+specified (with --smtp-pass or a configuration variable), then the
+user is prompted for a password while the input is masked for privacy.
 
 --smtp-ssl::
        If set, connects to the SMTP server using SSL.
@@ -117,6 +146,17 @@ The --cc option must be repeated for each user you want on the cc list.
         Default is the value of 'sendemail.suppressfrom' configuration value;
         if that is unspecified, default to --no-suppress-from.
 
+--suppress-cc::
+       Specify an additional category of recipients to suppress the
+       auto-cc of.  'self' will avoid including the sender, 'author' will
+       avoid including the patch author, 'cc' will avoid including anyone
+       mentioned in Cc lines in the patch, 'sob' will avoid including
+       anyone mentioned in Signed-off-by lines, and 'cccmd' will avoid
+       running the --cc-cmd.  'all' will suppress all auto cc values.
+       Default is the value of 'sendemail.suppresscc' configuration value;
+       if that is unspecified, default to 'self' if --suppress-from is
+       specified, as well as 'sob' if --no-signed-off-cc is specified.
+
 --thread, --no-thread::
        If this is set, the In-Reply-To header will be set on each email sent.
        If disabled with "--no-thread", no emails will have the In-Reply-To
index b1d527f74cd72a7c749f1067a91a6d2c6b6235d3..340f1be02a21495182584b0c09512a8608c1a61a 100644 (file)
@@ -161,6 +161,13 @@ New features:
 +
 Any other arguments are passed directly to `git log'
 
+'blame'::
+       Show what revision and author last modified each line of a file. This is
+       identical to `git blame', but SVN revision numbers are shown instead of git
+       commit hashes.
++
+All arguments are passed directly to `git blame'.
+
 --
 'find-rev'::
        When given an SVN revision number of the form 'rN', returns the
index 17aee93ec5c89e1f1d0d499acaf08bf3cc300662..e22c4c4ae9c9b1cefca1c4c64ae03562bc3f301b 100644 (file)
@@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.5.4/git.html[documentation for release 1.5.4]
+* link:v1.5.4.1/git.html[documentation for release 1.5.4.1]
 
 * release notes for
+  link:RelNotes-1.5.4.1.txt[1.5.4.1],
   link:RelNotes-1.5.4.txt[1.5.4].
 
 * link:v1.5.3.8/git.html[documentation for release 1.5.3.8]
index a11b1bbeee9568adbf8a3e32f77d14b132985061..46dad5b2a1ce6d70d540df8a40da94702dde68c7 100644 (file)
@@ -1946,7 +1946,7 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
        if (!ce)
                return 0;
 
-       if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+       if (S_ISGITLINK(ce->ce_mode)) {
                strbuf_grow(buf, 100);
                strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1));
        } else {
@@ -2023,7 +2023,7 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
 
 static int verify_index_match(struct cache_entry *ce, struct stat *st)
 {
-       if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+       if (S_ISGITLINK(ce->ce_mode)) {
                if (!S_ISDIR(st->st_mode))
                        return -1;
                return 0;
@@ -2082,12 +2082,12 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
                                return error("%s: does not match index",
                                             old_name);
                        if (cached)
-                               st_mode = ntohl(ce->ce_mode);
+                               st_mode = ce->ce_mode;
                } else if (stat_ret < 0)
                        return error("%s: %s", old_name, strerror(errno));
 
                if (!cached)
-                       st_mode = ntohl(ce_mode_from_stat(ce, st.st_mode));
+                       st_mode = ce_mode_from_stat(ce, st.st_mode);
 
                if (patch->is_new < 0)
                        patch->is_new = 0;
@@ -2388,7 +2388,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
        ce = xcalloc(1, ce_size);
        memcpy(ce->name, path, namelen);
        ce->ce_mode = create_ce_mode(mode);
-       ce->ce_flags = htons(namelen);
+       ce->ce_flags = namelen;
        if (S_ISGITLINK(mode)) {
                const char *s = buf;
 
index 9b4c02e87f7ef4a20d269ab9315b6977cf7fb3fb..c7e68874e7f02ac150343ab6d7a0aa95601f2ba3 100644 (file)
@@ -2092,7 +2092,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
        if (!mode) {
                int pos = cache_name_pos(path, len);
                if (0 <= pos)
-                       mode = ntohl(active_cache[pos]->ce_mode);
+                       mode = active_cache[pos]->ce_mode;
                else
                        /* Let's not bother reading from HEAD tree */
                        mode = S_IFREG | 0644;
index a43f2019959a950639f013ff534610e228d8c149..717eb18da0e0cbe901dd8a660649388ccc0a24e1 100644 (file)
@@ -160,7 +160,7 @@ static int list_paths(struct path_list *list, const char *with_tree,
 
        for (i = 0; i < active_nr; i++) {
                struct cache_entry *ce = active_cache[i];
-               if (ce->ce_flags & htons(CE_UPDATE))
+               if (ce->ce_flags & CE_UPDATE)
                        continue;
                if (!pathspec_match(pattern, m, ce->name, 0))
                        continue;
index 7a148a2c26591d82e6057d610182445eae5fe171..3428483134156f4e1761aa47ed2e8098a294808c 100644 (file)
@@ -19,6 +19,7 @@ static int all;       /* Default to annotated tags only */
 static int tags;       /* But allow any tags if --tags is specified */
 static int abbrev = DEFAULT_ABBREV;
 static int max_candidates = 10;
+const char *pattern = NULL;
 
 struct commit_name {
        int prio; /* annotated tag = 2, tag = 1, head = 0 */
@@ -57,9 +58,11 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void
         * Otherwise only annotated tags are used.
         */
        if (!prefixcmp(path, "refs/tags/")) {
-               if (object->type == OBJ_TAG)
+               if (object->type == OBJ_TAG) {
                        prio = 2;
-               else
+                       if (pattern && fnmatch(pattern, path + 10, 0))
+                               prio = 0;
+               } else
                        prio = 1;
        }
        else
@@ -253,7 +256,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN(0, "tags",       &tags, "use any tag in .git/refs/tags"),
                OPT__ABBREV(&abbrev),
                OPT_INTEGER(0, "candidates", &max_candidates,
-                                       "consider <n> most recent tags (default: 10)"),
+                           "consider <n> most recent tags (default: 10)"),
+               OPT_STRING(0, "match",       &pattern, "pattern",
+                          "only consider tags matching <pattern>"),
                OPT_END(),
        };
 
@@ -266,12 +271,19 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
        save_commit_buffer = 0;
 
        if (contains) {
-               const char **args = xmalloc((4 + argc) * sizeof(char*));
+               const char **args = xmalloc((6 + argc) * sizeof(char*));
                int i = 0;
                args[i++] = "name-rev";
                args[i++] = "--name-only";
-               if (!all)
+               args[i++] = "--no-undefined";
+               if (!all) {
                        args[i++] = "--tags";
+                       if (pattern) {
+                               char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1);
+                               sprintf(s, "--refs=refs/tags/%s", pattern);
+                               args[i++] = s;
+                       }
+               }
                memcpy(args + i, argv, argc * sizeof(char*));
                args[i + argc] = NULL;
                return cmd_name_rev(i + argc, args, prefix);
index e68e01592d044a2c2570f096668856eef5caa1a2..f40135248a5a1e2012c4cb01667f037407aae800 100644 (file)
@@ -7,6 +7,7 @@
 #include "pack.h"
 #include "sideband.h"
 #include "fetch-pack.h"
+#include "remote.h"
 #include "run-command.h"
 
 static int transfer_unpack_limit = -1;
@@ -548,14 +549,14 @@ static int get_pack(int xd[2], char **pack_lockfile)
 }
 
 static struct ref *do_fetch_pack(int fd[2],
+               const struct ref *orig_ref,
                int nr_match,
                char **match,
                char **pack_lockfile)
 {
-       struct ref *ref;
+       struct ref *ref = copy_ref_list(orig_ref);
        unsigned char sha1[20];
 
-       get_remote_heads(fd[0], &ref, 0, NULL, 0);
        if (is_repository_shallow() && !server_supports("shallow"))
                die("Server does not support shallow clients");
        if (server_supports("multi_ack")) {
@@ -573,10 +574,6 @@ static struct ref *do_fetch_pack(int fd[2],
                        fprintf(stderr, "Server supports side-band\n");
                use_sideband = 1;
        }
-       if (!ref) {
-               packet_flush(fd[1]);
-               die("no matching remote head");
-       }
        if (everything_local(&ref, nr_match, match)) {
                packet_flush(fd[1]);
                goto all_done;
@@ -650,8 +647,10 @@ static void fetch_pack_setup(void)
 int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 {
        int i, ret, nr_heads;
-       struct ref *ref;
+       struct ref *ref = NULL;
        char *dest = NULL, **heads;
+       int fd[2];
+       struct child_process *conn;
 
        nr_heads = 0;
        heads = NULL;
@@ -706,9 +705,33 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        if (!dest)
                usage(fetch_pack_usage);
 
-       ref = fetch_pack(&args, dest, nr_heads, heads, NULL);
+       conn = git_connect(fd, (char *)dest, args.uploadpack,
+                          args.verbose ? CONNECT_VERBOSE : 0);
+       if (conn) {
+               get_remote_heads(fd[0], &ref, 0, NULL, 0);
+
+               ref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, NULL);
+               close(fd[0]);
+               close(fd[1]);
+               if (finish_connect(conn))
+                       ref = NULL;
+       } else {
+               ref = NULL;
+       }
        ret = !ref;
 
+       if (!ret && nr_heads) {
+               /* If the heads to pull were given, we should have
+                * consumed all of them by matching the remote.
+                * Otherwise, 'git-fetch remote no-such-ref' would
+                * silently succeed without issuing an error.
+                */
+               for (i = 0; i < nr_heads; i++)
+                       if (heads[i] && heads[i][0]) {
+                               error("no such remote ref %s", heads[i]);
+                               ret = 1;
+                       }
+       }
        while (ref) {
                printf("%s %s\n",
                       sha1_to_hex(ref->old_sha1), ref->name);
@@ -719,16 +742,15 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 }
 
 struct ref *fetch_pack(struct fetch_pack_args *my_args,
+                      int fd[], struct child_process *conn,
+                      const struct ref *ref,
                const char *dest,
                int nr_heads,
                char **heads,
                char **pack_lockfile)
 {
-       int i, ret;
-       int fd[2];
-       struct child_process *conn;
-       struct ref *ref;
        struct stat st;
+       struct ref *ref_cpy;
 
        fetch_pack_setup();
        memcpy(&args, my_args, sizeof(args));
@@ -737,29 +759,15 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
                        st.st_mtime = 0;
        }
 
-       conn = git_connect(fd, (char *)dest, args.uploadpack,
-                          args.verbose ? CONNECT_VERBOSE : 0);
        if (heads && nr_heads)
                nr_heads = remove_duplicates(nr_heads, heads);
-       ref = do_fetch_pack(fd, nr_heads, heads, pack_lockfile);
-       close(fd[0]);
-       close(fd[1]);
-       ret = finish_connect(conn);
-
-       if (!ret && nr_heads) {
-               /* If the heads to pull were given, we should have
-                * consumed all of them by matching the remote.
-                * Otherwise, 'git-fetch remote no-such-ref' would
-                * silently succeed without issuing an error.
-                */
-               for (i = 0; i < nr_heads; i++)
-                       if (heads[i] && heads[i][0]) {
-                               error("no such remote ref %s", heads[i]);
-                               ret = 1;
-                       }
+       if (!ref) {
+               packet_flush(fd[1]);
+               die("no matching remote head");
        }
+       ref_cpy = do_fetch_pack(fd, ref, nr_heads, heads, pack_lockfile);
 
-       if (!ret && args.depth > 0) {
+       if (args.depth > 0) {
                struct cache_time mtime;
                char *shallow = git_path("shallow");
                int fd;
@@ -787,8 +795,5 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
                }
        }
 
-       if (ret)
-               ref = NULL;
-
-       return ref;
+       return ref_cpy;
 }
index 320e235682340f07cb28b0a4de0c39b6bd9da383..ac335f20feba9a9e68098c9e89d69cf61c1c3ed7 100644 (file)
@@ -557,6 +557,8 @@ static int do_fetch(struct transport *transport,
 
        free_refs(fetch_map);
 
+       transport_disconnect(transport);
+
        return 0;
 }
 
index 2a6e94deaf6b27f27ad7655ebf6072a05b25017c..cc7524be80f51bca45ef8a205900cf50135bfc5d 100644 (file)
@@ -360,6 +360,9 @@ static int fsck_commit(struct commit *commit)
                fprintf(stderr, "Checking commit %s\n",
                        sha1_to_hex(commit->object.sha1));
 
+       if (!commit->date)
+               return objerror(&commit->object, "invalid author/committer line");
+
        if (memcmp(buffer, "tree ", 5))
                return objerror(&commit->object, "invalid format - expected 'tree' line");
        if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
@@ -378,9 +381,6 @@ static int fsck_commit(struct commit *commit)
                return objerror(&commit->object, "could not load commit's tree %s", tree_sha1);
        if (!commit->parents && show_root)
                printf("root %s\n", sha1_to_hex(commit->object.sha1));
-       if (!commit->date)
-               printf("bad commit date in %s\n",
-                      sha1_to_hex(commit->object.sha1));
        return 0;
 }
 
@@ -765,7 +765,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                        struct blob *blob;
                        struct object *obj;
 
-                       mode = ntohl(active_cache[i]->ce_mode);
+                       mode = active_cache[i]->ce_mode;
                        if (S_ISGITLINK(mode))
                                continue;
                        blob = lookup_blob(active_cache[i]->sha1);
index 0d6cc7361f6e1a70e4d3d9e24913d60e8ceb9e58..9180b39e3f22e5d3805649c655c61aafc7861968 100644 (file)
@@ -331,7 +331,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
                struct cache_entry *ce = active_cache[i];
                char *name;
                int kept;
-               if (!S_ISREG(ntohl(ce->ce_mode)))
+               if (!S_ISREG(ce->ce_mode))
                        continue;
                if (!pathspec_matches(paths, ce->name))
                        continue;
@@ -387,7 +387,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
 
        for (nr = 0; nr < active_nr; nr++) {
                struct cache_entry *ce = active_cache[nr];
-               if (!S_ISREG(ntohl(ce->ce_mode)))
+               if (!S_ISREG(ce->ce_mode))
                        continue;
                if (!pathspec_matches(paths, ce->name))
                        continue;
index e1393b8d1e74c03ff2b45ec93e268daa2e286fd8..5d7cdda93314b1d40f5f512897e8a35af0480a8f 100644 (file)
@@ -141,9 +141,9 @@ static void copy_templates(const char *git_dir, int len, const char *template_di
                 */
                template_dir = DEFAULT_GIT_TEMPLATE_DIR;
                if (!is_absolute_path(template_dir)) {
-                       const char *exec_path = git_exec_path();
-                       template_dir = prefix_path(exec_path, strlen(exec_path),
-                                                  template_dir);
+                       struct strbuf d = STRBUF_INIT;
+                       strbuf_addf(&d, "%s/%s", git_exec_path(), template_dir);
+                       template_dir = strbuf_detach(&d, NULL);
                }
        }
        strcpy(template_path, template_dir);
index 0f0ab2da167c57402efad9dc09e58964759c23cd..d56e33e251036274dbe48e300fcebd8289624cbe 100644 (file)
@@ -189,7 +189,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
                return;
 
        if (tag && *tag && show_valid_bit &&
-           (ce->ce_flags & htons(CE_VALID))) {
+           (ce->ce_flags & CE_VALID)) {
                static char alttag[4];
                memcpy(alttag, tag, 3);
                if (isalpha(tag[0]))
@@ -210,7 +210,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
        } else {
                printf("%s%06o %s %d\t",
                       tag,
-                      ntohl(ce->ce_mode),
+                      ce->ce_mode,
                       abbrev ? find_unique_abbrev(ce->sha1,abbrev)
                                : sha1_to_hex(ce->sha1),
                       ce_stage(ce));
@@ -242,7 +242,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)
                                continue;
                        if (show_unmerged && !ce_stage(ce))
                                continue;
-                       if (ce->ce_flags & htons(CE_UPDATE))
+                       if (ce->ce_flags & CE_UPDATE)
                                continue;
                        show_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);
                }
@@ -350,7 +350,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
                struct cache_entry *ce = active_cache[i];
                if (!ce_stage(ce))
                        continue;
-               ce->ce_flags |= htons(CE_STAGEMASK);
+               ce->ce_flags |= CE_STAGEMASK;
        }
 
        if (prefix) {
@@ -379,7 +379,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
                         */
                        if (last_stage0 &&
                            !strcmp(last_stage0->name, ce->name))
-                               ce->ce_flags |= htons(CE_UPDATE);
+                               ce->ce_flags |= CE_UPDATE;
                }
        }
 }
index 6dd31d1dd6c14677f91e8a0a941fb0f873b4c1fc..023754986e347a9529d6295d600f665f32f14577 100644 (file)
@@ -94,6 +94,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
 
        ref = transport_get_remote_refs(transport);
+       transport_disconnect(transport);
 
        if (!ref)
                return 1;
index a0c89a827b666d8e01ba64597b732205ce1a643e..f22c8b5f5da0031657f10ddb827d33a07056e3a5 100644 (file)
@@ -125,18 +125,18 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
 }
 
 /* returns a static buffer */
-static const charget_rev_name(struct object *o)
+static const char *get_rev_name(struct object *o)
 {
        static char buffer[1024];
        struct rev_name *n;
        struct commit *c;
 
        if (o->type != OBJ_COMMIT)
-               return "undefined";
+               return NULL;
        c = (struct commit *) o;
        n = c->util;
        if (!n)
-               return "undefined";
+               return NULL;
 
        if (!n->generation)
                return n->tip_name;
@@ -159,7 +159,7 @@ static char const * const name_rev_usage[] = {
 int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
        struct object_array revs = { 0, 0, NULL };
-       int all = 0, transform_stdin = 0;
+       int all = 0, transform_stdin = 0, allow_undefined = 1;
        struct name_ref_data data = { 0, 0, NULL };
        struct option opts[] = {
                OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"),
@@ -169,6 +169,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                OPT_GROUP(""),
                OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"),
                OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"),
+               OPT_BOOLEAN(0, "undefined", &allow_undefined, "allow to print `undefined` names"),
                OPT_END(),
        };
 
@@ -226,7 +227,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                                else if (++forty == 40 &&
                                                !ishex(*(p+1))) {
                                        unsigned char sha1[40];
-                                       const char *name = "undefined";
+                                       const char *name = NULL;
                                        char c = *(p+1);
 
                                        forty = 0;
@@ -240,11 +241,10 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                                        }
                                        *(p+1) = c;
 
-                                       if (!strcmp(name, "undefined"))
+                                       if (!name)
                                                continue;
 
-                                       fwrite(p_start, p - p_start + 1, 1,
-                                              stdout);
+                                       fwrite(p_start, p - p_start + 1, 1, stdout);
                                        printf(" (%s)", name);
                                        p_start = p + 1;
                                }
@@ -260,18 +260,32 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                max = get_max_object_index();
                for (i = 0; i < max; i++) {
                        struct object * obj = get_indexed_object(i);
+                       const char *name;
                        if (!obj)
                                continue;
                        if (!data.name_only)
                                printf("%s ", sha1_to_hex(obj->sha1));
-                       printf("%s\n", get_rev_name(obj));
+                       name = get_rev_name(obj);
+                       if (name)
+                               printf("%s\n", name);
+                       else if (allow_undefined)
+                               printf("undefined\n");
+                       else
+                               die("cannot describe '%s'", sha1_to_hex(obj->sha1));
                }
        } else {
                int i;
                for (i = 0; i < revs.nr; i++) {
+                       const char *name;
                        if (!data.name_only)
                                printf("%s ", revs.objects[i].name);
-                       printf("%s\n", get_rev_name(revs.objects[i].item));
+                       name = get_rev_name(revs.objects[i].item);
+                       if (name)
+                               printf("%s\n", name);
+                       else if (allow_undefined)
+                               printf("undefined\n");
+                       else
+                               die("cannot describe '%s'", sha1_to_hex(revs.objects[i].item->sha1));
                }
        }
 
index d3efeff03f89cc0f4d0da463ddf878c28effb31e..692a76126b027133fd046f03003fe8e49218f192 100644 (file)
@@ -68,7 +68,7 @@ static int allow_ofs_delta;
 static const char *base_name;
 static int progress = 1;
 static int window = 10;
-static uint32_t pack_size_limit;
+static uint32_t pack_size_limit, pack_size_limit_cfg;
 static int depth = 50;
 static int delta_search_threads = 1;
 static int pack_to_stdout;
@@ -1867,6 +1867,10 @@ static int git_pack_config(const char *k, const char *v)
                        die("bad pack.indexversion=%d", pack_idx_default_version);
                return 0;
        }
+       if (!strcmp(k, "pack.packsizelimit")) {
+               pack_size_limit_cfg = git_config_ulong(k, v);
+               return 0;
+       }
        return git_default_config(k, v);
 }
 
@@ -2096,6 +2100,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                }
                if (!prefixcmp(arg, "--max-pack-size=")) {
                        char *end;
+                       pack_size_limit_cfg = 0;
                        pack_size_limit = strtoul(arg+16, &end, 0) * 1024 * 1024;
                        if (!arg[16] || *end)
                                usage(pack_usage);
@@ -2220,6 +2225,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        if (pack_to_stdout != !base_name)
                usage(pack_usage);
 
+       if (!pack_to_stdout && !pack_size_limit)
+               pack_size_limit = pack_size_limit_cfg;
+
        if (pack_to_stdout && pack_size_limit)
                die("--max-pack-size cannot be used to build a pack for transfer.");
 
index c0ea0342b755c6749391803d19f852182c0c512e..57854017535a36fc1fcd7bb997f198956391199d 100644 (file)
@@ -45,8 +45,7 @@ static int read_cache_unmerged(void)
                                continue;
                        cache_tree_invalidate_path(active_cache_tree, ce->name);
                        last = ce;
-                       ce->ce_mode = 0;
-                       ce->ce_flags &= ~htons(CE_STAGEMASK);
+                       ce->ce_flags |= CE_REMOVE;
                }
                *dst++ = ce;
        }
index a9e3ebc13725cbe8658478797d6e1d8e097e5292..b0c17bde879d42c16fe5c7f0756763cd638f8bac 100644 (file)
@@ -149,8 +149,8 @@ static int find_conflict(struct path_list *conflict)
                if (ce_stage(e2) == 2 &&
                    ce_stage(e3) == 3 &&
                    ce_same_name(e2, e3) &&
-                   S_ISREG(ntohl(e2->ce_mode)) &&
-                   S_ISREG(ntohl(e3->ce_mode))) {
+                   S_ISREG(e2->ce_mode) &&
+                   S_ISREG(e3->ce_mode)) {
                        path_list_insert((const char *)e2->name, conflict);
                        i++; /* skip over both #2 and #3 */
                }
index 358af537476b0b558989f27fe60627af6977635b..e219859f9b04dc94a2444346541e86503085fdea 100644 (file)
@@ -8,6 +8,7 @@
 #include "exec_cmd.h"
 #include "utf8.h"
 #include "parse-options.h"
+#include "cache-tree.h"
 
 /*
  * This implements the builtins revert and cherry-pick.
@@ -270,7 +271,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                 * that represents the "current" state for merge-recursive
                 * to work on.
                 */
-               if (write_tree(head, 0, NULL))
+               if (write_cache_as_tree(head, 0, NULL))
                        die ("Your index file is unmerged.");
        } else {
                struct wt_status s;
@@ -357,7 +358,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        if (merge_recursive(sha1_to_hex(base->object.sha1),
                                sha1_to_hex(head), "HEAD",
                                sha1_to_hex(next->object.sha1), oneline) ||
-                       write_tree(head, 0, NULL)) {
+                       write_cache_as_tree(head, 0, NULL)) {
                add_to_msg("\nConflicts:\n\n");
                read_cache();
                for (i = 0; i < active_nr;) {
index c3a14c74ed00db592db55a018db37657148a9900..a8795d3d5fea9f340dfe21a71190f120c63856cd 100644 (file)
@@ -47,10 +47,10 @@ static int mark_valid(const char *path)
        if (0 <= pos) {
                switch (mark_valid_only) {
                case MARK_VALID:
-                       active_cache[pos]->ce_flags |= htons(CE_VALID);
+                       active_cache[pos]->ce_flags |= CE_VALID;
                        break;
                case UNMARK_VALID:
-                       active_cache[pos]->ce_flags &= ~htons(CE_VALID);
+                       active_cache[pos]->ce_flags &= ~CE_VALID;
                        break;
                }
                cache_tree_invalidate_path(active_cache_tree, path);
@@ -95,7 +95,7 @@ static int add_one_path(struct cache_entry *old, const char *path, int len, stru
        size = cache_entry_size(len);
        ce = xcalloc(1, size);
        memcpy(ce->name, path, len);
-       ce->ce_flags = htons(len);
+       ce->ce_flags = len;
        fill_stat_cache_info(ce, st);
        ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
 
@@ -139,7 +139,7 @@ static int process_directory(const char *path, int len, struct stat *st)
        /* Exact match: file or existing gitlink */
        if (pos >= 0) {
                struct cache_entry *ce = active_cache[pos];
-               if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+               if (S_ISGITLINK(ce->ce_mode)) {
 
                        /* Do nothing to the index if there is no HEAD! */
                        if (resolve_gitlink_ref(path, "HEAD", sha1) < 0)
@@ -183,7 +183,7 @@ static int process_file(const char *path, int len, struct stat *st)
        int pos = cache_name_pos(path, len);
        struct cache_entry *ce = pos < 0 ? NULL : active_cache[pos];
 
-       if (ce && S_ISGITLINK(ntohl(ce->ce_mode)))
+       if (ce && S_ISGITLINK(ce->ce_mode))
                return error("%s is already a gitlink, not replacing", path);
 
        return add_one_path(ce, path, len, st);
@@ -226,7 +226,7 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
        ce->ce_flags = create_ce_flags(len, stage);
        ce->ce_mode = create_ce_mode(mode);
        if (assume_unchanged)
-               ce->ce_flags |= htons(CE_VALID);
+               ce->ce_flags |= CE_VALID;
        option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
        option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
        if (add_cache_entry(ce, option))
@@ -246,14 +246,14 @@ static void chmod_path(int flip, const char *path)
        if (pos < 0)
                goto fail;
        ce = active_cache[pos];
-       mode = ntohl(ce->ce_mode);
+       mode = ce->ce_mode;
        if (!S_ISREG(mode))
                goto fail;
        switch (flip) {
        case '+':
-               ce->ce_mode |= htonl(0111); break;
+               ce->ce_mode |= 0111; break;
        case '-':
-               ce->ce_mode &= htonl(~0111); break;
+               ce->ce_mode &= ~0111; break;
        default:
                goto fail;
        }
index d16b9ed0098fd5c12fe83638f51cc3f10fd87ed7..e838d01233eb1d6cfaa0885c6b37c8726079b0ac 100644 (file)
 static const char write_tree_usage[] =
 "git-write-tree [--missing-ok] [--prefix=<prefix>/]";
 
-int write_tree(unsigned char *sha1, int missing_ok, const char *prefix)
-{
-       int entries, was_valid, newfd;
-
-       /* We can't free this memory, it becomes part of a linked list parsed atexit() */
-       struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-
-       newfd = hold_locked_index(lock_file, 1);
-
-       entries = read_cache();
-       if (entries < 0)
-               die("git-write-tree: error reading cache");
-
-       if (!active_cache_tree)
-               active_cache_tree = cache_tree();
-
-       was_valid = cache_tree_fully_valid(active_cache_tree);
-
-       if (!was_valid) {
-               if (cache_tree_update(active_cache_tree,
-                                     active_cache, active_nr,
-                                     missing_ok, 0) < 0)
-                       die("git-write-tree: error building trees");
-               if (0 <= newfd) {
-                       if (!write_cache(newfd, active_cache, active_nr) &&
-                           !commit_lock_file(lock_file))
-                               newfd = -1;
-               }
-               /* Not being able to write is fine -- we are only interested
-                * in updating the cache-tree part, and if the next caller
-                * ends up using the old index with unupdated cache-tree part
-                * it misses the work we did here, but that is just a
-                * performance penalty and not a big deal.
-                */
-       }
-
-       if (prefix) {
-               struct cache_tree *subtree =
-                       cache_tree_find(active_cache_tree, prefix);
-               if (!subtree)
-                       die("git-write-tree: prefix %s not found", prefix);
-               hashcpy(sha1, subtree->sha1);
-       }
-       else
-               hashcpy(sha1, active_cache_tree->sha1);
-
-       if (0 <= newfd)
-               rollback_lock_file(lock_file);
-
-       return 0;
-}
-
 int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
 {
        int missing_ok = 0, ret;
        const char *prefix = NULL;
        unsigned char sha1[20];
+       const char *me = "git-write-tree";
 
        git_config(git_default_config);
        while (1 < argc) {
@@ -84,8 +33,20 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
        if (argc > 2)
                die("too many options");
 
-       ret = write_tree(sha1, missing_ok, prefix);
-       printf("%s\n", sha1_to_hex(sha1));
-
+       ret = write_cache_as_tree(sha1, missing_ok, prefix);
+       switch (ret) {
+       case 0:
+               printf("%s\n", sha1_to_hex(sha1));
+               break;
+       case WRITE_TREE_UNREADABLE_INDEX:
+               die("%s: error reading the index", me);
+               break;
+       case WRITE_TREE_UNMERGED_INDEX:
+               die("%s: error building trees; the index is unmerged?", me);
+               break;
+       case WRITE_TREE_PREFIX_ERROR:
+               die("%s: prefix %s not found", me, prefix);
+               break;
+       }
        return ret;
 }
index cb675c4d7a80b99a06d6d04156e767ea025eb512..3d1628c597b21cc22b750809c27d4499e725d259 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -8,7 +8,6 @@ extern const char git_usage_string[];
 
 extern void list_common_cmds_help(void);
 extern void help_unknown_cmd(const char *cmd);
-extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
 extern void prune_packed_objects(int);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
index 50b35264fd0405a299700ef8bf4a61f416f30e46..39da54d1e56b5905655eafed1aff0f51c2540a8e 100644 (file)
@@ -320,13 +320,13 @@ static int update_one(struct cache_tree *it,
                }
                else {
                        sha1 = ce->sha1;
-                       mode = ntohl(ce->ce_mode);
+                       mode = ce->ce_mode;
                        entlen = pathlen - baselen;
                }
                if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1))
                        return error("invalid object %s", sha1_to_hex(sha1));
 
-               if (!ce->ce_mode)
+               if (ce->ce_flags & CE_REMOVE)
                        continue; /* entry being removed */
 
                strbuf_grow(&buffer, entlen + 100);
@@ -529,3 +529,58 @@ struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path)
        }
        return it;
 }
+
+int write_cache_as_tree(unsigned char *sha1, int missing_ok, const char *prefix)
+{
+       int entries, was_valid, newfd;
+
+       /*
+        * We can't free this memory, it becomes part of a linked list
+        * parsed atexit()
+        */
+       struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+       newfd = hold_locked_index(lock_file, 1);
+
+       entries = read_cache();
+       if (entries < 0)
+               return WRITE_TREE_UNREADABLE_INDEX;
+
+       if (!active_cache_tree)
+               active_cache_tree = cache_tree();
+
+       was_valid = cache_tree_fully_valid(active_cache_tree);
+
+       if (!was_valid) {
+               if (cache_tree_update(active_cache_tree,
+                                     active_cache, active_nr,
+                                     missing_ok, 0) < 0)
+                       return WRITE_TREE_UNMERGED_INDEX;
+               if (0 <= newfd) {
+                       if (!write_cache(newfd, active_cache, active_nr) &&
+                           !commit_lock_file(lock_file))
+                               newfd = -1;
+               }
+               /* Not being able to write is fine -- we are only interested
+                * in updating the cache-tree part, and if the next caller
+                * ends up using the old index with unupdated cache-tree part
+                * it misses the work we did here, but that is just a
+                * performance penalty and not a big deal.
+                */
+       }
+
+       if (prefix) {
+               struct cache_tree *subtree =
+                       cache_tree_find(active_cache_tree, prefix);
+               if (!subtree)
+                       return WRITE_TREE_PREFIX_ERROR;
+               hashcpy(sha1, subtree->sha1);
+       }
+       else
+               hashcpy(sha1, active_cache_tree->sha1);
+
+       if (0 <= newfd)
+               rollback_lock_file(lock_file);
+
+       return 0;
+}
index 8243228e49ffd7078a783582be6ce79c97541a9c..44aad426d319b83eda013e115f35e066cc590cb8 100644 (file)
@@ -30,4 +30,9 @@ int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int)
 
 struct cache_tree *cache_tree_find(struct cache_tree *, const char *);
 
+#define WRITE_TREE_UNREADABLE_INDEX (-1)
+#define WRITE_TREE_UNMERGED_INDEX (-2)
+#define WRITE_TREE_PREFIX_ERROR (-3)
+
+int write_cache_as_tree(unsigned char *sha1, int missing_ok, const char *prefix);
 #endif
diff --git a/cache.h b/cache.h
index 6abcee437238ccc2b46e7d7d74f79db3a4b682a1..3867ba7ff5c3946e0449c78b26880a71107730a0 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -3,6 +3,7 @@
 
 #include "git-compat-util.h"
 #include "strbuf.h"
+#include "hash.h"
 
 #include SHA1_HEADER
 #include <zlib.h>
@@ -94,48 +95,84 @@ struct cache_time {
  * We save the fields in big-endian order to allow using the
  * index file over NFS transparently.
  */
+struct ondisk_cache_entry {
+       struct cache_time ctime;
+       struct cache_time mtime;
+       unsigned int dev;
+       unsigned int ino;
+       unsigned int mode;
+       unsigned int uid;
+       unsigned int gid;
+       unsigned int size;
+       unsigned char sha1[20];
+       unsigned short flags;
+       char name[FLEX_ARRAY]; /* more */
+};
+
 struct cache_entry {
-       struct cache_time ce_ctime;
-       struct cache_time ce_mtime;
+       struct cache_entry *next;
+       unsigned int ce_ctime;
+       unsigned int ce_mtime;
        unsigned int ce_dev;
        unsigned int ce_ino;
        unsigned int ce_mode;
        unsigned int ce_uid;
        unsigned int ce_gid;
        unsigned int ce_size;
+       unsigned int ce_flags;
        unsigned char sha1[20];
-       unsigned short ce_flags;
        char name[FLEX_ARRAY]; /* more */
 };
 
 #define CE_NAMEMASK  (0x0fff)
 #define CE_STAGEMASK (0x3000)
-#define CE_UPDATE    (0x4000)
 #define CE_VALID     (0x8000)
 #define CE_STAGESHIFT 12
 
-#define create_ce_flags(len, stage) htons((len) | ((stage) << CE_STAGESHIFT))
-#define ce_namelen(ce) (CE_NAMEMASK & ntohs((ce)->ce_flags))
+/* In-memory only */
+#define CE_UPDATE    (0x10000)
+#define CE_REMOVE    (0x20000)
+#define CE_UPTODATE  (0x40000)
+#define CE_UNHASHED  (0x80000)
+
+static inline unsigned create_ce_flags(size_t len, unsigned stage)
+{
+       if (len >= CE_NAMEMASK)
+               len = CE_NAMEMASK;
+       return (len | (stage << CE_STAGESHIFT));
+}
+
+static inline size_t ce_namelen(const struct cache_entry *ce)
+{
+       size_t len = ce->ce_flags & CE_NAMEMASK;
+       if (len < CE_NAMEMASK)
+               return len;
+       return strlen(ce->name + CE_NAMEMASK) + CE_NAMEMASK;
+}
+
 #define ce_size(ce) cache_entry_size(ce_namelen(ce))
-#define ce_stage(ce) ((CE_STAGEMASK & ntohs((ce)->ce_flags)) >> CE_STAGESHIFT)
+#define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce))
+#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
+#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
+#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
 
 #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
 static inline unsigned int create_ce_mode(unsigned int mode)
 {
        if (S_ISLNK(mode))
-               return htonl(S_IFLNK);
+               return S_IFLNK;
        if (S_ISDIR(mode) || S_ISGITLINK(mode))
-               return htonl(S_IFGITLINK);
-       return htonl(S_IFREG | ce_permissions(mode));
+               return S_IFGITLINK;
+       return S_IFREG | ce_permissions(mode);
 }
 static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
 {
        extern int trust_executable_bit, has_symlinks;
        if (!has_symlinks && S_ISREG(mode) &&
-           ce && S_ISLNK(ntohl(ce->ce_mode)))
+           ce && S_ISLNK(ce->ce_mode))
                return ce->ce_mode;
        if (!trust_executable_bit && S_ISREG(mode)) {
-               if (ce && S_ISREG(ntohl(ce->ce_mode)))
+               if (ce && S_ISREG(ce->ce_mode))
                        return ce->ce_mode;
                return create_ce_mode(0666);
        }
@@ -146,14 +183,16 @@ static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned in
        S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
 
 #define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
+#define ondisk_cache_entry_size(len) ((offsetof(struct ondisk_cache_entry,name) + (len) + 8) & ~7)
 
 struct index_state {
        struct cache_entry **cache;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct cache_tree *cache_tree;
        time_t timestamp;
-       void *mmap;
-       size_t mmap_size;
+       void *alloc;
+       unsigned name_hash_initialized : 1;
+       struct hash_table name_hash;
 };
 
 extern struct index_state the_index;
@@ -177,6 +216,7 @@ extern struct index_state the_index;
 #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
 #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
 #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
+#define cache_name_exists(name, namelen) index_name_exists(&the_index, (name), (namelen))
 #endif
 
 enum object_type {
@@ -263,6 +303,7 @@ extern int read_index_from(struct index_state *, const char *path);
 extern int write_index(struct index_state *, int newfd);
 extern int discard_index(struct index_state *);
 extern int verify_path(const char *path);
+extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
 extern int index_name_pos(struct index_state *, const char *name, int namelen);
 #define ADD_CACHE_OK_TO_ADD 1          /* Ok to add */
 #define ADD_CACHE_OK_TO_REPLACE 2      /* Ok to replace file/directory */
index 3f4d3b1602cf4764184fe7df70a232e71a5058ec..3e72778e94267044780a180145eae0a6a229e3a3 100644 (file)
--- a/config.c
+++ b/config.c
@@ -498,9 +498,9 @@ const char *git_etc_gitconfig(void)
                system_wide = ETC_GITCONFIG;
                if (!is_absolute_path(system_wide)) {
                        /* interpret path relative to exec-dir */
-                       const char *exec_path = git_exec_path();
-                       system_wide = prefix_path(exec_path, strlen(exec_path),
-                                               system_wide);
+                       struct strbuf d = STRBUF_INIT;
+                       strbuf_addf(&d, "%s/%s", git_exec_path(), system_wide);
+                       system_wide = strbuf_detach(&d, NULL);
                }
        }
        return system_wide;
index 71597d4920ff11ed474be1a8bd39b4791611e422..5ac357278464324d82ac6912c1b7ad84d42d0743 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -474,14 +474,18 @@ char *get_port(char *host)
        return NULL;
 }
 
+static struct child_process no_fork;
+
 /*
- * This returns NULL if the transport protocol does not need fork(2), or a
- * struct child_process object if it does.  Once done, finish the connection
- * with finish_connect() with the value returned from this function
- * (it is safe to call finish_connect() with NULL to support the former
- * case).
+ * This returns a dummy child_process if the transport protocol does not
+ * need fork(2), or a struct child_process object if it does.  Once done,
+ * finish the connection with finish_connect() with the value returned from
+ * this function (it is safe to call finish_connect() with NULL to support
+ * the former case).
  *
- * If it returns, the connect is successful; it just dies on errors.
+ * If it returns, the connect is successful; it just dies on errors (this
+ * will hopefully be changed in a libification effort, to return NULL when
+ * the connection failed).
  */
 struct child_process *git_connect(int fd[2], const char *url_orig,
                                  const char *prog, int flags)
@@ -579,7 +583,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                free(url);
                if (free_path)
                        free(path);
-               return NULL;
+               return &no_fork;
        }
 
        conn = xcalloc(1, sizeof(*conn));
@@ -637,7 +641,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
 int finish_connect(struct child_process *conn)
 {
        int code;
-       if (!conn)
+       if (!conn || conn == &no_fork)
                return 0;
 
        code = finish_command(conn);
index 0d33f9a3dc9d5a593ce2691ab850ba1e38aa32de..4ea727b14303e397117067993dbda446ed154ea1 100755 (executable)
@@ -64,12 +64,41 @@ __gitdir ()
 
 __git_ps1 ()
 {
-       local b="$(git symbolic-ref HEAD 2>/dev/null)"
-       if [ -n "$b" ]; then
+       local g="$(git rev-parse --git-dir 2>/dev/null)"
+       if [ -n "$g" ]; then
+               local r
+               local b
+               if [ -d "$g/../.dotest" ]
+               then
+                       r="|AM/REBASE"
+                       b="$(git symbolic-ref HEAD 2>/dev/null)"
+               elif [ -f "$g/.dotest-merge/interactive" ]
+               then
+                       r="|REBASE-i"
+                       b="$(cat $g/.dotest-merge/head-name)"
+               elif [ -d "$g/.dotest-merge" ]
+               then
+                       r="|REBASE-m"
+                       b="$(cat $g/.dotest-merge/head-name)"
+               elif [ -f "$g/MERGE_HEAD" ]
+               then
+                       r="|MERGING"
+                       b="$(git symbolic-ref HEAD 2>/dev/null)"
+               else
+                       if [ -f $g/BISECT_LOG ]
+                       then
+                               r="|BISECTING"
+                       fi
+                       if ! b="$(git symbolic-ref HEAD 2>/dev/null)"
+                       then
+                               b="$(cut -c1-7 $g/HEAD)..."
+                       fi
+               fi
+
                if [ -n "$1" ]; then
-                       printf "$1" "${b##refs/heads/}"
+                       printf "$1" "${b##refs/heads/}$r"
                else
-                       printf " (%s)" "${b##refs/heads/}"
+                       printf " (%s)" "${b##refs/heads/}$r"
                fi
        fi
 }
index bb671d561ebc9af51bb9a5d52017e71fd81881e9..9f92cd250b2ba2c3057bff87daa1b7990f85403d 100644 (file)
@@ -105,6 +105,13 @@ selected element from l."
      (setq ,l (remove e ,l))
      e))
 
+(defvar git-blame-log-oneline-format
+  "format:[%cr] %cn: %s"
+  "*Formatting option used for describing current line in the minibuffer.
+
+This option is used to pass to git log --pretty= command-line option,
+and describe which commit the current line was made.")
+
 (defvar git-blame-dark-colors
   (git-blame-color-scale "0c" "04" "24" "1c" "2c" "34" "14" "3c")
   "*List of colors (format #RGB) to use in a dark environment.
@@ -371,7 +378,8 @@ See also function `git-blame-mode'."
 (defun git-describe-commit (hash)
   (with-temp-buffer
     (call-process "git" nil t nil
-                  "log" "-1" "--pretty=oneline"
+                  "log" "-1"
+                 (concat "--pretty=" git-blame-log-oneline-format)
                   hash)
     (buffer-substring (point-min) (1- (point-max)))))
 
index d8a06381f4140a777f03fda67f68838e4aa3e493..a8bf0ef883358c7eaf9a13dde8f371a86ff840f0 100644 (file)
@@ -35,7 +35,6 @@
 ;;
 ;; TODO
 ;;  - portability to XEmacs
-;;  - better handling of subprocess errors
 ;;  - diff against other branch
 ;;  - renaming files from the status buffer
 ;;  - creating tags
@@ -191,6 +190,18 @@ if there is already one that displays the same directory."
              (append (git-get-env-strings env) (list "git") args))
     (apply #'call-process "git" nil buffer nil args)))
 
+(defun git-call-process-display-error (&rest args)
+  "Wrapper for call-process that displays error messages."
+  (let* ((dir default-directory)
+         (buffer (get-buffer-create "*Git Command Output*"))
+         (ok (with-current-buffer buffer
+               (let ((default-directory dir)
+                     (buffer-read-only nil))
+                 (erase-buffer)
+                 (eq 0 (apply 'call-process "git" nil (list buffer t) nil args))))))
+    (unless ok (display-message-or-buffer buffer))
+    ok))
+
 (defun git-call-process-env-string (env &rest args)
   "Wrapper for call-process that sets environment strings,
 and returns the process output as a string."
@@ -377,7 +388,7 @@ and returns the process output as a string."
     (when reason
      (push reason args)
      (push "-m" args))
-    (eq 0 (apply #'git-call-process-env nil nil "update-ref" args))))
+    (apply 'git-call-process-display-error "update-ref" args)))
 
 (defun git-read-tree (tree &optional index-file)
   "Read a tree into the index file."
@@ -558,12 +569,15 @@ and returns the process output as a string."
                     (?\100 "   (type change file -> subproject)")
                     (?\120 "   (type change symlink -> subproject)")
                     (t "   (subproject)")))
+                  (?\110 nil)  ;; directory (internal, not a real git state)
                  (?\000  ;; deleted or unknown
                   (case old-type
                     (?\120 "   (symlink)")
                     (?\160 "   (subproject)")))
                  (t (format "   (unknown type %o)" new-type)))))
-    (if str (propertize str 'face 'git-status-face) "")))
+    (cond (str (propertize str 'face 'git-status-face))
+          ((eq new-type ?\110) "/")
+          (t ""))))
 
 (defun git-rename-as-string (info)
   "Return a string describing the copy or rename associated with INFO, or an empty string if none."
@@ -666,9 +680,11 @@ Return the list of files that haven't been handled."
     (with-temp-buffer
       (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files))
       (goto-char (point-min))
-      (while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
+      (while (re-search-forward "\\([^\0]*?\\)\\(/?\\)\0" nil t 1)
         (let ((name (match-string 1)))
-          (push (git-create-fileinfo default-state name) infolist)
+          (push (git-create-fileinfo default-state name 0
+                                     (if (string-equal "/" (match-string 2)) (lsh ?\110 9) 0))
+                infolist)
           (setq files (delete name files)))))
     (git-insert-info-list status infolist)
     files))
@@ -713,7 +729,7 @@ Return the list of files that haven't been handled."
 (defun git-run-ls-files-with-excludes (status files default-state &rest options)
   "Run git-ls-files on FILES with appropriate --exclude-from options."
   (let ((exclude-files (git-get-exclude-files)))
-    (apply #'git-run-ls-files status files default-state
+    (apply #'git-run-ls-files status files default-state "--directory"
            (concat "--exclude-per-directory=" git-per-dir-ignore-file)
            (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
 
@@ -735,6 +751,27 @@ Return the list of files that haven't been handled."
     (git-refresh-files)
     (git-refresh-ewoc-hf git-status)))
 
+(defun git-mark-files (status files)
+  "Mark all the specified FILES, and unmark the others."
+  (setq files (sort files #'string-lessp))
+  (let ((file (and files (pop files)))
+        (node (ewoc-nth status 0)))
+    (while node
+      (let ((info (ewoc-data node)))
+        (if (and file (string-equal (git-fileinfo->name info) file))
+            (progn
+              (unless (git-fileinfo->marked info)
+                (setf (git-fileinfo->marked info) t)
+                (setf (git-fileinfo->needs-refresh info) t))
+              (setq file (pop files))
+              (setq node (ewoc-next status node)))
+          (when (git-fileinfo->marked info)
+            (setf (git-fileinfo->marked info) nil)
+            (setf (git-fileinfo->needs-refresh info) t))
+          (if (and file (string-lessp file (git-fileinfo->name info)))
+              (setq file (pop files))
+            (setq node (ewoc-next status node))))))))
+
 (defun git-marked-files ()
   "Return a list of all marked files, or if none a list containing just the file at cursor position."
   (unless git-status (error "Not in git-status buffer."))
@@ -840,16 +877,17 @@ Return the list of files that haven't been handled."
                       (if (or (not (string-equal tree head-tree))
                               (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? "))
                           (let ((commit (git-commit-tree buffer tree head)))
-                            (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
-                            (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
-                            (with-current-buffer buffer (erase-buffer))
-                           (git-update-status-files (git-get-filenames files) 'uptodate)
-                            (git-call-process-env nil nil "rerere")
-                            (git-call-process-env nil nil "gc" "--auto")
-                            (git-refresh-files)
-                            (git-refresh-ewoc-hf git-status)
-                            (message "Committed %s." commit)
-                            (git-run-hook "post-commit" nil))
+                            (when commit
+                              (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
+                              (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
+                              (with-current-buffer buffer (erase-buffer))
+                              (git-update-status-files (git-get-filenames files) 'uptodate)
+                              (git-call-process-env nil nil "rerere")
+                              (git-call-process-env nil nil "gc" "--auto")
+                              (git-refresh-files)
+                              (git-refresh-ewoc-hf git-status)
+                              (message "Committed %s." commit)
+                              (git-run-hook "post-commit" nil)))
                         (message "Commit aborted."))))
                 (message "No files to commit.")))
           (delete-file index-file))))))
@@ -957,11 +995,12 @@ Return the list of files that haven't been handled."
   "Add marked file(s) to the index cache."
   (interactive)
   (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored))))
+    ;; FIXME: add support for directories
     (unless files
       (push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
-    (apply #'git-call-process-env nil nil "update-index" "--add" "--" files)
-    (git-update-status-files files 'uptodate)
-    (git-success-message "Added" files)))
+    (when (apply 'git-call-process-display-error "update-index" "--add" "--" files)
+      (git-update-status-files files 'uptodate)
+      (git-success-message "Added" files))))
 
 (defun git-ignore-file ()
   "Add marked file(s) to the ignore list."
@@ -983,16 +1022,19 @@ Return the list of files that haven't been handled."
          (format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" "")))
         (progn
           (dolist (name files)
-            (when (file-exists-p name) (delete-file name)))
-          (apply #'git-call-process-env nil nil "update-index" "--remove" "--" files)
-          (git-update-status-files files nil)
-          (git-success-message "Removed" files))
+            (ignore-errors
+              (if (file-directory-p name)
+                  (delete-directory name)
+                (delete-file name))))
+          (when (apply 'git-call-process-display-error "update-index" "--remove" "--" files)
+            (git-update-status-files files nil)
+            (git-success-message "Removed" files)))
       (message "Aborting"))))
 
 (defun git-revert-file ()
   "Revert changes to the marked file(s)."
   (interactive)
-  (let ((files (git-marked-files))
+  (let ((files (git-marked-files-state 'added 'deleted 'modified 'unmerged))
         added modified)
     (when (and files
                (yes-or-no-p
@@ -1003,21 +1045,31 @@ Return the list of files that haven't been handled."
           ('deleted (push (git-fileinfo->name info) modified))
           ('unmerged (push (git-fileinfo->name info) modified))
           ('modified (push (git-fileinfo->name info) modified))))
-      (when added
-        (apply #'git-call-process-env nil nil "update-index" "--force-remove" "--" added))
-      (when modified
-        (apply #'git-call-process-env nil nil "checkout" "HEAD" modified))
-      (git-update-status-files (append added modified) 'uptodate)
-      (git-success-message "Reverted" (git-get-filenames files)))))
+      ;; check if a buffer contains one of the files and isn't saved
+      (dolist (file modified)
+        (let ((buffer (get-file-buffer file)))
+          (when (and buffer (buffer-modified-p buffer))
+            (error "Buffer %s is modified. Please kill or save modified buffers before reverting." (buffer-name buffer)))))
+      (let ((ok (and
+                 (or (not added)
+                     (apply 'git-call-process-display-error "update-index" "--force-remove" "--" added))
+                 (or (not modified)
+                     (apply 'git-call-process-display-error "checkout" "HEAD" modified)))))
+        (git-update-status-files (append added modified) 'uptodate)
+        (when ok
+          (dolist (file modified)
+            (let ((buffer (get-file-buffer file)))
+              (when buffer (with-current-buffer buffer (revert-buffer t t t)))))
+          (git-success-message "Reverted" (git-get-filenames files)))))))
 
 (defun git-resolve-file ()
   "Resolve conflicts in marked file(s)."
   (interactive)
   (let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
     (when files
-      (apply #'git-call-process-env nil nil "update-index" "--" files)
-      (git-update-status-files files 'uptodate)
-      (git-success-message "Resolved" files))))
+      (when (apply 'git-call-process-display-error "update-index" "--" files)
+        (git-update-status-files files 'uptodate)
+        (git-success-message "Resolved" files)))))
 
 (defun git-remove-handled ()
   "Remove handled files from the status list."
@@ -1063,6 +1115,16 @@ Return the list of files that haven't been handled."
         (message "Inserting unknown files...done"))
     (git-remove-handled)))
 
+(defun git-expand-directory (info)
+  "Expand the directory represented by INFO to list its files."
+  (when (eq (lsh (git-fileinfo->new-perm info) -9) ?\110)
+    (let ((dir (git-fileinfo->name info)))
+      (git-set-filenames-state git-status (list dir) nil)
+      (git-run-ls-files-with-excludes git-status (list (concat dir "/")) 'unknown "-o")
+      (git-refresh-files)
+      (git-refresh-ewoc-hf git-status)
+      t)))
+
 (defun git-setup-diff-buffer (buffer)
   "Setup a buffer for displaying a diff."
   (let ((dir default-directory))
@@ -1199,7 +1261,8 @@ Return the list of files that haven't been handled."
       (goto-char (point-min))
       (when (re-search-forward "\n+\\'" nil t)
         (replace-match "\n" t t))
-      (when sign-off (git-append-sign-off committer-name committer-email)))))
+      (when sign-off (git-append-sign-off committer-name committer-email)))
+    buffer))
 
 (defun git-commit-file ()
   "Commit the marked file(s), asking for a commit message."
@@ -1232,14 +1295,61 @@ Return the list of files that haven't been handled."
       (setq buffer-file-coding-system coding-system)
       (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))))
 
+(defun git-setup-commit-buffer (commit)
+  "Setup the commit buffer with the contents of COMMIT."
+  (let (author-name author-email subject date msg)
+    (with-temp-buffer
+      (let ((coding-system (git-get-logoutput-coding-system)))
+        (git-call-process-env t nil "log" "-1" commit)
+        (goto-char (point-min))
+        (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t)
+          (setq author-name (match-string 1))
+          (setq author-email (match-string 2)))
+        (when (re-search-forward "^Date: *\\(.*\\)$" nil t)
+          (setq date (match-string 1)))
+        (while (re-search-forward "^    \\(.*\\)$" nil t)
+          (push (match-string 1) msg))
+        (setq msg (nreverse msg))
+        (setq subject (pop msg))
+        (while (and msg (zerop (length (car msg))) (pop msg)))))
+    (git-setup-log-buffer (get-buffer-create "*git-commit*")
+                          author-name author-email subject date
+                          (mapconcat #'identity msg "\n"))))
+
+(defun git-get-commit-files (commit)
+  "Retrieve the list of files modified by COMMIT."
+  (let (files)
+    (with-temp-buffer
+      (git-call-process-env t nil "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit)
+      (goto-char (point-min))
+      (while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
+        (push (match-string 1) files)))
+    files))
+
+(defun git-amend-commit ()
+  "Undo the last commit on HEAD, and set things up to commit an
+amended version of it."
+  (interactive)
+  (unless git-status (error "Not in git-status buffer."))
+  (when (git-empty-db-p) (error "No commit to amend."))
+  (let* ((commit (git-rev-parse "HEAD"))
+         (files (git-get-commit-files commit)))
+    (when (git-call-process-display-error "reset" "--soft" "HEAD^")
+      (git-update-status-files (copy-sequence files) 'uptodate)
+      (git-mark-files git-status files)
+      (git-refresh-files)
+      (git-setup-commit-buffer commit)
+      (git-commit-file))))
+
 (defun git-find-file ()
   "Visit the current file in its own buffer."
   (interactive)
   (unless git-status (error "Not in git-status buffer."))
   (let ((info (ewoc-data (ewoc-locate git-status))))
-    (find-file (git-fileinfo->name info))
-    (when (eq 'unmerged (git-fileinfo->state info))
-      (smerge-mode 1))))
+    (unless (git-expand-directory info)
+      (find-file (git-fileinfo->name info))
+      (when (eq 'unmerged (git-fileinfo->state info))
+        (smerge-mode 1)))))
 
 (defun git-find-file-other-window ()
   "Visit the current file in its own buffer in another window."
@@ -1309,6 +1419,7 @@ Return the list of files that haven't been handled."
 
 (unless git-status-mode-map
   (let ((map (make-keymap))
+        (commit-map (make-sparse-keymap))
         (diff-map (make-sparse-keymap))
         (toggle-map (make-sparse-keymap)))
     (suppress-keymap map)
@@ -1317,6 +1428,7 @@ Return the list of files that haven't been handled."
     (define-key map " "   'git-next-file)
     (define-key map "a"   'git-add-file)
     (define-key map "c"   'git-commit-file)
+    (define-key map "\C-c" commit-map)
     (define-key map "d"    diff-map)
     (define-key map "="   'git-diff-file)
     (define-key map "f"   'git-find-file)
@@ -1342,6 +1454,8 @@ Return the list of files that haven't been handled."
     (define-key map "x"   'git-remove-handled)
     (define-key map "\C-?" 'git-unmark-file-up)
     (define-key map "\M-\C-?" 'git-unmark-all)
+    ; the commit submap
+    (define-key commit-map "\C-a" 'git-amend-commit)
     ; the diff submap
     (define-key diff-map "b" 'git-diff-file-base)
     (define-key diff-map "c" 'git-diff-file-combined)
index c80a6da2522b690e15f84fedf52a132078cd265a..781a0cbbbc96d7bf5c6d77518f2d6c9d043ba68b 100755 (executable)
@@ -469,9 +469,7 @@ class P4Submit(Command):
                 optparse.make_option("--origin", dest="origin"),
                 optparse.make_option("--reset", action="store_true", dest="reset"),
                 optparse.make_option("--log-substitutions", dest="substFile"),
-                optparse.make_option("--dry-run", action="store_true"),
                 optparse.make_option("--direct", dest="directSubmit", action="store_true"),
-                optparse.make_option("--trust-me-like-a-fool", dest="trustMeLikeAFool", action="store_true"),
                 optparse.make_option("-M", dest="detectRename", action="store_true"),
         ]
         self.description = "Submit changes from git to the perforce depot."
@@ -479,12 +477,10 @@ class P4Submit(Command):
         self.firstTime = True
         self.reset = False
         self.interactive = True
-        self.dryRun = False
         self.substFile = ""
         self.firstTime = True
         self.origin = ""
         self.directSubmit = False
-        self.trustMeLikeAFool = False
         self.detectRename = False
         self.verbose = False
         self.isWindows = (platform.system() == "Windows")
@@ -681,57 +677,30 @@ class P4Submit(Command):
                 separatorLine += "\r"
             separatorLine += "\n"
 
-            response = "e"
-            if self.trustMeLikeAFool:
-                response = "y"
-
-            firstIteration = True
-            while response == "e":
-                if not firstIteration:
-                    response = raw_input("Do you want to submit this change? [y]es/[e]dit/[n]o/[s]kip ")
-                firstIteration = False
-                if response == "e":
-                    [handle, fileName] = tempfile.mkstemp()
-                    tmpFile = os.fdopen(handle, "w+")
-                    tmpFile.write(submitTemplate + separatorLine + diff)
-                    tmpFile.close()
-                    defaultEditor = "vi"
-                    if platform.system() == "Windows":
-                        defaultEditor = "notepad"
-                    editor = os.environ.get("EDITOR", defaultEditor);
-                    system(editor + " " + fileName)
-                    tmpFile = open(fileName, "rb")
-                    message = tmpFile.read()
-                    tmpFile.close()
-                    os.remove(fileName)
-                    submitTemplate = message[:message.index(separatorLine)]
-                    if self.isWindows:
-                        submitTemplate = submitTemplate.replace("\r\n", "\n")
-
-            if response == "y" or response == "yes":
-               if self.dryRun:
-                   print submitTemplate
-                   raw_input("Press return to continue...")
-               else:
-                   if self.directSubmit:
-                       print "Submitting to git first"
-                       os.chdir(self.oldWorkingDirectory)
-                       write_pipe("git commit -a -F -", submitTemplate)
-                       os.chdir(self.clientPath)
-
-                   write_pipe("p4 submit -i", submitTemplate)
-            elif response == "s":
-                for f in editedFiles:
-                    system("p4 revert \"%s\"" % f);
-                for f in filesToAdd:
-                    system("p4 revert \"%s\"" % f);
-                    system("rm %s" %f)
-                for f in filesToDelete:
-                    system("p4 delete \"%s\"" % f);
-                return
-            else:
-                print "Not submitting!"
-                self.interactive = False
+            [handle, fileName] = tempfile.mkstemp()
+            tmpFile = os.fdopen(handle, "w+")
+            tmpFile.write(submitTemplate + separatorLine + diff)
+            tmpFile.close()
+            defaultEditor = "vi"
+            if platform.system() == "Windows":
+                defaultEditor = "notepad"
+            editor = os.environ.get("EDITOR", defaultEditor);
+            system(editor + " " + fileName)
+            tmpFile = open(fileName, "rb")
+            message = tmpFile.read()
+            tmpFile.close()
+            os.remove(fileName)
+            submitTemplate = message[:message.index(separatorLine)]
+            if self.isWindows:
+                submitTemplate = submitTemplate.replace("\r\n", "\n")
+
+            if self.directSubmit:
+                print "Submitting to git first"
+                os.chdir(self.oldWorkingDirectory)
+                write_pipe("git commit -a -F -", submitTemplate)
+                os.chdir(self.clientPath)
+
+            write_pipe("p4 submit -i", submitTemplate)
         else:
             fileName = "submit.txt"
             file = open(fileName, "w+")
@@ -828,10 +797,8 @@ class P4Submit(Command):
                 sync = P4Sync()
                 sync.run([])
 
-                response = raw_input("Do you want to rebase current HEAD from Perforce now using git-p4 rebase? [y]es/[n]o ")
-                if response == "y" or response == "yes":
-                    rebase = P4Rebase()
-                    rebase.rebase()
+                rebase = P4Rebase()
+                rebase.rebase()
             os.remove(self.configFile)
 
         return True
@@ -964,9 +931,13 @@ class P4Sync(Command):
             stat = filedata[j]
             j += 1
             text = ''
-            while j < len(filedata) and filedata[j]['code'] in ('text',
-                                                                'binary'):
-                text += filedata[j]['data']
+            while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'):
+                tmp = filedata[j]['data']
+                if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
+                    tmp = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', tmp)
+                elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
+                    tmp = re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', tmp)
+                text += tmp
                 j += 1
 
 
@@ -1640,6 +1611,11 @@ class P4Rebase(Command):
         return self.rebase()
 
     def rebase(self):
+        if os.system("git update-index --refresh") != 0:
+            die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up-to-date or stash away all your changes with git stash.");
+        if len(read_pipe("git diff-index HEAD --")) > 0:
+            die("You have uncommited changes. Please commit them before rebasing or stash them away with git stash.");
+
         [upstream, settings] = findUpstreamBranchPoint()
         if len(upstream) == 0:
             die("Cannot find upstream branchpoint for rebase")
@@ -1670,7 +1646,7 @@ class P4Clone(P4Sync):
         depotPath = args[0]
         depotDir = re.sub("(@[^@]*)$", "", depotPath)
         depotDir = re.sub("(#[^#]*)$", "", depotDir)
-        depotDir = re.sub(r"\.\.\.$,", "", depotDir)
+        depotDir = re.sub(r"\.\.\.$", "", depotDir)
         depotDir = re.sub(r"/$", "", depotDir)
         return os.path.split(depotDir)[1]
 
index d85d8f34ba055ad7e02bf5d61ae4a675ce0be1d3..03eaa7cef35dfaec0a8f61b2b96892e5604ed731 100644 (file)
@@ -9,6 +9,7 @@
 #include "revision.h"
 #include "cache-tree.h"
 #include "path-list.h"
+#include "unpack-trees.h"
 
 /*
  * diff-files
@@ -37,7 +38,7 @@ static int get_mode(const char *path, int *mode)
        if (!path || !strcmp(path, "/dev/null"))
                *mode = 0;
        else if (!strcmp(path, "-"))
-               *mode = ntohl(create_ce_mode(0666));
+               *mode = create_ce_mode(0666);
        else if (stat(path, &st))
                return error("Could not access '%s'", path);
        else
@@ -384,7 +385,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                                        continue;
                        }
                        else
-                               dpath->mode = ntohl(ce_mode_from_stat(ce, st.st_mode));
+                               dpath->mode = ce_mode_from_stat(ce, st.st_mode);
 
                        while (i < entries) {
                                struct cache_entry *nce = active_cache[i];
@@ -398,10 +399,10 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                                 */
                                stage = ce_stage(nce);
                                if (2 <= stage) {
-                                       int mode = ntohl(nce->ce_mode);
+                                       int mode = nce->ce_mode;
                                        num_compare_stages++;
                                        hashcpy(dpath->parent[stage-2].sha1, nce->sha1);
-                                       dpath->parent[stage-2].mode = ntohl(ce_mode_from_stat(nce, mode));
+                                       dpath->parent[stage-2].mode = ce_mode_from_stat(nce, mode);
                                        dpath->parent[stage-2].status =
                                                DIFF_STATUS_MODIFIED;
                                }
@@ -435,6 +436,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                                continue;
                }
 
+               if (ce_uptodate(ce))
+                       continue;
                if (lstat(ce->name, &st) < 0) {
                        if (errno != ENOENT && errno != ENOTDIR) {
                                perror(ce->name);
@@ -442,15 +445,15 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                        }
                        if (silent_on_removed)
                                continue;
-                       diff_addremove(&revs->diffopt, '-', ntohl(ce->ce_mode),
+                       diff_addremove(&revs->diffopt, '-', ce->ce_mode,
                                       ce->sha1, ce->name, NULL);
                        continue;
                }
                changed = ce_match_stat(ce, &st, ce_option);
                if (!changed && !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
                        continue;
-               oldmode = ntohl(ce->ce_mode);
-               newmode = ntohl(ce_mode_from_stat(ce, st.st_mode));
+               oldmode = ce->ce_mode;
+               newmode = ce_mode_from_stat(ce, st.st_mode);
                diff_change(&revs->diffopt, oldmode, newmode,
                            ce->sha1, (changed ? null_sha1 : ce->sha1),
                            ce->name, NULL);
@@ -471,7 +474,7 @@ static void diff_index_show_file(struct rev_info *revs,
                                 struct cache_entry *ce,
                                 unsigned char *sha1, unsigned int mode)
 {
-       diff_addremove(&revs->diffopt, prefix[0], ntohl(mode),
+       diff_addremove(&revs->diffopt, prefix[0], mode,
                       sha1, ce->name, NULL);
 }
 
@@ -550,14 +553,14 @@ static int show_modified(struct rev_info *revs,
                p->len = pathlen;
                memcpy(p->path, new->name, pathlen);
                p->path[pathlen] = 0;
-               p->mode = ntohl(mode);
+               p->mode = mode;
                hashclr(p->sha1);
                memset(p->parent, 0, 2 * sizeof(struct combine_diff_parent));
                p->parent[0].status = DIFF_STATUS_MODIFIED;
-               p->parent[0].mode = ntohl(new->ce_mode);
+               p->parent[0].mode = new->ce_mode;
                hashcpy(p->parent[0].sha1, new->sha1);
                p->parent[1].status = DIFF_STATUS_MODIFIED;
-               p->parent[1].mode = ntohl(old->ce_mode);
+               p->parent[1].mode = old->ce_mode;
                hashcpy(p->parent[1].sha1, old->sha1);
                show_combined_diff(p, 2, revs->dense_combined_merges, revs);
                free(p);
@@ -569,119 +572,154 @@ static int show_modified(struct rev_info *revs,
            !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
                return 0;
 
-       mode = ntohl(mode);
-       oldmode = ntohl(oldmode);
-
        diff_change(&revs->diffopt, oldmode, mode,
                    old->sha1, sha1, old->name, NULL);
        return 0;
 }
 
-static int diff_cache(struct rev_info *revs,
-                     struct cache_entry **ac, int entries,
-                     const char **pathspec,
-                     int cached, int match_missing)
+/*
+ * This turns all merge entries into "stage 3". That guarantees that
+ * when we read in the new tree (into "stage 1"), we won't lose sight
+ * of the fact that we had unmerged entries.
+ */
+static void mark_merge_entries(void)
 {
-       while (entries) {
-               struct cache_entry *ce = *ac;
-               int same = (entries > 1) && ce_same_name(ce, ac[1]);
+       int i;
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+               if (!ce_stage(ce))
+                       continue;
+               ce->ce_flags |= CE_STAGEMASK;
+       }
+}
 
-               if (DIFF_OPT_TST(&revs->diffopt, QUIET) &&
-                       DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES))
-                       break;
+/*
+ * This gets a mix of an existing index and a tree, one pathname entry
+ * at a time. The index entry may be a single stage-0 one, but it could
+ * also be multiple unmerged entries (in which case idx_pos/idx_nr will
+ * give you the position and number of entries in the index).
+ */
+static void do_oneway_diff(struct unpack_trees_options *o,
+       struct cache_entry *idx,
+       struct cache_entry *tree,
+       int idx_pos, int idx_nr)
+{
+       struct rev_info *revs = o->unpack_data;
+       int match_missing, cached;
 
-               if (!ce_path_match(ce, pathspec))
-                       goto skip_entry;
+       /*
+        * Backward compatibility wart - "diff-index -m" does
+        * not mean "do not ignore merges", but "match_missing".
+        *
+        * But with the revision flag parsing, that's found in
+        * "!revs->ignore_merges".
+        */
+       cached = o->index_only;
+       match_missing = !revs->ignore_merges;
+
+       if (cached && idx && ce_stage(idx)) {
+               if (tree)
+                       diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode, idx->sha1);
+               return;
+       }
+
+       /*
+        * Something added to the tree?
+        */
+       if (!tree) {
+               show_new_file(revs, idx, cached, match_missing);
+               return;
+       }
 
-               switch (ce_stage(ce)) {
-               case 0:
-                       /* No stage 1 entry? That means it's a new file */
-                       if (!same) {
-                               show_new_file(revs, ce, cached, match_missing);
+       /*
+        * Something removed from the tree?
+        */
+       if (!idx) {
+               diff_index_show_file(revs, "-", tree, tree->sha1, tree->ce_mode);
+               return;
+       }
+
+       /* Show difference between old and new */
+       show_modified(revs, tree, idx, 1, cached, match_missing);
+}
+
+/*
+ * Count how many index entries go with the first one
+ */
+static inline int count_skip(const struct cache_entry *src, int pos)
+{
+       int skip = 1;
+
+       /* We can only have multiple entries if the first one is not stage-0 */
+       if (ce_stage(src)) {
+               struct cache_entry **p = active_cache + pos;
+               int namelen = ce_namelen(src);
+
+               for (;;) {
+                       const struct cache_entry *ce;
+                       pos++;
+                       if (pos >= active_nr)
                                break;
-                       }
-                       /* Show difference between old and new */
-                       show_modified(revs, ac[1], ce, 1,
-                                     cached, match_missing);
-                       break;
-               case 1:
-                       /* No stage 3 (merge) entry?
-                        * That means it's been deleted.
-                        */
-                       if (!same) {
-                               diff_index_show_file(revs, "-", ce,
-                                                    ce->sha1, ce->ce_mode);
+                       ce = *++p;
+                       if (ce_namelen(ce) != namelen)
                                break;
-                       }
-                       /* We come here with ce pointing at stage 1
-                        * (original tree) and ac[1] pointing at stage
-                        * 3 (unmerged).  show-modified with
-                        * report-missing set to false does not say the
-                        * file is deleted but reports true if work
-                        * tree does not have it, in which case we
-                        * fall through to report the unmerged state.
-                        * Otherwise, we show the differences between
-                        * the original tree and the work tree.
-                        */
-                       if (!cached &&
-                           !show_modified(revs, ce, ac[1], 0,
-                                          cached, match_missing))
+                       if (memcmp(ce->name, src->name, namelen))
                                break;
-                       diff_unmerge(&revs->diffopt, ce->name,
-                                    ntohl(ce->ce_mode), ce->sha1);
-                       break;
-               case 3:
-                       diff_unmerge(&revs->diffopt, ce->name,
-                                    0, null_sha1);
-                       break;
-
-               default:
-                       die("impossible cache entry stage");
+                       skip++;
                }
-
-skip_entry:
-               /*
-                * Ignore all the different stages for this file,
-                * we've handled the relevant cases now.
-                */
-               do {
-                       ac++;
-                       entries--;
-               } while (entries && ce_same_name(ce, ac[0]));
        }
-       return 0;
+       return skip;
 }
 
 /*
- * This turns all merge entries into "stage 3". That guarantees that
- * when we read in the new tree (into "stage 1"), we won't lose sight
- * of the fact that we had unmerged entries.
+ * The unpack_trees() interface is designed for merging, so
+ * the different source entries are designed primarily for
+ * the source trees, with the old index being really mainly
+ * used for being replaced by the result.
+ *
+ * For diffing, the index is more important, and we only have a
+ * single tree.
+ *
+ * We're supposed to return how many index entries we want to skip.
+ *
+ * This wrapper makes it all more readable, and takes care of all
+ * the fairly complex unpack_trees() semantic requirements, including
+ * the skipping, the path matching, the type conflict cases etc.
  */
-static void mark_merge_entries(void)
+static int oneway_diff(struct cache_entry **src,
+       struct unpack_trees_options *o,
+       int index_pos)
 {
-       int i;
-       for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
-               if (!ce_stage(ce))
-                       continue;
-               ce->ce_flags |= htons(CE_STAGEMASK);
-       }
+       int skip = 0;
+       struct cache_entry *idx = src[0];
+       struct cache_entry *tree = src[1];
+       struct rev_info *revs = o->unpack_data;
+
+       if (index_pos >= 0)
+               skip = count_skip(idx, index_pos);
+
+       /*
+        * Unpack-trees generates a DF/conflict entry if
+        * there was a directory in the index and a tree
+        * in the tree. From a diff standpoint, that's a
+        * delete of the tree and a create of the file.
+        */
+       if (tree == o->df_conflict_entry)
+               tree = NULL;
+
+       if (ce_path_match(idx ? idx : tree, revs->prune_data))
+               do_oneway_diff(o, idx, tree, index_pos, skip);
+
+       return skip;
 }
 
 int run_diff_index(struct rev_info *revs, int cached)
 {
-       int ret;
        struct object *ent;
        struct tree *tree;
        const char *tree_name;
-       int match_missing = 0;
-
-       /*
-        * Backward compatibility wart - "diff-index -m" does
-        * not mean "do not ignore merges", but totally different.
-        */
-       if (!revs->ignore_merges)
-               match_missing = 1;
+       struct unpack_trees_options opts;
+       struct tree_desc t;
 
        mark_merge_entries();
 
@@ -690,13 +728,20 @@ int run_diff_index(struct rev_info *revs, int cached)
        tree = parse_tree_indirect(ent->sha1);
        if (!tree)
                return error("bad tree object %s", tree_name);
-       if (read_tree(tree, 1, revs->prune_data))
-               return error("unable to read tree object %s", tree_name);
-       ret = diff_cache(revs, active_cache, active_nr, revs->prune_data,
-                        cached, match_missing);
+
+       memset(&opts, 0, sizeof(opts));
+       opts.head_idx = 1;
+       opts.index_only = cached;
+       opts.merge = 1;
+       opts.fn = oneway_diff;
+       opts.unpack_data = revs;
+
+       init_tree_desc(&t, tree->buffer, tree->size);
+       unpack_trees(1, &t, &opts);
+
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
-       return ret;
+       return 0;
 }
 
 int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
@@ -706,6 +751,8 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
        int i;
        struct cache_entry **dst;
        struct cache_entry *last = NULL;
+       struct unpack_trees_options opts;
+       struct tree_desc t;
 
        /*
         * This is used by git-blame to run diff-cache internally;
@@ -722,8 +769,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
                        cache_tree_invalidate_path(active_cache_tree,
                                                   ce->name);
                        last = ce;
-                       ce->ce_mode = 0;
-                       ce->ce_flags &= ~htons(CE_STAGEMASK);
+                       ce->ce_flags |= CE_REMOVE;
                }
                *dst++ = ce;
        }
@@ -734,8 +780,15 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
        tree = parse_tree_indirect(tree_sha1);
        if (!tree)
                die("bad tree object %s", sha1_to_hex(tree_sha1));
-       if (read_tree(tree, 1, opt->paths))
-               return error("unable to read tree %s", sha1_to_hex(tree_sha1));
-       return diff_cache(&revs, active_cache, active_nr, revs.prune_data,
-                         1, 0);
+
+       memset(&opts, 0, sizeof(opts));
+       opts.head_idx = 1;
+       opts.index_only = 1;
+       opts.merge = 1;
+       opts.fn = oneway_diff;
+       opts.unpack_data = &revs;
+
+       init_tree_desc(&t, tree->buffer, tree->size);
+       unpack_trees(1, &t, &opts);
+       return 0;
 }
diff --git a/diff.c b/diff.c
index 4d2e23ae1b7dd4bd43d5c03871ce8ab519272115..cd8bc4dcc32757dfed9374fa8769599a6a7857ad 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1520,17 +1520,22 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
        if (pos < 0)
                return 0;
        ce = active_cache[pos];
-       if ((lstat(name, &st) < 0) ||
-           !S_ISREG(st.st_mode) || /* careful! */
-           ce_match_stat(ce, &st, 0) ||
-           hashcmp(sha1, ce->sha1))
+
+       /*
+        * This is not the sha1 we are looking for, or
+        * unreusable because it is not a regular file.
+        */
+       if (hashcmp(sha1, ce->sha1) || !S_ISREG(ce->ce_mode))
                return 0;
-       /* we return 1 only when we can stat, it is a regular file,
-        * stat information matches, and sha1 recorded in the cache
-        * matches.  I.e. we know the file in the work tree really is
-        * the same as the <name, sha1> pair.
+
+       /*
+        * If ce matches the file in the work tree, we can reuse it.
         */
-       return 1;
+       if (ce_uptodate(ce) ||
+           (!lstat(name, &st) && !ce_match_stat(ce, &st, 0)))
+               return 1;
+
+       return 0;
 }
 
 static int populate_from_stdin(struct diff_filespec *s)
diff --git a/dir.c b/dir.c
index 3e345c2fc508eb82e66f4ffe2c825670f0bddd99..6543105b9622212430a9e5ed131a81074e019d9a 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -346,7 +346,7 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len)
 
 struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
 {
-       if (cache_name_pos(pathname, len) >= 0)
+       if (cache_name_exists(pathname, len))
                return NULL;
 
        ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
@@ -391,7 +391,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
                        break;
                if (endchar == '/')
                        return index_directory;
-               if (!endchar && S_ISGITLINK(ntohl(ce->ce_mode)))
+               if (!endchar && S_ISGITLINK(ce->ce_mode))
                        return index_gitdir;
        }
        return index_nonexistent;
diff --git a/entry.c b/entry.c
index 257ab46e943f1f8b7445f01c10d949224c17112f..44f4b897d4ff7afff7dbab92ce8754645ad8da6a 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -103,7 +103,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
        int fd;
        long wrote;
 
-       switch (ntohl(ce->ce_mode) & S_IFMT) {
+       switch (ce->ce_mode & S_IFMT) {
                char *new;
                struct strbuf buf;
                unsigned long size;
@@ -129,7 +129,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
                        strcpy(path, ".merge_file_XXXXXX");
                        fd = mkstemp(path);
                } else
-                       fd = create_file(path, ntohl(ce->ce_mode));
+                       fd = create_file(path, ce->ce_mode);
                if (fd < 0) {
                        free(new);
                        return error("git-checkout-index: unable to create file %s (%s)",
@@ -221,7 +221,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
                unlink(path);
                if (S_ISDIR(st.st_mode)) {
                        /* If it is a gitlink, leave it alone! */
-                       if (S_ISGITLINK(ntohl(ce->ce_mode)))
+                       if (S_ISGITLINK(ce->ce_mode))
                                return 0;
                        if (!state->force)
                                return error("%s is a directory", path);
index a7888ea302cde44b072cc019394ae43dbb4cf95d..8d35ef60bf6d975939362a9c01ece4026140f8c9 100644 (file)
@@ -16,6 +16,8 @@ struct fetch_pack_args
 };
 
 struct ref *fetch_pack(struct fetch_pack_args *args,
+               int fd[], struct child_process *conn,
+               const struct ref *ref,
                const char *dest,
                int nr_heads,
                char **heads,
index d2e50c34292bc0ccb6e914ed595da9fd8a7e141d..2a8ad1e9f4cbc2c21b83fd72fae1e0d2e582fbdc 100755 (executable)
@@ -5,6 +5,7 @@ use Getopt::Std;
 use File::Temp qw(tempdir);
 use Data::Dumper;
 use File::Basename qw(basename dirname);
+use File::Spec;
 
 our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w);
 
@@ -15,17 +16,15 @@ $opt_h && usage();
 die "Need at least one commit identifier!" unless @ARGV;
 
 if ($opt_w) {
+       # Remember where GIT_DIR is before changing to CVS checkout
        unless ($ENV{GIT_DIR}) {
-               # Remember where our GIT_DIR is before changing to CVS checkout
+               # No GIT_DIR set. Figure it out for ourselves
                my $gd =`git-rev-parse --git-dir`;
                chomp($gd);
-               if ($gd eq '.git') {
-                       my $wd = `pwd`;
-                       chomp($wd);
-                       $gd = $wd."/.git"       ;
-               }
                $ENV{GIT_DIR} = $gd;
        }
+       # Make sure GIT_DIR is absolute
+       $ENV{GIT_DIR} = File::Spec->rel2abs($ENV{GIT_DIR});
 
        if (! -d $opt_w."/CVS" ) {
                die "$opt_w is not a CVS checkout";
index 34438cdf5cc219ac71cbc90a23c23550276f5af2..081d7550a7dbc91d8360822a4da3ca51c66a33a2 100644 (file)
@@ -13,6 +13,7 @@ GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
 
 uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
 uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
+uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
 
 SCRIPT_SH = git-gui.sh
 GITGUI_MAIN := git-gui
@@ -93,7 +94,14 @@ endif
 
 TCL_PATH   ?= tclsh
 TCLTK_PATH ?= wish
-TKFRAMEWORK = /Library/Frameworks/Tk.framework/Resources/Wish.app
+
+ifeq ($(uname_S),Darwin)
+       TKFRAMEWORK = /Library/Frameworks/Tk.framework/Resources/Wish.app
+       ifeq ($(shell expr "$(uname_R)" : '9\.'),2)
+               TKFRAMEWORK = /System/Library/Frameworks/Tk.framework/Resources/Wish\ Shell.app
+       endif
+       TKEXECUTABLE = $(shell basename "$(TKFRAMEWORK)" .app)
+endif
 
 ifeq ($(findstring $(MAKEFLAGS),s),s)
 QUIET_GEN =
@@ -147,7 +155,7 @@ git-gui: GIT-VERSION-FILE GIT-GUI-VARS
        echo then >>$@+ && \
        echo '  'echo \'git-gui version '$(GITGUI_VERSION)'\' >>$@+ && \
        echo else >>$@+ && \
-       echo '  'exec \''$(libdir_SQ)/Git Gui.app/Contents/MacOS/Wish'\' \
+       echo '  'exec \''$(libdir_SQ)/Git Gui.app/Contents/MacOS/$(subst \,,$(TKEXECUTABLE))'\' \
                '"$$0" "$$@"' >>$@+ && \
        echo fi >>$@+ && \
        chmod +x $@+ && \
@@ -157,14 +165,15 @@ Git\ Gui.app: GIT-VERSION-FILE GIT-GUI-VARS \
                macosx/Info.plist \
                macosx/git-gui.icns \
                macosx/AppMain.tcl \
-               $(TKFRAMEWORK)/Contents/MacOS/Wish
+               $(TKFRAMEWORK)/Contents/MacOS/$(TKEXECUTABLE)
        $(QUIET_GEN)rm -rf '$@' '$@'+ && \
        mkdir -p '$@'+/Contents/MacOS && \
        mkdir -p '$@'+/Contents/Resources/Scripts && \
-       cp '$(subst ','\'',$(TKFRAMEWORK))/Contents/MacOS/Wish' \
+       cp '$(subst ','\'',$(subst \,,$(TKFRAMEWORK)/Contents/MacOS/$(TKEXECUTABLE)))' \
                '$@'+/Contents/MacOS && \
        cp macosx/git-gui.icns '$@'+/Contents/Resources && \
        sed -e 's/@@GITGUI_VERSION@@/$(GITGUI_VERSION)/g' \
+               -e 's/@@GITGUI_TKEXECUTABLE@@/$(TKEXECUTABLE)/g' \
                macosx/Info.plist \
                >'$@'+/Contents/Info.plist && \
        sed -e 's|@@gitexecdir@@|$(gitexecdir_SQ)|' \
index f42e461fd42ad37075d433f3509d6bd415a0b97a..5d65272e26dd08fc3abb8bf8cdbf57065da53bcc 100755 (executable)
@@ -612,6 +612,7 @@ set default_config(gui.pruneduringfetch) false
 set default_config(gui.trustmtime) false
 set default_config(gui.diffcontext) 5
 set default_config(gui.newbranchtemplate) {}
+set default_config(gui.spellingdictionary) {}
 set default_config(gui.fontui) [font configure font_ui]
 set default_config(gui.fontdiff) [font configure font_diff]
 set font_descs {
@@ -1683,6 +1684,7 @@ set is_quitting 0
 proc do_quit {} {
        global ui_comm is_quitting repo_config commit_type
        global GITGUI_BCK_exists GITGUI_BCK_i
+       global ui_comm_spell
 
        if {$is_quitting} return
        set is_quitting 1
@@ -1710,6 +1712,12 @@ proc do_quit {} {
                        }
                }
 
+               # -- Cancel our spellchecker if its running.
+               #
+               if {[info exists ui_comm_spell]} {
+                       $ui_comm_spell stop
+               }
+
                # -- Remove our editor backup, its not needed.
                #
                after cancel $GITGUI_BCK_i
@@ -2454,7 +2462,7 @@ $ctxm add separator
 $ctxm add command \
        -label [mc "Sign Off"] \
        -command do_signoff
-bind_button3 $ui_comm "tk_popup $ctxm %X %Y"
+set ui_comm_ctxm $ctxm
 
 # -- Diff Header
 #
@@ -2857,6 +2865,30 @@ if {[winfo exists $ui_comm]} {
        }
 
        backup_commit_buffer
+
+       # -- If the user has aspell available we can drive it
+       #    in pipe mode to spellcheck the commit message.
+       #
+       set spell_cmd [list |]
+       set spell_dict [get_config gui.spellingdictionary]
+       lappend spell_cmd aspell
+       if {$spell_dict ne {}} {
+               lappend spell_cmd --master=$spell_dict
+       }
+       lappend spell_cmd --mode=none
+       lappend spell_cmd --encoding=utf-8
+       lappend spell_cmd pipe
+       if {$spell_dict eq {none}
+        || [catch {set spell_fd [open $spell_cmd r+]} spell_err]} {
+               bind_button3 $ui_comm [list tk_popup $ui_comm_ctxm %X %Y]
+       } else {
+               set ui_comm_spell [spellcheck::init \
+                       $spell_fd \
+                       $ui_comm \
+                       $ui_comm_ctxm \
+               ]
+       }
+       unset -nocomplain spell_cmd spell_fd spell_err spell_dict
 }
 
 lock_index begin-read
index 719fc547b3e157cdb14a8a09ba77a1d7c5f1f585..47be8eb97ab79188bba8dca18f3784327302ba9a 100644 (file)
@@ -4,6 +4,7 @@
 proc do_about {} {
        global appvers copyright oguilib
        global tcl_patchLevel tk_patchLevel
+       global ui_comm_spell
 
        set w .about_dialog
        toplevel $w
@@ -40,6 +41,10 @@ proc do_about {} {
                append v "Tcl version $tcl_patchLevel"
                append v ", Tk version $tk_patchLevel"
        }
+       if {[info exists ui_comm_spell]} {
+               append v "\n"
+               append v [$ui_comm_spell version]
+       }
 
        set d {}
        append d "git wrapper: $::_git\n"
index f24396692474e3da66f9502b2f06f13583a3deeb..6e1411711bd46df08c09fd2bc371b017050a8368 100644 (file)
@@ -280,7 +280,7 @@ The rescan will be automatically started now.
        } elseif {[is_config_true gui.trustmtime]} {
                _readtree $this
        } else {
-               ui_status {Refreshing file status...}
+               ui_status [mc "Refreshing file status..."]
                set fd [git_read update-index \
                        -q \
                        --unmerged \
@@ -320,7 +320,7 @@ method _readtree {} {
        set readtree_d {}
        $::main_status start \
                [mc "Updating working directory to '%s'..." [_name $this]] \
-               {files checked out}
+               [mc "files checked out"]
 
        set fd [git_read --stderr read-tree \
                -m \
@@ -447,7 +447,7 @@ If you wanted to be on a branch, create one now starting from 'This Detached Che
        } else {
                repository_state commit_type HEAD MERGE_HEAD
                set PARENT $HEAD
-               ui_status "Checked out '$name'."
+               ui_status [mc "Checked out '%s'." $name]
        }
        delete_this
 }
index 947b201c328eef044cfe21b463e8228031f6f8cd..40a710355751836e78b65101592b753266f507ca 100644 (file)
@@ -218,7 +218,7 @@ A good commit message has the following format:
                return
        }
 
-       ui_status {Calling pre-commit hook...}
+       ui_status [mc "Calling pre-commit hook..."]
        set pch_error {}
        fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
        fileevent $fd_ph readable \
@@ -233,7 +233,7 @@ proc commit_prehook_wait {fd_ph curHEAD msg_p} {
        if {[eof $fd_ph]} {
                if {[catch {close $fd_ph}]} {
                        catch {file delete $msg_p}
-                       ui_status {Commit declined by pre-commit hook.}
+                       ui_status [mc "Commit declined by pre-commit hook."]
                        hook_failed_popup pre-commit $pch_error
                        unlock_index
                } else {
@@ -256,7 +256,7 @@ proc commit_commitmsg {curHEAD msg_p} {
                return
        }
 
-       ui_status {Calling commit-msg hook...}
+       ui_status [mc "Calling commit-msg hook..."]
        set pch_error {}
        fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
        fileevent $fd_ph readable \
@@ -271,7 +271,7 @@ proc commit_commitmsg_wait {fd_ph curHEAD msg_p} {
        if {[eof $fd_ph]} {
                if {[catch {close $fd_ph}]} {
                        catch {file delete $msg_p}
-                       ui_status {Commit declined by commit-msg hook.}
+                       ui_status [mc "Commit declined by commit-msg hook."]
                        hook_failed_popup commit-msg $pch_error
                        unlock_index
                } else {
@@ -284,7 +284,7 @@ proc commit_commitmsg_wait {fd_ph curHEAD msg_p} {
 }
 
 proc commit_writetree {curHEAD msg_p} {
-       ui_status {Committing changes...}
+       ui_status [mc "Committing changes..."]
        set fd_wt [git_read write-tree]
        fileevent $fd_wt readable \
                [list commit_committree $fd_wt $curHEAD $msg_p]
@@ -301,7 +301,7 @@ proc commit_committree {fd_wt curHEAD msg_p} {
        if {[catch {close $fd_wt} err]} {
                catch {file delete $msg_p}
                error_popup [strcat [mc "write-tree failed:"] "\n\n$err"]
-               ui_status {Commit failed.}
+               ui_status [mc "Commit failed."]
                unlock_index
                return
        }
@@ -345,7 +345,7 @@ A rescan will be automatically started now.
        if {[catch {set cmt_id [eval git $cmd]} err]} {
                catch {file delete $msg_p}
                error_popup [strcat [mc "commit-tree failed:"] "\n\n$err"]
-               ui_status {Commit failed.}
+               ui_status [mc "Commit failed."]
                unlock_index
                return
        }
@@ -365,7 +365,7 @@ A rescan will be automatically started now.
                } err]} {
                catch {file delete $msg_p}
                error_popup [strcat [mc "update-ref failed:"] "\n\n$err"]
-               ui_status {Commit failed.}
+               ui_status [mc "Commit failed."]
                unlock_index
                return
        }
index 30a244cc170a344ba99be1c7be07fdebe4333bde..3c1fce7475d362d1880d915ff4bdf168fda28593 100644 (file)
@@ -310,7 +310,7 @@ proc add_helper {txt paths} {
                update_index \
                        $txt \
                        $pathList \
-                       [concat $after {ui_status {Ready to commit.}}]
+                       [concat $after {ui_status [mc "Ready to commit."]}]
        }
 }
 
index 63e14279c183b1d0b8a62926816bb44ab6dc519c..cc26b0780882b6e8bde122c2a724d110f439d84f 100644 (file)
@@ -116,8 +116,7 @@ method _start {} {
        lappend cmd HEAD
        lappend cmd $name
 
-       set msg [mc "Merging %s and %s" $current_branch $stitle]
-       ui_status "$msg..."
+       ui_status [mc "Merging %s and %s..." $current_branch $stitle]
        set cons [console::new [mc "Merge"] "merge $stitle"]
        console::exec $cons $cmd [cb _finish $cons]
 
@@ -236,7 +235,7 @@ Continue with resetting the current changes?"]
                set fd [git_read --stderr read-tree --reset -u -v HEAD]
                fconfigure $fd -blocking 0 -translation binary
                fileevent $fd readable [namespace code [list _reset_wait $fd]]
-               $::main_status start [mc "Aborting"] {files reset}
+               $::main_status start [mc "Aborting"] [mc "files reset"]
        } else {
                unlock_index
        }
index f812e5e89a1f21e2ee96a90e83958a472539bdd5..3bfa2edf1ad8e5abb48df360758177a594dab1a1 100644 (file)
@@ -5,6 +5,7 @@ proc save_config {} {
        global default_config font_descs
        global repo_config global_config
        global repo_config_new global_config_new
+       global ui_comm_spell
 
        foreach option $font_descs {
                set name [lindex $option 0]
@@ -52,11 +53,23 @@ proc save_config {} {
                        set repo_config($name) $value
                }
        }
+
+       if {[info exists repo_config(gui.spellingdictionary)]} {
+               set value $repo_config(gui.spellingdictionary)
+               if {$value eq {none}} {
+                       if {[info exists ui_comm_spell]} {
+                               $ui_comm_spell stop
+                       }
+               } elseif {[info exists ui_comm_spell]} {
+                       $ui_comm_spell lang $value
+               }
+       }
 }
 
 proc do_options {} {
        global repo_config global_config font_descs
        global repo_config_new global_config_new
+       global ui_comm_spell
 
        array unset repo_config_new
        array unset global_config_new
@@ -159,6 +172,34 @@ proc do_options {} {
                }
        }
 
+       set all_dicts [linsert \
+               [spellcheck::available_langs] \
+               0 \
+               none]
+       incr optid
+       foreach f {repo global} {
+               if {![info exists ${f}_config_new(gui.spellingdictionary)]} {
+                       if {[info exists ui_comm_spell]} {
+                               set value [$ui_comm_spell lang]
+                       } else {
+                               set value none
+                       }
+                       set ${f}_config_new(gui.spellingdictionary) $value
+               }
+
+               frame $w.$f.$optid
+               label $w.$f.$optid.l -text [mc "Spelling Dictionary:"]
+               eval tk_optionMenu $w.$f.$optid.v \
+                       ${f}_config_new(gui.spellingdictionary) \
+                       $all_dicts
+               pack $w.$f.$optid.l -side left -anchor w -fill x
+               pack $w.$f.$optid.v -side left -anchor w \
+                       -fill x -expand 1 \
+                       -padx 5
+               pack $w.$f.$optid -side top -anchor w -fill x
+       }
+       unset all_dicts
+
        set all_fonts [lsort [font families]]
        foreach option $font_descs {
                set name [lindex $option 0]
diff --git a/git-gui/lib/spellcheck.tcl b/git-gui/lib/spellcheck.tcl
new file mode 100644 (file)
index 0000000..01c2c4f
--- /dev/null
@@ -0,0 +1,363 @@
+# git-gui spellchecking support through aspell
+# Copyright (C) 2008 Shawn Pearce
+
+class spellcheck {
+
+field s_fd     {} ; # pipe to aspell
+field s_version   ; # aspell version string
+field s_lang      ; # current language code
+
+field w_text      ; # text widget we are spelling
+field w_menu      ; # context menu for the widget
+field s_menuidx 0 ; # last index of insertion into $w_menu
+
+field s_i              ; # timer registration for _run callbacks
+field s_clear        0 ; # did we erase mispelled tags yet?
+field s_seen    [list] ; # lines last seen from $w_text in _run
+field s_checked [list] ; # lines already checked
+field s_pending [list] ; # [$line $data] sent to aspell
+field s_suggest        ; # array, list of suggestions, keyed by misspelling
+
+constructor init {pipe_fd ui_text ui_menu} {
+       set w_text $ui_text
+       set w_menu $ui_menu
+
+       _connect $this $pipe_fd
+       return $this
+}
+
+method _connect {pipe_fd} {
+       fconfigure $pipe_fd \
+               -encoding utf-8 \
+               -eofchar {} \
+               -translation lf
+
+       if {[gets $pipe_fd s_version] <= 0} {
+               close $pipe_fd
+               error [mc "Not connected to aspell"]
+       }
+       if {{@(#) } ne [string range $s_version 0 4]} {
+               close $pipe_fd
+               error [strcat [mc "Unrecognized aspell version"] ": $s_version"]
+       }
+       set s_version [string range $s_version 5 end]
+
+       puts $pipe_fd !             ; # enable terse mode
+       puts $pipe_fd {$$cr master} ; # fetch the language
+       flush $pipe_fd
+
+       gets $pipe_fd s_lang
+       regexp {[/\\]([^/\\]+)\.[^\.]+$} $s_lang _ s_lang
+
+       if {$::default_config(gui.spellingdictionary) eq {}
+        && [get_config gui.spellingdictionary] eq {}} {
+               set ::default_config(gui.spellingdictionary) $s_lang
+       }
+
+       if {$s_fd ne {}} {
+               catch {close $s_fd}
+       }
+       set s_fd $pipe_fd
+
+       fconfigure $s_fd -blocking 0
+       fileevent $s_fd readable [cb _read]
+
+       $w_text tag conf misspelled \
+               -foreground red \
+               -underline 1
+       bind_button3 $w_text [cb _popup_suggest %X %Y @%x,%y]
+
+       array unset s_suggest
+       set s_seen    [list]
+       set s_checked [list]
+       set s_pending [list]
+       _run $this
+}
+
+method lang {{n {}}} {
+       if {$n ne {} && $s_lang ne $n} {
+               set spell_cmd [list |]
+               lappend spell_cmd aspell
+               lappend spell_cmd --master=$n
+               lappend spell_cmd --mode=none
+               lappend spell_cmd --encoding=UTF-8
+               lappend spell_cmd pipe
+               _connect $this [open $spell_cmd r+]
+       }
+       return $s_lang
+}
+
+method version {} {
+       return "$s_version, $s_lang"
+}
+
+method stop {} {
+       while {$s_menuidx > 0} {
+               $w_menu delete 0
+               incr s_menuidx -1
+       }
+       $w_text tag delete misspelled
+
+       catch {close $s_fd}
+       catch {after cancel $s_i}
+       set s_fd {}
+       set s_i {}
+       set s_lang {}
+}
+
+method _popup_suggest {X Y pos} {
+       while {$s_menuidx > 0} {
+               $w_menu delete 0
+               incr s_menuidx -1
+       }
+
+       set b_loc [$w_text index "$pos wordstart"]
+       set e_loc [_wordend $this $b_loc]
+       set orig  [$w_text get $b_loc $e_loc]
+       set tags  [$w_text tag names $b_loc]
+
+       if {[lsearch -exact $tags misspelled] >= 0} {
+               if {[info exists s_suggest($orig)]} {
+                       set cnt 0
+                       foreach s $s_suggest($orig) {
+                               if {$cnt < 5} {
+                                       $w_menu insert $s_menuidx command \
+                                               -label $s \
+                                               -command [cb _replace $b_loc $e_loc $s]
+                                       incr s_menuidx
+                                       incr cnt
+                               } else {
+                                       break
+                               }
+                       }
+               } else {
+                       $w_menu insert $s_menuidx command \
+                               -label [mc "No Suggestions"] \
+                               -state disabled
+                       incr s_menuidx
+               }
+               $w_menu insert $s_menuidx separator
+               incr s_menuidx
+       }
+
+       $w_text mark set saved-insert insert
+       tk_popup $w_menu $X $Y
+}
+
+method _replace {b_loc e_loc word} {
+       $w_text configure -autoseparators 0
+       $w_text edit separator
+
+       $w_text delete $b_loc $e_loc
+       $w_text insert $b_loc $word
+
+       $w_text edit separator
+       $w_text configure -autoseparators 1
+       $w_text mark set insert saved-insert
+}
+
+method _restart_timer {} {
+       set s_i [after 300 [cb _run]]
+}
+
+proc _match_length {max_line arr_name} {
+       upvar $arr_name a
+
+       if {[llength $a] > $max_line} {
+               set a [lrange $a 0 $max_line]
+       }
+       while {[llength $a] <= $max_line} {
+               lappend a {}
+       }
+}
+
+method _wordend {pos} {
+       set pos  [$w_text index "$pos wordend"]
+       set tags [$w_text tag names $pos]
+       while {[lsearch -exact $tags misspelled] >= 0} {
+               set pos  [$w_text index "$pos +1c"]
+               set tags [$w_text tag names $pos]
+       }
+       return $pos
+}
+
+method _run {} {
+       set cur_pos  [$w_text index {insert -1c}]
+       set cur_line [lindex [split $cur_pos .] 0]
+       set max_line [lindex [split [$w_text index end] .] 0]
+       _match_length $max_line s_seen
+       _match_length $max_line s_checked
+
+       # Nothing in the message buffer?  Nothing to spellcheck.
+       #
+       if {$cur_line == 1
+        && $max_line == 2
+        && [$w_text get 1.0 end] eq "\n"} {
+               array unset s_suggest
+               _restart_timer $this
+               return
+       }
+
+       set active 0
+       for {set n 1} {$n <= $max_line} {incr n} {
+               set s [$w_text get "$n.0" "$n.end"]
+
+               # Don't spellcheck the current line unless we are at
+               # a word boundary.  The user might be typing on it.
+               #
+               if {$n == $cur_line
+                && ![regexp {^\W$} [$w_text get $cur_pos insert]]} {
+
+                       # If the current word is mispelled remove the tag
+                       # but force a spellcheck later.
+                       #
+                       set tags [$w_text tag names $cur_pos]
+                       if {[lsearch -exact $tags misspelled] >= 0} {
+                               $w_text tag remove misspelled \
+                                       "$cur_pos wordstart" \
+                                       [_wordend $this $cur_pos]
+                               lset s_seen    $n $s
+                               lset s_checked $n {}
+                       }
+
+                       continue
+               }
+
+               if {[lindex $s_seen    $n] eq $s
+                && [lindex $s_checked $n] ne $s} {
+                       # Don't send empty lines to Aspell it doesn't check them.
+                       #
+                       if {$s eq {}} {
+                               lset s_checked $n $s
+                               continue
+                       }
+
+                       # Don't send typical s-b-o lines as the emails are
+                       # almost always misspelled according to Aspell.
+                       #
+                       if {[regexp -nocase {^[a-z-]+-by:.*<.*@.*>$} $s]} {
+                               $w_text tag remove misspelled "$n.0" "$n.end"
+                               lset s_checked $n $s
+                               continue
+                       }
+
+                       puts $s_fd ^$s
+                       lappend s_pending [list $n $s]
+                       set active 1
+               } else {
+                       # Delay until another idle loop to make sure we don't
+                       # spellcheck lines the user is actively changing.
+                       #
+                       lset s_seen $n $s
+               }
+       }
+
+       if {$active} {
+               set s_clear 1
+               flush $s_fd
+       } else {
+               _restart_timer $this
+       }
+}
+
+method _read {} {
+       while {[gets $s_fd line] >= 0} {
+               set lineno [lindex $s_pending 0 0]
+
+               if {$s_clear} {
+                       $w_text tag remove misspelled "$lineno.0" "$lineno.end"
+                       set s_clear 0
+               }
+
+               if {$line eq {}} {
+                       lset s_checked $lineno [lindex $s_pending 0 1]
+                       set s_pending [lrange $s_pending 1 end]
+                       set s_clear 1
+                       continue
+               }
+
+               set sugg [list]
+               switch -- [string range $line 0 1] {
+               {& } {
+                       set line [split [string range $line 2 end] :]
+                       set info [split [lindex $line 0] { }]
+                       set orig [lindex $info 0]
+                       set offs [lindex $info 2]
+                       foreach s [split [lindex $line 1] ,] {
+                               lappend sugg [string range $s 1 end]
+                       }
+               }
+               {# } {
+                       set info [split [string range $line 2 end] { }]
+                       set orig [lindex $info 0]
+                       set offs [lindex $info 1]
+               }
+               default {
+                       puts stderr "<spell> $line"
+                       continue
+               }
+               }
+
+               incr offs -1
+               set b_loc "$lineno.$offs"
+               set e_loc [$w_text index "$lineno.$offs wordend"]
+               set curr [$w_text get $b_loc $e_loc]
+
+               # At least for English curr = "bob", orig = "bob's"
+               # so Tk didn't include the 's but Aspell did.  We
+               # try to round out the word.
+               #
+               while {$curr ne $orig
+                && [string equal -length [llength $curr] $curr $orig]} {
+                       set n_loc  [$w_text index "$e_loc +1c"]
+                       set n_curr [$w_text get $b_loc $n_loc]
+                       if {$n_curr eq $curr} {
+                               break
+                       }
+                       set curr  $n_curr
+                       set e_loc $n_loc
+               }
+
+               if {$curr eq $orig} {
+                       $w_text tag add misspelled $b_loc $e_loc
+                       if {[llength $sugg] > 0} {
+                               set s_suggest($orig) $sugg
+                       } else {
+                               unset -nocomplain s_suggest($orig)
+                       }
+               } else {
+                       unset -nocomplain s_suggest($orig)
+               }
+       }
+
+       fconfigure $s_fd -block 1
+       if {[eof $s_fd]} {
+               if {![catch {close $s_fd} err]} {
+                       set err [mc "unexpected eof from aspell"]
+               }
+               catch {after cancel $s_i}
+               $w_text tag remove misspelled 1.0 end
+               error_popup [strcat "Spell Checker Failed" "\n\n" $err]
+               return
+       }
+       fconfigure $s_fd -block 0
+
+       if {[llength $s_pending] == 0} {
+               _restart_timer $this
+       }
+}
+
+proc available_langs {} {
+       set langs [list]
+       catch {
+               set fd [open [list | aspell dump dicts] r]
+               while {[gets $fd line] >= 0} {
+                       if {$line eq {}} continue
+                       lappend langs $line
+               }
+               close $fd
+       }
+       return $langs
+}
+
+}
index 99913ec57a346ad996fefa2d3f1f51be3265170b..b3bf15fa1c1a41265460664417caf47265553a4f 100644 (file)
@@ -5,7 +5,7 @@
        <key>CFBundleDevelopmentRegion</key>
        <string>English</string>
        <key>CFBundleExecutable</key>
-       <string>Wish</string>
+       <string>@@GITGUI_TKEXECUTABLE@@</string>
        <key>CFBundleGetInfoString</key>
        <string>Git Gui @@GITGUI_VERSION@@ Â© 2006-2007 Shawn Pearce, et. al.</string>
        <key>CFBundleIconFile</key>
index 2dfe07e06f7bbbdc1dd439431e8c56ecf9c60cae..d7c38f9c73fbeb887f8e0dcfe73af84d3edf8138 100644 (file)
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-24 10:36+0100\n"
-"PO-Revision-Date: 2008-01-15 20:33+0100\n"
+"POT-Creation-Date: 2008-02-02 10:14+0100\n"
+"PO-Revision-Date: 2008-02-02 10:18+0100\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
 "MIME-Version: 1.0\n"
@@ -343,7 +343,9 @@ msgstr "Online-Dokumentation"
 #: git-gui.sh:2201
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
-msgstr "Fehler: Verzeichnis Â»%s« kann nicht gelesen werden: Datei oder Verzeichnis nicht gefunden"
+msgstr ""
+"Fehler: Verzeichnis Â»%s« kann nicht gelesen werden: Datei oder Verzeichnis "
+"nicht gefunden"
 
 #: git-gui.sh:2234
 msgid "Current Branch:"
@@ -371,19 +373,19 @@ msgstr "Erste Versionsbeschreibung:"
 
 #: git-gui.sh:2370
 msgid "Amended Commit Message:"
-msgstr "Nachgebesserte Versionsbeschreibung:"
+msgstr "Nachgebesserte Beschreibung:"
 
 #: git-gui.sh:2371
 msgid "Amended Initial Commit Message:"
-msgstr "Nachgebesserte erste Versionsbeschreibung:"
+msgstr "Nachgebesserte erste Beschreibung:"
 
 #: git-gui.sh:2372
 msgid "Amended Merge Commit Message:"
-msgstr "Nachgebesserte Zusammenführungs-Versionsbeschreibung:"
+msgstr "Nachgebesserte Zusammenführungs-Beschreibung:"
 
 #: git-gui.sh:2373
 msgid "Merge Commit Message:"
-msgstr "Zusammenführungs-Versionsbeschreibung:"
+msgstr "Zusammenführungs-Beschreibung:"
 
 #: git-gui.sh:2374
 msgid "Commit Message:"
@@ -397,31 +399,31 @@ msgstr "Alle kopieren"
 msgid "File:"
 msgstr "Datei:"
 
-#: git-gui.sh:2545
-msgid "Refresh"
-msgstr "Aktualisieren"
-
-#: git-gui.sh:2566
+#: git-gui.sh:2573
 msgid "Apply/Reverse Hunk"
 msgstr "Kontext anwenden/umkehren"
 
-#: git-gui.sh:2572
-msgid "Decrease Font Size"
-msgstr "Schriftgröße verkleinern"
-
-#: git-gui.sh:2576
-msgid "Increase Font Size"
-msgstr "Schriftgröße vergrößern"
-
-#: git-gui.sh:2581
+#: git-gui.sh:2579
 msgid "Show Less Context"
 msgstr "Weniger Zeilen anzeigen"
 
-#: git-gui.sh:2588
+#: git-gui.sh:2586
 msgid "Show More Context"
 msgstr "Mehr Zeilen anzeigen"
 
-#: git-gui.sh:2602
+#: git-gui.sh:2594
+msgid "Refresh"
+msgstr "Aktualisieren"
+
+#: git-gui.sh:2615
+msgid "Decrease Font Size"
+msgstr "Schriftgröße verkleinern"
+
+#: git-gui.sh:2619
+msgid "Increase Font Size"
+msgstr "Schriftgröße vergrößern"
+
+#: git-gui.sh:2630
 msgid "Unstage Hunk From Commit"
 msgstr "Kontext aus Bereitstellung herausnehmen"
 
@@ -542,7 +544,7 @@ msgstr "Kopiert oder verschoben durch:"
 
 #: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19
 msgid "Checkout Branch"
-msgstr "Zweig umstellen"
+msgstr "Auf Zweig umstellen"
 
 #: lib/branch_checkout.tcl:23
 msgid "Checkout"
@@ -805,11 +807,15 @@ msgstr ""
 msgid "Updating working directory to '%s'..."
 msgstr "Arbeitskopie umstellen auf Â»%s«..."
 
+#: lib/checkout_op.tcl:323
+msgid "files checked out"
+msgstr "Dateien aktualisiert"
+
 #: lib/checkout_op.tcl:353
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
 msgstr ""
-"Zweig umstellen von Â»%s« abgebrochen (Zusammenführen der Dateien ist "
+"Auf Zweig Â»%s« umstellen abgebrochen (Zusammenführen der Dateien ist "
 "notwendig)."
 
 #: lib/checkout_op.tcl:354
@@ -1069,15 +1075,21 @@ msgstr "Für Objekt konnte kein Hardlink erstellt werden: %s"
 
 #: lib/choose_repository.tcl:847
 msgid "Cannot fetch branches and objects.  See console output for details."
-msgstr "Zweige und Objekte konnten nicht angefordert werden.  Kontrollieren Sie die Ausgaben auf der Konsole für weitere Angaben."
+msgstr ""
+"Zweige und Objekte konnten nicht angefordert werden.  Kontrollieren Sie die "
+"Ausgaben auf der Konsole für weitere Angaben."
 
 #: lib/choose_repository.tcl:858
 msgid "Cannot fetch tags.  See console output for details."
-msgstr "Markierungen konnten nicht angefordert werden.  Kontrollieren Sie die Ausgaben auf der Konsole für weitere Angaben."
+msgstr ""
+"Markierungen konnten nicht angefordert werden.  Kontrollieren Sie die "
+"Ausgaben auf der Konsole für weitere Angaben."
 
 #: lib/choose_repository.tcl:882
 msgid "Cannot determine HEAD.  See console output for details."
-msgstr "Die Zweigspitze (HEAD) konnte nicht gefunden werden.  Kontrollieren Sie die Ausgaben auf der Konsole für weitere Angaben."
+msgstr ""
+"Die Zweigspitze (HEAD) konnte nicht gefunden werden.  Kontrollieren Sie die "
+"Ausgaben auf der Konsole für weitere Angaben."
 
 #: lib/choose_repository.tcl:891
 #, tcl-format
@@ -1273,11 +1285,40 @@ msgstr ""
 "\n"
 "- Rest: Eine ausführliche Beschreibung, warum diese Ã„nderung hilfreich ist.\n"
 
-#: lib/commit.tcl:257
+#: lib/commit.tcl:207
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "Warning: Tcl/Tk unterstützt die Zeichencodierung Â»%s« nicht."
+
+#: lib/commit.tcl:221
+msgid "Calling pre-commit hook..."
+msgstr ""
+
+#: lib/commit.tcl:236
+msgid "Commit declined by pre-commit hook."
+msgstr ""
+
+#: lib/commit.tcl:259
+msgid "Calling commit-msg hook..."
+msgstr ""
+
+#: lib/commit.tcl:274
+msgid "Commit declined by commit-msg hook."
+msgstr ""
+
+#: lib/commit.tcl:287
+msgid "Committing changes..."
+msgstr "Änderungen eintragen..."
+
+#: lib/commit.tcl:303
 msgid "write-tree failed:"
 msgstr "write-tree fehlgeschlagen:"
 
-#: lib/commit.tcl:275
+#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+msgid "Commit failed."
+msgstr "Eintragen fehlgeschlagen."
+
+#: lib/commit.tcl:321
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "Version Â»%s« scheint beschädigt zu sein"
@@ -1301,12 +1342,7 @@ msgstr ""
 msgid "No changes to commit."
 msgstr "Keine Ã„nderungen, die eingetragen werden können."
 
-#: lib/commit.tcl:303
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "Warning: Tcl/Tk unterstützt die Zeichencodierung Â»%s« nicht."
-
-#: lib/commit.tcl:317
+#: lib/commit.tcl:347
 msgid "commit-tree failed:"
 msgstr "commit-tree fehlgeschlagen:"
 
@@ -1440,7 +1476,8 @@ msgstr "Fehler beim Laden des Vergleichs:"
 
 #: lib/diff.tcl:302
 msgid "Failed to unstage selected hunk."
-msgstr "Fehler beim Herausnehmen des gewählten Kontexts aus der Bereitstellung."
+msgstr ""
+"Fehler beim Herausnehmen des gewählten Kontexts aus der Bereitstellung."
 
 #: lib/diff.tcl:309
 msgid "Failed to stage selected hunk."
@@ -1471,7 +1508,10 @@ msgstr "Fehler in Bereitstellung"
 msgid ""
 "Updating the Git index failed.  A rescan will be automatically started to "
 "resynchronize git-gui."
-msgstr "Das Aktualisieren der Git-Bereitstellung ist fehlgeschlagen. Eine allgemeine Git-Aktualisierung wird jetzt gestartet, um git-gui wieder mit git zu synchronisieren."
+msgstr ""
+"Das Aktualisieren der Git-Bereitstellung ist fehlgeschlagen. Eine allgemeine "
+"Git-Aktualisierung wird jetzt gestartet, um git-gui wieder mit git zu "
+"synchronisieren."
 
 #: lib/index.tcl:27
 msgid "Continue"
@@ -1486,6 +1526,10 @@ msgstr "Bereitstellung freigeben"
 msgid "Unstaging %s from commit"
 msgstr "Datei Â»%s« aus der Bereitstellung herausnehmen"
 
+#: lib/index.tcl:313
+msgid "Ready to commit."
+msgstr "Bereit zum Eintragen."
+
 #: lib/index.tcl:326
 #, tcl-format
 msgid "Adding %s"
@@ -1503,7 +1547,8 @@ msgstr "Änderungen in den gewählten %i Dateien verwerfen?"
 
 #: lib/index.tcl:389
 msgid "Any unstaged changes will be permanently lost by the revert."
-msgstr "Alle nicht bereitgestellten Ã„nderungen werden beim Verwerfen verloren gehen."
+msgstr ""
+"Alle nicht bereitgestellten Ã„nderungen werden beim Verwerfen verloren gehen."
 
 #: lib/index.tcl:392
 msgid "Do Nothing"
@@ -1641,7 +1686,11 @@ msgstr ""
 msgid "Aborting"
 msgstr "Abbruch"
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:238
+msgid "files reset"
+msgstr "Dateien zurückgesetzt"
+
+#: lib/merge.tcl:265
 msgid "Abort failed."
 msgstr "Abbruch fehlgeschlagen."
 
index dfa48ae26364b5d30a810bfa9b55984bba582692..3f139da6c7aae3cdc81312289083c1752c6e2629 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-24 10:36+0100\n"
+"POT-Creation-Date: 2008-02-02 10:14+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -386,31 +386,31 @@ msgstr ""
 msgid "File:"
 msgstr ""
 
-#: git-gui.sh:2545
-msgid "Refresh"
+#: git-gui.sh:2573
+msgid "Apply/Reverse Hunk"
 msgstr ""
 
-#: git-gui.sh:2566
-msgid "Apply/Reverse Hunk"
+#: git-gui.sh:2579
+msgid "Show Less Context"
 msgstr ""
 
-#: git-gui.sh:2572
-msgid "Decrease Font Size"
+#: git-gui.sh:2586
+msgid "Show More Context"
 msgstr ""
 
-#: git-gui.sh:2576
-msgid "Increase Font Size"
+#: git-gui.sh:2594
+msgid "Refresh"
 msgstr ""
 
-#: git-gui.sh:2581
-msgid "Show Less Context"
+#: git-gui.sh:2615
+msgid "Decrease Font Size"
 msgstr ""
 
-#: git-gui.sh:2588
-msgid "Show More Context"
+#: git-gui.sh:2619
+msgid "Increase Font Size"
 msgstr ""
 
-#: git-gui.sh:2602
+#: git-gui.sh:2630
 msgid "Unstage Hunk From Commit"
 msgstr ""
 
@@ -766,6 +766,10 @@ msgstr ""
 msgid "Updating working directory to '%s'..."
 msgstr ""
 
+#: lib/checkout_op.tcl:323
+msgid "files checked out"
+msgstr ""
+
 #: lib/checkout_op.tcl:353
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
@@ -1182,11 +1186,40 @@ msgid ""
 "- Remaining lines: Describe why this change is good.\n"
 msgstr ""
 
-#: lib/commit.tcl:257
+#: lib/commit.tcl:207
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr ""
+
+#: lib/commit.tcl:221
+msgid "Calling pre-commit hook..."
+msgstr ""
+
+#: lib/commit.tcl:236
+msgid "Commit declined by pre-commit hook."
+msgstr ""
+
+#: lib/commit.tcl:259
+msgid "Calling commit-msg hook..."
+msgstr ""
+
+#: lib/commit.tcl:274
+msgid "Commit declined by commit-msg hook."
+msgstr ""
+
+#: lib/commit.tcl:287
+msgid "Committing changes..."
+msgstr ""
+
+#: lib/commit.tcl:303
 msgid "write-tree failed:"
 msgstr ""
 
-#: lib/commit.tcl:275
+#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+msgid "Commit failed."
+msgstr ""
+
+#: lib/commit.tcl:321
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr ""
@@ -1204,12 +1237,7 @@ msgstr ""
 msgid "No changes to commit."
 msgstr ""
 
-#: lib/commit.tcl:303
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr ""
-
-#: lib/commit.tcl:317
+#: lib/commit.tcl:347
 msgid "commit-tree failed:"
 msgstr ""
 
@@ -1373,6 +1401,10 @@ msgstr ""
 msgid "Unstaging %s from commit"
 msgstr ""
 
+#: lib/index.tcl:313
+msgid "Ready to commit."
+msgstr ""
+
 #: lib/index.tcl:326
 #, tcl-format
 msgid "Adding %s"
@@ -1491,7 +1523,11 @@ msgstr ""
 msgid "Aborting"
 msgstr ""
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:238
+msgid "files reset"
+msgstr ""
+
+#: lib/merge.tcl:265
 msgid "Abort failed."
 msgstr ""
 
index d13e4c1fea93f0c345f6638bfd8a3715c73fa693..5cd69513cf84111d1152d07f8cda77b201ffc416 100755 (executable)
@@ -1,5 +1,6 @@
 #!/usr/bin/perl -w
 
+use strict;
 use Git;
 my $git = Git->repository();
 
@@ -296,12 +297,13 @@ sub add_remote {
 
 sub update_remote {
        my ($name) = @_;
+       my @remotes;
 
         my $conf = $git->config("remotes." . $name);
        if (defined($conf)) {
                @remotes = split(' ', $conf);
        } elsif ($name eq 'default') {
-               undef @remotes;
+               @remotes = ();
                for (sort keys %$remote) {
                        my $do_fetch = $git->config_bool("remote." . $_ .
                                                    ".skipDefaultUpdate");
@@ -341,7 +343,7 @@ sub rm_remote {
        my @refs = $git->command('for-each-ref',
                '--format=%(refname) %(objectname)', "refs/remotes/$name");
        for (@refs) {
-               ($ref, $object) = split;
+               my ($ref, $object) = split;
                $git->command(qw(update-ref -d), $ref, $object);
        }
        return 0;
@@ -352,7 +354,7 @@ sub add_usage {
        exit(1);
 }
 
-local $VERBOSE = 0;
+my $VERBOSE = 0;
 @ARGV = grep {
        if ($_ eq '-v' or $_ eq '--verbose') {
                $VERBOSE=1;
@@ -395,7 +397,7 @@ elsif ($ARGV[0] eq 'update') {
                update_remote("default");
                exit(1);
        }
-       for ($i = 1; $i < @ARGV; $i++) {
+       for (my $i = 1; $i < @ARGV; $i++) {
                update_remote($ARGV[$i]);
        }
 }
index a1a9d14b00d01844509a87c598314e2ffbbfa47c..59601e36e854195c93f0a49bc3884567ed502595 100755 (executable)
@@ -24,8 +24,6 @@ use Data::Dumper;
 use Term::ANSIColor;
 use Git;
 
-$SIG{INT} = sub { print color("reset"), "\n"; exit };
-
 package FakeTerm;
 sub new {
        my ($class, $reason) = @_;
@@ -88,6 +86,12 @@ Options:
 
    --smtp-ssl     If set, connects to the SMTP server using SSL.
 
+   --suppress-cc  Suppress the specified category of auto-CC.  The category
+                 can be one of 'author' for the patch author, 'self' to
+                 avoid copying yourself, 'sob' for Signed-off-by lines,
+                 'cccmd' for the output of the cccmd, or 'all' to suppress
+                 all of these.
+
    --suppress-from Suppress sending emails to yourself. Defaults to off.
 
    --thread       Specify that the "In-Reply-To:" header should be set on all
@@ -157,7 +161,7 @@ my $compose_filename = ".msg.$$";
 
 # Variables we fill in automatically, or via prompting:
 my (@to,@cc,@initial_cc,@bcclist,@xh,
-       $initial_reply_to,$initial_subject,@files,$author,$sender,$compose,$time);
+       $initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time);
 
 my $envelope_sender;
 
@@ -177,15 +181,16 @@ my ($quiet, $dry_run) = (0, 0);
 
 # Variables with corresponding config settings
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd);
-my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_authpass, $smtp_ssl);
+my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_ssl);
 my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts);
 my ($no_validate);
+my (@suppress_cc);
 
 my %config_bool_settings = (
     "thread" => [\$thread, 1],
     "chainreplyto" => [\$chain_reply_to, 1],
-    "suppressfrom" => [\$suppress_from, 0],
-    "signedoffcc" => [\$signed_off_cc, 1],
+    "suppressfrom" => [\$suppress_from, undef],
+    "signedoffcc" => [\$signed_off_cc, undef],
     "smtpssl" => [\$smtp_ssl, 0],
 );
 
@@ -199,8 +204,32 @@ my %config_settings = (
     "aliasfiletype" => \$aliasfiletype,
     "bcc" => \@bcclist,
     "aliasesfile" => \@alias_files,
+    "suppresscc" => \@suppress_cc,
 );
 
+# Handle Uncouth Termination
+sub signal_handler {
+
+       # Make text normal
+       print color("reset"), "\n";
+
+       # SMTP password masked
+       system "stty echo";
+
+       # tmp files from --compose
+       if (-e $compose_filename) {
+               print "'$compose_filename' contains an intermediate version of the email you were composing.\n";
+       }
+       if (-e ($compose_filename . ".final")) {
+               print "'$compose_filename.final' contains the composed email.\n"
+       }
+
+       exit;
+};
+
+$SIG{TERM} = \&signal_handler;
+$SIG{INT}  = \&signal_handler;
+
 # Begin by accumulating all the variables (defined above), that we will end up
 # needing, first, from the command line:
 
@@ -214,13 +243,14 @@ my $rc = GetOptions("sender|from=s" => \$sender,
                    "smtp-server=s" => \$smtp_server,
                    "smtp-server-port=s" => \$smtp_server_port,
                    "smtp-user=s" => \$smtp_authuser,
-                   "smtp-pass=s" => \$smtp_authpass,
+                   "smtp-pass:s" => \$smtp_authpass,
                    "smtp-ssl!" => \$smtp_ssl,
                    "identity=s" => \$identity,
                    "compose" => \$compose,
                    "quiet" => \$quiet,
                    "cc-cmd=s" => \$cc_cmd,
                    "suppress-from!" => \$suppress_from,
+                   "suppress-cc=s" => \@suppress_cc,
                    "signed-off-cc|signed-off-by-cc!" => \$signed_off_cc,
                    "dry-run" => \$dry_run,
                    "envelope-sender=s" => \$envelope_sender,
@@ -266,6 +296,35 @@ foreach my $setting (values %config_bool_settings) {
        ${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]}));
 }
 
+# Set CC suppressions
+my(%suppress_cc);
+if (@suppress_cc) {
+       foreach my $entry (@suppress_cc) {
+               die "Unknown --suppress-cc field: '$entry'\n"
+                       unless $entry =~ /^(all|cccmd|cc|author|self|sob)$/;
+               $suppress_cc{$entry} = 1;
+       }
+}
+
+if ($suppress_cc{'all'}) {
+       foreach my $entry (qw (ccmd cc author self sob)) {
+               $suppress_cc{$entry} = 1;
+       }
+       delete $suppress_cc{'all'};
+}
+
+# If explicit old-style ones are specified, they trump --suppress-cc.
+$suppress_cc{'self'} = $suppress_from if defined $suppress_from;
+$suppress_cc{'sob'} = $signed_off_cc if defined $signed_off_cc;
+
+# Debugging, print out the suppressions.
+if (0) {
+       print "suppressions:\n";
+       foreach my $entry (keys %suppress_cc) {
+               printf "  %-5s -> $suppress_cc{$entry}\n", $entry;
+       }
+}
+
 my ($repoauthor) = $repo->ident_person('author');
 my ($repocommitter) = $repo->ident_person('committer');
 
@@ -355,9 +414,12 @@ if (@files) {
 my $prompting = 0;
 if (!defined $sender) {
        $sender = $repoauthor || $repocommitter;
-       do {
+
+       while (1) {
                $_ = $term->readline("Who should the emails appear to be from? [$sender] ");
-       } while (!defined $_);
+               last if defined $_;
+               print "\n";
+       }
 
        $sender = $_ if ($_);
        print "Emails will be sent from: ", $sender, "\n";
@@ -365,10 +427,14 @@ if (!defined $sender) {
 }
 
 if (!@to) {
-       do {
-               $_ = $term->readline("Who should the emails be sent to? ",
-                               "");
-       } while (!defined $_);
+
+
+       while (1) {
+               $_ = $term->readline("Who should the emails be sent to? ", "");
+               last if defined $_;
+               print "\n";
+       }
+
        my $to = $_;
        push @to, split /,/, $to;
        $prompting++;
@@ -390,19 +456,22 @@ sub expand_aliases {
 @bcclist = expand_aliases(@bcclist);
 
 if (!defined $initial_subject && $compose) {
-       do {
-               $_ = $term->readline("What subject should the initial email start with? ",
-                       $initial_subject);
-       } while (!defined $_);
+       while (1) {
+               $_ = $term->readline("What subject should the initial email start with? ", $initial_subject);
+               last if defined $_;
+               print "\n";
+       }
+
        $initial_subject = $_;
        $prompting++;
 }
 
 if ($thread && !defined $initial_reply_to && $prompting) {
-       do {
-               $_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ",
-                       $initial_reply_to);
-       } while (!defined $_);
+       while (1) {
+               $_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ", $initial_reply_to);
+               last if defined $_;
+               print "\n";
+       }
 
        $initial_reply_to = $_;
 }
@@ -453,9 +522,11 @@ EOT
        close(C);
        close(C2);
 
-       do {
+       while (1) {
                $_ = $term->readline("Send this email? (y|n) ");
-       } while (!defined $_);
+               last if defined $_;
+               print "\n";
+       }
 
        if (uc substr($_,0,1) ne 'Y') {
                cleanup_compose_files();
@@ -647,9 +718,26 @@ X-Mailer: git-send-email $gitversion
                        die "Unable to initialize SMTP properly.  Is there something wrong with your config?";
                }
 
-               if ((defined $smtp_authuser) && (defined $smtp_authpass)) {
+               if (defined $smtp_authuser) {
+
+                       if (!defined $smtp_authpass) {
+
+                               system "stty -echo";
+
+                               do {
+                                       print "Password: ";
+                                       $_ = <STDIN>;
+                                       print "\n";
+                               } while (!defined $_);
+
+                               chomp($smtp_authpass = $_);
+
+                               system "stty echo";
+                       }
+
                        $auth ||= $smtp->auth( $smtp_authuser, $smtp_authpass ) or die $smtp->message;
                }
+
                $smtp->mail( $raw_from ) or die $smtp->message;
                $smtp->to( @recipients ) or die $smtp->message;
                $smtp->data or die $smtp->message;
@@ -711,11 +799,14 @@ foreach my $t (@files) {
 
                                } elsif (/^(Cc|From):\s+(.*)$/) {
                                        if (unquote_rfc2047($2) eq $sender) {
-                                               next if ($suppress_from);
+                                               next if ($suppress_cc{'self'});
                                        }
                                        elsif ($1 eq 'From') {
                                                ($author, $author_encoding)
                                                  = unquote_rfc2047($2);
+                                               next if ($suppress_cc{'author'});
+                                       } else {
+                                               next if ($suppress_cc{'cc'});
                                        }
                                        printf("(mbox) Adding cc: %s from line '%s'\n",
                                                $2, $_) unless $quiet;
@@ -742,7 +833,7 @@ foreach my $t (@files) {
                                # line 2 = subject
                                # So let's support that, too.
                                $input_format = 'lots';
-                               if (@cc == 0) {
+                               if (@cc == 0 && !$suppress_cc{'cc'}) {
                                        printf("(non-mbox) Adding cc: %s from line '%s'\n",
                                                $_, $_) unless $quiet;
 
@@ -759,10 +850,11 @@ foreach my $t (@files) {
                        }
                } else {
                        $message .=  $_;
-                       if (/^(Signed-off-by|Cc): (.*)$/i && $signed_off_cc) {
+                       if (/^(Signed-off-by|Cc): (.*)$/i) {
+                               next if ($suppress_cc{'sob'});
                                my $c = $2;
                                chomp $c;
-                               next if ($c eq $sender and $suppress_from);
+                               next if ($c eq $sender and $suppress_cc{'self'});
                                push @cc, $c;
                                printf("(sob) Adding cc: %s from line '%s'\n",
                                        $c, $_) unless $quiet;
@@ -771,7 +863,7 @@ foreach my $t (@files) {
        }
        close F;
 
-       if (defined $cc_cmd) {
+       if (defined $cc_cmd && !$suppress_cc{'cccmd'}) {
                open(F, "$cc_cmd $t |")
                        or die "(cc-cmd) Could not execute '$cc_cmd'";
                while(<F>) {
index 75e97cc72fb7906f098717797eb1ac0303ef2a19..05fb3582d92e05e6b95ff8f249ae2ecb9892b45b 100755 (executable)
@@ -186,6 +186,9 @@ my %cmd = (
                    "Show info about the latest SVN revision
                     on the current branch",
                    { 'url' => \$_url, } ],
+       'blame' => [ \&Git::SVN::Log::cmd_blame,
+                   "Show what revision and author last modified each line of a file",
+                   {} ],
 );
 
 my $cmd;
@@ -1247,7 +1250,8 @@ use File::Path qw/mkpath/;
 use File::Copy qw/copy/;
 use IPC::Open3;
 
-my $_repack_nr;
+my ($_gc_nr, $_gc_period);
+
 # properties that we do not log:
 my %SKIP_PROP;
 BEGIN {
@@ -1408,9 +1412,10 @@ sub read_all_remotes {
 }
 
 sub init_vars {
-       $_repack = 1000 unless (defined $_repack && $_repack > 0);
-       $_repack_nr = $_repack;
-       $_repack_flags ||= '-d';
+       $_gc_nr = $_gc_period = 1000;
+       if (defined $_repack || defined $_repack_flags) {
+              warn "Repack options are obsolete; they have no effect.\n";
+       }
 }
 
 sub verify_remotes_sanity {
@@ -2096,6 +2101,10 @@ sub restore_commit_header_env {
        }
 }
 
+sub gc {
+       command_noisy('gc', '--auto');
+};
+
 sub do_git_commit {
        my ($self, $log_entry) = @_;
        my $lr = $self->last_rev;
@@ -2149,12 +2158,9 @@ sub do_git_commit {
                                   0, $self->svm_uuid);
        }
        print " = $commit ($self->{ref_id})\n";
-       if ($_repack && (--$_repack_nr == 0)) {
-               $_repack_nr = $_repack;
-               # repack doesn't use any arguments with spaces in them, does it?
-               print "Running git repack $_repack_flags ...\n";
-               command_noisy('repack', split(/\s+/, $_repack_flags));
-               print "Done repacking\n";
+       if (--$_gc_nr == 0) {
+               $_gc_nr = $_gc_period;
+               gc();
        }
        return $commit;
 }
@@ -2226,7 +2232,12 @@ sub find_parent_branch {
                # just grow a tail if we're not unique enough :x
                $ref_id .= '-' while find_ref($ref_id);
                print STDERR "Initializing parent: $ref_id\n";
-               $gs = Git::SVN->init($new_url, '', $ref_id, $ref_id, 1);
+               my ($u, $p) = ($new_url, '');
+               if ($u =~ s#^\Q$url\E(/|$)##) {
+                       $p = $u;
+                       $u = $url;
+               }
+               $gs = Git::SVN->init($u, $p, $self->{repo_id}, $ref_id, 1);
        }
        my ($r0, $parent) = $gs->find_rev_before($r, 1);
        if (!defined $r0 || !defined $parent) {
@@ -3983,6 +3994,7 @@ sub gs_fetch_loop_common {
                $max += $inc;
                $max = $head if ($max > $head);
        }
+       Git::SVN::gc();
 }
 
 sub match_globs {
@@ -4439,6 +4451,24 @@ out:
        print commit_log_separator unless $incremental || $oneline;
 }
 
+sub cmd_blame {
+       my $path = shift;
+
+       config_pager();
+       run_pager();
+
+       my ($fh, $ctx) = command_output_pipe('blame', @_, $path);
+       while (my $line = <$fh>) {
+               if ($line =~ /^\^?([[:xdigit:]]+)\s/) {
+                       my (undef, $rev, undef) = ::cmt_metadata($1);
+                       $rev = sprintf('%-10s', $rev);
+                       $line =~ s/^\^?[[:xdigit:]]+(\s)/$rev$1/;
+               }
+               print $line;
+       }
+       command_close_pipe($fh, $ctx);
+}
+
 package Git::SVN::Migration;
 # these version numbers do NOT correspond to actual version numbers
 # of git nor git-svn.  They are just relative.
index 5560e4dc560587c4a551c24a1ee59df394726fc9..25ab725bba48e62f34f8a2fab56eda82fc77be48 100644 (file)
@@ -240,11 +240,12 @@ proc getcommitlines {fd view}  {
        set listed 1
        if {$j >= 0 && [string match "commit *" $cmit]} {
            set ids [string range $cmit 7 [expr {$j - 1}]]
-           if {[string match {[-<>]*} $ids]} {
+           if {[string match {[-^<>]*} $ids]} {
                switch -- [string index $ids 0] {
                    "-" {set listed 0}
-                   "<" {set listed 2}
-                   ">" {set listed 3}
+                   "^" {set listed 2}
+                   "<" {set listed 3}
+                   ">" {set listed 4}
                }
                set ids [string range $ids 1 end]
            }
@@ -632,6 +633,7 @@ proc makewindow {} {
     global findtype findtypemenu findloc findstring fstring geometry
     global entries sha1entry sha1string sha1but
     global diffcontextstring diffcontext
+    global ignorespace
     global maincursor textcursor curtextcursor
     global rowctxmenu fakerowmenu mergemax wrapcomment
     global highlight_files gdttype
@@ -849,6 +851,9 @@ proc makewindow {} {
     trace add variable diffcontextstring write diffcontextchange
     lappend entries .bleft.mid.diffcontext
     pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext -side left
+    checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \
+       -command changeignorespace -variable ignorespace
+    pack .bleft.mid.ignspace -side left -padx 5
     set ctext .bleft.ctext
     text $ctext -background $bgcolor -foreground $fgcolor \
        -state disabled -font textfont \
@@ -1307,45 +1312,45 @@ proc keys {} {
     }
     toplevel $w
     wm title $w [mc "Gitk key bindings"]
-    message $w.m -text [mc "
-Gitk key bindings:
-
-<$M1T-Q>               Quit
-<Home>         Move to first commit
-<End>          Move to last commit
-<Up>, p, i     Move up one commit
-<Down>, n, k   Move down one commit
-<Left>, z, j   Go back in history list
-<Right>, x, l  Go forward in history list
-<PageUp>       Move up one page in commit list
-<PageDown>     Move down one page in commit list
-<$M1T-Home>    Scroll to top of commit list
-<$M1T-End>     Scroll to bottom of commit list
-<$M1T-Up>      Scroll commit list up one line
-<$M1T-Down>    Scroll commit list down one line
-<$M1T-PageUp>  Scroll commit list up one page
-<$M1T-PageDown>        Scroll commit list down one page
-<Shift-Up>     Find backwards (upwards, later commits)
-<Shift-Down>   Find forwards (downwards, earlier commits)
-<Delete>, b    Scroll diff view up one page
-<Backspace>    Scroll diff view up one page
-<Space>                Scroll diff view down one page
-u              Scroll diff view up 18 lines
-d              Scroll diff view down 18 lines
-<$M1T-F>               Find
-<$M1T-G>               Move to next find hit
-<Return>       Move to next find hit
-/              Move to next find hit, or redo find
-?              Move to previous find hit
-f              Scroll diff view to next file
-<$M1T-S>               Search for next hit in diff view
-<$M1T-R>               Search for previous hit in diff view
-<$M1T-KP+>     Increase font size
-<$M1T-plus>    Increase font size
-<$M1T-KP->     Decrease font size
-<$M1T-minus>   Decrease font size
-<F5>           Update
-"] \
+    message $w.m -text "
+[mc "Gitk key bindings:"]
+
+[mc "<%s-Q>            Quit" $M1T]
+[mc "<Home>            Move to first commit"]
+[mc "<End>             Move to last commit"]
+[mc "<Up>, p, i        Move up one commit"]
+[mc "<Down>, n, k      Move down one commit"]
+[mc "<Left>, z, j      Go back in history list"]
+[mc "<Right>, x, l     Go forward in history list"]
+[mc "<PageUp>  Move up one page in commit list"]
+[mc "<PageDown>        Move down one page in commit list"]
+[mc "<%s-Home> Scroll to top of commit list" $M1T]
+[mc "<%s-End>  Scroll to bottom of commit list" $M1T]
+[mc "<%s-Up>   Scroll commit list up one line" $M1T]
+[mc "<%s-Down> Scroll commit list down one line" $M1T]
+[mc "<%s-PageUp>       Scroll commit list up one page" $M1T]
+[mc "<%s-PageDown>     Scroll commit list down one page" $M1T]
+[mc "<Shift-Up>        Find backwards (upwards, later commits)"]
+[mc "<Shift-Down>      Find forwards (downwards, earlier commits)"]
+[mc "<Delete>, b       Scroll diff view up one page"]
+[mc "<Backspace>       Scroll diff view up one page"]
+[mc "<Space>           Scroll diff view down one page"]
+[mc "u         Scroll diff view up 18 lines"]
+[mc "d         Scroll diff view down 18 lines"]
+[mc "<%s-F>            Find" $M1T]
+[mc "<%s-G>            Move to next find hit" $M1T]
+[mc "<Return>  Move to next find hit"]
+[mc "/         Move to next find hit, or redo find"]
+[mc "?         Move to previous find hit"]
+[mc "f         Scroll diff view to next file"]
+[mc "<%s-S>            Search for next hit in diff view" $M1T]
+[mc "<%s-R>            Search for previous hit in diff view" $M1T]
+[mc "<%s-KP+>  Increase font size" $M1T]
+[mc "<%s-plus> Increase font size" $M1T]
+[mc "<%s-KP->  Decrease font size" $M1T]
+[mc "<%s-minus>        Decrease font size" $M1T]
+[mc "<F5>              Update"]
+" \
            -justify left -bg white -border 2 -relief groove
     pack $w.m -side top -fill both -padx 2 -pady 2
     button $w.ok -text [mc "Close"] -command "destroy $w" -default active
@@ -3627,23 +3632,23 @@ proc drawcmittext {id row col} {
     global linehtag linentag linedtag selectedline
     global canvxmax boldrows boldnamerows fgcolor nullid nullid2
 
-    # listed is 0 for boundary, 1 for normal, 2 for left, 3 for right
+    # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
     set listed [lindex $commitlisted $row]
     if {$id eq $nullid} {
        set ofill red
     } elseif {$id eq $nullid2} {
        set ofill green
     } else {
-       set ofill [expr {$listed != 0? "blue": "white"}]
+       set ofill [expr {$listed != 0 ? $listed == 2 ? "gray" : "blue" : "white"}]
     }
     set x [xc $row $col]
     set y [yc $row]
     set orad [expr {$linespc / 3}]
-    if {$listed <= 1} {
+    if {$listed <= 2} {
        set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
                   [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
                   -fill $ofill -outline $fgcolor -width 1 -tags circle]
-    } elseif {$listed == 2} {
+    } elseif {$listed == 3} {
        # triangle pointing left for left-side commits
        set t [$canv create polygon \
                   [expr {$x - $orad}] $y \
@@ -5270,13 +5275,21 @@ proc diffcontextchange {n1 n2 op} {
     }
 }
 
+proc changeignorespace {} {
+    reselectline
+}
+
 proc getblobdiffs {ids} {
     global blobdifffd diffids env
     global diffinhdr treediffs
     global diffcontext
+    global ignorespace
     global limitdiffs viewfiles curview
 
     set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
+    if {$ignorespace} {
+       append cmd " -w"
+    }
     if {$limitdiffs && $viewfiles($curview) ne {}} {
        set cmd [concat $cmd -- $viewfiles($curview)]
     }
@@ -6137,11 +6150,7 @@ proc domktag {} {
        return
     }
     if {[catch {
-       set dir [gitdir]
-       set fname [file join $dir "refs/tags" $tag]
-       set f [open $fname w]
-       puts $f $id
-       close $f
+       exec git tag $tag $id
     } err]} {
        error_popup "[mc "Error creating tag:"] $err"
        return
@@ -8459,6 +8468,7 @@ set bgcolor white
 set fgcolor black
 set diffcolors {red "#00a000" blue}
 set diffcontext 3
+set ignorespace 0
 set selectbgcolor gray85
 
 ## For msgcat loading, first locate the installation location.
index ae2d05763fc2d41c9bffdad8809e4e370389412c..5e88637b5e0e67143692bac336a57ab6cc1c0c38 100755 (executable)
@@ -1620,7 +1620,7 @@ sub git_get_project_url_list {
        my $path = shift;
 
        $git_dir = "$projectroot/$path";
-       open my $fd, "$projectroot/$path/cloneurl"
+       open my $fd, "$git_dir/cloneurl"
                or return wantarray ?
                @{ config_to_multi(git_get_project_config('url')) } :
                   config_to_multi(git_get_project_config('url'));
@@ -5565,7 +5565,7 @@ XML
                        or next;
 
                # print element (entry, item)
-               my $co_url = href(-full=>1, action=>"commit", hash=>$commit);
+               my $co_url = href(-full=>1, action=>"commitdiff", hash=>$commit);
                if ($format eq 'rss') {
                        print "<item>\n" .
                              "<title>" . esc_html($co{'title'}) . "</title>\n" .
index fa719cb0b1bd227423587c9e41eed77c755465a4..bbb700b54eab2a14f60e090231a196874ff3a9e0 100644 (file)
@@ -48,7 +48,7 @@ static int merge_entry(int pos, const char *path)
                        break;
                found++;
                strcpy(hexbuf[stage], sha1_to_hex(ce->sha1));
-               sprintf(ownbuf[stage], "%o", ntohl(ce->ce_mode));
+               sprintf(ownbuf[stage], "%o", ce->ce_mode);
                arguments[stage] = hexbuf[stage];
                arguments[stage + 4] = ownbuf[stage];
        } while (++pos < active_nr);
index 34e3167cafc3d09e1a2b32bc9a5c64b4de1e442d..dd52342539cd05e59bb13e956d51251417f21e3c 100644 (file)
@@ -333,7 +333,7 @@ static struct path_list *get_unmerged(void)
                        item->util = xcalloc(1, sizeof(struct stage_data));
                }
                e = item->util;
-               e->stages[ce_stage(ce)].mode = ntohl(ce->ce_mode);
+               e->stages[ce_stage(ce)].mode = ce->ce_mode;
                hashcpy(e->stages[ce_stage(ce)].sha, ce->sha1);
        }
 
index 5a5ebe27b0db0506dc1a6606d30e328c53275c18..50b6528001fe4bafdfe70126dc2078860c3d1969 100644 (file)
--- a/object.c
+++ b/object.c
@@ -140,7 +140,8 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
        if (type == OBJ_BLOB) {
                struct blob *blob = lookup_blob(sha1);
                if (blob) {
-                       parse_blob_buffer(blob, buffer, size);
+                       if (parse_blob_buffer(blob, buffer, size))
+                               return NULL;
                        obj = &blob->object;
                }
        } else if (type == OBJ_TREE) {
@@ -148,14 +149,16 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
                if (tree) {
                        obj = &tree->object;
                        if (!tree->object.parsed) {
-                               parse_tree_buffer(tree, buffer, size);
+                               if (parse_tree_buffer(tree, buffer, size))
+                                       return NULL;
                                eaten = 1;
                        }
                }
        } else if (type == OBJ_COMMIT) {
                struct commit *commit = lookup_commit(sha1);
                if (commit) {
-                       parse_commit_buffer(commit, buffer, size);
+                       if (parse_commit_buffer(commit, buffer, size))
+                               return NULL;
                        if (!commit->buffer) {
                                commit->buffer = buffer;
                                eaten = 1;
@@ -165,7 +168,8 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
        } else if (type == OBJ_TAG) {
                struct tag *tag = lookup_tag(sha1);
                if (tag) {
-                       parse_tag_buffer(tag, buffer, size);
+                       if (parse_tag_buffer(tag, buffer, size))
+                              return NULL;
                        obj = &tag->object;
                }
        } else {
index 6383401e2dd6bef3251a105ac136312dfdcdeb3c..00f289f2f470c0f4b95e0fcac043aa2e054d1ce6 100644 (file)
@@ -176,7 +176,7 @@ static void add_cache_refs(struct rev_info *revs)
                 * lookup_blob() on them, to avoid populating the hash table
                 * with invalid information
                 */
-               if (S_ISGITLINK(ntohl(active_cache[i]->ce_mode)))
+               if (S_ISGITLINK(active_cache[i]->ce_mode))
                        continue;
 
                lookup_blob(active_cache[i]->sha1);
index 7db55883d65fd28c2eaa291b5688273532988d88..e45f4b3d61c2982ca20c910a7fc11c5bd89204be 100644 (file)
 
 struct index_state the_index;
 
+static unsigned int hash_name(const char *name, int namelen)
+{
+       unsigned int hash = 0x123;
+
+       do {
+               unsigned char c = *name++;
+               hash = hash*101 + c;
+       } while (--namelen);
+       return hash;
+}
+
+static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
+{
+       void **pos;
+       unsigned int hash = hash_name(ce->name, ce_namelen(ce));
+
+       pos = insert_hash(hash, ce, &istate->name_hash);
+       if (pos) {
+               ce->next = *pos;
+               *pos = ce;
+       }
+}
+
+static void lazy_init_name_hash(struct index_state *istate)
+{
+       int nr;
+
+       if (istate->name_hash_initialized)
+               return;
+       for (nr = 0; nr < istate->cache_nr; nr++)
+               hash_index_entry(istate, istate->cache[nr]);
+       istate->name_hash_initialized = 1;
+}
+
+static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
+{
+       istate->cache[nr] = ce;
+       if (istate->name_hash_initialized)
+               hash_index_entry(istate, ce);
+}
+
+/*
+ * We don't actually *remove* it, we can just mark it invalid so that
+ * we won't find it in lookups.
+ *
+ * Not only would we have to search the lists (simple enough), but
+ * we'd also have to rehash other hash buckets in case this makes the
+ * hash bucket empty (common). So it's much better to just mark
+ * it.
+ */
+static void remove_hash_entry(struct index_state *istate, struct cache_entry *ce)
+{
+       ce->ce_flags |= CE_UNHASHED;
+}
+
+static void replace_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
+{
+       struct cache_entry *old = istate->cache[nr];
+
+       if (ce != old) {
+               remove_hash_entry(istate, old);
+               set_index_entry(istate, nr, ce);
+       }
+       istate->cache_changed = 1;
+}
+
+int index_name_exists(struct index_state *istate, const char *name, int namelen)
+{
+       unsigned int hash = hash_name(name, namelen);
+       struct cache_entry *ce;
+
+       lazy_init_name_hash(istate);
+       ce = lookup_hash(hash, &istate->name_hash);
+
+       while (ce) {
+               if (!(ce->ce_flags & CE_UNHASHED)) {
+                       if (!cache_name_compare(name, namelen, ce->name, ce->ce_flags))
+                               return 1;
+               }
+               ce = ce->next;
+       }
+       return 0;
+}
+
 /*
  * This only updates the "non-critical" parts of the directory
  * cache, ie the parts that aren't tracked by GIT, and only used
@@ -30,20 +114,19 @@ struct index_state the_index;
  */
 void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
 {
-       ce->ce_ctime.sec = htonl(st->st_ctime);
-       ce->ce_mtime.sec = htonl(st->st_mtime);
-#ifdef USE_NSEC
-       ce->ce_ctime.nsec = htonl(st->st_ctim.tv_nsec);
-       ce->ce_mtime.nsec = htonl(st->st_mtim.tv_nsec);
-#endif
-       ce->ce_dev = htonl(st->st_dev);
-       ce->ce_ino = htonl(st->st_ino);
-       ce->ce_uid = htonl(st->st_uid);
-       ce->ce_gid = htonl(st->st_gid);
-       ce->ce_size = htonl(st->st_size);
+       ce->ce_ctime = st->st_ctime;
+       ce->ce_mtime = st->st_mtime;
+       ce->ce_dev = st->st_dev;
+       ce->ce_ino = st->st_ino;
+       ce->ce_uid = st->st_uid;
+       ce->ce_gid = st->st_gid;
+       ce->ce_size = st->st_size;
 
        if (assume_unchanged)
-               ce->ce_flags |= htons(CE_VALID);
+               ce->ce_flags |= CE_VALID;
+
+       if (S_ISREG(st->st_mode))
+               ce_mark_uptodate(ce);
 }
 
 static int ce_compare_data(struct cache_entry *ce, struct stat *st)
@@ -116,7 +199,7 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
                        return DATA_CHANGED;
                break;
        case S_IFDIR:
-               if (S_ISGITLINK(ntohl(ce->ce_mode)))
+               if (S_ISGITLINK(ce->ce_mode))
                        return 0;
        default:
                return TYPE_CHANGED;
@@ -128,14 +211,17 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
 {
        unsigned int changed = 0;
 
-       switch (ntohl(ce->ce_mode) & S_IFMT) {
+       if (ce->ce_flags & CE_REMOVE)
+               return MODE_CHANGED | DATA_CHANGED | TYPE_CHANGED;
+
+       switch (ce->ce_mode & S_IFMT) {
        case S_IFREG:
                changed |= !S_ISREG(st->st_mode) ? TYPE_CHANGED : 0;
                /* We consider only the owner x bit to be relevant for
                 * "mode changes"
                 */
                if (trust_executable_bit &&
-                   (0100 & (ntohl(ce->ce_mode) ^ st->st_mode)))
+                   (0100 & (ce->ce_mode ^ st->st_mode)))
                        changed |= MODE_CHANGED;
                break;
        case S_IFLNK:
@@ -149,32 +235,18 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
                else if (ce_compare_gitlink(ce))
                        changed |= DATA_CHANGED;
                return changed;
-       case 0: /* Special case: unmerged file in index */
-               return MODE_CHANGED | DATA_CHANGED | TYPE_CHANGED;
        default:
-               die("internal error: ce_mode is %o", ntohl(ce->ce_mode));
+               die("internal error: ce_mode is %o", ce->ce_mode);
        }
-       if (ce->ce_mtime.sec != htonl(st->st_mtime))
+       if (ce->ce_mtime != (unsigned int) st->st_mtime)
                changed |= MTIME_CHANGED;
-       if (ce->ce_ctime.sec != htonl(st->st_ctime))
+       if (ce->ce_ctime != (unsigned int) st->st_ctime)
                changed |= CTIME_CHANGED;
 
-#ifdef USE_NSEC
-       /*
-        * nsec seems unreliable - not all filesystems support it, so
-        * as long as it is in the inode cache you get right nsec
-        * but after it gets flushed, you get zero nsec.
-        */
-       if (ce->ce_mtime.nsec != htonl(st->st_mtim.tv_nsec))
-               changed |= MTIME_CHANGED;
-       if (ce->ce_ctime.nsec != htonl(st->st_ctim.tv_nsec))
-               changed |= CTIME_CHANGED;
-#endif
-
-       if (ce->ce_uid != htonl(st->st_uid) ||
-           ce->ce_gid != htonl(st->st_gid))
+       if (ce->ce_uid != (unsigned int) st->st_uid ||
+           ce->ce_gid != (unsigned int) st->st_gid)
                changed |= OWNER_CHANGED;
-       if (ce->ce_ino != htonl(st->st_ino))
+       if (ce->ce_ino != (unsigned int) st->st_ino)
                changed |= INODE_CHANGED;
 
 #ifdef USE_STDEV
@@ -183,16 +255,22 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
         * clients will have different views of what "device"
         * the filesystem is on
         */
-       if (ce->ce_dev != htonl(st->st_dev))
+       if (ce->ce_dev != (unsigned int) st->st_dev)
                changed |= INODE_CHANGED;
 #endif
 
-       if (ce->ce_size != htonl(st->st_size))
+       if (ce->ce_size != (unsigned int) st->st_size)
                changed |= DATA_CHANGED;
 
        return changed;
 }
 
+static int is_racy_timestamp(struct index_state *istate, struct cache_entry *ce)
+{
+       return (istate->timestamp &&
+               ((unsigned int)istate->timestamp) <= ce->ce_mtime);
+}
+
 int ie_match_stat(struct index_state *istate,
                  struct cache_entry *ce, struct stat *st,
                  unsigned int options)
@@ -205,7 +283,7 @@ int ie_match_stat(struct index_state *istate,
         * If it's marked as always valid in the index, it's
         * valid whatever the checked-out copy says.
         */
-       if (!ignore_valid && (ce->ce_flags & htons(CE_VALID)))
+       if (!ignore_valid && (ce->ce_flags & CE_VALID))
                return 0;
 
        changed = ce_match_stat_basic(ce, st);
@@ -226,9 +304,7 @@ int ie_match_stat(struct index_state *istate,
         * whose mtime are the same as the index file timestamp more
         * carefully than others.
         */
-       if (!changed &&
-           istate->timestamp &&
-           istate->timestamp <= ntohl(ce->ce_mtime.sec)) {
+       if (!changed && is_racy_timestamp(istate, ce)) {
                if (assume_racy_is_modified)
                        changed |= DATA_CHANGED;
                else
@@ -257,7 +333,7 @@ int ie_modified(struct index_state *istate,
         * the length field is zero.  For other cases the ce_size
         * should match the SHA1 recorded in the index entry.
         */
-       if ((changed & DATA_CHANGED) && ce->ce_size != htonl(0))
+       if ((changed & DATA_CHANGED) && ce->ce_size != 0)
                return changed;
 
        changed_fs = ce_modified_check_fs(ce, st);
@@ -320,7 +396,7 @@ int index_name_pos(struct index_state *istate, const char *name, int namelen)
        while (last > first) {
                int next = (last + first) >> 1;
                struct cache_entry *ce = istate->cache[next];
-               int cmp = cache_name_compare(name, namelen, ce->name, ntohs(ce->ce_flags));
+               int cmp = cache_name_compare(name, namelen, ce->name, ce->ce_flags);
                if (!cmp)
                        return next;
                if (cmp < 0) {
@@ -335,6 +411,9 @@ int index_name_pos(struct index_state *istate, const char *name, int namelen)
 /* Remove entry, return true if there are more entries to go.. */
 int remove_index_entry_at(struct index_state *istate, int pos)
 {
+       struct cache_entry *ce = istate->cache[pos];
+
+       remove_hash_entry(istate, ce);
        istate->cache_changed = 1;
        istate->cache_nr--;
        if (pos >= istate->cache_nr)
@@ -405,7 +484,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
        size = cache_entry_size(namelen);
        ce = xcalloc(1, size);
        memcpy(ce->name, path, namelen);
-       ce->ce_flags = htons(namelen);
+       ce->ce_flags = namelen;
        fill_stat_cache_info(ce, &st);
 
        if (trust_executable_bit && has_symlinks)
@@ -427,6 +506,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
            !ie_match_stat(istate, istate->cache[pos], &st, ce_option)) {
                /* Nothing changed, really */
                free(ce);
+               ce_mark_uptodate(istate->cache[pos]);
                return 0;
        }
 
@@ -583,7 +663,7 @@ static int has_file_name(struct index_state *istate,
                        continue;
                if (p->name[len] != '/')
                        continue;
-               if (!ce_stage(p) && !p->ce_mode)
+               if (p->ce_flags & CE_REMOVE)
                        continue;
                retval = -1;
                if (!ok_to_replace)
@@ -616,7 +696,7 @@ static int has_dir_name(struct index_state *istate,
                }
                len = slash - name;
 
-               pos = index_name_pos(istate, name, ntohs(create_ce_flags(len, stage)));
+               pos = index_name_pos(istate, name, create_ce_flags(len, stage));
                if (pos >= 0) {
                        /*
                         * Found one, but not so fast.  This could
@@ -626,7 +706,7 @@ static int has_dir_name(struct index_state *istate,
                         * it is Ok to have a directory at the same
                         * path.
                         */
-                       if (stage || istate->cache[pos]->ce_mode) {
+                       if (!(istate->cache[pos]->ce_flags & CE_REMOVE)) {
                                retval = -1;
                                if (!ok_to_replace)
                                        break;
@@ -648,8 +728,9 @@ static int has_dir_name(struct index_state *istate,
                            (p->name[len] != '/') ||
                            memcmp(p->name, name, len))
                                break; /* not our subdirectory */
-                       if (ce_stage(p) == stage && (stage || p->ce_mode))
-                               /* p is at the same stage as our entry, and
+                       if (ce_stage(p) == stage && !(p->ce_flags & CE_REMOVE))
+                               /*
+                                * p is at the same stage as our entry, and
                                 * is a subdirectory of what we are looking
                                 * at, so we cannot have conflicts at our
                                 * level or anything shorter.
@@ -679,7 +760,7 @@ static int check_file_directory_conflict(struct index_state *istate,
        /*
         * When ce is an "I am going away" entry, we allow it to be added
         */
-       if (!ce_stage(ce) && !ce->ce_mode)
+       if (ce->ce_flags & CE_REMOVE)
                return 0;
 
        /*
@@ -704,12 +785,11 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
        int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
 
        cache_tree_invalidate_path(istate->cache_tree, ce->name);
-       pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags));
+       pos = index_name_pos(istate, ce->name, ce->ce_flags);
 
        /* existing match? Just replace it. */
        if (pos >= 0) {
-               istate->cache_changed = 1;
-               istate->cache[pos] = ce;
+               replace_index_entry(istate, pos, ce);
                return 0;
        }
        pos = -pos-1;
@@ -736,7 +816,7 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
                if (!ok_to_replace)
                        return error("'%s' appears as both a file and as a directory",
                                     ce->name);
-               pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags));
+               pos = index_name_pos(istate, ce->name, ce->ce_flags);
                pos = -pos-1;
        }
        return pos + 1;
@@ -769,7 +849,7 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
                memmove(istate->cache + pos + 1,
                        istate->cache + pos,
                        (istate->cache_nr - pos - 1) * sizeof(ce));
-       istate->cache[pos] = ce;
+       set_index_entry(istate, pos, ce);
        istate->cache_changed = 1;
        return 0;
 }
@@ -794,6 +874,9 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
        int changed, size;
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
 
+       if (ce_uptodate(ce))
+               return ce;
+
        if (lstat(ce->name, &st) < 0) {
                if (err)
                        *err = errno;
@@ -810,10 +893,17 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
                 * valid again, under "assume unchanged" mode.
                 */
                if (ignore_valid && assume_unchanged &&
-                   !(ce->ce_flags & htons(CE_VALID)))
+                   !(ce->ce_flags & CE_VALID))
                        ; /* mark this one VALID again */
-               else
+               else {
+                       /*
+                        * We do not mark the index itself "modified"
+                        * because CE_UPTODATE flag is in-core only;
+                        * we are not going to write this change out.
+                        */
+                       ce_mark_uptodate(ce);
                        return ce;
+               }
        }
 
        if (ie_modified(istate, ce, &st, options)) {
@@ -826,7 +916,6 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
        updated = xmalloc(size);
        memcpy(updated, ce, size);
        fill_stat_cache_info(updated, &st);
-
        /*
         * If ignore_valid is not set, we should leave CE_VALID bit
         * alone.  Otherwise, paths marked with --no-assume-unchanged
@@ -834,8 +923,8 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
         * automatically, which is not really what we want.
         */
        if (!ignore_valid && assume_unchanged &&
-           !(ce->ce_flags & htons(CE_VALID)))
-               updated->ce_flags &= ~htons(CE_VALID);
+           !(ce->ce_flags & CE_VALID))
+               updated->ce_flags &= ~CE_VALID;
 
        return updated;
 }
@@ -880,7 +969,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
                                /* If we are doing --really-refresh that
                                 * means the index is not valid anymore.
                                 */
-                               ce->ce_flags &= ~htons(CE_VALID);
+                               ce->ce_flags &= ~CE_VALID;
                                istate->cache_changed = 1;
                        }
                        if (quiet)
@@ -889,11 +978,8 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
                        has_errors = 1;
                        continue;
                }
-               istate->cache_changed = 1;
-               /* You can NOT just free istate->cache[i] here, since it
-                * might not be necessarily malloc()ed but can also come
-                * from mmap(). */
-               istate->cache[i] = new;
+
+               replace_index_entry(istate, i, new);
        }
        return has_errors;
 }
@@ -942,16 +1028,58 @@ int read_index(struct index_state *istate)
        return read_index_from(istate, get_index_file());
 }
 
+static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_entry *ce)
+{
+       size_t len;
+
+       ce->ce_ctime = ntohl(ondisk->ctime.sec);
+       ce->ce_mtime = ntohl(ondisk->mtime.sec);
+       ce->ce_dev   = ntohl(ondisk->dev);
+       ce->ce_ino   = ntohl(ondisk->ino);
+       ce->ce_mode  = ntohl(ondisk->mode);
+       ce->ce_uid   = ntohl(ondisk->uid);
+       ce->ce_gid   = ntohl(ondisk->gid);
+       ce->ce_size  = ntohl(ondisk->size);
+       /* On-disk flags are just 16 bits */
+       ce->ce_flags = ntohs(ondisk->flags);
+       hashcpy(ce->sha1, ondisk->sha1);
+
+       len = ce->ce_flags & CE_NAMEMASK;
+       if (len == CE_NAMEMASK)
+               len = strlen(ondisk->name);
+       /*
+        * NEEDSWORK: If the original index is crafted, this copy could
+        * go unchecked.
+        */
+       memcpy(ce->name, ondisk->name, len + 1);
+}
+
+static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
+{
+       long per_entry;
+
+       per_entry = sizeof(struct cache_entry) - sizeof(struct ondisk_cache_entry);
+
+       /*
+        * Alignment can cause differences. This should be "alignof", but
+        * since that's a gcc'ism, just use the size of a pointer.
+        */
+       per_entry += sizeof(void *);
+       return ondisk_size + entries*per_entry;
+}
+
 /* remember to discard_cache() before reading a different cache! */
 int read_index_from(struct index_state *istate, const char *path)
 {
        int fd, i;
        struct stat st;
-       unsigned long offset;
+       unsigned long src_offset, dst_offset;
        struct cache_header *hdr;
+       void *mmap;
+       size_t mmap_size;
 
        errno = EBUSY;
-       if (istate->mmap)
+       if (istate->alloc)
                return istate->cache_nr;
 
        errno = ENOENT;
@@ -967,31 +1095,47 @@ int read_index_from(struct index_state *istate, const char *path)
                die("cannot stat the open index (%s)", strerror(errno));
 
        errno = EINVAL;
-       istate->mmap_size = xsize_t(st.st_size);
-       if (istate->mmap_size < sizeof(struct cache_header) + 20)
+       mmap_size = xsize_t(st.st_size);
+       if (mmap_size < sizeof(struct cache_header) + 20)
                die("index file smaller than expected");
 
-       istate->mmap = xmmap(NULL, istate->mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+       mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
        close(fd);
+       if (mmap == MAP_FAILED)
+               die("unable to map index file");
 
-       hdr = istate->mmap;
-       if (verify_hdr(hdr, istate->mmap_size) < 0)
+       hdr = mmap;
+       if (verify_hdr(hdr, mmap_size) < 0)
                goto unmap;
 
        istate->cache_nr = ntohl(hdr->hdr_entries);
        istate->cache_alloc = alloc_nr(istate->cache_nr);
        istate->cache = xcalloc(istate->cache_alloc, sizeof(struct cache_entry *));
 
-       offset = sizeof(*hdr);
+       /*
+        * The disk format is actually larger than the in-memory format,
+        * due to space for nsec etc, so even though the in-memory one
+        * has room for a few  more flags, we can allocate using the same
+        * index size
+        */
+       istate->alloc = xmalloc(estimate_cache_size(mmap_size, istate->cache_nr));
+
+       src_offset = sizeof(*hdr);
+       dst_offset = 0;
        for (i = 0; i < istate->cache_nr; i++) {
+               struct ondisk_cache_entry *disk_ce;
                struct cache_entry *ce;
 
-               ce = (struct cache_entry *)((char *)(istate->mmap) + offset);
-               offset = offset + ce_size(ce);
-               istate->cache[i] = ce;
+               disk_ce = (struct ondisk_cache_entry *)((char *)mmap + src_offset);
+               ce = (struct cache_entry *)((char *)istate->alloc + dst_offset);
+               convert_from_disk(disk_ce, ce);
+               set_index_entry(istate, i, ce);
+
+               src_offset += ondisk_ce_size(ce);
+               dst_offset += ce_size(ce);
        }
        istate->timestamp = st.st_mtime;
-       while (offset <= istate->mmap_size - 20 - 8) {
+       while (src_offset <= mmap_size - 20 - 8) {
                /* After an array of active_nr index entries,
                 * there can be arbitrary number of extended
                 * sections, each of which is prefixed with
@@ -999,40 +1143,37 @@ int read_index_from(struct index_state *istate, const char *path)
                 * in 4-byte network byte order.
                 */
                unsigned long extsize;
-               memcpy(&extsize, (char *)(istate->mmap) + offset + 4, 4);
+               memcpy(&extsize, (char *)mmap + src_offset + 4, 4);
                extsize = ntohl(extsize);
                if (read_index_extension(istate,
-                                        ((const char *) (istate->mmap)) + offset,
-                                        (char *) (istate->mmap) + offset + 8,
+                                        (const char *) mmap + src_offset,
+                                        (char *) mmap + src_offset + 8,
                                         extsize) < 0)
                        goto unmap;
-               offset += 8;
-               offset += extsize;
+               src_offset += 8;
+               src_offset += extsize;
        }
+       munmap(mmap, mmap_size);
        return istate->cache_nr;
 
 unmap:
-       munmap(istate->mmap, istate->mmap_size);
+       munmap(mmap, mmap_size);
        errno = EINVAL;
        die("index file corrupt");
 }
 
 int discard_index(struct index_state *istate)
 {
-       int ret;
-
        istate->cache_nr = 0;
        istate->cache_changed = 0;
        istate->timestamp = 0;
+       free_hash(&istate->name_hash);
        cache_tree_free(&(istate->cache_tree));
-       if (istate->mmap == NULL)
-               return 0;
-       ret = munmap(istate->mmap, istate->mmap_size);
-       istate->mmap = NULL;
-       istate->mmap_size = 0;
+       free(istate->alloc);
+       istate->alloc = NULL;
 
        /* no need to throw away allocated active_cache */
-       return ret;
+       return 0;
 }
 
 #define WRITE_BUFFER_SIZE 8192
@@ -1144,10 +1285,32 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
                 * file, and never calls us, so the cached size information
                 * for "frotz" stays 6 which does not match the filesystem.
                 */
-               ce->ce_size = htonl(0);
+               ce->ce_size = 0;
        }
 }
 
+static int ce_write_entry(SHA_CTX *c, int fd, struct cache_entry *ce)
+{
+       int size = ondisk_ce_size(ce);
+       struct ondisk_cache_entry *ondisk = xcalloc(1, size);
+
+       ondisk->ctime.sec = htonl(ce->ce_ctime);
+       ondisk->ctime.nsec = 0;
+       ondisk->mtime.sec = htonl(ce->ce_mtime);
+       ondisk->mtime.nsec = 0;
+       ondisk->dev  = htonl(ce->ce_dev);
+       ondisk->ino  = htonl(ce->ce_ino);
+       ondisk->mode = htonl(ce->ce_mode);
+       ondisk->uid  = htonl(ce->ce_uid);
+       ondisk->gid  = htonl(ce->ce_gid);
+       ondisk->size = htonl(ce->ce_size);
+       hashcpy(ondisk->sha1, ce->sha1);
+       ondisk->flags = htons(ce->ce_flags);
+       memcpy(ondisk->name, ce->name, ce_namelen(ce));
+
+       return ce_write(c, fd, ondisk, size);
+}
+
 int write_index(struct index_state *istate, int newfd)
 {
        SHA_CTX c;
@@ -1157,7 +1320,7 @@ int write_index(struct index_state *istate, int newfd)
        int entries = istate->cache_nr;
 
        for (i = removed = 0; i < entries; i++)
-               if (!cache[i]->ce_mode)
+               if (cache[i]->ce_flags & CE_REMOVE)
                        removed++;
 
        hdr.hdr_signature = htonl(CACHE_SIGNATURE);
@@ -1170,12 +1333,11 @@ int write_index(struct index_state *istate, int newfd)
 
        for (i = 0; i < entries; i++) {
                struct cache_entry *ce = cache[i];
-               if (!ce->ce_mode)
+               if (ce->ce_flags & CE_REMOVE)
                        continue;
-               if (istate->timestamp &&
-                   istate->timestamp <= ntohl(ce->ce_mtime.sec))
+               if (is_racy_timestamp(istate, ce))
                        ce_smudge_racily_clean_entry(ce);
-               if (ce_write(&c, newfd, ce, ce_size(ce)) < 0)
+               if (ce_write_entry(&c, newfd, ce) < 0)
                        return -1;
        }
 
index 13e11645e1fd6b61812cc3e88d2ad6cd42cad9ce..be8489e4e5fc98c6e38036a40fb5ca8c0db77ebc 100644 (file)
@@ -695,7 +695,7 @@ int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode)
                                break;
                        if (ce_stage(ce) == stage) {
                                hashcpy(sha1, ce->sha1);
-                               *mode = ntohl(ce->ce_mode);
+                               *mode = ce->ce_mode;
                                return 0;
                        }
                        pos++;
diff --git a/t/.gitattributes b/t/.gitattributes
new file mode 100644 (file)
index 0000000..562b12e
--- /dev/null
@@ -0,0 +1 @@
+* -whitespace
index 36f251761739c6cda0053bbe26cc6331ed7be40c..73ed11bfe24edf56879d90a43c8df0813332fb08 100644 (file)
--- a/t/README
+++ b/t/README
@@ -160,14 +160,12 @@ library for your script to use.
 
  - test_expect_failure <message> <script>
 
-   This is the opposite of test_expect_success.  If <script>
-   yields success, test is considered a failure.
-
-   Example:
-
-       test_expect_failure \
-           'git-update-index without --add should fail adding.' \
-           'git-update-index should-be-empty'
+   This is NOT the opposite of test_expect_success, but is used
+   to mark a test that demonstrates a known breakage.  Unlike
+   the usual test_expect_success tests, which say "ok" on
+   success and "FAIL" on failure, this will say "FIXED" on
+   success and "still broken" on failure.  Failures from these
+   tests won't cause -i (immediate) to stop.
 
  - test_debug <script>
 
index 4e49d590651363631a1f3a795d3c1679a70fb05f..92de08822702f780c6895a5d2c070ee009ecfef7 100755 (executable)
@@ -46,13 +46,25 @@ test_expect_success \
     '.git/objects should have 3 subdirectories.' \
     'test $(wc -l < full-of-directories) = 3'
 
+################################################################
+# Test harness
+test_expect_success 'success is reported like this' '
+    :
+'
+test_expect_failure 'pretend we have a known breakage' '
+    false
+'
+test_expect_failure 'pretend we have fixed a known breakage' '
+    :
+'
+
 ################################################################
 # Basics of the basics
 
 # updating a new file without --add should fail.
-test_expect_failure \
-    'git update-index without --add should fail adding.' \
-    'git update-index should-be-empty'
+test_expect_success 'git update-index without --add should fail adding.' '
+    ! git update-index should-be-empty
+'
 
 # and with --add it should succeed, even if it is empty (it used to fail).
 test_expect_success \
@@ -70,9 +82,9 @@ test_expect_success \
 
 # Removing paths.
 rm -f should-be-empty full-of-directories
-test_expect_failure \
-    'git update-index without --remove should fail removing.' \
-    'git update-index should-be-empty'
+test_expect_success 'git update-index without --remove should fail removing.' '
+    ! git update-index should-be-empty
+'
 
 test_expect_success \
     'git update-index with --remove should be able to remove.' \
@@ -204,9 +216,9 @@ test_expect_success \
     'put invalid objects into the index.' \
     'git update-index --index-info < badobjects'
 
-test_expect_failure \
-    'writing this tree without --missing-ok.' \
-    'git write-tree'
+test_expect_success 'writing this tree without --missing-ok.' '
+    ! git write-tree
+'
 
 test_expect_success \
     'writing this tree with --missing-ok.' \
@@ -297,4 +309,24 @@ test_expect_success 'absolute path works as expected' '
        test "$sym" = "$(test-absolute-path $dir2/syml)"
 '
 
+test_expect_success 'very long name in the index handled sanely' '
+
+       a=a && # 1
+       a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 16
+       a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 256
+       a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 4096
+       a=${a}q &&
+
+       >path4 &&
+       git update-index --add path4 &&
+       (
+               git ls-files -s path4 |
+               sed -e "s/      .*/     /" |
+               tr -d "\012"
+               echo "$a"
+       ) | git update-index --index-info &&
+       len=$(git ls-files "a*" | wc -c) &&
+       test $len = 4098
+'
+
 test_done
index cad95f35adad5864e99ef5cd1633c820ff25b6c0..818c8621f239dd7f61a2d79e9c65779dbbf9f6ef 100755 (executable)
@@ -243,14 +243,14 @@ test_expect_success \
     test `printf "$ttt$sss$sss$sss" | git stripspace | wc -l` -gt 0
 '
 
-test_expect_failure \
+test_expect_success \
     'text plus spaces without newline at end should not show spaces' '
-    printf "$ttt$sss" | git stripspace | grep -q "  " ||
-    printf "$ttt$ttt$sss" | git stripspace | grep -q "  " ||
-    printf "$ttt$ttt$ttt$sss" | git stripspace | grep -q "  " ||
-    printf "$ttt$sss$sss" | git stripspace | grep -q "  " ||
-    printf "$ttt$ttt$sss$sss" | git stripspace | grep -q "  " ||
-    printf "$ttt$sss$sss$sss" | git stripspace | grep -q "  "
+    ! (printf "$ttt$sss" | git stripspace | grep -q "  ") &&
+    ! (printf "$ttt$ttt$sss" | git stripspace | grep -q "  ") &&
+    ! (printf "$ttt$ttt$ttt$sss" | git stripspace | grep -q "  ") &&
+    ! (printf "$ttt$sss$sss" | git stripspace | grep -q "  ") &&
+    ! (printf "$ttt$ttt$sss$sss" | git stripspace | grep -q "  ") &&
+    ! (printf "$ttt$sss$sss$sss" | git stripspace | grep -q "  ")
 '
 
 test_expect_success \
@@ -280,14 +280,14 @@ test_expect_success \
     git diff expect actual
 '
 
-test_expect_failure \
+test_expect_success \
     'text plus spaces at end should not show spaces' '
-    echo "$ttt$sss" | git stripspace | grep -q "  " ||
-    echo "$ttt$ttt$sss" | git stripspace | grep -q "  " ||
-    echo "$ttt$ttt$ttt$sss" | git stripspace | grep -q "  " ||
-    echo "$ttt$sss$sss" | git stripspace | grep -q "  " ||
-    echo "$ttt$ttt$sss$sss" | git stripspace | grep -q "  " ||
-    echo "$ttt$sss$sss$sss" | git stripspace | grep -q "  "
+    ! (echo "$ttt$sss" | git stripspace | grep -q "  ") &&
+    ! (echo "$ttt$ttt$sss" | git stripspace | grep -q "  ") &&
+    ! (echo "$ttt$ttt$ttt$sss" | git stripspace | grep -q "  ") &&
+    ! (echo "$ttt$sss$sss" | git stripspace | grep -q "  ") &&
+    ! (echo "$ttt$ttt$sss$sss" | git stripspace | grep -q "  ") &&
+    ! (echo "$ttt$sss$sss$sss" | git stripspace | grep -q "  ")
 '
 
 test_expect_success \
@@ -339,13 +339,13 @@ test_expect_success \
     git diff expect actual
 '
 
-test_expect_failure \
+test_expect_success \
     'spaces without newline at end should not show spaces' '
-    printf "" | git stripspace | grep -q " " ||
-    printf "$sss" | git stripspace | grep -q " " ||
-    printf "$sss$sss" | git stripspace | grep -q " " ||
-    printf "$sss$sss$sss" | git stripspace | grep -q " " ||
-    printf "$sss$sss$sss$sss" | git stripspace | grep -q " "
+    ! (printf "" | git stripspace | grep -q " ") &&
+    ! (printf "$sss" | git stripspace | grep -q " ") &&
+    ! (printf "$sss$sss" | git stripspace | grep -q " ") &&
+    ! (printf "$sss$sss$sss" | git stripspace | grep -q " ") &&
+    ! (printf "$sss$sss$sss$sss" | git stripspace | grep -q " ")
 '
 
 test_expect_success \
index 0a3b55d1212ddeda87f1f3ffa4c4e2fbefb44553..0e2933a984fcf6a23b60e54ed43c411c699d4347 100755 (executable)
@@ -87,9 +87,9 @@ test_expect_success 'unambiguously abbreviated option with "="' '
        git diff expect output
 '
 
-test_expect_failure 'ambiguously abbreviated option' '
+test_expect_success 'ambiguously abbreviated option' '
        test-parse-options --strin 123;
-        test $? != 129
+       test $? = 129
 '
 
 cat > expect << EOF
index 37add1b50472e23ccb6b938ac6cdadba0c097fb8..6c065bfa21b68e23fb92fbb25aea0907cb22aa3e 100755 (executable)
@@ -210,12 +210,12 @@ DF (file) when tree B require DF to be a directory by having DF/DF
 
 END_OF_CASE_TABLE
 
-test_expect_failure \
-    '1 - must not have an entry not in A.' \
-    "rm -f .git/index XX &&
+test_expect_success '1 - must not have an entry not in A.' "
+     rm -f .git/index XX &&
      echo XX >XX &&
      git update-index --add XX &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '2 - must match B in !O && !A && B case.' \
@@ -248,13 +248,14 @@ test_expect_success \
      echo extra >>AN &&
      git read-tree -m $tree_O $tree_A $tree_B"
 
-test_expect_failure \
-    '3 (fail) - must match A in !O && A && !B case.' \
-    "rm -f .git/index AN &&
+test_expect_success \
+    '3 (fail) - must match A in !O && A && !B case.' "
+     rm -f .git/index AN &&
      cp .orig-A/AN AN &&
      echo extra >>AN &&
      git update-index --add AN &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '4 - must match and be up-to-date in !O && A && B && A!=B case.' \
@@ -264,21 +265,23 @@ test_expect_success \
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' \
-    "rm -f .git/index AA &&
+test_expect_success \
+    '4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' "
+     rm -f .git/index AA &&
      cp .orig-A/AA AA &&
      git update-index --add AA &&
      echo extra >>AA &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' \
-    "rm -f .git/index AA &&
+test_expect_success \
+    '4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' "
+     rm -f .git/index AA &&
      cp .orig-A/AA AA &&
      echo extra >>AA &&
      git update-index --add AA &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '5 - must match in !O && A && B && A==B case.' \
@@ -297,34 +300,38 @@ test_expect_success \
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '5 (fail) - must match A in !O && A && B && A==B case.' \
-    "rm -f .git/index LL &&
+test_expect_success \
+    '5 (fail) - must match A in !O && A && B && A==B case.' "
+     rm -f .git/index LL &&
      cp .orig-A/LL LL &&
      echo extra >>LL &&
      git update-index --add LL &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '6 - must not exist in O && !A && !B case' \
-    "rm -f .git/index DD &&
+test_expect_success \
+    '6 - must not exist in O && !A && !B case' "
+     rm -f .git/index DD &&
      echo DD >DD
      git update-index --add DD &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '7 - must not exist in O && !A && B && O!=B case' \
-    "rm -f .git/index DM &&
+test_expect_success \
+    '7 - must not exist in O && !A && B && O!=B case' "
+     rm -f .git/index DM &&
      cp .orig-B/DM DM &&
      git update-index --add DM &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '8 - must not exist in O && !A && B && O==B case' \
-    "rm -f .git/index DN &&
+test_expect_success \
+    '8 - must not exist in O && !A && B && O==B case' "
+     rm -f .git/index DN &&
      cp .orig-B/DN DN &&
      git update-index --add DN &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '9 - must match and be up-to-date in O && A && !B && O!=A case' \
@@ -334,21 +341,23 @@ test_expect_success \
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' \
-    "rm -f .git/index MD &&
+test_expect_success \
+    '9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' "
+     rm -f .git/index MD &&
      cp .orig-A/MD MD &&
      git update-index --add MD &&
      echo extra >>MD &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' \
-    "rm -f .git/index MD &&
+test_expect_success \
+    '9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' "
+     rm -f .git/index MD &&
      cp .orig-A/MD MD &&
      echo extra >>MD &&
      git update-index --add MD &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '10 - must match and be up-to-date in O && A && !B && O==A case' \
@@ -358,21 +367,23 @@ test_expect_success \
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '10 (fail) - must match and be up-to-date in O && A && !B && O==A case' \
-    "rm -f .git/index ND &&
+test_expect_success \
+    '10 (fail) - must match and be up-to-date in O && A && !B && O==A case' "
+     rm -f .git/index ND &&
      cp .orig-A/ND ND &&
      git update-index --add ND &&
      echo extra >>ND &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '10 (fail) - must match and be up-to-date in O && A && !B && O==A case' \
-    "rm -f .git/index ND &&
+test_expect_success \
+    '10 (fail) - must match and be up-to-date in O && A && !B && O==A case' "
+     rm -f .git/index ND &&
      cp .orig-A/ND ND &&
      echo extra >>ND &&
      git update-index --add ND &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '11 - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' \
@@ -382,21 +393,23 @@ test_expect_success \
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' \
-    "rm -f .git/index MM &&
+test_expect_success \
+    '11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' "
+     rm -f .git/index MM &&
      cp .orig-A/MM MM &&
      git update-index --add MM &&
      echo extra >>MM &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' \
-    "rm -f .git/index MM &&
+test_expect_success \
+    '11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' "
+     rm -f .git/index MM &&
      cp .orig-A/MM MM &&
      echo extra >>MM &&
      git update-index --add MM &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '12 - must match A in O && A && B && O!=A && A==B case' \
@@ -415,13 +428,14 @@ test_expect_success \
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '12 (fail) - must match A in O && A && B && O!=A && A==B case' \
-    "rm -f .git/index SS &&
+test_expect_success \
+    '12 (fail) - must match A in O && A && B && O!=A && A==B case' "
+     rm -f .git/index SS &&
      cp .orig-A/SS SS &&
      echo extra >>SS &&
      git update-index --add SS &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '13 - must match A in O && A && B && O!=A && O==B case' \
@@ -457,21 +471,23 @@ test_expect_success \
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' \
-    "rm -f .git/index NM &&
+test_expect_success \
+    '14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' "
+     rm -f .git/index NM &&
      cp .orig-A/NM NM &&
      git update-index --add NM &&
      echo extra >>NM &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' \
-    "rm -f .git/index NM &&
+test_expect_success \
+    '14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' "
+     rm -f .git/index NM &&
      cp .orig-A/NM NM &&
      echo extra >>NM &&
      git update-index --add NM &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '15 - must match A in O && A && B && O==A && O==B case' \
@@ -490,13 +506,14 @@ test_expect_success \
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '15 (fail) - must match A in O && A && B && O==A && O==B case' \
-    "rm -f .git/index NN &&
+test_expect_success \
+    '15 (fail) - must match A in O && A && B && O==A && O==B case' "
+     rm -f .git/index NN &&
      cp .orig-A/NN NN &&
      echo extra >>NN &&
      git update-index --add NN &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     ! git read-tree -m $tree_O $tree_A $tree_B
+"
 
 # #16
 test_expect_success \
index 991d3c5e9c5c8dc9e59b0105010f1b77d4bf3a3f..dcb3108c290813dae178394011bb2b44f2f7ca9e 100755 (executable)
@@ -101,8 +101,8 @@ echo "Play, play, play" >>hello
 echo "Lots of fun" >>example
 git commit -m 'Some fun.' -i hello example
 
-test_expect_failure 'git resolve now fails' '
-       git merge -m "Merge work in mybranch" mybranch
+test_expect_success 'git resolve now fails' '
+       git merge -m "Merge work in mybranch" mybranch
 '
 
 cat > hello << EOF
@@ -156,6 +156,8 @@ test_expect_success 'git show-branch' 'cmp show-branch2.expect show-branch2.outp
 
 test_expect_success 'git repack' 'git repack'
 test_expect_success 'git prune-packed' 'git prune-packed'
-test_expect_failure '-> only packed objects' 'find -type f .git/objects/[0-9a-f][0-9a-f]'
+test_expect_success '-> only packed objects' '
+       ! find -type f .git/objects/[0-9a-f][0-9a-f]
+'
 
 test_done
index d9e358e1b4d7d64c2347fe0aab4a0b736f82fcf3..4928a571144b3fb9ec38312d917e9f95e40ceb99 100755 (executable)
@@ -200,8 +200,9 @@ test_expect_success 'non-match' \
 test_expect_success 'non-match value' \
        'test wow = $(git config --get nextsection.nonewline !for)'
 
-test_expect_failure 'ambiguous get' \
-       'git config --get nextsection.nonewline'
+test_expect_success 'ambiguous get' '
+       ! git config --get nextsection.nonewline
+'
 
 test_expect_success 'get multivar' \
        'git config --get-all nextsection.nonewline'
@@ -221,13 +222,17 @@ EOF
 
 test_expect_success 'multivar replace' 'cmp .git/config expect'
 
-test_expect_failure 'ambiguous value' 'git config nextsection.nonewline'
+test_expect_success 'ambiguous value' '
+       ! git config nextsection.nonewline
+'
 
-test_expect_failure 'ambiguous unset' \
-       'git config --unset nextsection.nonewline'
+test_expect_success 'ambiguous unset' '
+       ! git config --unset nextsection.nonewline
+'
 
-test_expect_failure 'invalid unset' \
-       'git config --unset somesection.nonewline'
+test_expect_success 'invalid unset' '
+       ! git config --unset somesection.nonewline
+'
 
 git config --unset nextsection.nonewline "wow3$"
 
@@ -243,7 +248,7 @@ EOF
 
 test_expect_success 'multivar unset' 'cmp .git/config expect'
 
-test_expect_failure 'invalid key' 'git config inval.2key blabla'
+test_expect_success 'invalid key' '! git config inval.2key blabla'
 
 test_expect_success 'correct key' 'git config 123456.a123 987'
 
@@ -424,8 +429,9 @@ EOF
 
 test_expect_success "rename succeeded" "git diff expect .git/config"
 
-test_expect_failure "rename non-existing section" \
-       'git config --rename-section branch."world domination" branch.drei'
+test_expect_success "rename non-existing section" '
+       ! git config --rename-section branch."world domination" branch.drei
+'
 
 test_expect_success "rename succeeded" "git diff expect .git/config"
 
@@ -536,14 +542,14 @@ test_expect_success bool '
         done &&
        cmp expect result'
 
-test_expect_failure 'invalid bool (--get)' '
+test_expect_success 'invalid bool (--get)' '
 
        git config bool.nobool foobar &&
-       git config --bool --get bool.nobool'
+       git config --bool --get bool.nobool'
 
-test_expect_failure 'invalid bool (set)' '
+test_expect_success 'invalid bool (set)' '
 
-       git config --bool bool.nobool foobar'
+       git config --bool bool.nobool foobar'
 
 rm .git/config
 
@@ -604,8 +610,9 @@ EOF
 
 test_expect_success 'quoting' 'cmp .git/config expect'
 
-test_expect_failure 'key with newline' 'git config key.with\\\
-newline 123'
+test_expect_success 'key with newline' '
+       ! git config "key.with
+newline" 123'
 
 test_expect_success 'value with newline' 'git config key.sub value.with\\\
 newline'
index 37fc1c8d36ba6e0a12c83d0c38c87a37863870e5..9be0770e7627ee094349af85b0d4702e156ff6cd 100755 (executable)
@@ -40,7 +40,8 @@ test_expect_success 'gitdir required mode on normal repos' '
        (git apply --check --index test.patch &&
        cd test && git apply --check --index ../test.patch)'
 
-test_expect_failure 'gitdir required mode on unsupported repo' '
-       (cd test2 && git apply --check --index ../test.patch)'
+test_expect_success 'gitdir required mode on unsupported repo' '
+       (cd test2 && ! git apply --check --index ../test.patch)
+'
 
 test_done
index 71ab2dd0eedd5fa79300fe84fbd168b538dfd36c..78cd41245b301e731dc0fdd0066b3d3bac4f5405 100755 (executable)
@@ -51,23 +51,23 @@ test_expect_success \
         test $B"' = $(cat .git/'"$m"')'
 rm -f .git/$m
 
-test_expect_failure \
-       '(not) create HEAD with old sha1' \
-       "git update-ref HEAD $A $B"
-test_expect_failure \
-       "(not) prior created .git/$m" \
-       "test -f .git/$m"
+test_expect_success '(not) create HEAD with old sha1' "
+       ! git update-ref HEAD $A $B
+"
+test_expect_success "(not) prior created .git/$m" "
+       ! test -f .git/$m
+"
 rm -f .git/$m
 
 test_expect_success \
        "create HEAD" \
        "git update-ref HEAD $A"
-test_expect_failure \
-       '(not) change HEAD with wrong SHA1' \
-       "git update-ref HEAD $B $Z"
-test_expect_failure \
-       "(not) changed .git/$m" \
-       "test $B"' = $(cat .git/'"$m"')'
+test_expect_success '(not) change HEAD with wrong SHA1' "
+       ! git update-ref HEAD $B $Z
+"
+test_expect_success "(not) changed .git/$m" "
+       ! test $B"' = $(cat .git/'"$m"')
+'
 rm -f .git/$m
 
 : a repository with working tree always has reflog these days...
index ac84335b0a47fe1d26794e4c92f00d0ed051e540..5141fab7cf567fc5d16a8e9a296d2aff5f6c67e6 100755 (executable)
@@ -36,9 +36,9 @@ mkdir path0
 date >path0/file0
 date >path1
 
-test_expect_failure \
+test_expect_success \
     'git checkout-index without -f should fail on conflicting work tree.' \
-    'git checkout-index -a'
+    'git checkout-index -a'
 
 test_expect_success \
     'git checkout-index with -f should succeed.' \
index f7a00559209872fab5c79896ed5bc71ba64c884e..0f441bcef768ca68ead9bfa2d06b714b8d2c35ac 100755 (executable)
@@ -16,12 +16,12 @@ echo frotz >path0 &&
 git update-index --add path0 &&
 t=$(git write-tree)'
 
-test_expect_failure \
+test_expect_success \
 'without -u, git checkout-index smudges stat information.' '
 rm -f path0 &&
 git read-tree $t &&
 git checkout-index -f -a &&
-git diff-files | diff - /dev/null'
+git diff-files | diff - /dev/null'
 
 test_expect_success \
 'with -u, git checkout-index picks up stat information from new files.' '
index f78945ed8e8fe5fea3c2656460a120363c546185..4a723dc0e564d3b9f8b38a4f85d1b2e917a6edda 100755 (executable)
@@ -67,16 +67,16 @@ test_expect_success 'checkout with simple prefix' '
 
 '
 
-test_expect_failure 'relative path outside tree should fail' \
-       'git checkout HEAD -- ../../Makefile'
+test_expect_success 'relative path outside tree should fail' \
+       'git checkout HEAD -- ../../Makefile'
 
-test_expect_failure 'incorrect relative path to file should fail (1)' \
-       'git checkout HEAD -- ../file0'
+test_expect_success 'incorrect relative path to file should fail (1)' \
+       'git checkout HEAD -- ../file0'
 
-test_expect_failure 'incorrect relative path should fail (2)' \
-       '( cd dir1 && git checkout HEAD -- ./file0 )'
+test_expect_success 'incorrect relative path should fail (2)' \
+       '( cd dir1 && git checkout HEAD -- ./file0 )'
 
-test_expect_failure 'incorrect relative path should fail (3)' \
-       '( cd dir1 && git checkout HEAD -- ../../file0 )'
+test_expect_success 'incorrect relative path should fail (3)' \
+       '( cd dir1 && git checkout HEAD -- ../../file0 )'
 
 test_done
index 04a1ed1a6b9dd4eabc2b95d348b77b0fd08b0da4..9beaecd18b25cb7e22b09c11234c05a8fa1d4116 100755 (executable)
@@ -44,8 +44,8 @@ date >path1/file1
 
 for p in path0/file0 path1/file1 path2 path3
 do
-       test_expect_failure \
+       test_expect_success \
            "git update-index to add conflicting path $p should fail." \
-           "git update-index --add -- $p"
+           "git update-index --add -- $p"
 done
 test_done
index c83f820ad2d8588b8e5e15b3cb55172b26b7c33e..f4da869932e40429f94d19a9cf2e18dc1e838f0a 100755 (executable)
@@ -15,9 +15,9 @@ touch foo bar
 git update-index --add foo bar
 git-commit -m "add foo bar"
 
-test_expect_failure \
+test_expect_success \
     'git ls-files --error-unmatch should fail with unmatched path.' \
-    'git ls-files --error-unmatch foo bar-does-not-match'
+    'git ls-files --error-unmatch foo bar-does-not-match'
 
 test_expect_success \
     'git ls-files --error-unmatch should succeed eith matched paths.' \
index ef1eeb7d8ac349821559dd358470f10ae0531739..d21081d0f19bc52e1f1da54c220bc30a026f1093 100755 (executable)
@@ -17,10 +17,11 @@ test_expect_success \
      git-commit -m "Initial commit." &&
      HEAD=$(git rev-parse --verify HEAD)'
 
-test_expect_failure \
-    'git branch --help should not have created a bogus branch' \
-    'git branch --help </dev/null >/dev/null 2>/dev/null || :
-     test -f .git/refs/heads/--help'
+test_expect_success \
+    'git branch --help should not have created a bogus branch' '
+     git branch --help </dev/null >/dev/null 2>/dev/null;
+     ! test -f .git/refs/heads/--help
+'
 
 test_expect_success \
     'git branch abc should create a branch' \
@@ -71,17 +72,17 @@ test_expect_success \
         git branch -m n/n n
         test -f .git/logs/refs/heads/n'
 
-test_expect_failure \
-    'git branch -m o/o o should fail when o/p exists' \
-       'git branch o/o &&
+test_expect_success 'git branch -m o/o o should fail when o/p exists' '
+       git branch o/o &&
         git branch o/p &&
-        git branch -m o/o o'
+       ! git branch -m o/o o
+'
 
-test_expect_failure \
-    'git branch -m q r/q should fail when r exists' \
-       'git branch q &&
-         git branch r &&
-         git branch -m q r/q'
+test_expect_success 'git branch -m q r/q should fail when r exists' '
+       git branch q &&
+       git branch r &&
+       ! git branch -m q r/q
+'
 
 mv .git/config .git/config-saved
 
@@ -108,12 +109,13 @@ test_expect_success 'config information was renamed, too' \
        "test $(git config branch.s.dummy) = Hello &&
         ! git config branch.s/s/dummy"
 
-test_expect_failure \
-    'git branch -m u v should fail when the reflog for u is a symlink' \
-    'git branch -l u &&
+test_expect_success \
+    'git branch -m u v should fail when the reflog for u is a symlink' '
+     git branch -l u &&
      mv .git/logs/refs/heads/u real-u &&
      ln -s real-u .git/logs/refs/heads/u &&
-     git branch -m u v'
+     ! git branch -m u v
+'
 
 test_expect_success 'test tracking setup via --track' \
     'git config remote.local.url . &&
index 4ddc6342a94b9e7b39153cf5ac93ae3c3fb3c31e..b64ccfbc5bcf40717f8e04bdadc841bc8cd6c51f 100755 (executable)
@@ -39,12 +39,12 @@ test_expect_success \
      git show-ref b >result &&
      diff expect result'
 
-test_expect_failure \
-    'git branch c/d should barf if branch c exists' \
-    'git branch c &&
+test_expect_success 'git branch c/d should barf if branch c exists' '
+     git branch c &&
      git pack-refs --all &&
-     rm .git/refs/heads/c &&
-     git branch c/d'
+     rm -f .git/refs/heads/c &&
+     ! git branch c/d
+'
 
 test_expect_success \
     'see if a branch still exists after git pack-refs --prune' \
@@ -54,11 +54,11 @@ test_expect_success \
      git show-ref e >result &&
      diff expect result'
 
-test_expect_failure \
-    'see if git pack-refs --prune remove ref files' \
-    'git branch f &&
+test_expect_success 'see if git pack-refs --prune remove ref files' '
+     git branch f &&
      git pack-refs --all --prune &&
-     ls .git/refs/heads/f'
+     ! test -f .git/refs/heads/f
+'
 
 test_expect_success \
     'git branch g should work when git branch g/h has been deleted' \
@@ -69,11 +69,11 @@ test_expect_success \
      git pack-refs --all &&
      git branch -d g'
 
-test_expect_failure \
-    'git branch i/j/k should barf if branch i exists' \
-    'git branch i &&
+test_expect_success 'git branch i/j/k should barf if branch i exists' '
+     git branch i &&
      git pack-refs --all --prune &&
-     git branch i/j/k'
+     ! git branch i/j/k
+'
 
 test_expect_success \
     'test git branch k after branch k/l/m and k/lm have been deleted' \
index 95e33b52100bb360ab3fcfa1b419b66321340b8a..496f4ec17217769228954116528b0fc0c1ef2a62 100755 (executable)
@@ -42,9 +42,9 @@ test_expect_success \
 test_expect_success 'rebase against master' '
      git rebase master'
 
-test_expect_failure \
+test_expect_success \
     'the rebase operation should not have destroyed author information' \
-    'git log | grep "Author:" | grep "<>"'
+    'git log | grep "Author:" | grep "<>"'
 
 test_expect_success 'rebase after merge master' '
      git reset --hard topic &&
index 657f68104d52558668119234a0637ac2bca33c0a..0a26099658f4307f06fe594feb3fc046ff267076 100755 (executable)
@@ -31,8 +31,8 @@ test_expect_success setup '
        git branch skip-merge skip-reference
        '
 
-test_expect_failure 'rebase with git am -3 (default)' '
-       git rebase master
+test_expect_success 'rebase with git am -3 (default)' '
+       git rebase master
 '
 
 test_expect_success 'rebase --skip with am -3' '
@@ -53,7 +53,7 @@ test_expect_success 'rebase moves back to skip-reference' '
 
 test_expect_success 'checkout skip-merge' 'git checkout -f skip-merge'
 
-test_expect_failure 'rebase with --merge' 'git rebase --merge master'
+test_expect_success 'rebase with --merge' '! git rebase --merge master'
 
 test_expect_success 'rebase --skip with --merge' '
        git rebase --skip
index b1ee622ef7887407c93bc55d95827f9eb7a2b951..f542f0af41989aee51f75b9844abf97943d9e33f 100755 (executable)
@@ -59,15 +59,16 @@ test_expect_success \
      echo "other content" > foo
      git rm --cached foo'
 
-test_expect_failure \
-    'Test that git rm --cached foo fails if the index matches neither the file nor HEAD' \
-    'echo content > foo
+test_expect_success \
+    'Test that git rm --cached foo fails if the index matches neither the file nor HEAD' '
+     echo content > foo
      git add foo
      git commit -m foo
      echo "other content" > foo
      git add foo
      echo "yet another content" > foo
-     git rm --cached foo'
+     ! git rm --cached foo
+'
 
 test_expect_success \
     'Test that git rm --cached -f foo works in case where --cached only did not' \
@@ -106,9 +107,9 @@ embedded'"
 
 if test "$test_failed_remove" = y; then
 chmod a-w .
-test_expect_failure \
+test_expect_success \
     'Test that "git rm -f" fails if its rm fails' \
-    'git rm -f baz'
+    'git rm -f baz'
 chmod 775 .
 else
     test_expect_success 'skipping removal failure (perhaps running as root?)' :
@@ -212,8 +213,8 @@ test_expect_success 'Recursive with -r -f' '
        ! test -d frotz
 '
 
-test_expect_failure 'Remove nonexistent file returns nonzero exit status' '
-       git rm nonexistent
+test_expect_success 'Remove nonexistent file returns nonzero exit status' '
+       git rm nonexistent
 '
 
 test_done
index 74f06ec730c0bdf34b6197b6cd6e6b774055aab8..7c25634fc2962fc7b71d7d34ea6ac8d6c8061559 100755 (executable)
@@ -46,21 +46,25 @@ test_expect_success 'stat binary diff (copy) -- should not fail.' \
        'git-checkout master
         git apply --stat --summary C.diff'
 
-test_expect_failure 'check binary diff -- should fail.' \
-       'git-checkout master
-        git apply --check B.diff'
-
-test_expect_failure 'check binary diff (copy) -- should fail.' \
-       'git-checkout master
-        git apply --check C.diff'
-
-test_expect_failure 'check incomplete binary diff with replacement -- should fail.' \
-       'git-checkout master
-        git apply --check --allow-binary-replacement B.diff'
+test_expect_success 'check binary diff -- should fail.' \
+       'git-checkout master &&
+        ! git apply --check B.diff'
+
+test_expect_success 'check binary diff (copy) -- should fail.' \
+       'git-checkout master &&
+        ! git apply --check C.diff'
+
+test_expect_success \
+       'check incomplete binary diff with replacement -- should fail.' '
+       git-checkout master &&
+       ! git apply --check --allow-binary-replacement B.diff
+'
 
-test_expect_failure 'check incomplete binary diff with replacement (copy) -- should fail.' \
-       'git-checkout master
-        git apply --check --allow-binary-replacement C.diff'
+test_expect_success \
+    'check incomplete binary diff with replacement (copy) -- should fail.' '
+        git-checkout master &&
+        ! git apply --check --allow-binary-replacement C.diff
+'
 
 test_expect_success 'check binary diff with replacement.' \
        'git-checkout master
@@ -73,42 +77,42 @@ test_expect_success 'check binary diff with replacement (copy).' \
 # Now we start applying them.
 
 do_reset () {
-       rm -f file?
-       git-reset --hard
+       rm -f file? &&
+       git-reset --hard &&
        git-checkout -f master
 }
 
-test_expect_failure 'apply binary diff -- should fail.' \
-       'do_reset
-        git apply B.diff'
+test_expect_success 'apply binary diff -- should fail.' \
+       'do_reset &&
+        git apply B.diff'
 
-test_expect_failure 'apply binary diff -- should fail.' \
-       'do_reset
-        git apply --index B.diff'
+test_expect_success 'apply binary diff -- should fail.' \
+       'do_reset &&
+        git apply --index B.diff'
 
-test_expect_failure 'apply binary diff (copy) -- should fail.' \
-       'do_reset
-        git apply C.diff'
+test_expect_success 'apply binary diff (copy) -- should fail.' \
+       'do_reset &&
+        git apply C.diff'
 
-test_expect_failure 'apply binary diff (copy) -- should fail.' \
-       'do_reset
-        git apply --index C.diff'
+test_expect_success 'apply binary diff (copy) -- should fail.' \
+       'do_reset &&
+        git apply --index C.diff'
 
 test_expect_success 'apply binary diff without replacement.' \
-       'do_reset
+       'do_reset &&
         git apply BF.diff'
 
 test_expect_success 'apply binary diff without replacement (copy).' \
-       'do_reset
+       'do_reset &&
         git apply CF.diff'
 
 test_expect_success 'apply binary diff.' \
-       'do_reset
+       'do_reset &&
         git apply --allow-binary-replacement --index BF.diff &&
         test -z "$(git diff --name-status binary)"'
 
 test_expect_success 'apply binary diff (copy).' \
-       'do_reset
+       'do_reset &&
         git apply --allow-binary-replacement --index CF.diff &&
         test -z "$(git diff --name-status binary)"'
 
index 1c6bec044a00faf24e275280e0b9fa667356f2b3..d74103988201b0c189e7a2564bfb0894e434c056 100755 (executable)
@@ -29,8 +29,8 @@ test_expect_success setup \
 
 # test
 
-test_expect_failure 'apply at the end' \
-    'git apply --index test-patch'
+test_expect_success 'apply at the end' \
+    'git apply --index test-patch'
 
 cat >test-patch <<\EOF
 diff a/file b/file
@@ -47,7 +47,7 @@ b
 c'
 git update-index file
 
-test_expect_failure 'apply at the beginning' \
-       'git apply --index test-patch'
+test_expect_success 'apply at the beginning' \
+       'git apply --index test-patch'
 
 test_done
index 6e594bf1e211e246cdc02ab22d7be4a9da91d5d2..cd3c149800395553cc973317ef41e89e53771f60 100755 (executable)
@@ -264,8 +264,14 @@ test_expect_success \
      cp -f     .git/objects/9d/235ed07cd19811a6ceb342de82f190e49c9f68 \
                .git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67'
 
-test_expect_failure \
+test_expect_success \
     'make sure index-pack detects the SHA1 collision' \
-    'git-index-pack -o bad.idx test-3.pack'
+    '! git-index-pack -o bad.idx test-3.pack'
+
+test_expect_success \
+    'honor pack.packSizeLimit' \
+    'git config pack.packSizeLimit 200 &&
+     packname_4=$(git pack-objects test-4 <obj-list) &&
+     test 3 = $(ls test-4-*.pack | wc -l)'
 
 test_done
index 2a2878b57229016ad473ccfd65ff7f609ba7d966..67b9a7b84a6a411d44b6b5f90f7b33d6160b5702 100755 (executable)
@@ -42,9 +42,9 @@ test_expect_success \
     'both packs should be identical' \
     'cmp "test-1-${pack1}.pack" "test-2-${pack2}.pack"'
 
-test_expect_failure \
+test_expect_success \
     'index v1 and index v2 should be different' \
-    'cmp "test-1-${pack1}.idx" "test-2-${pack2}.idx"'
+    'cmp "test-1-${pack1}.idx" "test-2-${pack2}.idx"'
 
 test_expect_success \
     'index-pack with index version 1' \
@@ -78,9 +78,9 @@ test_expect_success \
     'git verify-pack -v "test-3-${pack3}.pack"'
 
 test "$have_64bits" &&
-test_expect_failure \
+test_expect_success \
     '64-bit offsets: should be different from previous index v2 results' \
-    'cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"'
+    'cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"'
 
 test "$have_64bits" &&
 test_expect_success \
@@ -112,22 +112,22 @@ test_expect_success \
          bs=1 count=20 conv=notrunc &&
        git cat-file blob "$delta_sha1" > blob_2 )'
 
-test_expect_failure \
+test_expect_success \
     '[index v1] 3) corrupted delta happily returned wrong data' \
-    'cmp blob_1 blob_2'
+    'cmp blob_1 blob_2'
 
-test_expect_failure \
+test_expect_success \
     '[index v1] 4) confirm that the pack is actually corrupted' \
-    'git fsck --full $commit'
+    'git fsck --full $commit'
 
 test_expect_success \
     '[index v1] 5) pack-objects happily reuses corrupted data' \
     'pack4=$(git pack-objects test-4 <obj-list) &&
      test -f "test-4-${pack1}.pack"'
 
-test_expect_failure \
+test_expect_success \
     '[index v1] 6) newly created pack is BAD !' \
-    'git verify-pack -v "test-4-${pack1}.pack"'
+    'git verify-pack -v "test-4-${pack1}.pack"'
 
 test_expect_success \
     '[index v2] 1) stream pack to repository' \
@@ -150,16 +150,16 @@ test_expect_success \
          bs=1 count=20 conv=notrunc &&
        git cat-file blob "$delta_sha1" > blob_4 )'
 
-test_expect_failure \
+test_expect_success \
     '[index v2] 3) corrupted delta happily returned wrong data' \
-    'cmp blob_3 blob_4'
+    'cmp blob_3 blob_4'
 
-test_expect_failure \
+test_expect_success \
     '[index v2] 4) confirm that the pack is actually corrupted' \
-    'git fsck --full $commit'
+    'git fsck --full $commit'
 
-test_expect_failure \
+test_expect_success \
     '[index v2] 5) pack-objects refuses to reuse corrupted data' \
-    'git pack-objects test-5 <obj-list'
+    'git pack-objects test-5 <obj-list'
 
 test_done
index 9734fc542f821c3659321be4c9ffa76ffd88a888..9a12024241aea21b6ebdbe692b2d7c173d4d2579 100755 (executable)
@@ -60,8 +60,8 @@ echo STDERR post-update >&2
 EOF
 chmod u+x victim/.git/hooks/post-update
 
-test_expect_failure push '
-       git-send-pack --force ./victim/.git master tofail >send.out 2>send.err
+test_expect_success push '
+    ! git-send-pack --force ./victim/.git master tofail >send.out 2>send.err
 '
 
 test_expect_success 'updated as expected' '
@@ -112,8 +112,8 @@ test_expect_success 'all *-receive hook args are empty' '
        ! test -s victim/.git/post-receive.args
 '
 
-test_expect_failure 'send-pack produced no output' '
-       test -s send.out
+test_expect_success 'send-pack produced no output' '
+       test -s send.out
 '
 
 cat <<EOF >expect
index 1c4b0b32ab90b2af0b08521045d4bfb1d5609b4d..1394047a8dc3e87476e223db42936d59845f803b 100755 (executable)
@@ -30,9 +30,9 @@ EOF
     chmod u+x clone${clone}/.git/hooks/post-merge
 done
 
-test_expect_failure 'post-merge does not run for up-to-date ' '
+test_expect_success 'post-merge does not run for up-to-date ' '
         GIT_DIR=clone1/.git git merge $commit0 &&
-       test -e clone1/.git/post-merge.args
+       ! test -f clone1/.git/post-merge.args
 '
 
 test_expect_success 'post-merge runs as expected ' '
index 7b6798d8b50f878c8957a60c058f6ad307f72789..788b4a5aae17d33ad688446bb60973952c3ca918 100755 (executable)
@@ -176,7 +176,7 @@ test_expect_success "deepening fetch in shallow repo" \
 test_expect_success "clone shallow object count" \
        "test \"count: 18\" = \"$(grep count count.shallow)\""
 
-test_expect_failure "pull in shallow repo with missing merge base" \
-       "(cd shallow; git pull --depth 4 .. A)"
+test_expect_success "pull in shallow repo with missing merge base" \
+       "(cd shallow && ! git pull --depth 4 .. A)"
 
 test_done
index 02882c1e4bdcef725b3575ccee6fb298f01c21b4..9b948c14e6f92e8154373154b32a6d450ead47af 100755 (executable)
@@ -95,7 +95,7 @@ test_expect_success 'fetch following tags' '
 
 '
 
-test_expect_failure 'fetch must not resolve short tag name' '
+test_expect_success 'fetch must not resolve short tag name' '
 
        cd "$D" &&
 
@@ -103,11 +103,11 @@ test_expect_failure 'fetch must not resolve short tag name' '
        cd five &&
        git init &&
 
-       git fetch .. anno:five
+       git fetch .. anno:five
 
 '
 
-test_expect_failure 'fetch must not resolve short remote name' '
+test_expect_success 'fetch must not resolve short remote name' '
 
        cd "$D" &&
        git-update-ref refs/remotes/six/HEAD HEAD
@@ -116,7 +116,7 @@ test_expect_failure 'fetch must not resolve short remote name' '
        cd six &&
        git init &&
 
-       git fetch .. six:six
+       git fetch .. six:six
 
 '
 
@@ -139,10 +139,10 @@ test_expect_success 'create bundle 2' '
        git bundle create bundle2 master~2..master
 '
 
-test_expect_failure 'unbundle 1' '
+test_expect_success 'unbundle 1' '
        cd "$D/bundle" &&
        git checkout -b some-branch &&
-       git fetch "$D/bundle1" master:master
+       git fetch "$D/bundle1" master:master
 '
 
 test_expect_success 'bundle 1 has only 3 files ' '
index cc8949e3eff7b8d7802c5cdea5eddc7c2f1f9a53..8b0509106951c5ddf2995862bc2a3887c963bfe6 100755 (executable)
@@ -26,9 +26,8 @@ test_expect_success 'setup and corrupt repository' '
 
 '
 
-test_expect_failure 'fsck fails' '
-
-       git fsck
+test_expect_success 'fsck fails' '
+       ! git fsck
 '
 
 test_expect_success 'upload-pack fails due to error in pack-objects' '
@@ -46,9 +45,8 @@ test_expect_success 'corrupt repo differently' '
 
 '
 
-test_expect_failure 'fsck fails' '
-
-       git fsck
+test_expect_success 'fsck fails' '
+       ! git fsck
 '
 test_expect_success 'upload-pack fails due to error in rev-list' '
 
@@ -66,9 +64,9 @@ test_expect_success 'create empty repository' '
 
 '
 
-test_expect_failure 'fetch fails' '
+test_expect_success 'fetch fails' '
 
-       git fetch .. master
+       git fetch .. master
 
 '
 
index 1776b377f3c787977b145980f05aa74da5038657..acf34cec8f0ce5930f48a6e31ef84b8843097d74 100755 (executable)
@@ -11,13 +11,13 @@ remove the directory before attempting a clone again.'
 
 . ./test-lib.sh
 
-test_expect_failure \
+test_expect_success \
     'clone of non-existent source should fail' \
-    'git-clone foo bar'
+    'git-clone foo bar'
 
-test_expect_failure \
+test_expect_success \
     'failed clone should not leave a directory' \
-    'cd bar'
+    '! test -d bar'
 
 # Need a repo to clone
 test_create_repo foo
@@ -27,9 +27,9 @@ test_create_repo foo
 
 # source repository given to git-clone should be relative to the
 # current path not to the target dir
-test_expect_failure \
+test_expect_success \
     'clone of non-existent (relative to $PWD) source should fail' \
-    'git-clone ../foo baz'
+    'git-clone ../foo baz'
 
 test_expect_success \
     'clone should work now that source exists' \
index 1908dc8b06aa5a5d217d70c26f64c3700d593be0..910ccb4fff561360ba7060ab8df71c0349c4207a 100755 (executable)
@@ -87,10 +87,10 @@ test_valid_repo"
 
 cd "$base_dir"
 
-test_expect_failure 'that info/alternates is necessary' \
+test_expect_success 'that info/alternates is necessary' \
 'cd C &&
-rm .git/objects/info/alternates &&
-test_valid_repo'
+rm -f .git/objects/info/alternates &&
+! (test_valid_repo)'
 
 cd "$base_dir"
 
@@ -101,9 +101,11 @@ test_valid_repo'
 
 cd "$base_dir"
 
-test_expect_failure 'that relative alternate is only possible for current dir' \
-'cd D &&
-test_valid_repo'
+test_expect_success \
+    'that relative alternate is only possible for current dir' '
+    cd D &&
+    ! (test_valid_repo)
+'
 
 cd "$base_dir"
 
diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh
new file mode 100755 (executable)
index 0000000..be3d238
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='properly cull all ancestors'
+
+. ./test-lib.sh
+
+commit () {
+       test_tick &&
+       echo $1 >file &&
+       git commit -a -m $1 &&
+       git tag $1
+}
+
+test_expect_success setup '
+
+       touch file &&
+       git add file &&
+
+       commit one &&
+
+       test_tick=$(($test_tick - 2400))
+
+       commit two &&
+       commit three &&
+       commit four &&
+
+       git log --pretty=oneline --abbrev-commit
+'
+
+test_expect_failure 'one is ancestor of others and should not be shown' '
+
+       git rev-list one --not four >result &&
+       >expect &&
+       diff -u expect result
+
+'
+
+test_done
index ae3b6f28315d54349601a5c4e162a25949b626ec..86419964b441ebcc15592b872933915472c463d2 100755 (executable)
@@ -66,8 +66,8 @@ test_expect_success "merge result added missing LF" \
        "git diff test.txt test2.txt"
 
 cp test.txt backup.txt
-test_expect_failure "merge with conflicts" \
-       "git merge-file test.txt orig.txt new3.txt"
+test_expect_success "merge with conflicts" \
+       "git merge-file test.txt orig.txt new3.txt"
 
 cat > expect.txt << EOF
 <<<<<<< test.txt
@@ -89,8 +89,8 @@ EOF
 test_expect_success "expected conflict markers" "git diff test.txt expect.txt"
 
 cp backup.txt test.txt
-test_expect_failure "merge with conflicts, using -L" \
-       "git merge-file -L 1 -L 2 test.txt orig.txt new3.txt"
+test_expect_success "merge with conflicts, using -L" \
+       "git merge-file -L 1 -L 2 test.txt orig.txt new3.txt"
 
 cat > expect.txt << EOF
 <<<<<<< 1
@@ -113,8 +113,8 @@ test_expect_success "expected conflict markers, with -L" \
        "git diff test.txt expect.txt"
 
 sed "s/ tu / TU /" < new1.txt > new5.txt
-test_expect_failure "conflict in removed tail" \
-       "git merge-file -p orig.txt new1.txt new5.txt > out"
+test_expect_success "conflict in removed tail" \
+       "git merge-file -p orig.txt new1.txt new5.txt > out"
 
 cat > expect << EOF
 Dominus regit me,
index c154f03cf5f80198b9b8d19f6b0c04db11c79965..149ea8543af153d39c8cba8c43bb3436aaaef3b4 100755 (executable)
@@ -60,7 +60,7 @@ git update-index a1 &&
 GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F
 '
 
-test_expect_failure "combined merge conflicts" "git merge -m final G"
+test_expect_success "combined merge conflicts" "! git merge -m final G"
 
 cat > expect << EOF
 <<<<<<< HEAD:a1
index 950c2e9b632f59a9405ba2100eb077836223291d..6004deb43228836f61e0c4b8762a2511a2c1780a 100755 (executable)
@@ -30,30 +30,29 @@ echo plain-file > symlink &&
 git add symlink &&
 git-commit -m b-file'
 
-test_expect_failure \
+test_expect_success \
 'merge master into b-symlink, which has a different symbolic link' '
-! git-checkout b-symlink ||
-git-merge master'
+git-checkout b-symlink &&
+git-merge master'
 
 test_expect_success \
 'the merge result must be a file' '
 test -f symlink'
 
-test_expect_failure \
+test_expect_success \
 'merge master into b-file, which has a file instead of a symbolic link' '
-! (git-reset --hard &&
-git-checkout b-file) ||
-git-merge master'
+git-reset --hard && git-checkout b-file &&
+! git-merge master'
 
 test_expect_success \
 'the merge result must be a file' '
 test -f symlink'
 
-test_expect_failure \
+test_expect_success \
 'merge b-file, which has a file instead of a symbolic link, into master' '
-! (git-reset --hard &&
-git-checkout master) ||
-git-merge b-file'
+git-reset --hard &&
+git-checkout master &&
+git-merge b-file'
 
 test_expect_success \
 'the merge result must be a file' '
index 0724864e562a53e7079c021f8b331c5b8213ac98..2328b699474cbe338def30179b07f25fa7fa357a 100755 (executable)
@@ -26,7 +26,7 @@ test_expect_success 'final^1^1^1 = final^^^' "test $(git rev-parse final^1^1^1)
 test_expect_success 'final^1^2' "test $(git rev-parse start2) = $(git rev-parse final^1^2)"
 test_expect_success 'final^1^2 != final^1^1' "test $(git rev-parse final^1^2) != $(git rev-parse final^1^1)"
 test_expect_success 'final^1^3 not valid' "if git rev-parse --verify final^1^3; then false; else :; fi"
-test_expect_failure '--verify start2^1' 'git rev-parse --verify start2^1'
+test_expect_success '--verify start2^1' '! git rev-parse --verify start2^1'
 test_expect_success '--verify start2^0' 'git rev-parse --verify start2^0'
 
 test_expect_success 'repack for next test' 'git repack -a -d'
index 8a23aaf21b1977fab66aa2989cac56635251ecb6..f46ec93c83d990c0887d3ff9d59f9d365b4d5eed 100755 (executable)
@@ -43,8 +43,8 @@ test_expect_success 'Check atom names are valid' '
        test -z "$bad"
 '
 
-test_expect_failure 'Check invalid atoms names are errors' '
-       git-for-each-ref --format="%(INVALID)" refs/heads
+test_expect_success 'Check invalid atoms names are errors' '
+       git-for-each-ref --format="%(INVALID)" refs/heads
 '
 
 test_expect_success 'Check format specifiers are ignored in naming date atoms' '
@@ -63,8 +63,8 @@ test_expect_success 'Check valid format specifiers for date fields' '
        git-for-each-ref --format="%(authordate:rfc2822)" refs/heads
 '
 
-test_expect_failure 'Check invalid format specifiers are errors' '
-       git-for-each-ref --format="%(authordate:INVALID)" refs/heads
+test_expect_success 'Check invalid format specifiers are errors' '
+       git-for-each-ref --format="%(authordate:INVALID)" refs/heads
 '
 
 cat >expected <<\EOF
index b730c900b18543cd363ceaeb2dc6f6681c61524d..b1243b4163d231bb0f29fd91fe7fa15c34463c82 100755 (executable)
@@ -78,9 +78,9 @@ test_expect_success \
      git diff-tree -r -M --name-status  HEAD^ HEAD | \
      grep "^R100..*path2/README..*path1/path2/README"'
 
-test_expect_failure \
+test_expect_success \
     'do not move directory over existing directory' \
-    'mkdir path0 && mkdir path0/path2 && git mv path2 path0'
+    'mkdir path0 && mkdir path0/path2 && git mv path2 path0'
 
 test_expect_success \
     'move into "."' \
index 68b2b92879c5e187a33d34d3daf54bb3c131e40f..c8b4f65f380f3941c75bd6ed52975777d2b28d67 100755 (executable)
@@ -107,8 +107,8 @@ do
                diff expected actual
        '
 
-        test_expect_failure "grep -c $L (no /dev/null)" '
-               git grep -c test $H | grep -q "/dev/null"
+       test_expect_success "grep -c $L (no /dev/null)" '
+               ! git grep -c test $H | grep -q /dev/null
         '
 
 done
index df496a95ff16b1a42cfa140cc3d1c4449e4023ab..75cd33bde8e5906ad68ecdcc904248745d79975c 100755 (executable)
@@ -26,8 +26,8 @@ test_expect_success 'listing all tags in an empty tree should output nothing' '
        test `git-tag | wc -l` -eq 0
 '
 
-test_expect_failure 'looking for a tag in an empty tree should fail' \
-       'tag_exists mytag'
+test_expect_success 'looking for a tag in an empty tree should fail' \
+       '! (tag_exists mytag)'
 
 test_expect_success 'creating a tag in an empty tree should fail' '
        ! git-tag mynotag &&
@@ -83,9 +83,9 @@ test_expect_success \
 
 # special cases for creating tags:
 
-test_expect_failure \
+test_expect_success \
        'trying to create a tag with the name of one existing should fail' \
-       'git tag mytag'
+       'git tag mytag'
 
 test_expect_success \
        'trying to create a tag with a non-valid name should fail' '
@@ -146,8 +146,8 @@ test_expect_success \
        ! tag_exists myhead
 '
 
-test_expect_failure 'trying to delete an already deleted tag should fail' \
-       'git-tag -d mytag'
+test_expect_success 'trying to delete an already deleted tag should fail' \
+       'git-tag -d mytag'
 
 # listing various tags with pattern matching:
 
@@ -265,16 +265,16 @@ test_expect_success \
        test $(git rev-parse non-annotated-tag) = $(git rev-parse HEAD)
 '
 
-test_expect_failure 'trying to verify an unknown tag should fail' \
-       'git-tag -v unknown-tag'
+test_expect_success 'trying to verify an unknown tag should fail' \
+       'git-tag -v unknown-tag'
 
-test_expect_failure \
+test_expect_success \
        'trying to verify a non-annotated and non-signed tag should fail' \
-       'git-tag -v non-annotated-tag'
+       'git-tag -v non-annotated-tag'
 
-test_expect_failure \
+test_expect_success \
        'trying to verify many non-annotated or unknown tags, should fail' \
-       'git-tag -v unknown-tag1 non-annotated-tag unknown-tag2'
+       'git-tag -v unknown-tag1 non-annotated-tag unknown-tag2'
 
 # creating annotated tags:
 
@@ -1027,21 +1027,21 @@ test_expect_success \
 
 # try to sign with bad user.signingkey
 git config user.signingkey BobTheMouse
-test_expect_failure \
+test_expect_success \
        'git-tag -s fails if gpg is misconfigured' \
-       'git tag -s -m tail tag-gpg-failure'
+       'git tag -s -m tail tag-gpg-failure'
 git config --unset user.signingkey
 
 # try to verify without gpg:
 
 rm -rf gpghome
-test_expect_failure \
+test_expect_success \
        'verify signed tag fails when public key is not present' \
-       'git-tag -v signed-tag'
+       'git-tag -v signed-tag'
 
-test_expect_failure \
+test_expect_success \
        'git-tag -a fails if tag annotation is empty' '
-       GIT_EDITOR=cat git tag -a initial-comment
+       ! (GIT_EDITOR=cat git tag -a initial-comment)
 '
 
 test_expect_success \
index 66d40430b293b2c1f8c7bc72416730e502c41f58..0d9874bfd7082f9ef16c1f6b3ff8a848a19d8937 100755 (executable)
@@ -36,28 +36,28 @@ test_expect_success \
     'test -d path0 &&
      test -f path0/COPYING'
 
-test_expect_failure \
+test_expect_success \
     'checking lack of path1/path2/COPYING' \
-    'test -f path1/path2/COPYING'
+    'test -f path1/path2/COPYING'
 
-test_expect_failure \
+test_expect_success \
     'checking lack of path1/COPYING' \
-    'test -f path1/COPYING'
+    'test -f path1/COPYING'
 
-test_expect_failure \
+test_expect_success \
     'checking lack of COPYING' \
-    'test -f COPYING'
+    'test -f COPYING'
 
-test_expect_failure \
+test_expect_success \
     'checking checking lack of path1/COPYING-TOO' \
-    'test -f path0/COPYING-TOO'
+    'test -f path0/COPYING-TOO'
 
-test_expect_failure \
+test_expect_success \
     'checking lack of path1/path2' \
-    'test -d path1/path2'
+    'test -d path1/path2'
 
-test_expect_failure \
+test_expect_success \
     'checking lack of path1' \
-    'test -d path1'
+    'test -d path1'
 
 test_done
index 73d8a00e2cca907e562c50c70a10c02e3c0e02ca..dbf1ace29ef8ad178a0ad8539e6bde30482ee60f 100755 (executable)
@@ -214,6 +214,22 @@ test_expect_success 'checkout to detach HEAD with branchname^' '
        fi
 '
 
+test_expect_success 'checkout to detach HEAD with :/message' '
+
+       git checkout -f master && git clean -f &&
+       git checkout ":/Initial" &&
+       H=$(git rev-parse --verify HEAD) &&
+       M=$(git show-ref -s --verify refs/heads/master) &&
+       test "z$H" = "z$M" &&
+       if git symbolic-ref HEAD >/dev/null 2>&1
+       then
+               echo "OOPS, HEAD is still symbolic???"
+               false
+       else
+               : happy
+       fi
+'
+
 test_expect_success 'checkout to detach HEAD with HEAD^0' '
 
        git checkout -f master && git clean -f &&
index 55043d102f575bd92ea61f3beccc350ba3e7cd4c..361886c3d62b8bf81db0be5999c1e98c4e972f6a 100755 (executable)
@@ -17,49 +17,49 @@ test_expect_success \
         git-add file && \
         git-status | grep 'Initial commit'"
 
-test_expect_failure \
+test_expect_success \
        "fail initial amend" \
-       "git-commit --amend"
+       "git-commit --amend"
 
 test_expect_success \
        "initial commit" \
        "git-commit -m initial"
 
-test_expect_failure \
+test_expect_success \
        "invalid options 1" \
-       "git-commit -m foo -m bar -F file"
+       "git-commit -m foo -m bar -F file"
 
-test_expect_failure \
+test_expect_success \
        "invalid options 2" \
-       "git-commit -C HEAD -m illegal"
+       "git-commit -C HEAD -m illegal"
 
-test_expect_failure \
+test_expect_success \
        "using paths with -a" \
        "echo King of the bongo >file &&
-       git-commit -m foo -a file"
+       git-commit -m foo -a file"
 
-test_expect_failure \
+test_expect_success \
        "using paths with --interactive" \
        "echo bong-o-bong >file &&
-       echo 7 | git-commit -m foo --interactive file"
+       echo 7 | git-commit -m foo --interactive file"
 
-test_expect_failure \
+test_expect_success \
        "using invalid commit with -C" \
-       "git-commit -C bogus"
+       "git-commit -C bogus"
 
-test_expect_failure \
+test_expect_success \
        "testing nothing to commit" \
-       "git-commit -m initial"
+       "git-commit -m initial"
 
 test_expect_success \
        "next commit" \
        "echo 'bongo bongo bongo' >file \
         git-commit -m next -a"
 
-test_expect_failure \
+test_expect_success \
        "commit message from non-existing file" \
        "echo 'more bongo: bongo bongo bongo bongo' >file && \
-        git-commit -F gah -a"
+        git-commit -F gah -a"
 
 # Empty except stray tabs and spaces on a few lines.
 sed -e 's/@$//' >msg <<EOF
@@ -68,9 +68,9 @@ sed -e 's/@$//' >msg <<EOF
   @
 Signed-off-by: hula
 EOF
-test_expect_failure \
+test_expect_success \
        "empty commit message" \
-       "git-commit -F msg -a"
+       "git-commit -F msg -a"
 
 test_expect_success \
        "commit message from file" \
@@ -88,10 +88,10 @@ test_expect_success \
        "amend commit" \
        "VISUAL=./editor git-commit --amend"
 
-test_expect_failure \
+test_expect_success \
        "passing -m and -F" \
        "echo 'enough with the bongos' >file && \
-        git-commit -F msg -m amending ."
+        git-commit -F msg -m amending ."
 
 test_expect_success \
        "using message from other commit" \
index d787cac2f7c09c0d84cb4cdfdf68401b660d3c6c..2dd5a5e30279e6c3e5ac2be9425c19328a65aff1 100755 (executable)
@@ -52,11 +52,11 @@ cat > "$HOOK" <<EOF
 exit 1
 EOF
 
-test_expect_failure 'with failing hook' '
+test_expect_success 'with failing hook' '
 
        echo "another" >> file &&
        git add file &&
-       git commit -m "another"
+       git commit -m "another"
 
 '
 
index 751b11300bb887d552697879201217ca1dcff648..eff36aaee32200075f815de43af781cb115ea38e 100755 (executable)
@@ -98,20 +98,20 @@ cat > "$HOOK" <<EOF
 exit 1
 EOF
 
-test_expect_failure 'with failing hook' '
+test_expect_success 'with failing hook' '
 
        echo "another" >> file &&
        git add file &&
-       git commit -m "another"
+       git commit -m "another"
 
 '
 
-test_expect_failure 'with failing hook (editor)' '
+test_expect_success 'with failing hook (editor)' '
 
        echo "more another" >> file &&
        git add file &&
        echo "more another" > FAKE_MSG &&
-       GIT_EDITOR="$FAKE_EDITOR" git commit
+       ! (GIT_EDITOR="$FAKE_EDITOR" git commit)
 
 '
 
index 614cf50d195bb3b055fd8166d425eeffa7106509..4e24ab3a7db96d396c108dd0cbe677aa067c9460 100755 (executable)
@@ -56,19 +56,19 @@ test_expect_success "$name" "
 
 
 name='detect node change from file to directory #1'
-test_expect_failure "$name" "
+test_expect_success "$name" "
        mkdir dir/new_file &&
        mv dir/file dir/new_file/file &&
        mv dir/new_file dir/file &&
        git update-index --remove dir/file &&
        git update-index --add dir/file/file &&
-       git commit -m '$name'  &&
-       git-svn set-tree --find-copies-harder --rmdir \
+       git commit -m '$name' &&
+       git-svn set-tree --find-copies-harder --rmdir \
                remotes/git-svn..mybranch" || true
 
 
 name='detect node change from directory to file #1'
-test_expect_failure "$name" "
+test_expect_success "$name" "
        rm -rf dir '$GIT_DIR'/index &&
        git checkout -f -b mybranch2 remotes/git-svn &&
        mv bar/zzz zzz &&
@@ -77,12 +77,12 @@ test_expect_failure "$name" "
        git update-index --remove -- bar/zzz &&
        git update-index --add -- bar &&
        git commit -m '$name' &&
-       git-svn set-tree --find-copies-harder --rmdir \
+       git-svn set-tree --find-copies-harder --rmdir \
                remotes/git-svn..mybranch2" || true
 
 
 name='detect node change from file to directory #2'
-test_expect_failure "$name" "
+test_expect_success "$name" "
        rm -f '$GIT_DIR'/index &&
        git checkout -f -b mybranch3 remotes/git-svn &&
        rm bar/zzz &&
@@ -91,12 +91,12 @@ test_expect_failure "$name" "
        echo yyy > bar/zzz/yyy &&
        git update-index --add bar/zzz/yyy &&
        git commit -m '$name' &&
-       git-svn set-tree --find-copies-harder --rmdir \
+       git-svn set-tree --find-copies-harder --rmdir \
                remotes/git-svn..mybranch3" || true
 
 
 name='detect node change from directory to file #2'
-test_expect_failure "$name" "
+test_expect_success "$name" "
        rm -f '$GIT_DIR'/index &&
        git checkout -f -b mybranch4 remotes/git-svn &&
        rm -rf dir &&
@@ -105,7 +105,7 @@ test_expect_failure "$name" "
        echo asdf > dir &&
        git update-index --add -- dir &&
        git commit -m '$name' &&
-       git-svn set-tree --find-copies-harder --rmdir \
+       git-svn set-tree --find-copies-harder --rmdir \
                remotes/git-svn..mybranch4" || true
 
 
@@ -213,18 +213,18 @@ EOF
 
 test_expect_success "$name" "git diff a expected"
 
-test_expect_failure 'exit if remote refs are ambigious' "
+test_expect_success 'exit if remote refs are ambigious' "
         git config --add svn-remote.svn.fetch \
                               bar:refs/remotes/git-svn &&
-        git-svn migrate
-        "
+       ! git-svn migrate
+"
 
-test_expect_failure 'exit if init-ing a would clobber a URL' "
+test_expect_success 'exit if init-ing a would clobber a URL' "
         svnadmin create ${PWD}/svnrepo2 &&
         svn mkdir -m 'mkdir bar' ${svnrepo}2/bar &&
         git config --unset svn-remote.svn.fetch \
                                 '^bar:refs/remotes/git-svn$' &&
-        git-svn init ${svnrepo}2/bar
+       ! git-svn init ${svnrepo}2/bar
         "
 
 test_expect_success \
index 79b7968eaf4d4bfa3673edf78e31ca43b54becb3..f74ab1269e3fa79a34aa9f9a3a64df08728e6354 100755 (executable)
@@ -24,11 +24,11 @@ test_expect_success 'commit change from svn side' "
        rm -rf t.svn
        "
 
-test_expect_failure 'commit conflicting change from git' "
+test_expect_success 'commit conflicting change from git' "
        echo second line from git >> file &&
        git commit -a -m 'second line from git' &&
-       git-svn commit-diff -r1 HEAD~1 HEAD $svnrepo
-       " || true
+       git-svn commit-diff -r1 HEAD~1 HEAD $svnrepo
+"
 
 test_expect_success 'commit complementing change from git' "
        git reset --hard HEAD~1 &&
@@ -39,7 +39,7 @@ test_expect_success 'commit complementing change from git' "
        git-svn commit-diff -r2 HEAD~1 HEAD $svnrepo
        "
 
-test_expect_failure 'dcommit fails to commit because of conflict' "
+test_expect_success 'dcommit fails to commit because of conflict' "
        git-svn init $svnrepo &&
        git-svn fetch &&
        git reset --hard refs/remotes/git-svn &&
@@ -52,8 +52,8 @@ test_expect_failure 'dcommit fails to commit because of conflict' "
        rm -rf t.svn &&
        echo 'fourth line from git' >> file &&
        git commit -a -m 'fourth line from git' &&
-       git-svn dcommit
-       " || true
+       git-svn dcommit
+       "
 
 test_expect_success 'dcommit does the svn equivalent of an index merge' "
        git reset --hard refs/remotes/git-svn &&
@@ -76,15 +76,15 @@ test_expect_success 'commit another change from svn side' "
        rm -rf t.svn
        "
 
-test_expect_failure 'multiple dcommit from git-svn will not clobber svn' "
+test_expect_success 'multiple dcommit from git-svn will not clobber svn' "
        git reset --hard refs/remotes/git-svn &&
        echo new file >> new-file &&
        git update-index --add new-file &&
        git commit -a -m 'new file' &&
        echo clobber > file &&
        git commit -a -m 'clobber' &&
-       git svn dcommit
-       " || true
+       git svn dcommit
+       "
 
 
 test_expect_success 'check that rebase really failed' 'test -d .dotest'
index 745254665dd2d8f73b8c511b39aca82682bf1bbb..ca8a00ed0af0382c20a4883e5aa5f1914b6ecd3f 100755 (executable)
@@ -54,10 +54,10 @@ test_expect_success 'change file but in unrelated area' "
                test x\"\`sed -n -e 61p < file\`\" = x6611
        "
 
-test_expect_failure 'attempt to dcommit with a dirty index' '
+test_expect_success 'attempt to dcommit with a dirty index' '
        echo foo >>file &&
        git add file &&
-       git svn dcommit
+       git svn dcommit
 '
 
 test_done
index a15222ced4d75e70f49df08159cff9dd8d4167a4..49d57a81ec1cc55f83fb63436631dfdc7d647e0b 100755 (executable)
@@ -2,7 +2,7 @@
 #
 # Copyright (c) Robin Rosenberg
 #
-test_description='CVS export comit. '
+test_description='Test export of commits to CVS'
 
 . ./test-lib.sh
 
@@ -246,4 +246,20 @@ test_expect_success \
        ;;
 esac
 
+test_expect_success '-w option should work with relative GIT_DIR' '
+      mkdir W &&
+      echo foobar >W/file1.txt &&
+      echo bazzle >W/file2.txt &&
+      git add W/file1.txt &&
+      git add W/file2.txt &&
+      git commit -m "More updates" &&
+      id=$(git rev-list --max-count=1 HEAD) &&
+      (cd "$GIT_DIR" &&
+      GIT_DIR=. git cvsexportcommit -w "$CVSWORK" -c $id &&
+      check_entries "$CVSWORK/W" "file1.txt/1.1/|file2.txt/1.1/" &&
+      diff -u "$CVSWORK/W/file1.txt" ../W/file1.txt &&
+      diff -u "$CVSWORK/W/file2.txt" ../W/file2.txt
+      )
+'
+
 test_done
index 0595041af5d310f905306c6a289945cde26d88fc..cceedbb2b7efc20b79155889ed20c5a7866fed9c 100755 (executable)
@@ -165,9 +165,9 @@ from refs/heads/master
 M 755 0000000000000000000000000000000000000001 zero1
 
 INPUT_END
-test_expect_failure \
-    'B: fail on invalid blob sha1' \
-    'git-fast-import <input'
+test_expect_success 'B: fail on invalid blob sha1' '
+    ! git-fast-import <input
+'
 rm -f .git/objects/pack_* .git/objects/index_*
 
 cat >input <<INPUT_END
@@ -180,9 +180,9 @@ COMMIT
 from refs/heads/master
 
 INPUT_END
-test_expect_failure \
-    'B: fail on invalid branch name ".badbranchname"' \
-    'git-fast-import <input'
+test_expect_success 'B: fail on invalid branch name ".badbranchname"' '
+    ! git-fast-import <input
+'
 rm -f .git/objects/pack_* .git/objects/index_*
 
 cat >input <<INPUT_END
@@ -195,9 +195,9 @@ COMMIT
 from refs/heads/master
 
 INPUT_END
-test_expect_failure \
-    'B: fail on invalid branch name "bad[branch]name"' \
-    'git-fast-import <input'
+test_expect_success 'B: fail on invalid branch name "bad[branch]name"' '
+    ! git-fast-import <input
+'
 rm -f .git/objects/pack_* .git/objects/index_*
 
 cat >input <<INPUT_END
@@ -339,9 +339,9 @@ COMMIT
 from refs/heads/branch^0
 
 INPUT_END
-test_expect_failure \
-    'E: rfc2822 date, --date-format=raw' \
-    'git-fast-import --date-format=raw <input'
+test_expect_success 'E: rfc2822 date, --date-format=raw' '
+    ! git-fast-import --date-format=raw <input
+'
 test_expect_success \
     'E: rfc2822 date, --date-format=rfc2822' \
     'git-fast-import --date-format=rfc2822 <input'
index 75d1ce433d3110e6fe24be00aef8dcc7592d8082..0a20971ebb693250ab1fa78cdd9ec269e7129f6e 100755 (executable)
@@ -156,15 +156,19 @@ test_expect_success 'req_Root (strict paths)' \
   'cat request-anonymous | git-cvsserver --strict-paths pserver $SERVERDIR >log 2>&1 &&
    tail -n1 log | grep -q "^I LOVE YOU$"'
 
-test_expect_failure 'req_Root failure (strict-paths)' \
-  'cat request-anonymous | git-cvsserver --strict-paths pserver $WORKDIR >log 2>&1'
+test_expect_success 'req_Root failure (strict-paths)' '
+    ! cat request-anonymous |
+    git-cvsserver --strict-paths pserver $WORKDIR >log 2>&1
+'
 
 test_expect_success 'req_Root (w/o strict-paths)' \
   'cat request-anonymous | git-cvsserver pserver $WORKDIR/ >log 2>&1 &&
    tail -n1 log | grep -q "^I LOVE YOU$"'
 
-test_expect_failure 'req_Root failure (w/o strict-paths)' \
-  'cat request-anonymous | git-cvsserver pserver $WORKDIR/gitcvs >log 2>&1'
+test_expect_success 'req_Root failure (w/o strict-paths)' '
+    ! cat request-anonymous |
+    git-cvsserver pserver $WORKDIR/gitcvs >log 2>&1
+'
 
 cat >request-base  <<EOF
 BEGIN AUTH REQUEST
@@ -179,8 +183,10 @@ test_expect_success 'req_Root (base-path)' \
   'cat request-base | git-cvsserver --strict-paths --base-path $WORKDIR/ pserver $SERVERDIR >log 2>&1 &&
    tail -n1 log | grep -q "^I LOVE YOU$"'
 
-test_expect_failure 'req_Root failure (base-path)' \
-  'cat request-anonymous | git-cvsserver --strict-paths --base-path $WORKDIR pserver $SERVERDIR >log 2>&1'
+test_expect_success 'req_Root failure (base-path)' '
+    ! cat request-anonymous |
+    git-cvsserver --strict-paths --base-path $WORKDIR pserver $SERVERDIR >log 2>&1
+'
 
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1
 
@@ -188,9 +194,8 @@ test_expect_success 'req_Root (export-all)' \
   'cat request-anonymous | git-cvsserver --export-all pserver $WORKDIR >log 2>&1 &&
    tail -n1 log | grep -q "^I LOVE YOU$"'
 
-test_expect_failure 'req_Root failure (export-all w/o whitelist)' \
-  'cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 ||
-   false'
+test_expect_success 'req_Root failure (export-all w/o whitelist)' \
+  '! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
 
 test_expect_success 'req_Root (everything together)' \
   'cat request-base | git-cvsserver --export-all --strict-paths --base-path $WORKDIR/ pserver $SERVERDIR >log 2>&1 &&
@@ -290,15 +295,16 @@ test_expect_success 'cvs update (update existing file)' \
 
 cd "$WORKDIR"
 #TODO: cvsserver doesn't support update w/o -d
-test_expect_failure "cvs update w/o -d doesn't create subdir (TODO)" \
-  'mkdir test &&
+test_expect_failure "cvs update w/o -d doesn't create subdir (TODO)" '
+   mkdir test &&
    echo >test/empty &&
    git add test &&
    git commit -q -m "Single Subdirectory" &&
    git push gitcvs.git >/dev/null &&
    cd cvswork &&
    GIT_CONFIG="$git_config" cvs -Q update &&
-   test ! -d test'
+   test ! -d test
+'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (subdirectories)' \
index 142540e1b175e44da0d6e522140d404fb5a795bf..da47bd7c9eaac7f01106c290fab8364e1ce838a4 100644 (file)
@@ -139,6 +139,8 @@ fi
 
 test_failure=0
 test_count=0
+test_fixed=0
+test_broken=0
 
 trap 'echo >&5 "FATAL: Unexpected exit with code $?"; exit 1' exit
 
@@ -171,6 +173,17 @@ test_failure_ () {
        test "$immediate" = "" || { trap - exit; exit 1; }
 }
 
+test_known_broken_ok_ () {
+       test_count=$(expr "$test_count" + 1)
+       test_fixed=$(($test_fixed+1))
+       say_color "" "  FIXED $test_count: $@"
+}
+
+test_known_broken_failure_ () {
+       test_count=$(expr "$test_count" + 1)
+       test_broken=$(($test_broken+1))
+       say_color skip "  still broken $test_count: $@"
+}
 
 test_debug () {
        test "$debug" = "" || eval "$1"
@@ -211,13 +224,13 @@ test_expect_failure () {
        error "bug in the test script: not 2 parameters to test-expect-failure"
        if ! test_skip "$@"
        then
-               say >&3 "expecting failure: $2"
+               say >&3 "checking known breakage: $2"
                test_run_ "$2"
-               if [ "$?" = 0 -a "$eval_ret" != 0 -a "$eval_ret" -lt 129 ]
+               if [ "$?" = 0 -a "$eval_ret" = 0 ]
                then
-                       test_ok_ "$1"
+                       test_known_broken_ok_ "$1"
                else
-                       test_failure_ "$@"
+                   test_known_broken_failure_ "$1"
                fi
        fi
        echo >&3 ""
@@ -274,6 +287,18 @@ test_create_repo () {
 
 test_done () {
        trap - exit
+
+       if test "$test_fixed" != 0
+       then
+               say_color pass "fixed $test_fixed known breakage(s)"
+       fi
+       if test "$test_broken" != 0
+       then
+               say_color error "still have $test_broken known breakage(s)"
+               msg="remaining $(($test_count-$test_broken)) test(s)"
+       else
+               msg="$test_count test(s)"
+       fi
        case "$test_failure" in
        0)
                # We could:
@@ -284,11 +309,11 @@ test_done () {
                # The Makefile provided will clean this test area so
                # we will leave things as they are.
 
-               say_color pass "passed all $test_count test(s)"
+               say_color pass "passed all $msg"
                exit 0 ;;
 
        *)
-               say_color error "failed $test_failure among $test_count test(s)"
+               say_color error "failed $test_failure among $msg"
                exit 1 ;;
 
        esac
index 497f85372173f6f270a4c0ee9474f165bb884413..397983d1155bed967bd48ad47dbbb81cb2e45168 100644 (file)
@@ -562,6 +562,8 @@ struct git_transport_data {
        unsigned thin : 1;
        unsigned keep : 1;
        int depth;
+       struct child_process *conn;
+       int fd[2];
        const char *uploadpack;
        const char *receivepack;
 };
@@ -592,20 +594,20 @@ static int set_git_option(struct transport *connection,
        return 1;
 }
 
+static int connect_setup(struct transport *transport)
+{
+       struct git_transport_data *data = transport->data;
+       data->conn = git_connect(data->fd, transport->url, data->uploadpack, 0);
+       return 0;
+}
+
 static struct ref *get_refs_via_connect(struct transport *transport)
 {
        struct git_transport_data *data = transport->data;
        struct ref *refs;
-       int fd[2];
-       char *dest = xstrdup(transport->url);
-       struct child_process *conn = git_connect(fd, dest, data->uploadpack, 0);
 
-       get_remote_heads(fd[0], &refs, 0, NULL, 0);
-       packet_flush(fd[1]);
-
-       finish_connect(conn);
-
-       free(dest);
+       connect_setup(transport);
+       get_remote_heads(data->fd[0], &refs, 0, NULL, 0);
 
        return refs;
 }
@@ -616,7 +618,7 @@ static int fetch_refs_via_pack(struct transport *transport,
        struct git_transport_data *data = transport->data;
        char **heads = xmalloc(nr_heads * sizeof(*heads));
        char **origh = xmalloc(nr_heads * sizeof(*origh));
-       struct ref *refs;
+       const struct ref *refs;
        char *dest = xstrdup(transport->url);
        struct fetch_pack_args args;
        int i;
@@ -631,13 +633,27 @@ static int fetch_refs_via_pack(struct transport *transport,
 
        for (i = 0; i < nr_heads; i++)
                origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
-       refs = fetch_pack(&args, dest, nr_heads, heads, &transport->pack_lockfile);
+
+       refs = transport_get_remote_refs(transport);
+       if (!data->conn) {
+               struct ref *refs_tmp;
+               connect_setup(transport);
+               get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0);
+               free_refs(refs_tmp);
+       }
+
+       refs = fetch_pack(&args, data->fd, data->conn, transport->remote_refs,
+                         dest, nr_heads, heads, &transport->pack_lockfile);
+       close(data->fd[0]);
+       close(data->fd[1]);
+       if (finish_connect(data->conn))
+               refs = NULL;
+       data->conn = NULL;
 
        for (i = 0; i < nr_heads; i++)
                free(origh[i]);
        free(origh);
        free(heads);
-       free_refs(refs);
        free(dest);
        return (refs ? 0 : -1);
 }
@@ -660,7 +676,15 @@ static int git_transport_push(struct transport *transport, int refspec_nr, const
 
 static int disconnect_git(struct transport *transport)
 {
-       free(transport->data);
+       struct git_transport_data *data = transport->data;
+       if (data->conn) {
+               packet_flush(data->fd[1]);
+               close(data->fd[0]);
+               close(data->fd[1]);
+               finish_connect(data->conn);
+       }
+
+       free(data);
        return 0;
 }
 
@@ -720,6 +744,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
                ret->disconnect = disconnect_git;
 
                data->thin = 1;
+               data->conn = NULL;
                data->uploadpack = "git-upload-pack";
                if (remote && remote->uploadpack)
                        data->uploadpack = remote->uploadpack;
diff --git a/tree.c b/tree.c
index 8c0819fa721771f6dc2e727860345376edb3ea60..87708ef420aaa328c9a8dfcefbcf4aa7ba6d4b72 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -142,8 +142,8 @@ static int cmp_cache_name_compare(const void *a_, const void *b_)
 
        ce1 = *((const struct cache_entry **)a_);
        ce2 = *((const struct cache_entry **)b_);
-       return cache_name_compare(ce1->name, ntohs(ce1->ce_flags),
-                                 ce2->name, ntohs(ce2->ce_flags));
+       return cache_name_compare(ce1->name, ce1->ce_flags,
+                                 ce2->name, ce2->ce_flags);
 }
 
 int read_tree(struct tree *tree, int stage, const char **match)
index aa2513ed798969c02fc5e14097666eb4c4c02bae..ff46fd62fdceeefc2ebd98ae1c9483964cb37bd5 100644 (file)
@@ -289,7 +289,6 @@ static struct checkout state;
 static void check_updates(struct cache_entry **src, int nr,
                        struct unpack_trees_options *o)
 {
-       unsigned short mask = htons(CE_UPDATE);
        unsigned cnt = 0, total = 0;
        struct progress *progress = NULL;
        char last_symlink[PATH_MAX];
@@ -297,7 +296,7 @@ static void check_updates(struct cache_entry **src, int nr,
        if (o->update && o->verbose_update) {
                for (total = cnt = 0; cnt < nr; cnt++) {
                        struct cache_entry *ce = src[cnt];
-                       if (!ce->ce_mode || ce->ce_flags & mask)
+                       if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
                                total++;
                }
 
@@ -310,15 +309,15 @@ static void check_updates(struct cache_entry **src, int nr,
        while (nr--) {
                struct cache_entry *ce = *src++;
 
-               if (!ce->ce_mode || ce->ce_flags & mask)
+               if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
                        display_progress(progress, ++cnt);
-               if (!ce->ce_mode) {
+               if (ce->ce_flags & CE_REMOVE) {
                        if (o->update)
                                unlink_entry(ce->name, last_symlink);
                        continue;
                }
-               if (ce->ce_flags & mask) {
-                       ce->ce_flags &= ~mask;
+               if (ce->ce_flags & CE_UPDATE) {
+                       ce->ce_flags &= ~CE_UPDATE;
                        if (o->update) {
                                checkout_entry(ce, &state, NULL);
                                *last_symlink = '\0';
@@ -408,7 +407,7 @@ static void verify_uptodate(struct cache_entry *ce,
                 * submodules that are marked to be automatically
                 * checked out.
                 */
-               if (S_ISGITLINK(ntohl(ce->ce_mode)))
+               if (S_ISGITLINK(ce->ce_mode))
                        return;
                errno = 0;
        }
@@ -450,7 +449,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
        int cnt = 0;
        unsigned char sha1[20];
 
-       if (S_ISGITLINK(ntohl(ce->ce_mode)) &&
+       if (S_ISGITLINK(ce->ce_mode) &&
            resolve_gitlink_ref(ce->name, "HEAD", sha1) == 0) {
                /* If we are not going to update the submodule, then
                 * we don't care.
@@ -481,7 +480,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
                 */
                if (!ce_stage(ce)) {
                        verify_uptodate(ce, o);
-                       ce->ce_mode = 0;
+                       ce->ce_flags |= CE_REMOVE;
                }
                cnt++;
        }
@@ -568,7 +567,7 @@ static void verify_absent(struct cache_entry *ce, const char *action,
                cnt = cache_name_pos(ce->name, strlen(ce->name));
                if (0 <= cnt) {
                        struct cache_entry *ce = active_cache[cnt];
-                       if (!ce_stage(ce) && !ce->ce_mode)
+                       if (ce->ce_flags & CE_REMOVE)
                                return;
                }
 
@@ -580,7 +579,7 @@ static void verify_absent(struct cache_entry *ce, const char *action,
 static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
                struct unpack_trees_options *o)
 {
-       merge->ce_flags |= htons(CE_UPDATE);
+       merge->ce_flags |= CE_UPDATE;
        if (old) {
                /*
                 * See if we can re-use the old CE directly?
@@ -601,7 +600,7 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
                invalidate_ce_path(merge);
        }
 
-       merge->ce_flags &= ~htons(CE_STAGEMASK);
+       merge->ce_flags &= ~CE_STAGEMASK;
        add_cache_entry(merge, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
        return 1;
 }
@@ -613,7 +612,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
                verify_uptodate(old, o);
        else
                verify_absent(ce, "removed", o);
-       ce->ce_mode = 0;
+       ce->ce_flags |= CE_REMOVE;
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
        invalidate_ce_path(ce);
        return 1;
@@ -634,7 +633,7 @@ static void show_stage_entry(FILE *o,
        else
                fprintf(o, "%s%06o %s %d\t%s\n",
                        label,
-                       ntohl(ce->ce_mode),
+                       ce->ce_mode,
                        sha1_to_hex(ce->sha1),
                        ce_stage(ce),
                        ce->name);
@@ -920,7 +919,7 @@ int oneway_merge(struct cache_entry **src,
                        struct stat st;
                        if (lstat(old->name, &st) ||
                            ce_match_stat(old, &st, CE_MATCH_IGNORE_VALID))
-                               old->ce_flags |= htons(CE_UPDATE);
+                               old->ce_flags |= CE_UPDATE;
                }
                return keep_entry(old, o);
        }
index 5517faafad6ca3f327c81ab6e8e5f2701ff0ad14..197a0044aa71fc9ca310b36b279b8382487a40a7 100644 (file)
@@ -25,6 +25,7 @@ struct unpack_trees_options {
        int merge_size;
 
        struct cache_entry *df_conflict_entry;
+       void *unpack_data;
 };
 
 extern int unpack_trees(unsigned n, struct tree_desc *t,
index 991e373785bec5f2aab2d2a09c7d1cb6e3640196..0b060934e218f1d6fb95777ea7dd7faef1c2dd93 100644 (file)
@@ -217,19 +217,12 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q,
                wt_status_print_trailer(s);
 }
 
-static void wt_read_cache(struct wt_status *s)
-{
-       discard_cache();
-       read_cache_from(s->index_file);
-}
-
 static void wt_status_print_initial(struct wt_status *s)
 {
        int i;
        struct strbuf buf;
 
        strbuf_init(&buf, 0);
-       wt_read_cache(s);
        if (active_nr) {
                s->commitable = 1;
                wt_status_print_cached_header(s);
@@ -256,7 +249,6 @@ static void wt_status_print_updated(struct wt_status *s)
        rev.diffopt.detect_rename = 1;
        rev.diffopt.rename_limit = 100;
        rev.diffopt.break_opt = 0;
-       wt_read_cache(s);
        run_diff_index(&rev, 1);
 }
 
@@ -268,7 +260,6 @@ static void wt_status_print_changed(struct wt_status *s)
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = wt_status_print_changed_cb;
        rev.diffopt.format_callback_data = s;
-       wt_read_cache(s);
        run_diff_files(&rev, 0);
 }
 
@@ -335,7 +326,6 @@ static void wt_status_print_verbose(struct wt_status *s)
        setup_revisions(0, NULL, &rev, s->reference);
        rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
        rev.diffopt.detect_rename = 1;
-       wt_read_cache(s);
        run_diff_index(&rev, 1);
 
        fflush(stdout);